diff --git a/.gitignore b/.gitignore index acd33544c90dc16..4e8ee8a37f91857 100644 --- a/.gitignore +++ b/.gitignore @@ -250,3 +250,7 @@ define_sanity_output.txt # Running OpenDream locally tgstation.json + +# Ignore cached sound files. +/sound/tts_cache/**/* +/sound/tts_scrambled/**/* diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 901e2a05e15dd7e..5276841624765f0 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -12,7 +12,7 @@ #define CHANNEL_ELEVATOR 1014 //THIS SHOULD ALWAYS BE THE LOWEST ONE! //KEEP IT UPDATED -#define CHANNEL_HIGHEST_AVAILABLE 1013 +#define CHANNEL_HIGHEST_AVAILABLE 1012 //ORIGINAL: 1013 #define MAX_INSTRUMENT_CHANNELS (128 * 6) diff --git a/config/config.txt b/config/config.txt index b36e6dcb455585b..380370a77ab96b3 100644 --- a/config/config.txt +++ b/config/config.txt @@ -6,6 +6,7 @@ $include comms.txt $include logging.txt $include resources.txt $include nova/config_nova.txt +$include ss220/ss220_config.txt $include interviews.txt $include lua.txt $include auxtools.txt diff --git a/config/ss220/ss220_config.txt b/config/ss220/ss220_config.txt new file mode 100644 index 000000000000000..d7284e7bca3926f --- /dev/null +++ b/config/ss220/ss220_config.txt @@ -0,0 +1,4 @@ +## Text-to-speech +#TTS_TOKEN_SILERO mytoken +#TTS_ENABLED +#TTS_CACHE diff --git a/modular_ss220/modules/tts/code/_tts_defines.dm b/modular_ss220/modules/tts/code/_tts_defines.dm new file mode 100644 index 000000000000000..674d44c92968243 --- /dev/null +++ b/modular_ss220/modules/tts/code/_tts_defines.dm @@ -0,0 +1,78 @@ +#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 1013 + +#define TTS_TRAIT_PITCH_WHISPER (1<<1) +#define TTS_TRAIT_RATE_FASTER (1<<2) +#define TTS_TRAIT_RATE_MEDIUM (1<<3) + +#define rustg_file_write_b64decode(text, fname) RUSTG_CALL(RUST_G, "file_write")(text, fname, "true") + +// Hashing Operations // +#define rustg_hash_string(algorithm, text) RUSTG_CALL(RUST_G, "hash_string")(algorithm, text) +#define rustg_hash_file(algorithm, fname) RUSTG_CALL(RUST_G, "hash_file")(algorithm, fname) + +#define RUSTG_HASH_MD5 "md5" + +#ifdef RUSTG_OVERRIDE_BUILTINS + #define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing)) +#endif + +// Text Operations // +#define rustg_cyrillic_to_latin(text) RUSTG_CALL(RUST_G, "cyrillic_to_latin")("[text]") +#define rustg_latin_to_cyrillic(text) RUSTG_CALL(RUST_G, "latin_to_cyrillic")("[text]") + +#define TTS_CATEGORY_OTHER "Другое" +#define TTS_CATEGORY_WARCRAFT3 "WarCraft 3" +#define TTS_CATEGORY_HALFLIFE2 "Half-Life 2" +#define TTS_CATEGORY_STARCRAFT "StarCraft" +#define TTS_CATEGORY_PORTAL2 "Portal 2" +#define TTS_CATEGORY_STALKER "STALKER" +#define TTS_CATEGORY_DOTA2 "Dota 2" +#define TTS_CATEGORY_LOL "League of Legends" +#define TTS_CATEGORY_FALLOUT "Fallout" +#define TTS_CATEGORY_FALLOUT2 "Fallout 2" +#define TTS_CATEGORY_POSTAL2 "Postal 2" +#define TTS_CATEGORY_TEAMFORTRESS2 "Team Fortress 2" +#define TTS_CATEGORY_ATOMIC_HEART "Atomic Heart" +#define TTS_CATEGORY_OVERWATCH "Overwatch" +#define TTS_CATEGORY_SKYRIM "Skyrim" +#define TTS_CATEGORY_RITA "Rita" +#define TTS_CATEGORY_METRO "Metro" +#define TTS_CATEGORY_HEROESOFTHESTORM "Heroes of the Storm" +#define TTS_CATEGORY_HEARTHSTONE "Hearthstone" +#define TTS_CATEGORY_VALORANT "Valorant" +#define TTS_CATEGORY_EVILISLANDS "Evil Islands" + +#define TTS_GENDER_ANY "Любой" +#define TTS_GENDER_MALE "Мужской" +#define TTS_GENDER_FEMALE "Женский" + +#define TTS_PHRASES list(\ + "Так звучит мой голос.",\ + "Так я звучу.",\ + "Я.",\ + "Поставьте свою подпись.",\ + "Пора за работу.",\ + "Дело сделано.",\ + "Станция Нанотрейзен.",\ + "Офицер СБ.",\ + "Капитан.",\ + "Вульпканин.",\ + "Съешь же ещё этих мягких французских булок, да выпей чаю.",\ + "Клоун, прекрати разбрасывать банановые кожурки офицерам под ноги!",\ + "Капитан, вы уверены что хотите назначить клоуна на должность главы персонала?",\ + ) + +#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) + +/proc/error(msg) + log_world("## ERROR: [msg]") diff --git a/modular_ss220/modules/tts/code/providers/silero.dm b/modular_ss220/modules/tts/code/providers/silero.dm new file mode 100644 index 000000000000000..69a3788d5a158b4 --- /dev/null +++ b/modular_ss220/modules/tts/code/providers/silero.dm @@ -0,0 +1,57 @@ +/datum/tts_provider/silero + name = "Silero" + is_enabled = TRUE + +/datum/tts_provider/silero/request(text, datum/tts_seed/silero/seed, datum/callback/proc_callback) + if(throttle_check()) + return FALSE + + var/api_url = "https://api-tts.silero.ai/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 + req_body["ssml"] = TRUE + req_body["speaker"] = seed.value + req_body["lang"] = "ru" + req_body["remote_id"] = "[world.port]" + req_body["put_accent"] = TRUE + req_body["put_yo"] = FALSE + 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) + + return TRUE + +/datum/tts_provider/silero/process_response(datum/http_response/response) + var/data = json_decode(response.body) + // log_debug(response.body) + + if(data["timings"]["003_tts_time"] > 3) + is_throttled = TRUE + throttled_until = world.time + 15 SECONDS + + return data["results"][1]["audio"] + + //var/sha1 = data["original_sha1"] + +/datum/tts_provider/silero/pitch_whisper(text) + return {"[text]"} + +/datum/tts_provider/silero/rate_faster(text) + return {"[text]"} + +/datum/tts_provider/silero/rate_medium(text) + return {"[text]"} diff --git a/modular_ss220/modules/tts/code/seeds/silero.dm b/modular_ss220/modules/tts/code/seeds/silero.dm new file mode 100644 index 000000000000000..789f5503e205f58 --- /dev/null +++ b/modular_ss220/modules/tts/code/seeds/silero.dm @@ -0,0 +1,3357 @@ +/datum/tts_seed/silero + provider = /datum/tts_provider/silero + +/datum/tts_seed/silero/arthas + name = "Arthas" + value = "arthas" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/kelthuzad + name = "Kelthuzad" + value = "kelthuzad" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/anubarak + name = "Anubarak" + value = "anubarak" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/thrall + name = "Thrall" + value = "thrall" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/grunt + name = "Grunt" + value = "grunt" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/cairne + name = "Cairne" + value = "cairne" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/rexxar + name = "Rexxar" + value = "rexxar" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/uther + name = "Uther" + value = "uther" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/jaina + name = "Jaina" + value = "jaina" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/kael + name = "Kael" + value = "kael" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/garithos + name = "Garithos" + value = "garithos" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/maiev + name = "Maiev" + value = "maiev" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/naisha + name = "Naisha" + value = "naisha" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/tyrande + name = "Tyrande" + value = "tyrande" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/furion + name = "Furion" + value = "furion" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/illidan + name = "Illidan" + value = "illidan" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/ladyvashj + name = "Ladyvashj" + value = "ladyvashj" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/narrator + name = "Narrator" + value = "narrator" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/medivh + name = "Medivh" + value = "medivh" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/villagerm + name = "Villagerm" + value = "villagerm" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/xenia + name = "Xenia" + value = "xenia" + category = TTS_CATEGORY_OTHER + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/illidan_f + name = "Illidan_f" + value = "illidan_f" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/peon + name = "Peon" + value = "peon" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/chen + name = "Chen" + value = "chen" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dread_bm + name = "Dread_bm" + value = "dread_bm" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sylvanas + name = "Sylvanas" + value = "sylvanas" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/priest + name = "Priest" + value = "priest" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/acolyte + name = "Acolyte" + value = "acolyte" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/muradin + name = "Muradin" + value = "muradin" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dread_t + name = "Dread_t" + value = "dread_t" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/mannoroth + name = "Mannoroth" + value = "mannoroth" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sorceress + name = "Sorceress" + value = "sorceress" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/peasant + name = "Peasant" + value = "peasant" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/alyx + name = "Alyx" + value = "alyx" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/glados + name = "Glados" + value = "glados" + category = TTS_CATEGORY_PORTAL2 + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/announcer + name = "Announcer" + value = "announcer" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/wheatley + name = "Wheatley" + value = "wheatley" + category = TTS_CATEGORY_PORTAL2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/barney + name = "Barney" + value = "barney" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/raynor + name = "Raynor" + value = "raynor" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/kerrigan + name = "Kerrigan" + value = "kerrigan" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/tusk + name = "Tusk" + value = "tusk" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/earth + name = "Earth" + value = "earth" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/wraith + name = "Wraith" + value = "wraith" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/meepo + name = "Meepo" + value = "meepo" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/lina + name = "Lina" + value = "lina" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/bristle + name = "Bristle" + value = "bristle" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gyro + name = "Gyro" + value = "gyro" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/treant + name = "Treant" + value = "treant" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/lancer + name = "Lancer" + value = "lancer" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/clockwerk + name = "Clockwerk" + value = "clockwerk" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/batrider + name = "Batrider" + value = "batrider" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/kotl + name = "Kotl" + value = "kotl" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/kunkka + name = "Kunkka" + value = "kunkka" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/pudge + name = "Pudge" + value = "pudge" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/juggernaut + name = "Juggernaut" + value = "juggernaut" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/vort_e2 + name = "Vort_e2" + value = "vort_e2" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/luna + name = "Luna" + value = "luna" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/omni + name = "Omni" + value = "omni" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sniper + name = "Sniper" + value = "sniper" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/skywrath + name = "Skywrath" + value = "skywrath" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/bounty + name = "Bounty" + value = "bounty" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/huskar + name = "Huskar" + value = "huskar" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/windranger + name = "Windranger" + value = "windranger" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/bloodseeker + name = "Bloodseeker" + value = "bloodseeker" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/templar + name = "Templar" + value = "templar" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/ranger + name = "Ranger" + value = "ranger" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/shaker + name = "Shaker" + value = "shaker" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/mortred + name = "Mortred" + value = "mortred" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/queen + name = "Queen" + value = "queen" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/storm + name = "Storm" + value = "storm" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/tide + name = "Tide" + value = "tide" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/evelynn + name = "Evelynn" + value = "evelynn" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 4 + +/datum/tts_seed/silero/riki + name = "Riki" + value = "riki" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/antimage + name = "Antimage" + value = "antimage" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/witchdoctor + name = "Witchdoctor" + value = "witchdoctor" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/doom + name = "Doom" + value = "doom" + category = TTS_CATEGORY_DOTA2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/yuumi + name = "Yuumi" + value = "yuumi" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/bandit + name = "Bandit" + value = "bandit" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/pantheon + name = "pantheon" + value = "pantheon" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/tychus + name = "Tychus" + value = "tychus" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/breen + name = "Breen" + value = "breen" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/kleiner + name = "Kleiner" + value = "kleiner" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/father + name = "Father" + value = "father" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/tosh + name = "Tosh" + value = "tosh" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/stetmann + name = "Stetmann" + value = "stetmann" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/hanson + name = "Hanson" + value = "hanson" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/swann + name = "Swann" + value = "swann" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/hill + name = "Hill" + value = "hill" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gman_e2 + name = "Gman_e2" + value = "gman_e2" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/valerian + name = "Valerian" + value = "valerian" + category = TTS_CATEGORY_STARCRAFT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gman + name = "Gman" + value = "gman" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/vort + name = "Vort" + value = "vort" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/aradesh + name = "Aradesh" + value = "aradesh" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dornan + name = "Dornan" + value = "dornan" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/elder + name = "Elder" + value = "elder" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/harris + name = "Harris" + value = "harris" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/cabbot + name = "Cabbot" + value = "cabbot" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/decker + name = "Decker" + value = "decker" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dick + name = "Dick" + value = "dick" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/officer + name = "Officer" + value = "officer" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/frank + name = "Frank" + value = "frank" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/gizmo + name = "Gizmo" + value = "gizmo" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/hakunin + name = "Hakunin" + value = "hakunin" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/harold + name = "Harold" + value = "harold" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/harry + name = "Harry" + value = "harry" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/jain + name = "Jain" + value = "jain" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/maxson + name = "Maxson" + value = "maxson" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/killian + name = "Killian" + value = "killian" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/laura + name = "Laura" + value = "laura" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/lieutenant + name = "Lieutenant" + value = "lieutenant" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/loxley + name = "Loxley" + value = "loxley" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/lynette + name = "Lynette" + value = "lynette" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/marcus + name = "Marcus" + value = "marcus" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/master + name = "Master" + value = "master" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/morpheus + name = "Morpheus" + value = "morpheus" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/myron + name = "Myron" + value = "myron" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/nicole + name = "Nicole" + value = "nicole" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/overseer + name = "Overseer" + value = "overseer" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/rhombus + name = "Rhombus" + value = "rhombus" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/_set + name = "Set" + value = "set" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sulik + name = "Sulik" + value = "sulik" + category = TTS_CATEGORY_FALLOUT2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/tandi + name = "Tandi" + value = "tandi" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/vree + name = "Vree" + value = "vree" + category = TTS_CATEGORY_FALLOUT + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/dude + name = "Dude" + value = "dude" + category = TTS_CATEGORY_POSTAL2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/archmage + name = "Archmage" + value = "archmage" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/demoman + name = "Demoman" + value = "demoman" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/engineer + name = "Engineer" + value = "engineer" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/heavy + name = "Heavy" + value = "heavy" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/medic + name = "Medic" + value = "medic" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/scout + name = "Scout" + value = "scout" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sniper_tf + name = "Sniper_tf" + value = "sniper_tf" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/soldier + name = "Soldier" + value = "soldier" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/spy + name = "Spy" + value = "spy" + category = TTS_CATEGORY_TEAMFORTRESS2 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/admiral + name = "Admiral" + value = "admiral" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/alchemist + name = "Alchemist" + value = "alchemist" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/archimonde + name = "Archimonde" + value = "archimonde" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/breaker + name = "Breaker" + value = "breaker" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/captain + name = "Captain" + value = "captain" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/dryad + name = "Dryad" + value = "dryad" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/elf_eng + name = "Elf_eng" + value = "elf_eng" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/footman + name = "Footman" + value = "footman" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/grom + name = "Grom" + value = "grom" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/hh + name = "Hh" + value = "hh" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/huntress + name = "Huntress" + value = "huntress" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/keeper + name = "Keeper" + value = "keeper" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/naga_m + name = "Naga_m" + value = "naga_m" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/naga_rg + name = "Naga_rg" + value = "naga_rg" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/peasant_w + name = "Peasant_w" + value = "peasant_w" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/rifleman + name = "Rifleman" + value = "rifleman" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/satyr + name = "Satyr" + value = "satyr" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sylvanas_w + name = "Sylvanas_w" + value = "sylvanas_w" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/voljin + name = "Voljin" + value = "voljin" + category = TTS_CATEGORY_WARCRAFT3 + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/sidorovich + name = "Sidorovich" + value = "sidorovich" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/p3 + name = "P3" + value = "p3" + category = TTS_CATEGORY_ATOMIC_HEART + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/hraz + name = "Hraz" + value = "hraz" + category = TTS_CATEGORY_ATOMIC_HEART + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/tereshkova + name = "Tereshkova" + value = "tereshkova" + category = TTS_CATEGORY_ATOMIC_HEART + gender = TTS_GENDER_FEMALE + donator_level = 4 + +/datum/tts_seed/silero/babazina + name = "Babazina" + value = "babazina" + category = TTS_CATEGORY_ATOMIC_HEART + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/darius + name = "Darius" + value = "darius" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/trundle + name = "Trundle" + value = "trundle" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/garen + name = "Garen" + value = "garen" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/kled + name = "Kled" + value = "kled" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/ekko + name = "Ekko" + value = "ekko" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/volibear + name = "Volibear" + value = "volibear" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/samira + name = "Samira" + value = "samira" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/swain + name = "Swain" + value = "swain" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/udyr + name = "Udyr" + value = "udyr" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/dr_mundo + name = "Dr_mundo" + value = "dr_mundo" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/graves + name = "Graves" + value = "graves" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/rakan + name = "Rakan" + value = "rakan" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/gangplank + name = "Gangplank" + value = "gangplank" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/riven + name = "Riven" + value = "riven" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/katarina + name = "Katarina" + value = "katarina" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/ahri + name = "Ahri" + value = "ahri" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/ornn + name = "Ornn" + value = "ornn" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/braum + name = "Braum" + value = "braum" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/fizz + name = "Fizz" + value = "fizz" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/draven + name = "Draven" + value = "draven" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/qiyana + name = "Qiyana" + value = "qiyana" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/ksante + name = "Ksante" + value = "ksante" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/talon + name = "Talon" + value = "talon" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/shyvana + name = "Shyvana" + value = "shyvana" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/zenyatta + name = "Zenyatta" + value = "zenyatta" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/kiriko + name = "Kiriko" + value = "kiriko" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/hanzo + name = "Hanzo" + value = "hanzo" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/roadhog + name = "Roadhog" + value = "roadhog" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/sigma + name = "Sigma" + value = "sigma" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/soldier_76 + name = "Soldier_76" + value = "soldier_76" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/junkrat + name = "Junkrat" + value = "junkrat" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/tracer + name = "Tracer" + value = "tracer" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/genji + name = "Genji" + value = "genji" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/echo + name = "Echo" + value = "echo" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/sojourn + name = "Sojourn" + value = "sojourn" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/winston + name = "Winston" + value = "winston" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/reaper + name = "Reaper" + value = "reaper" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/training_robot + name = "Training_robot" + value = "training_robot" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_ANY + 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 = 2 + +/datum/tts_seed/silero/esbern + name = "Esbern" + value = "esbern" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/m_argo + name = "M_argo" + value = "m_argo" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 2 + +/datum/tts_seed/silero/m_coward + name = "M_coward" + value = "m_coward" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/farkas + name = "Farkas" + value = "farkas" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/f_khajiit + name = "F_khajiit" + value = "f_khajiit" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/m_citizen + name = "M_citizen" + value = "m_citizen" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 3 + +/datum/tts_seed/silero/odahviing + name = "Odahviing" + value = "odahviing" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/kodlak + name = "Kodlak" + value = "kodlak" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 2 + +/datum/tts_seed/silero/emperor + name = "Emperor" + value = "emperor" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/hagraven + name = "Hagraven" + value = "hagraven" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/nazir + name = "Nazir" + value = "nazir" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/dremora + name = "Dremora" + value = "dremora" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/alduin + name = "Alduin" + value = "alduin" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/malkoran + name = "Malkoran" + value = "malkoran" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/barbas + name = "Barbas" + value = "barbas" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/hermaeus + name = "Hermaeus" + value = "hermaeus" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/hakon + name = "Hakon" + value = "hakon" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/rita + name = "Rita" + value = "rita" + category = TTS_CATEGORY_RITA + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/barman + name = "Barman" + value = "barman" + category = TTS_CATEGORY_STALKER + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/bridger2 + name = "Bridger2" + value = "bridger2" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/bridger3 + name = "Bridger3" + value = "bridger3" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/cannibal3 + name = "Cannibal3" + value = "cannibal3" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/bridger1 + name = "Bridger1" + value = "bridger1" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/cannibal2 + name = "Cannibal2" + value = "cannibal2" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/slave1 + name = "Slave1" + value = "slave1" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/slave3 + name = "Slave3" + value = "slave3" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/mira + name = "Mira" + value = "mira" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/valeera + name = "Valeera" + value = "valeera" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/rehgar + name = "Rehgar" + value = "rehgar" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/yrel + name = "Yrel" + value = "yrel" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/volskaya + name = "Volskaya" + value = "volskaya" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/necromancer + name = "Necromancer" + value = "necromancer" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/zuljin + name = "Zuljin" + value = "zuljin" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/samuro + name = "Samuro" + value = "samuro" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/tyrael + name = "Tyrael" + value = "tyrael" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/athena + name = "Athena" + value = "athena" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/default + name = "Default" + value = "default" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/chromie + name = "Chromie" + value = "chromie" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/orphea + name = "Orphea" + value = "orphea" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/adjutant + name = "Adjutant" + value = "adjutant" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/vanndara + name = "Vanndara" + value = "vanndara" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/mechatassadar + name = "Mechatassadar" + value = "mechatassadar" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/blackheart + name = "Blackheart" + value = "blackheart" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/olaf + name = "Olaf" + value = "olaf" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/alarak + name = "Alarak" + value = "alarak" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/dva + name = "Dva" + value = "dva" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/toy18 + name = "Toy18" + value = "toy18" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_ANY + 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 = 2 + +/datum/tts_seed/silero/lucio + name = "Lucio" + value = "lucio" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/angel + name = "Angel" + value = "angel" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/thunderking + name = "Thunderking" + value = "thunderking" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/dr_boom + name = "Dr_boom" + value = "dr_boom" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/hooktusk + name = "Hooktusk" + value = "hooktusk" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/sinclari + name = "Sinclari" + value = "sinclari" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/kazakus + name = "Kazakus" + value = "kazakus" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/moroes + name = "Moroes" + value = "moroes" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/maiev_hs + name = "Maiev_hs" + value = "maiev_hs" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/zentimo + name = "Zentimo" + value = "zentimo" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/rastakhan + name = "Rastakhan" + value = "rastakhan" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/innkeeper + name = "Innkeeper" + value = "innkeeper" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/togwaggle + name = "Togwaggle" + value = "togwaggle" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/biggs + name = "Biggs" + value = "biggs" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/brann + name = "Brann" + value = "brann" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + 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 = 3 + +/datum/tts_seed/silero/siamat + name = "Siamat" + value = "siamat" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/omnotron + name = "Omnotron" + value = "omnotron" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/putricide + name = "Putricide" + value = "putricide" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/khadgar + name = "Khadgar" + value = "khadgar" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/zoie + name = "Zoie" + value = "zoie" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/azalina + name = "Azalina" + value = "azalina" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/chu + name = "Chu" + value = "chu" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/tekahn + name = "Tekahn" + value = "tekahn" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/sthara + name = "Sthara" + value = "sthara" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/dovo + name = "Dovo" + value = "dovo" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/shaw + name = "Shaw" + value = "shaw" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/greymane + name = "Greymane" + value = "greymane" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/willow + name = "Willow" + value = "willow" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/haro + name = "Haro" + value = "haro" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/hagatha + name = "Hagatha" + value = "hagatha" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/reno + name = "Reno" + value = "reno" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/ozara + name = "Ozara" + value = "ozara" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/loti + name = "Loti" + value = "loti" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/tarkus + name = "Tarkus" + value = "tarkus" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/voone + name = "Voone" + value = "voone" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/tala + name = "Tala" + value = "tala" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/edra + name = "Edra" + value = "edra" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/myra + name = "Myra" + value = "myra" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/smiggs + name = "Smiggs" + value = "smiggs" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/timothy + name = "Timothy" + value = "timothy" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/wendy + name = "Wendy" + value = "wendy" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/hannigan + name = "Hannigan" + value = "hannigan" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/vargoth + name = "Vargoth" + value = "vargoth" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/jolene + name = "Jolene" + value = "jolene" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/kyriss + name = "Kyriss" + value = "kyriss" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/saurfang + name = "Saurfang" + value = "saurfang" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/kizi + name = "Kizi" + value = "kizi" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/slate + name = "Slate" + value = "slate" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/hesutu + name = "Hesutu" + value = "hesutu" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/hancho + name = "Hancho" + value = "hancho" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/gnomenapper + name = "Gnomenapper" + value = "gnomenapper" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/valdera + name = "Valdera" + value = "valdera" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/disidra + name = "Disidra" + value = "disidra" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/omu + name = "Omu" + value = "omu" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/floop + name = "Floop" + value = "floop" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/belloc + name = "Belloc" + value = "belloc" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/xurios + name = "Xurios" + value = "xurios" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/wagtoggle + name = "Wagtoggle" + value = "wagtoggle" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/belnaara + name = "Belnaara" + value = "belnaara" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/lilayell + name = "Lilayell" + value = "lilayell" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/candlebeard + name = "Candlebeard" + value = "candlebeard" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/awilo + name = "Awilo" + value = "awilo" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/marei + name = "Marei" + value = "marei" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/applebough + name = "Applebough" + value = "applebough" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/lazul + name = "Lazul" + value = "lazul" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/arwyn + name = "Arwyn" + value = "arwyn" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/glowtron + name = "Glowtron" + value = "glowtron" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/cardish + name = "Cardish" + value = "cardish" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/robold + name = "Robold" + value = "robold" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/malfurion + name = "Malfurion" + value = "malfurion" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/deathwhisper + name = "Deathwhisper" + value = "deathwhisper" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/janna + name = "Janna" + value = "janna" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/cassiopeia + name = "Cassiopeia" + value = "cassiopeia" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/taliyah + name = "Taliyah" + value = "taliyah" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/neeko + name = "Neeko" + value = "neeko" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/taric + name = "Taric" + value = "taric" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/akshan + name = "Akshan" + value = "akshan" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/tristana + name = "Tristana" + value = "tristana" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/sylas + name = "Sylas" + value = "sylas" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/sejuani + name = "Sejuani" + value = "sejuani" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/anivia + name = "Anivia" + value = "anivia" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/vayne + name = "Vayne" + value = "vayne" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/karma + name = "Karma" + value = "karma" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/nilah + name = "Nilah" + value = "nilah" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/olaf_lol + name = "Olaf_lol" + value = "olaf_lol" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/quinn + name = "Quinn" + value = "quinn" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/lissandra + name = "Lissandra" + value = "lissandra" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/hecarim + name = "Hecarim" + value = "hecarim" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/vi + name = "Vi" + value = "vi" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/zyra + name = "Zyra" + value = "zyra" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/zac + name = "Zac" + value = "zac" + category = TTS_CATEGORY_LOL + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/moira + name = "Moira" + value = "moira" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/ashe + name = "Ashe" + value = "ashe" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/brigitte + name = "Brigitte" + value = "brigitte" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/mercy + name = "Mercy" + value = "mercy" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/lucio_ov + name = "Lucio_ov" + value = "lucio_ov" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/symmetra + name = "Symmetra" + value = "symmetra" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/zarya + name = "Zarya" + value = "zarya" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/cassidy + name = "Cassidy" + value = "cassidy" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/baptiste + name = "Baptiste" + value = "baptiste" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/junker_queen + name = "Junker_queen" + value = "junker_queen" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/doomfist + name = "Doomfist" + value = "doomfist" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/pharah + name = "Pharah" + value = "pharah" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/sombra + name = "Sombra" + value = "sombra" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/ana + name = "Ana" + value = "ana" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/widowmaker + name = "Widowmaker" + value = "widowmaker" + category = TTS_CATEGORY_OVERWATCH + gender = TTS_GENDER_FEMALE + donator_level = 4 + +/datum/tts_seed/silero/harbor + name = "Harbor" + value = "harbor" + category = TTS_CATEGORY_VALORANT + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/sage + name = "Sage" + value = "sage" + category = TTS_CATEGORY_VALORANT + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/brimstone + name = "Brimstone" + value = "brimstone" + category = TTS_CATEGORY_VALORANT + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/sova + name = "Sova" + value = "sova" + category = TTS_CATEGORY_VALORANT + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/f_shrill + name = "F_shrill" + value = "f_shrill" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + 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 = 2 + +/datum/tts_seed/silero/m_soldier + name = "M_soldier" + value = "m_soldier" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/sven + name = "Sven" + value = "sven" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/eorlund + name = "Eorlund" + value = "eorlund" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/f_nord + name = "F_nord" + value = "f_nord" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/lydia + name = "Lydia" + value = "lydia" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/motierre + name = "Motierre" + value = "motierre" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/tullius + name = "Tullius" + value = "tullius" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/festus + name = "Festus" + value = "festus" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/olava + name = "Olava" + value = "olava" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/hadvar + name = "Hadvar" + value = "hadvar" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/arngeir + name = "Arngeir" + value = "arngeir" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/nazeem + name = "Nazeem" + value = "nazeem" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/falion + name = "Falion" + value = "falion" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/f_coward + name = "F_coward" + value = "f_coward" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + 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 = 2 + +/datum/tts_seed/silero/m_commoner + name = "M_commoner" + value = "m_commoner" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/elisif + name = "Elisif" + value = "elisif" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/paarthurnax + name = "Paarthurnax" + value = "paarthurnax" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/grelka + name = "Grelka" + value = "grelka" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/f_commoner + name = "F_commoner" + value = "f_commoner" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/ebony + name = "Ebony" + value = "ebony" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/ulfric + name = "Ulfric" + value = "ulfric" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/farengar + name = "Farengar" + value = "farengar" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/astrid + name = "Astrid" + value = "astrid" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/brynjolf + name = "Brynjolf" + value = "brynjolf" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/maven + name = "Maven" + value = "maven" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/f_orc + name = "F_orc" + value = "f_orc" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/delphine + name = "Delphine" + value = "delphine" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/grelod + name = "Grelod" + value = "grelod" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/tolfdir + name = "Tolfdir" + value = "tolfdir" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/m_bandit + name = "M_bandit" + value = "m_bandit" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/karliah + name = "Karliah" + value = "karliah" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/felldir + name = "Felldir" + value = "felldir" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/ancano + name = "Ancano" + value = "ancano" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/mercer + name = "Mercer" + value = "mercer" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/vex + name = "Vex" + value = "vex" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/mirabelle + name = "Mirabelle" + value = "mirabelle" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/aventus + name = "Aventus" + value = "aventus" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/tsun + name = "Tsun" + value = "tsun" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/elenwen + name = "Elenwen" + value = "elenwen" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/gormlaith + name = "Gormlaith" + value = "gormlaith" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/dragon + name = "Dragon" + value = "dragon" + category = TTS_CATEGORY_SKYRIM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/overwatch + name = "Overwatch" + value = "overwatch" + category = TTS_CATEGORY_HALFLIFE2 + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/zak + name = "Zak" + value = "zak" + category = TTS_CATEGORY_EVILISLANDS + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/merc2 + name = "Merc2" + value = "merc2" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/forest1 + name = "Forest1" + value = "forest1" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/bandit3 + name = "Bandit3" + value = "bandit3" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/forest2 + name = "Forest2" + value = "forest2" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/merc1 + name = "Merc1" + value = "merc1" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/bandit2 + name = "Bandit2" + value = "bandit2" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/forest3 + name = "Forest3" + value = "forest3" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/tribal3 + name = "Tribal3" + value = "tribal3" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/slave2 + name = "Slave2" + value = "slave2" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/miller + name = "Miller" + value = "miller" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/krest + name = "Krest" + value = "krest" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/tribal1 + name = "Tribal1" + value = "tribal1" + category = TTS_CATEGORY_METRO + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/abathur + name = "Abathur" + value = "abathur" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/erik + name = "Erik" + value = "erik" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/varian + name = "Varian" + value = "varian" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/anduin + name = "Anduin" + value = "anduin" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/deckard + name = "Deckard" + value = "deckard" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/malfurion_h + name = "Malfurion_h" + value = "malfurion_h" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 4 + +/datum/tts_seed/silero/demonhunter + name = "Demonhunter" + value = "demonhunter" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/demon + name = "Demon" + value = "demon" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + 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 = 1 + +/datum/tts_seed/silero/ladyofthorns + name = "Ladyofthorns" + value = "ladyofthorns" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/barbarian + name = "Barbarian" + value = "barbarian" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/crusader + name = "Crusader" + value = "crusader" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/whitemane + name = "Whitemane" + value = "whitemane" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/nexushunter + name = "Nexushunter" + value = "nexushunter" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/greymane_h + name = "Greymane_h" + value = "greymane_h" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/gardensdayannouncer + name = "Gardensdayannouncer" + value = "gardensdayannouncer" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/drekthar + name = "Drekthar" + value = "drekthar" + category = TTS_CATEGORY_HEROESOFTHESTORM + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/squeamlish + name = "Squeamlish" + value = "squeamlish" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/dagg + name = "Dagg" + value = "dagg" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/brukan + name = "Brukan" + value = "brukan" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/bolan + name = "Bolan" + value = "bolan" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/goya + name = "Goya" + value = "goya" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/stargazer + name = "Stargazer" + value = "stargazer" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/eudora + name = "Eudora" + value = "eudora" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/mozaki + name = "Mozaki" + value = "mozaki" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/katrana + name = "Katrana" + value = "katrana" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/malacrass + name = "Malacrass" + value = "malacrass" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/elise + name = "Elise" + value = "elise" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/flark + name = "Flark" + value = "flark" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/rhogi + name = "Rhogi" + value = "rhogi" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/gallywix + name = "Gallywix" + value = "gallywix" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/talanji + name = "Talanji" + value = "talanji" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/tierra + name = "Tierra" + value = "tierra" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/zenda + name = "Zenda" + value = "zenda" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/baechao + name = "Baechao" + value = "baechao" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/lilian + name = "Lilian" + value = "lilian" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/aranna + name = "Aranna" + value = "aranna" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/oshi + name = "Oshi" + value = "oshi" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/norroa + name = "Norroa" + value = "norroa" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/turalyon + name = "Turalyon" + value = "turalyon" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 2 + +/datum/tts_seed/silero/aki + name = "Aki" + value = "aki" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/lunara + name = "Lunara" + value = "lunara" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/bob + name = "Bob" + value = "bob" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/illucia + name = "Illucia" + value = "illucia" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/yrel_hs + name = "Yrel_hs" + value = "yrel_hs" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/fireheart + name = "Fireheart" + value = "fireheart" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/lanathel + name = "Lanathel" + value = "lanathel" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + 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 = 1 + +/datum/tts_seed/silero/draemus + name = "Draemus" + value = "draemus" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/rasil + name = "Rasil" + value = "rasil" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/kalec + name = "Kalec" + value = "kalec" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/karastamper + name = "Karastamper" + value = "karastamper" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/george + name = "George" + value = "george" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/pollark + name = "Pollark" + value = "pollark" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/stelina + name = "Stelina" + value = "stelina" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/kasa + name = "Kasa" + value = "kasa" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 2 + +/datum/tts_seed/silero/whirt + name = "Whirt" + value = "whirt" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 1 + +/datum/tts_seed/silero/anarii + name = "Anarii" + value = "anarii" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/ilza + name = "Ilza" + value = "ilza" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/avozu + name = "Avozu" + value = "avozu" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 1 + +/datum/tts_seed/silero/jeklik + name = "Jeklik" + value = "jeklik" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 1 + +/datum/tts_seed/silero/zibb + name = "Zibb" + value = "zibb" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/thrud + name = "Thrud" + value = "thrud" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_MALE + donator_level = 3 + +/datum/tts_seed/silero/isiset + name = "Isiset" + value = "isiset" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_FEMALE + donator_level = 3 + +/datum/tts_seed/silero/akazamzarak + name = "Akazamzarak" + value = "akazamzarak" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + donator_level = 2 + +/datum/tts_seed/silero/arha + name = "Arha" + value = "arha" + category = TTS_CATEGORY_HEARTHSTONE + gender = TTS_GENDER_ANY + +/datum/tts_seed/silero/aidar + name = "Aidar" + value = "aidar" + category = TTS_CATEGORY_OTHER + gender = TTS_GENDER_MALE + +/datum/tts_seed/silero/baya + name = "Baya" + value = "baya" + category = TTS_CATEGORY_OTHER + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/kseniya + name = "Kseniya" + value = "kseniya" + category = TTS_CATEGORY_OTHER + gender = TTS_GENDER_FEMALE + +/datum/tts_seed/silero/eugene + name = "Eugene" + value = "eugene" + category = TTS_CATEGORY_OTHER + gender = TTS_GENDER_MALE diff --git a/modular_ss220/modules/tts/code/tts_configuration.dm b/modular_ss220/modules/tts/code/tts_configuration.dm new file mode 100644 index 000000000000000..cedf01f7f9c59c2 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_configuration.dm @@ -0,0 +1,11 @@ +/datum/config_entry/flag/tts_enabled + default = FALSE + protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + +/datum/config_entry/string/tts_token_silero + default = "" + protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN + +/datum/config_entry/flag/tts_cache + default = FALSE + protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN diff --git a/modular_ss220/modules/tts/code/tts_mob_Hear.dm b/modular_ss220/modules/tts/code/tts_mob_Hear.dm new file mode 100644 index 000000000000000..9d60041c6ca9e69 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_mob_Hear.dm @@ -0,0 +1,70 @@ +/mob/proc/Hear_tts(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range) + if(!SStts.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/mob/living/carbon/human/human_speaker = real_speaker + var/tts_seed = istype(human_speaker) && human_speaker.tts_seed || "Arthas" + + 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, 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_ss220/modules/tts/code/tts_numbers.dm b/modular_ss220/modules/tts/code/tts_numbers.dm new file mode 100644 index 000000000000000..c330ce48a285aff --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_numbers.dm @@ -0,0 +1,170 @@ +/proc/num_in_words(n) + return get_num_in_words(n) + +/proc/dec_in_words(n) + return get_num_in_words(n, TRUE) + +/proc/get_num_in_words(n, decimal = FALSE) + var/static/datum/number/num + if(!num) + num = new /datum/number + + if(num.cache["[n]"]) + return num.cache["[n]"] + + var/result + if(decimal) + result = num.decimal2words(n) + else + result = num.int2words(n) + + result = " [result] " + num.cache["[n]"] = result + return result + +/datum/number + var/static/list/units = list( + "ноль", + + list("один", "одна"), + list("два", "две"), + + "три", "четыре", "пять", + "шесть", "семь", "восемь", "девять" + ) + + var/static/list/teens = list( + "десять", "одиннадцать", + "двенадцать", "тринадцать", + "четырнадцать", "пятнадцать", + "шестнадцать", "семнадцать", + "восемнадцать", "девятнадцать" + ) + + var/static/list/tens = list( + "десять", + "двадцать", "тридцать", + "сорок", "пятьдесят", + "шестьдесят", "семьдесят", + "восемьдесят", "девяносто" + ) + + var/static/list/hundreds = list( + "сто", "двести", + "триста", "четыреста", + "пятьсот", "шестьсот", + "семьсот", "восемьсот", + "девятьсот" + ) + + var/static/list/orders = list( + list(list("тысяча", "тысячи", "тысяч"), "f"), + list(list("миллион", "миллиона", "миллионов"), "m"), + list(list("миллиард", "миллиарда", "миллиардов"), "m"), + list(list("триллион", "триллиона", "триллионов"), "m"), + list(list("квадриллион", "квадриллиона", "квадриллионов"), "m"), + list(list("квинтиллион", "квинтиллиона", "квинтиллионов"), "m"), + ) + + var/static/list/decimal_int_units = list(list("целая", "целых", "целых"), "f") + + var/static/list/decimal_exp_units = list( + list(list("десятая", "десятых", "десятых"), "f"), + list(list("сотая", "сотых", "сотых"), "f"), + list(list("тысячная", "тысячных", "тысячных"), "f"), + ) + + var/static/minus = "минус" + + var/static/cache = list() + +/datum/number/proc/thousand(rest, sex) +// """Converts numbers from 19 to 999""" + var/prev = 0 + 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) ) + for(var/list in data) + + var/names = list[1] + var/x = list[2] + + var/cur = round(((rest - prev) % x) * 10 / x) + 1 + prev = rest % x + + if(x == 10 && use_teens) + plural = 3 + name += teens[cur] + else if(cur == 1) + continue + else if(x == 10) + var/name_ = names[cur] + if(islist(name_)) + name_ = name_[sex == "m" ? 1 : 2] + name += name_ + if(cur >= 3 && cur <= 5) + plural = 2 + else if(cur == 2) + plural = 1 + else + plural = 3 + else + name += names[cur-1] + + return list(plural, name) + +/datum/number/proc/int2words(textnum, list/main_units = list(list("", "", ""), "m")) +// http://ru.wikipedia.org/wiki/Gettext#.D0.9C.D0.BD.D0.BE.D0.B6.D0.B5.D1.81.D1.82.D0.B2.D0.B5.D0.BD.D0.BD.D1.8B.D0.B5_.D1.87.D0.B8.D1.81.D0.BB.D0.B0_2 + + var/list/_orders = list(main_units) + orders + + var/num = text2num(textnum) + if(num == 0) + return trim(jointext(list(units[1], _orders[1][1][3]), " ")) + + var/negative = FALSE + if(num < 0) + negative = TRUE + textnum = copytext_char(textnum, 2, 0) + + var/ord = 1 + var/list/name = list() + + while(textnum) + var/next_thousand = text2num(copytext_char(textnum, -3, 0)) + var/list/thousand_result = thousand(next_thousand, _orders[ord][2]) + var/plural = thousand_result[1] + var/list/nme = thousand_result[2] + + if(length(nme) || ord == 1) + name += _orders[ord][1][plural] + + name += nme + textnum = copytext_char(textnum, 1, -3) + ord += 1 + + if(negative) + name += minus + + var/temp_name = name + name = list() + for(var/i = length_char(temp_name), i >= 1, i--) + name += temp_name[i] + + var/result = trim(jointext(name, " ")) + return result + +/datum/number/proc/decimal2words(textvalue, places = 3) + var/pieces = splittext_char(textvalue, ".") + var/integral = pieces[1] + var/exp = copytext_char(pieces[2], 1, places + 1) + var/list/exp_units = decimal_exp_units[length_char(exp)] + + var/result = trim("[int2words(integral, decimal_int_units)] [int2words(exp, exp_units)]") + return result diff --git a/modular_ss220/modules/tts/code/tts_preferences.dm b/modular_ss220/modules/tts/code/tts_preferences.dm new file mode 100644 index 000000000000000..6253ff074d419e0 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_preferences.dm @@ -0,0 +1,73 @@ +/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 SStts.tts_providers) + var/datum/tts_provider/provider = SStts.tts_providers[_provider] + providers += list(list( + "name" = provider.name, + "is_enabled" = provider.is_enabled, + )) + data["providers"] = providers + + var/list/seeds = list() + for(var/_seed in SStts.tts_seeds) + var/datum/tts_seed/seed = SStts.tts_seeds[_seed] + seeds += list(list( + "name" = seed.name, + "value" = seed.value, + "category" = seed.category, + "gender" = seed.gender, + "provider" = initial(seed.provider.name), + "donator_level" = seed.donator_level, + )) + data["seeds"] = seeds + + data["phrases"] = TTS_PHRASES + + return data + + +/datum/preferences/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if (.) + return + + switch (action) + if("listen") + var/phrase = params["phrase"] + var/seed_name = params["seed"] + + if((phrase in TTS_PHRASES) && (seed_name in SStts.tts_seeds)) + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), null, usr, phrase, 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) + 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/numeric/sound_tts_radio/create_default_value() + return 50 diff --git a/modular_ss220/modules/tts/code/tts_provider.dm b/modular_ss220/modules/tts/code/tts_provider.dm new file mode 100644 index 000000000000000..aa159eef07cde04 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_provider.dm @@ -0,0 +1,31 @@ +/datum/tts_provider + var/name = "STUB" + var/is_enabled = TRUE + + // Throttling + var/is_throttled = FALSE + var/throttled_until = 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) + return null + +/datum/tts_provider/proc/throttle_check() + if(is_throttled && throttled_until < world.time) + return TRUE + is_throttled = FALSE + return FALSE + +/datum/tts_provider/proc/pitch_whisper(text) + return text + +/datum/tts_provider/proc/rate_faster(text) + return text + +/datum/tts_provider/proc/rate_medium(text) + return text diff --git a/modular_ss220/modules/tts/code/tts_seed.dm b/modular_ss220/modules/tts/code/tts_seed.dm new file mode 100644 index 000000000000000..482275a3120c0bf --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_seed.dm @@ -0,0 +1,24 @@ +/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 humanoid (non-Xeno) mob, such as humans, plasmamen, lizards. +/mob/living/carbon/human + var/tts_seed + +/datum/preference/text/tts_seed/apply_to_human(mob/living/carbon/human/target, value) + target.tts_seed = value diff --git a/modular_ss220/modules/tts/code/tts_sound.dm b/modular_ss220/modules/tts/code/tts_sound.dm new file mode 100644 index 000000000000000..148a73a55f55049 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_sound.dm @@ -0,0 +1,44 @@ +// 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(GLOB.ffmpeg_cpuaffinity) + // taskset = "taskset -ac [GLOB.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_ss220/modules/tts/code/tts_sound_TEMPORARY.dm b/modular_ss220/modules/tts/code/tts_sound_TEMPORARY.dm new file mode 100644 index 000000000000000..a8bae493691ec14 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_sound_TEMPORARY.dm @@ -0,0 +1,77 @@ +// 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_ss220/modules/tts/code/tts_subsystem.dm b/modular_ss220/modules/tts/code/tts_subsystem.dm new file mode 100644 index 000000000000000..114f9c6d90b9cb3 --- /dev/null +++ b/modular_ss220/modules/tts/code/tts_subsystem.dm @@ -0,0 +1,576 @@ +SUBSYSTEM_DEF(tts) + name = "Text-to-Speech" + init_order = INIT_ORDER_DEFAULT + wait = 1 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 = "" + + // Simple Moving Average RPS + var/list/tts_rps_list = list() + var/tts_sma_rps = 0 + + // Requests per Second (RPS), only real API requests + var/tts_rps = 0 + var/tts_rps_counter = 0 + + // Total Requests per Second (TRPS), all TTS request, even reused + var/tts_trps = 0 + var/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 + + var/list/datum/tts_seed/tts_seeds = list() + var/list/tts_seeds_names = list() + var/list/tts_seeds_names_by_donator_levels = list() + 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" = "посетитель", + ) + +/datum/controller/subsystem/tts/stat_entry(msg) + msg += "tRPS:[tts_trps] " + msg += "rRPS:[tts_rrps] " + msg += "RPS:[tts_rps] " + msg += "smaRPS:[tts_sma_rps] | " + msg += "W:[tts_wanted] " + msg += "F:[tts_request_failed] " + msg += "S:[tts_request_succeeded] " + msg += "R:[tts_reused] " + return ..() + +/datum/controller/subsystem/tts/PreInit() + . = ..() + 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") + continue + 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) + +/datum/controller/subsystem/tts/Initialize(start_timeofday) + is_enabled = CONFIG_GET(flag/tts_enabled) + if(!is_enabled) + flags |= SS_NO_FIRE + + return SS_INIT_SUCCESS + +/datum/controller/subsystem/tts/fire() + tts_rps = tts_rps_counter + tts_rps_counter = 0 + tts_trps = tts_trps_counter + tts_trps_counter = 0 + tts_rrps = tts_rrps_counter + tts_rrps_counter = 0 + + tts_rps_list += tts_rps + if(tts_rps_list.len > 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) + + 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) + for(var/request in requests) + var/text = request[1] + var/datum/tts_seed/seed = request[2] + var/datum/callback/proc_callback = request[3] + 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) + + 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]" + sanitized_messages_cache_hit = 0 + sanitized_messages_cache_miss = 0 + +/datum/controller/subsystem/tts/Recover() + is_enabled = SStts.is_enabled + tts_wanted = SStts.tts_wanted + tts_request_failed = SStts.tts_request_failed + tts_request_succeeded = SStts.tts_request_succeeded + tts_reused = SStts.tts_reused + +/datum/controller/subsystem/tts/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 принудительно отключена!")) + return FALSE + + if(tts_rps_counter < tts_rps_limit) + var/datum/tts_provider/provider = seed.provider + provider.request(text, seed, proc_callback) + tts_rps_counter++ + return TRUE + + tts_requests_queue += list(list(text, seed, proc_callback)) + return TRUE + +/datum/controller/subsystem/tts/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) + if(!is_enabled) + return + if(!message) + return + if(isnull(listener) || !listener.client) + return + if(isnull(seed_name) || !(seed_name in tts_seeds)) + return + var/datum/tts_seed/seed = tts_seeds[seed_name] + + tts_wanted++ + tts_trps_counter++ + + var/datum/tts_provider/provider = seed.provider + if(!provider.is_enabled) + return + if(provider.throttle_check()) + return + + var/dirty_text = message + var/text = sanitize_tts_input(dirty_text) + + if(!text || length_char(text) > MAX_MESSAGE_LEN) + return + + if(traits & TTS_TRAIT_RATE_FASTER) + text = provider.rate_faster(text) + + if(traits & TTS_TRAIT_RATE_MEDIUM) + text = provider.rate_medium(text) + + if(traits & TTS_TRAIT_PITCH_WHISPER) + text = provider.pitch_whisper(text) + + var/hash = rustg_hash_string(RUSTG_HASH_MD5, text) + var/filename = "sound/tts_cache/[seed.name]/[hash]" + + var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX) + + if(fexists("[filename].ogg")) + tts_reused++ + tts_rrps_counter++ + play_tts(speaker, listener, filename, is_local, effect, preSFX, postSFX) + return + + 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) + LAZYADD(tts_queue[filename], play_tts_cb) + +/datum/controller/subsystem/tts/proc/get_tts_callback(atom/speaker, mob/listener, filename, datum/tts_seed/seed, is_local, effect, preSFX, postSFX, datum/http_response/response) + var/datum/tts_provider/provider = seed.provider + + // 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.") + 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])") + tts_request_failed++ + if(response.status_code) + if(tts_errors["[response.status_code]"]) + tts_errors["[response.status_code]"]++ + else + tts_errors += "[response.status_code]" + tts_errors["[response.status_code]"] = 1 + tts_error_raw = response.error + return + + tts_request_succeeded++ + + var/voice = provider.process_response(response) + if(!voice) + return + + rustg_file_write_b64decode(voice, "[filename].ogg") + + if (!CONFIG_GET(flag/tts_cache)) + addtimer(CALLBACK(src, PROC_REF(cleanup_tts_file), "[filename].ogg"), 30 SECONDS) + + for(var/datum/callback/cb in tts_queue[filename]) + cb.InvokeAsync() + tts_queue[filename] -= cb + + tts_queue -= filename + +/datum/controller/subsystem/tts/proc/play_tts(atom/speaker, mob/listener, filename, is_local = TRUE, effect = SOUND_EFFECT_NONE, 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/turf/turf_source = get_turf(speaker) + + 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/sound/output = sound(voice) + output.status = SOUND_STREAM + + if(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 + + if(output.volume <= 0) + return + + if(preSFX) + play_sfx(listener, preSFX, output.channel, output.volume, output.environment) + + SEND_SOUND(listener, output) + return + + if(preSFX) + play_sfx(listener, preSFX, output.channel, output.volume, output.environment) + + listener.playsound_local(turf_source, output, volume, sound_to_use = output, channel = channel, wait = TRUE) + + if(!output || output.volume <= 0) + return + + if(postSFX) + play_sfx(listener, postSFX, output.channel, output.volume, output.environment) + +/datum/controller/subsystem/tts/proc/play_sfx(mob/listener, sfx, channel, 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/tts/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/tts/proc/cleanup_tts_file(filename) + fdel(filename) + +/datum/controller/subsystem/tts/proc/get_available_seeds(owner) + var/list/_tts_seeds_names = list() + _tts_seeds_names |= tts_seeds_names + + if(!ismob(owner)) + return _tts_seeds_names + + var/mob/M = owner + + 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/tts/proc/get_random_seed(owner) + return pick(get_available_seeds(owner)) + +/datum/controller/subsystem/tts/proc/sanitize_tts_input(message) + var/hash + if(sanitized_messages_caching) + hash = rustg_hash_string(RUSTG_HASH_MD5, 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/words = new(@"(? { case Page.Quirks: pageContents = ; break; + case Page.Voice: + pageContents = ; + break; default: exhaustiveCheck(currentPage); } @@ -192,6 +198,17 @@ export const CharacterPreferenceWindow = (props) => { Quirks + + {Boolean(data.tts_enabled) && ( + + + Voice + + + )} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/VoicePage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/VoicePage.tsx new file mode 100644 index 000000000000000..29b86e4bd6a6f6f --- /dev/null +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/VoicePage.tsx @@ -0,0 +1,275 @@ +import { useBackend, useLocalState } from '../../backend'; +import { Button, LabeledList, Table, Section, Dropdown, Input, BlockQuote, Box, Icon } from '../../components'; +import { PreferencesMenuData } from './data'; + +const donatorTiers = { + 0: 'Бесплатные', + 1: 'Tier I', + 2: 'Tier II', + 3: 'Tier III', + 4: 'Tier IV', +}; + +const gendersIcons = { + 'Мужской': { + icon: 'mars', + color: 'blue', + }, + 'Женский': { + icon: 'venus', + color: 'purple', + }, + 'Любой': { + icon: 'venus-mars', + color: 'white', + }, +}; + +const getCheckboxGroup = ( + itemsList, + selectedList, + setSelected, + contentKey: string | null = null +) => { + return itemsList.map((item) => { + const title = (contentKey && item[contentKey]) ?? item; + return ( + { + if (selectedList.includes(item)) { + setSelected( + selectedList.filter( + (i) => ((contentKey && i[contentKey]) ?? i) !== item + ) + ); + } else { + setSelected([item, ...selectedList]); + } + }} + /> + ); + }); +}; + +export const VoicePage = (props, context) => { + const { act, data } = useBackend(context); + + const { + providers, + seeds, + phrases, + character_preferences: { + misc: { tts_seed }, + }, + } = data; + + const donator_level = 5; + + const categories = seeds + .map((seed) => seed.category) + .filter((category, i, a) => a.indexOf(category) === i); + const genders = seeds + .map((seed) => seed.gender) + .filter((gender, i, a) => a.indexOf(gender) === i); + const donatorLevels = seeds + .map((seed) => seed.donator_level) + .filter((level, i, a) => a.indexOf(level) === i) + .map((level) => donatorTiers[level]); + + const [selectedProviders, setSelectedProviders] = useLocalState( + context, + 'selectedProviders', + providers + ); + const [selectedGenders, setSelectedGenders] = useLocalState( + context, + 'selectedGenders', + genders + ); + const [selectedCategories, setSelectedCategories] = useLocalState( + context, + 'selectedCategories', + categories + ); + const [selectedDonatorLevels, setSelectedDonatorLevels] = useLocalState( + context, + 'selectedDonatorLevels', + donatorLevels + ); + const [selectedPhrase, setSelectedPhrase] = useLocalState( + context, + 'selectedPhrase', + phrases[0] + ); + const [searchtext, setSearchtext] = useLocalState(context, 'searchtext', ''); + + let providerCheckboxes = getCheckboxGroup( + providers, + selectedProviders, + setSelectedProviders, + 'name' + ); + let genderesCheckboxes = getCheckboxGroup( + genders, + selectedGenders, + setSelectedGenders + ); + let categoriesCheckboxes = getCheckboxGroup( + categories, + selectedCategories, + setSelectedCategories + ); + let donatorLevelsCheckboxes = getCheckboxGroup( + donatorLevels, + selectedDonatorLevels, + setSelectedDonatorLevels + ); + + let phrasesSelect = ( + setSelectedPhrase(value)} + /> + ); + + let searchBar = ( + setSearchtext(value)} + /> + ); + + const availableSeeds = seeds + .sort((a, b) => { + const aname = a.name.toLowerCase(); + const bname = b.name.toLowerCase(); + if (aname > bname) { + return 1; + } + if (aname < bname) { + return -1; + } + return 0; + }) + .filter( + (seed) => + selectedProviders.some((provider) => provider.name === seed.provider) && + selectedGenders.includes(seed.gender) && + selectedCategories.includes(seed.category) && + selectedDonatorLevels.includes(donatorTiers[seed.donator_level]) && + seed.name.toLowerCase().includes(searchtext.toLowerCase()) + ); + + let seedsRow = availableSeeds.map((seed) => { + return ( + + +