diff --git a/.github/workflows/ci_suite.yml b/.github/workflows/ci_suite.yml
index 39b5d7b222445..0eb8ae7d10837 100644
--- a/.github/workflows/ci_suite.yml
+++ b/.github/workflows/ci_suite.yml
@@ -70,6 +70,11 @@ jobs:
- name: Ticked File Enforcement
if: steps.linter-setup.conclusion == 'success' && !cancelled()
run: |
+ bash tools/ci/check_filedirs.sh tgstation.dme
+ bash tools/ci/check_changelogs.sh
+ bash tools/ci/check_grep.sh
+ bash tools/ci/check_misc.sh
+ bash tools/bandastation_check_grep.sh # BANDASTATION EDIT ADDITION - checking modular_bandastation code
tools/bootstrap/python tools/ticked_file_enforcement/ticked_file_enforcement.py < tools/ticked_file_enforcement/schemas/tgstation_dme.json
tools/bootstrap/python tools/ticked_file_enforcement/ticked_file_enforcement.py < tools/ticked_file_enforcement/schemas/unit_tests.json
- name: Check Define Sanity
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 7639f1b6a07fe..393bdf9d08737 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -178,12 +178,12 @@
switch(text2ascii(char))
// A .. Z
- if(65 to 90) //Uppercase Letters
+ if(65 to 90, 1040 to 1071, 1025) //Uppercase Letters //SS220 EDIT CHANGE - Cyrillic Fixes
number_of_alphanumeric++
last_char_group = LETTERS_DETECTED
// a .. z
- if(97 to 122) //Lowercase Letters
+ if(97 to 122, 1072 to 1103, 1105) //Lowercase Letters //SS220 EDIT CHANGE - Cyrillic Fixes
if(last_char_group == NO_CHARS_DETECTED || last_char_group == SPACES_DETECTED || cap_after_symbols && last_char_group == SYMBOLS_DETECTED) //start of a word
char = uppertext(char)
number_of_alphanumeric++
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 2a4854c37b858..99989e5cb849e 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -98,7 +98,7 @@
#endif
#ifndef PRELOAD_RSC //set to:
-#define PRELOAD_RSC 1 // 0 to allow using external resources or on-demand behaviour;
+#define PRELOAD_RSC 0 // 0 to allow using external resources or on-demand behaviour; BANDASTATION EDIT - Original: 1
#endif // 1 to use the default behaviour;
// 2 for preloading absolutely everything;
diff --git a/code/modules/unit_tests/barsigns.dm b/code/modules/unit_tests/barsigns.dm
index 7058dd5346dc9..a7223319f8e62 100644
--- a/code/modules/unit_tests/barsigns.dm
+++ b/code/modules/unit_tests/barsigns.dm
@@ -7,12 +7,14 @@
var/obj/machinery/barsign_type = /obj/machinery/barsign
var/icon/barsign_icon = initial(barsign_type.icon)
var/list/barsign_icon_states = icon_states(barsign_icon)
+ var/icon/barsign_icon_ss220 = 'modular_bandastation/barsigns/icons/barsigns.dmi' // BANDASTATION EDIT Barsigns
+ var/list/barsign_icon_states_ss220 = icon_states(barsign_icon_ss220)
// Check every datum real bar sign
for(var/sign_type in (subtypesof(/datum/barsign) - /datum/barsign/hiddensigns))
var/datum/barsign/sign = new sign_type()
- if(!(sign.icon in barsign_icon_states))
+ if(!(sign.icon in barsign_icon_states) && !(sign.icon in barsign_icon_states_ss220)) // BANDASTATION EDIT Barsigns
TEST_FAIL("Icon state for [sign_type] does not exist in [barsign_icon].")
/**
diff --git a/config/bandastation/bandastation_config.txt b/config/bandastation/bandastation_config.txt
new file mode 100644
index 0000000000000..bb67611772896
--- /dev/null
+++ b/config/bandastation/bandastation_config.txt
@@ -0,0 +1,4 @@
+## Text-to-speech
+#TTS_TOKEN_SILERO mytoken
+#TTS_ENABLED
+#TTS_CACHE
diff --git a/config/config.txt b/config/config.txt
index a26b6938496f0..3190a7ae9ce25 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -5,6 +5,7 @@ $include dbconfig.txt
$include comms.txt
$include logging.txt
$include resources.txt
+$include bandastation/bandastation_config.txt
$include interviews.txt
$include lua.txt
$include auxtools.txt
diff --git a/config/dynamic.json b/config/dynamic.json
index a4a1eb7ebdbfb..bea440d1850ae 100644
--- a/config/dynamic.json
+++ b/config/dynamic.json
@@ -4,7 +4,6 @@
"Traitors": {
"cost": 8,
"scaling_cost": 9,
- "weight": 0,
"required_candidates": 1,
"minimum_required_age": 0,
"requirements": [
@@ -40,89 +39,68 @@
},
"Changelings": {
- "weight": 0
},
"Heretics": {
- "weight": 0
},
"Wizard": {
- "weight": 0
},
"Blood Cult": {
- "weight": 0
},
"Nuclear Emergency": {
- "weight": 0
},
"Revolution": {
- "weight": 0
}
},
"Midround": {
"Syndicate Sleeper Agent": {
- "weight": 0
},
"Malfunctioning AI": {
- "weight": 0
},
"Wizard": {
- "weight": 0
},
"Nuclear Assault": {
- "weight": 0
},
"Blob": {
- "weight": 0
},
"Blob Infection": {
- "weight": 0
},
"Alien Infestation": {
- "weight": 0
},
"Nightmare": {
- "weight": 0
},
"Space Dragon": {
- "weight": 0
},
"Abductors": {
- "weight": 0
},
"Space Ninja": {
- "weight": 0
},
"Spiders": {
- "weight": 0
},
"Revenant": {
- "weight": 0
},
"Sentient Disease": {
- "weight": 0
},
"Space Pirates": {
- "weight": 0
},
"Obsessed": {
@@ -130,29 +108,23 @@
},
"Space Changeling": {
- "weight": 0
},
"Paradox Clone": {
- "weight": 0
}
},
"Latejoin": {
"Syndicate Infiltrator": {
- "weight": 0
},
"Provocateur": {
- "weight": 0
},
"Heretic Smuggler": {
- "weight": 0
},
"Stowaway Changeling": {
- "weight": 0
}
},
diff --git a/config/game_options.txt b/config/game_options.txt
index ae90dcb56b91e..7d0a22a01b263 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -33,8 +33,8 @@ COMMENDATION_PERCENT_POLL 0.05
## To speed things up make the number negative, to slow things down, make the number positive.
## These modify the run/walk speed of all mobs before the mob-specific modifiers are applied.
-RUN_DELAY 1.5
-WALK_DELAY 4
+RUN_DELAY 2.5
+WALK_DELAY 5
## The variables below affect the movement of specific mob types. THIS AFFECTS ALL SUBTYPES OF THE TYPE YOU CHOOSE!
## Entries completely override all subtypes. Later entries have precedence over earlier entries.
@@ -44,8 +44,8 @@ WALK_DELAY 4
##MULTIPLICATIVE_MOVESPEED /mob/living/silicon/robot 0
##MULTIPLICATIVE_MOVESPEED /mob/living/carbon/alien 0
##MULTIPLICATIVE_MOVESPEED /mob/living/simple_animal/slime 0
-MULTIPLICATIVE_MOVESPEED /mob/living/simple_animal 0
-MULTIPLICATIVE_MOVESPEED /mob/living/basic 0
+##MULTIPLICATIVE_MOVESPEED /mob/living/simple_animal 0
+##MULTIPLICATIVE_MOVESPEED /mob/living/basic 0
## NAMES ###
@@ -84,7 +84,7 @@ STATION_GOAL_BUDGET 1
## GAME MODES ###
## Uncomment to not send a roundstart intercept report. Gamemodes may override this.
-#NO_INTERCEPT_REPORT
+NO_INTERCEPT_REPORT
## Percent weight reductions for three of the most recent modes
@@ -158,7 +158,7 @@ ALLOW_AI_MULTICAM
## Secborg ###
## Uncomment to prevent the security cyborg model from being chosen
-#DISABLE_SECBORG
+DISABLE_SECBORG
## Peacekeeper Borg ###
## Uncomment to prevent the peacekeeper cyborg model from being chosen
@@ -167,7 +167,7 @@ ALLOW_AI_MULTICAM
## AWAY MISSIONS ###
## Uncomment to load one of the missions from awaymissionconfig.txt or away_missions/ at roundstart.
-#ROUNDSTART_AWAY
+ROUNDSTART_AWAY
## How long the delay is before the Away Mission gate opens. Default is half an hour.
## 600 is one minute.
@@ -193,28 +193,28 @@ MINIMAL_ACCESS_THRESHOLD 20
#ASSISTANTS_HAVE_MAINT_ACCESS
## Uncoment to give security maint access. Note that if you dectivate JOBS_HAVE_MINIMAL_ACCESS security already gets maint from that.
-#SECURITY_HAS_MAINT_ACCESS
+SECURITY_HAS_MAINT_ACCESS
## Uncomment to give everyone maint access.
#EVERYONE_HAS_MAINT_ACCESS
## Comment this out this to make security officers spawn in departmental security posts
-#SEC_START_BRIG
+SEC_START_BRIG
## This variable is how you may configure "Scaling Access" for Departmental Security Officers.
## Set to 0/commented out for "off", Departmental Security Officers will never get additional room-specific access (beyond general departmental doors).
## Set to 1 if you want to enable "Scaling Access", where Departmental Security Officers will get access to most rooms within a department depending on how many security officers there are relative to the number of people on a station.
## Set to 2 if you want Departmental Security Officers to always have access to all rooms in a department.
-DEPSEC_ACCESS_LEVEL 1
+DEPSEC_ACCESS_LEVEL 2
## GHOST INTERACTION ###
## Uncomment to let ghosts spin chairs. You may be wondering why this is a config option. Don't ask.
-#GHOST_INTERACTION
+GHOST_INTERACTION
## NEAR-DEATH EXPERIENCE ###
## Comment this out to disable mobs hearing ghosts when unconscious and very close to death
-NEAR_DEATH_EXPERIENCE
+#NEAR_DEATH_EXPERIENCE
## NON-VOCAL SILICONS ###
## Uncomment these to stop the AI, or cyborgs, from having vocal communication.
@@ -230,7 +230,7 @@ NEAR_DEATH_EXPERIENCE
## Set to 4 for "specified", silicons will start with an existing lawset. (If no specified lawset is identified, the AI will spawn with asimov.)
-DEFAULT_LAWS 0
+DEFAULT_LAWS 4
## SILICON ASIMOV SUPERIORITY OVERRIDE ###
## This makes "Asimov Superiority" show up as a perk for humans in the character creation menu even if asimov is not the default lawset, such as when used with asimov++
@@ -243,7 +243,7 @@ DEFAULT_LAWS 0
## See datums\ai_laws.dm for the full law lists
## IE, SPECIFIED_LAWS asimovpp, SPECIFIED_LAWS robocop, SPECIFIED_LAWS antimov
-SPECIFIED_LAWS asimovpp
+SPECIFIED_LAWS nt_default
## RANDOM LAWS ##
## ------------------------------------------------------------------------------------------
@@ -256,6 +256,7 @@ RANDOM_LAWS asimovpp
RANDOM_LAWS paladin
RANDOM_LAWS robocop
RANDOM_LAWS corporate
+RANDOM_LAWS nt_default
## Quirky laws. Shouldn't cause too much harm
#RANDOM_LAWS hippocratic
@@ -297,6 +298,7 @@ LAW_WEIGHT liveandletlive,5
LAW_WEIGHT peacekeeper,5
LAW_WEIGHT ten_commandments,5
LAW_WEIGHT nutimov,5
+LAW_WEIGHT nt_default,5
## Quirky laws. Shouldn't cause too much harm
LAW_WEIGHT reporter,3
@@ -485,7 +487,7 @@ MICE_ROUNDSTART 10
ROUNDSTART_TRAITS
## Uncomment to disable human moods.
-#DISABLE_HUMAN_MOOD
+DISABLE_HUMAN_MOOD
## Enable night shifts
#ENABLE_NIGHT_SHIFTS
diff --git a/interface/skin.dmf b/interface/skin.dmf
index ede8e37684078..b3e581d344f52 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -111,12 +111,12 @@ window "mapwindow"
size = 640x480
anchor1 = 0,0
anchor2 = 100,100
- font-family = "Grand9K Pixel"
- font-size = 6pt
+ font-family = "Arial"
+ font-size = 7pt
is-default = true
right-click = true
saved-params = "zoom;letterbox;zoom-mode"
- style = ".center { text-align: center; } .maptext { font-family: 'Grand9K Pixel'; font-size: 6pt; -dm-text-outline: 1px black; color: white; line-height: 1.0; } .command_headset { font-weight: bold; } .context { font-family: 'Pixellari'; font-size: 12pt; -dm-text-outline: 1px black; } .subcontext { font-family: 'TinyUnicode'; font-size: 12pt; line-height: 0.75; } .small { font-family: 'Spess Font'; font-size: 6pt; line-height: 1.4; } .big { font-family: 'Pixellari'; font-size: 12pt; } .reallybig { font-size: 12pt; } .extremelybig { font-size: 12pt; } .greentext { color: #00FF00; font-size: 6pt; } .redtext { color: #FF0000; font-size: 6pt; } .clown { color: #FF69BF; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-family: 'Spess Font'; font-size: 6pt; line-height: 1.4; }"
+ style = ".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7pt; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .command_headset { font-weight: bold; } .context { font-family: 'Small Fonts'; font-size: 12pt; -dm-text-outline: 1px black; } .subcontext { font-family: 'Small Fonts'; font-size: 12pt; line-height: 0.75; } .small { font-family: 'Small Fonts'; font-size: 6pt; line-height: 1.4; } .big { font-family: 'Small Fonts'; font-size: 12pt; } .reallybig { font-size: 12pt; } .extremelybig { font-size: 12pt; } .greentext { color: #00FF00; font-size: 6pt; } .redtext { color: #FF0000; font-size: 6pt; } .clown { color: #FF69BF; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-family: 'Small Fonts'; font-size: 6pt; line-height: 1.4; }"
elem "status_bar"
type = LABEL
pos = 0,464
diff --git a/modular_bandastation/_defines220/_defines220.dm b/modular_bandastation/_defines220/_defines220.dm
new file mode 100644
index 0000000000000..89c20b6a61efd
--- /dev/null
+++ b/modular_bandastation/_defines220/_defines220.dm
@@ -0,0 +1,4 @@
+/datum/modpack/defines220
+ name = "Дефайны220"
+ desc = "Добавляет дефайны, которые нам нужны"
+ author = "larentoun"
diff --git a/modular_bandastation/_defines220/_defines220.dme b/modular_bandastation/_defines220/_defines220.dme
new file mode 100644
index 0000000000000..2e4cf7f6c8a23
--- /dev/null
+++ b/modular_bandastation/_defines220/_defines220.dme
@@ -0,0 +1,10 @@
+#include "_defines220.dm"
+
+#include "code/defines/keybindings.dm"
+#include "code/defines/spans.dm"
+#include "code/signals_mob/signals_mob_ai.dm"
+#include "code/signals_mob/signals_mob_carbon.dm"
+#include "code/signals_mob/signals_mob_living.dm"
+#include "code/signals_mob/signals_mob_main.dm"
+#include "code/signals_mob/signals_mob_silicon.dm"
+#include "code/signals_mob/signals_mob_simple.dm"
diff --git a/modular_bandastation/_defines220/code/defines/keybindings.dm b/modular_bandastation/_defines220/code/defines/keybindings.dm
new file mode 100644
index 0000000000000..54cd3afa9f555
--- /dev/null
+++ b/modular_bandastation/_defines220/code/defines/keybindings.dm
@@ -0,0 +1,4 @@
+#define COMSIG_KB_MOB_PIXEL_SHIFT_DOWN "keybinding_mob_pixel_shift_down"
+#define COMSIG_KB_MOB_PIXEL_SHIFT_UP "keybinding_mob_pixel_shift_up"
+#define COMSIG_KB_CLIENT_LOOC_DOWN "keybinding_client_looc_down"
+#define COMSIG_KB_CLIENT_WHISPER_DOWN "keybinding_client_whisper_down"
diff --git a/modular_bandastation/_defines220/code/defines/spans.dm b/modular_bandastation/_defines220/code/defines/spans.dm
new file mode 100644
index 0000000000000..c596abf4ba3a7
--- /dev/null
+++ b/modular_bandastation/_defines220/code/defines/spans.dm
@@ -0,0 +1 @@
+#define span_maptext(str) ("" + str + " ")
diff --git a/modular_bandastation/_defines220/code/signals_mob/signals_mob_ai.dm b/modular_bandastation/_defines220/code/signals_mob/signals_mob_ai.dm
new file mode 100644
index 0000000000000..fa7df8ae680b9
--- /dev/null
+++ b/modular_bandastation/_defines220/code/signals_mob/signals_mob_ai.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon/ai
diff --git a/modular_bandastation/_defines220/code/signals_mob/signals_mob_carbon.dm b/modular_bandastation/_defines220/code/signals_mob/signals_mob_carbon.dm
new file mode 100644
index 0000000000000..ef6039c440fc8
--- /dev/null
+++ b/modular_bandastation/_defines220/code/signals_mob/signals_mob_carbon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/carbon
diff --git a/modular_bandastation/_defines220/code/signals_mob/signals_mob_living.dm b/modular_bandastation/_defines220/code/signals_mob/signals_mob_living.dm
new file mode 100644
index 0000000000000..db4d5bdb08e8c
--- /dev/null
+++ b/modular_bandastation/_defines220/code/signals_mob/signals_mob_living.dm
@@ -0,0 +1,9 @@
+// Signals for /mob/living
+
+//from base of living/set_pull_offset(): (mob/living/pull_target, grab_state)
+#define COMSIG_LIVING_SET_PULL_OFFSET "living_set_pull_offset"
+//from base of living/reset_pull_offsets(): (mob/living/pull_target, override)
+#define COMSIG_LIVING_RESET_PULL_OFFSETS "living_reset_pull_offsets"
+//from base of living/CanAllowThrough(): (atom/movable/mover, border_dir)
+#define COMSIG_LIVING_CAN_ALLOW_THROUGH "living_can_allow_through"
+ #define COMPONENT_LIVING_PASSABLE (1<<0)
diff --git a/modular_bandastation/_defines220/code/signals_mob/signals_mob_main.dm b/modular_bandastation/_defines220/code/signals_mob/signals_mob_main.dm
new file mode 100644
index 0000000000000..7aa41cead07d5
--- /dev/null
+++ b/modular_bandastation/_defines220/code/signals_mob/signals_mob_main.dm
@@ -0,0 +1 @@
+// Signals for /mob
diff --git a/modular_bandastation/_defines220/code/signals_mob/signals_mob_silicon.dm b/modular_bandastation/_defines220/code/signals_mob/signals_mob_silicon.dm
new file mode 100644
index 0000000000000..1e776fa5270e2
--- /dev/null
+++ b/modular_bandastation/_defines220/code/signals_mob/signals_mob_silicon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon
diff --git a/modular_bandastation/_defines220/code/signals_mob/signals_mob_simple.dm b/modular_bandastation/_defines220/code/signals_mob/signals_mob_simple.dm
new file mode 100644
index 0000000000000..937b109659eb5
--- /dev/null
+++ b/modular_bandastation/_defines220/code/signals_mob/signals_mob_simple.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/simple_animal
diff --git a/modular_bandastation/_helpers220/_helpers220.dm b/modular_bandastation/_helpers220/_helpers220.dm
new file mode 100644
index 0000000000000..f252781a26351
--- /dev/null
+++ b/modular_bandastation/_helpers220/_helpers220.dm
@@ -0,0 +1,4 @@
+/datum/modpack/defines220
+ name = "Хелперы220"
+ desc = "Добавляет хелперы, которые нам нужны"
+ author = "larentoun"
diff --git a/modular_bandastation/_helpers220/_helpers220.dme b/modular_bandastation/_helpers220/_helpers220.dme
new file mode 100644
index 0000000000000..203fab82857a2
--- /dev/null
+++ b/modular_bandastation/_helpers220/_helpers220.dme
@@ -0,0 +1 @@
+#include "_helpers220.dm"
diff --git a/modular_bandastation/_modpack.dm b/modular_bandastation/_modpack.dm
new file mode 100644
index 0000000000000..5569426fa6404
--- /dev/null
+++ b/modular_bandastation/_modpack.dm
@@ -0,0 +1,17 @@
+/datum/modpack
+ /// A string name for the modpack. Used for looking up other modpacks in init.
+ var/name
+ /// A string desc for the modpack. Can be used for modpack verb list as description.
+ var/desc
+ /// A string with authors of this modpack.
+ var/author
+
+/datum/modpack/proc/pre_initialize()
+ if(!name)
+ return "Modpack name is unset."
+
+/datum/modpack/proc/initialize()
+ return
+
+/datum/modpack/proc/post_initialize()
+ return
diff --git a/modular_bandastation/_modpacks.dm b/modular_bandastation/_modpacks.dm
new file mode 100644
index 0000000000000..347fcd019b6b9
--- /dev/null
+++ b/modular_bandastation/_modpacks.dm
@@ -0,0 +1,60 @@
+#define INIT_ORDER_MODPACKS 84
+
+SUBSYSTEM_DEF(modpacks)
+ name = "Modpacks"
+ init_order = INIT_ORDER_MODPACKS
+ flags = SS_NO_FIRE
+ var/list/loaded_modpacks = list()
+
+/datum/controller/subsystem/modpacks/Initialize()
+ var/list/all_modpacks = list()
+ for(var/modpack in subtypesof(/datum/modpack/))
+ all_modpacks.Add(new modpack)
+ // Pre-init and register all compiled modpacks.
+ for(var/datum/modpack/package as anything in all_modpacks)
+ var/fail_msg = package.pre_initialize()
+ if(QDELETED(package))
+ CRASH("Modpack of type [package.type] is null or queued for deletion.")
+ if(fail_msg)
+ CRASH("Modpack [package.name] failed to pre-initialize: [fail_msg].")
+ if(loaded_modpacks[package.name])
+ CRASH("Attempted to register duplicate modpack name [package.name].")
+ loaded_modpacks.Add(package)
+
+ // Handle init and post-init (two stages in case a modpack needs to implement behavior based on the presence of other packs).
+ for(var/datum/modpack/package as anything in all_modpacks)
+ var/fail_msg = package.initialize()
+ if(fail_msg)
+ CRASH("Modpack [(istype(package) && package.name) || "Unknown"] failed to initialize: [fail_msg]")
+ for(var/datum/modpack/package as anything in all_modpacks)
+ var/fail_msg = package.post_initialize()
+ if(fail_msg)
+ CRASH("Modpack [(istype(package) && package.name) || "Unknown"] failed to post-initialize: [fail_msg]")
+
+/client/verb/modpacks_list()
+ set name = "Modpacks List"
+ set category = "OOC"
+
+ if(!mob || !SSmodpacks.initialized)
+ return
+
+ if(length(SSmodpacks.loaded_modpacks))
+ . = "
Список модификаций "
+ for(var/datum/modpack/M as anything in SSmodpacks.loaded_modpacks)
+ if(M.name)
+ . += ""
+ . += "
[M.name] "
+
+ if(M.desc || M.author)
+ . += " "
+ if(M.desc)
+ . += " Описание: [M.desc]"
+ if(M.author)
+ . += "Автор: [M.author] "
+ . += " "
+
+ var/datum/browser/popup = new(mob, "modpacks_list", "Список Модификаций", 480, 580)
+ popup.set_content(.)
+ popup.open()
+ else
+ to_chat(src, "Этот сервер не использует какие-либо модификации.")
diff --git a/modular_bandastation/_signals220/_signals220.dm b/modular_bandastation/_signals220/_signals220.dm
new file mode 100644
index 0000000000000..a7b76146fb1be
--- /dev/null
+++ b/modular_bandastation/_signals220/_signals220.dm
@@ -0,0 +1,4 @@
+/datum/modpack/signals220
+ name = "Сигналы220"
+ desc = "Добавляет сигналы"
+ author = "larentoun"
diff --git a/modular_bandastation/_signals220/_signals220.dme b/modular_bandastation/_signals220/_signals220.dme
new file mode 100644
index 0000000000000..26671df7a7f98
--- /dev/null
+++ b/modular_bandastation/_signals220/_signals220.dme
@@ -0,0 +1,8 @@
+#include "_signals220.dm"
+
+#include "code/signals_mob/signals_mob_ai.dm"
+#include "code/signals_mob/signals_mob_carbon.dm"
+#include "code/signals_mob/signals_mob_living.dm"
+#include "code/signals_mob/signals_mob_main.dm"
+#include "code/signals_mob/signals_mob_silicon.dm"
+#include "code/signals_mob/signals_mob_simple.dm"
diff --git a/modular_bandastation/_signals220/code/signals_mob/signals_mob_ai.dm b/modular_bandastation/_signals220/code/signals_mob/signals_mob_ai.dm
new file mode 100644
index 0000000000000..fa7df8ae680b9
--- /dev/null
+++ b/modular_bandastation/_signals220/code/signals_mob/signals_mob_ai.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon/ai
diff --git a/modular_bandastation/_signals220/code/signals_mob/signals_mob_carbon.dm b/modular_bandastation/_signals220/code/signals_mob/signals_mob_carbon.dm
new file mode 100644
index 0000000000000..ef6039c440fc8
--- /dev/null
+++ b/modular_bandastation/_signals220/code/signals_mob/signals_mob_carbon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/carbon
diff --git a/modular_bandastation/_signals220/code/signals_mob/signals_mob_living.dm b/modular_bandastation/_signals220/code/signals_mob/signals_mob_living.dm
new file mode 100644
index 0000000000000..e1602f1bb705e
--- /dev/null
+++ b/modular_bandastation/_signals220/code/signals_mob/signals_mob_living.dm
@@ -0,0 +1,14 @@
+// Signals for /mob/living
+
+/mob/living/CanAllowThrough(atom/movable/mover, border_dir)
+ if(SEND_SIGNAL(src, COMSIG_LIVING_CAN_ALLOW_THROUGH, mover, border_dir) & COMPONENT_LIVING_PASSABLE)
+ return TRUE
+ return ..()
+
+/mob/living/set_pull_offsets(mob/living/pull_target, grab_state)
+ . = ..()
+ SEND_SIGNAL(pull_target, COMSIG_LIVING_SET_PULL_OFFSET, grab_state)
+
+/mob/living/reset_pull_offsets(mob/living/pull_target, override)
+ . = ..()
+ SEND_SIGNAL(pull_target, COMSIG_LIVING_RESET_PULL_OFFSETS, override)
diff --git a/modular_bandastation/_signals220/code/signals_mob/signals_mob_main.dm b/modular_bandastation/_signals220/code/signals_mob/signals_mob_main.dm
new file mode 100644
index 0000000000000..7aa41cead07d5
--- /dev/null
+++ b/modular_bandastation/_signals220/code/signals_mob/signals_mob_main.dm
@@ -0,0 +1 @@
+// Signals for /mob
diff --git a/modular_bandastation/_signals220/code/signals_mob/signals_mob_silicon.dm b/modular_bandastation/_signals220/code/signals_mob/signals_mob_silicon.dm
new file mode 100644
index 0000000000000..1e776fa5270e2
--- /dev/null
+++ b/modular_bandastation/_signals220/code/signals_mob/signals_mob_silicon.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/silicon
diff --git a/modular_bandastation/_signals220/code/signals_mob/signals_mob_simple.dm b/modular_bandastation/_signals220/code/signals_mob/signals_mob_simple.dm
new file mode 100644
index 0000000000000..937b109659eb5
--- /dev/null
+++ b/modular_bandastation/_signals220/code/signals_mob/signals_mob_simple.dm
@@ -0,0 +1 @@
+// Signals for /mob/living/simple_animal
diff --git a/modular_bandastation/aesthetics/_aesthetics.dm b/modular_bandastation/aesthetics/_aesthetics.dm
new file mode 100644
index 0000000000000..bcccd31589222
--- /dev/null
+++ b/modular_bandastation/aesthetics/_aesthetics.dm
@@ -0,0 +1,4 @@
+/datum/modpack/aesthetics
+ name = "Эстетика"
+ desc = "Обновление визуального ряда"
+ author = "larentoun, Aylong220"
diff --git a/modular_bandastation/aesthetics/_aesthetics.dme b/modular_bandastation/aesthetics/_aesthetics.dme
new file mode 100644
index 0000000000000..10130ed3b9ca1
--- /dev/null
+++ b/modular_bandastation/aesthetics/_aesthetics.dme
@@ -0,0 +1,43 @@
+#include "_aesthetics.dm"
+
+#include "airalarm\code\airalarm.dm"
+#include "airlocks\code\airlock.dm"
+#include "airlocks\code\airlock_types.dm"
+#include "airlocks\code\airlock_assembly_types.dm"
+#include "apc\code\apc.dm"
+// #include "atm\code\atm.dm" // Unused
+// #include "atmospherics\code\atmospherics.dm" // Need more sprites
+// #include "better_ids\code\better_ids.dm" // Need to change a lot
+#include "blastdoor\code\blastdoor.dm"
+// #include "boxes\code\boxes.dm" // Need to change a lot
+#include "cameras\code\cameras.dm"
+// #include "chairs\code\chairs.dm" // TG already uses these
+// #include "decals\code\decals.dm" // Didn't check this new one
+// #include "defib\code\defib.dm" // TG already uses these
+#include "dirwindows\code\dirwindows.dm"
+// #include "door_control\code\door_control.dm" // Unused
+// #include "extinguisher\code\extinguisher.dm" // Need more sprites
+// #include "firealarm\code\firealarm.dm" // Need more sprites
+// #include "floors\code\floors.dm" // Need to change a lot
+// #include "floors\code\tile_types.dm" // Need to change a lot
+// #include "hydroponics\code\hydroponics.dm" // TG already uses these
+// #include "intercom\code\intercom.dm" // TG already uses these
+#include "keycard\code\keycard.dm"
+#include "labeler\code\labeler.dm"
+// #include "library\code\library.dm" // TG already uses these
+#include "light_switch\code\light_switch.dm"
+#include "newscaster\code\newscaster.dm"
+// #include "piano\code\piano.dm" // Unused
+#include "racks\code\racks.dm"
+#include "requests_console\code\requests_console.dm"
+// #include "safe\code\safe.dm" // TG already uses these
+#include "shutters\code\shutters.dm"
+// #include "soap\code\soap.dm" // Unused
+#include "surgery_table\code\surgery_table.dm"
+// #include "toolboxes\code\toolboxes.dm" // TG already uses these
+// #include "wallcloset\code\wallcloset.dm" // Unused
+#include "walls\code\walls.dm"
+#include "windoor\code\windoor.dm"
+// #include "windows\code\windows.dm" // Need more sprites and fixes
+// #include "windowtint\code\windowtint.dm" // Unused?
+// #include "zippo\code\zippo.dm" // Unused
diff --git a/modular_bandastation/aesthetics/airalarm/code/airalarm.dm b/modular_bandastation/aesthetics/airalarm/code/airalarm.dm
new file mode 100644
index 0000000000000..092473ebbc4e7
--- /dev/null
+++ b/modular_bandastation/aesthetics/airalarm/code/airalarm.dm
@@ -0,0 +1,18 @@
+/obj/machinery/airalarm
+ icon = 'modular_bandastation/aesthetics/airalarm/icons/airalarm.dmi'
+ layer = ABOVE_WINDOW_LAYER
+
+/obj/machinery/airalarm/Initialize(mapload, ndir, nbuild)
+ . = ..()
+ switch(dir) // TODO: do it in dmi
+ if(NORTH)
+ dir = SOUTH
+ if(SOUTH)
+ dir = NORTH
+ if(EAST)
+ dir = WEST
+ if(WEST)
+ dir = EAST
+
+/obj/item/wallframe/airalarm
+ icon = 'modular_bandastation/aesthetics/airalarm/icons/airalarm.dmi'
diff --git a/modular_bandastation/aesthetics/airalarm/icons/airalarm.dmi b/modular_bandastation/aesthetics/airalarm/icons/airalarm.dmi
new file mode 100644
index 0000000000000..3f607849c6998
Binary files /dev/null and b/modular_bandastation/aesthetics/airalarm/icons/airalarm.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/code/airlock.dm b/modular_bandastation/aesthetics/airlocks/code/airlock.dm
new file mode 100644
index 0000000000000..66a2ebc07cb16
--- /dev/null
+++ b/modular_bandastation/aesthetics/airlocks/code/airlock.dm
@@ -0,0 +1,28 @@
+/obj/machinery/door/airlock
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/public.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/station/overlays.dmi'
+ note_overlay_file = 'modular_bandastation/aesthetics/airlocks/icons/station/overlays.dmi'
+
+ doorOpen = 'modular_bandastation/aesthetics/airlocks/sound/open.ogg'
+ doorClose = 'modular_bandastation/aesthetics/airlocks/sound/close.ogg'
+ boltUp = 'modular_bandastation/aesthetics/airlocks/sound/bolts_up.ogg'
+ boltDown = 'modular_bandastation/aesthetics/airlocks/sound/bolts_down.ogg'
+
+/obj/machinery/door/airlock/update_overlays()
+ . = ..()
+ if(!lights || !hasPower())
+ return
+ var/light_state
+ switch(airlock_state)
+ if(AIRLOCK_CLOSED)
+ if(!locked && !emergency)
+ light_state = "poweron"
+ if(AIRLOCK_OPEN)
+ if(locked)
+ light_state = AIRLOCK_LIGHT_BOLTS
+ else if(emergency)
+ light_state = AIRLOCK_LIGHT_EMERGENCY
+ else
+ light_state = "poweron"
+ light_state = "[light_state]_open"
+ . += get_airlock_overlay("lights_[light_state]", overlays_file, src, em_block = FALSE)
diff --git a/modular_bandastation/aesthetics/airlocks/code/airlock_assembly_types.dm b/modular_bandastation/aesthetics/airlocks/code/airlock_assembly_types.dm
new file mode 100644
index 0000000000000..c821e533b859e
--- /dev/null
+++ b/modular_bandastation/aesthetics/airlocks/code/airlock_assembly_types.dm
@@ -0,0 +1,137 @@
+/obj/structure/door_assembly
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/public.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/station/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_public
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station2/glass.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/station2/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_com
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/command.dmi'
+
+/obj/structure/door_assembly/door_assembly_cap
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/cap.dmi'
+
+/obj/structure/door_assembly/door_assembly_hop
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/hop.dmi'
+
+/obj/structure/door_assembly/door_assembly_cmo
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/cmo.dmi'
+
+/obj/structure/door_assembly/door_assembly_rd
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/rd.dmi'
+
+/obj/structure/door_assembly/door_assembly_hos
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/hos.dmi'
+
+/obj/structure/door_assembly/door_assembly_qm
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/qm.dmi'
+
+/obj/structure/door_assembly/door_assembly_ce
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/ce.dmi'
+
+/obj/structure/door_assembly/door_assembly_sec
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/security.dmi'
+
+/obj/structure/door_assembly/door_assembly_eng
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/engineering.dmi'
+
+/obj/structure/door_assembly/door_assembly_min
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/mining.dmi'
+
+/obj/structure/door_assembly/door_assembly_atmo
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/atmos.dmi'
+
+/obj/structure/door_assembly/door_assembly_research
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/research.dmi'
+
+/obj/structure/door_assembly/door_assembly_science
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/science.dmi'
+
+/obj/structure/door_assembly/door_assembly_med
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/medical.dmi'
+
+/obj/structure/door_assembly/door_assembly_viro
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/virology.dmi'
+
+/obj/structure/door_assembly/door_assembly_hydro
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/botany.dmi'
+
+/obj/structure/door_assembly/door_assembly_eva
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/eva.dmi'
+
+/obj/structure/door_assembly/door_assembly_service
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/service.dmi'
+
+/obj/structure/door_assembly/door_assembly_psych
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/psych.dmi'
+
+/obj/structure/door_assembly/door_assembly_bathroom
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/bathroom.dmi'
+
+/obj/structure/door_assembly/door_assembly_lawyer
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/corporate.dmi'
+
+/obj/structure/door_assembly/door_assembly_mai
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/maintenance.dmi'
+
+/obj/structure/door_assembly/door_assembly_extmai
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/maintenanceexternal.dmi'
+
+/obj/structure/door_assembly/door_assembly_ext
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/external/external.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/external/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_fre
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/freezer.dmi'
+
+/obj/structure/door_assembly/door_assembly_hatch
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/hatch/centcom.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_mhatch
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/hatch/maintenance.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_highsecurity
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/highsec/highsec.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/highsec/overlays.dmi'
+
+/* Unused
+/obj/structure/door_assembly/multi_tile
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/glass_large/glass_large.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/glass_large/overlays.dmi'
+*/
+
+/obj/structure/door_assembly/door_assembly_centcom
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/centcom/centcom.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/centcom/overlays.dmi'
+
+/obj/structure/door_assembly/door_assembly_gold
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/gold.dmi'
+
+/obj/structure/door_assembly/door_assembly_silver
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/silver.dmi'
+
+/obj/structure/door_assembly/door_assembly_diamond
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/diamond.dmi'
+
+/obj/structure/door_assembly/door_assembly_uranium
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/uranium.dmi'
+
+/obj/structure/door_assembly/door_assembly_plasma
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/plasma.dmi'
+
+/obj/structure/door_assembly/door_assembly_bananium
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/bananium.dmi'
+
+/* Unused
+/obj/structure/door_assembly/door_assembly_tranquillite
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/tranquilite.dmi'
+*/
+
+/obj/structure/door_assembly/door_assembly_sandstone
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/sandstone.dmi'
+
+/obj/structure/door_assembly/door_assembly_wood
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/wood.dmi'
diff --git a/modular_bandastation/aesthetics/airlocks/code/airlock_types.dm b/modular_bandastation/aesthetics/airlocks/code/airlock_types.dm
new file mode 100644
index 0000000000000..611a44eb257b4
--- /dev/null
+++ b/modular_bandastation/aesthetics/airlocks/code/airlock_types.dm
@@ -0,0 +1,233 @@
+/*
+ Station Airlocks Regular
+*/
+/obj/machinery/door/airlock/command
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/command.dmi'
+
+/obj/machinery/door/airlock/command/cap
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/cap.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_cap
+
+/obj/machinery/door/airlock/command/hop
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/hop.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_hop
+
+/obj/machinery/door/airlock/command/cmo
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/cmo.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_cmo
+
+/obj/machinery/door/airlock/command/rd
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/rd.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_rd
+
+/obj/machinery/door/airlock/command/hos
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/hos.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_hos
+
+/obj/machinery/door/airlock/command/qm
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/qm.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_qm
+
+/obj/machinery/door/airlock/command/ce
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/heads/ce.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_ce
+
+/obj/machinery/door/airlock/security
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/security.dmi'
+
+/obj/machinery/door/airlock/engineering
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/engineering.dmi'
+
+/obj/machinery/door/airlock/medical
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/medical.dmi'
+
+/obj/machinery/door/airlock/maintenance
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/maintenance.dmi'
+
+/obj/machinery/door/airlock/maintenance/external
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/maintenanceexternal.dmi'
+
+/obj/machinery/door/airlock/mining
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/mining.dmi'
+
+/obj/machinery/door/airlock/atmos
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/atmos.dmi'
+
+/obj/machinery/door/airlock/research
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/research.dmi'
+
+/obj/machinery/door/airlock/freezer
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/freezer.dmi'
+
+/obj/machinery/door/airlock/science
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/science.dmi'
+
+/obj/machinery/door/airlock/virology
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/virology.dmi'
+
+/obj/machinery/door/airlock/hydroponics
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/botany.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_hydro
+
+/obj/machinery/door/airlock/eva
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/eva.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_eva
+
+/obj/machinery/door/airlock/service
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/service.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_service
+
+/obj/machinery/door/airlock/psych
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/psych.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_psych
+
+/obj/machinery/door/airlock/bathroom
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/bathroom.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_bathroom
+
+/obj/machinery/door/airlock/lawyer
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/corporate.dmi'
+ assemblytype = /obj/structure/door_assembly/door_assembly_lawyer
+
+/*
+ Station Airlocks Glass
+*/
+/obj/machinery/door/airlock/command/cap/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/hop/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/cmo/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/rd/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/hos/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/qm/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/command/ce/glass
+ opacity = 0
+ glass = TRUE
+ normal_integrity = 400
+
+/obj/machinery/door/airlock/hydroponics/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/eva/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/service/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/psych/glass
+ opacity = 0
+ glass = TRUE
+
+/obj/machinery/door/airlock/lawyer/glass
+ opacity = 0
+ glass = TRUE
+
+/*
+ Station Airlocks Mineral
+*/
+/obj/machinery/door/airlock/gold
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/gold.dmi'
+
+/obj/machinery/door/airlock/silver
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/silver.dmi'
+
+/obj/machinery/door/airlock/diamond
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/diamond.dmi'
+
+/obj/machinery/door/airlock/uranium
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/uranium.dmi'
+
+/obj/machinery/door/airlock/plasma
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/plasma.dmi'
+
+/obj/machinery/door/airlock/bananium
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/bananium.dmi'
+
+/* Unused
+/obj/machinery/door/airlock/tranquillite
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/tranquilite.dmi'
+*/
+
+/obj/machinery/door/airlock/sandstone
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/sandstone.dmi'
+
+/obj/machinery/door/airlock/wood
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station/wood.dmi'
+
+/*
+ Station2 Airlocks
+*/
+/obj/machinery/door/airlock/public
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/station2/glass.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/station2/overlays.dmi'
+
+/*
+ External Airlocks
+*/
+/obj/machinery/door/airlock/external
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/external/external.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/external/overlays.dmi'
+ note_overlay_file = 'modular_bandastation/aesthetics/airlocks/icons/external/overlays.dmi'
+
+/*
+ CentCom Airlocks
+*/
+/obj/machinery/door/airlock/centcom
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/centcom/centcom.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/centcom/overlays.dmi'
+
+/*
+ Hatch Airlocks
+*/
+/obj/machinery/door/airlock/hatch
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/hatch/centcom.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi'
+ note_overlay_file = 'modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/obj/machinery/door/airlock/maintenance_hatch
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/hatch/maintenance.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi'
+ note_overlay_file = 'modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi'
+
+/*
+ High Security Airlocks
+*/
+/obj/machinery/door/airlock/highsecurity
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/highsec/highsec.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/highsec/overlays.dmi'
+
+/*
+ Misc Airlocks
+*/
+
+/* Unused?
+/obj/machinery/door/airlock/multi_tile
+ icon = 'modular_bandastation/aesthetics/airlocks/icons/glass_large/glass_large.dmi'
+ overlays_file = 'modular_bandastation/aesthetics/airlocks/icons/glass_large/overlays.dmi'
+ note_overlay_file = 'modular_bandastation/aesthetics/airlocks/icons/glass_large/overlays.dmi'
+*/
diff --git a/modular_bandastation/aesthetics/airlocks/icons/centcom/centcom.dmi b/modular_bandastation/aesthetics/airlocks/icons/centcom/centcom.dmi
new file mode 100644
index 0000000000000..9d45122dd12c4
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/centcom/centcom.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/centcom/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/centcom/overlays.dmi
new file mode 100644
index 0000000000000..eb4f3d8500210
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/centcom/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/external/external.dmi b/modular_bandastation/aesthetics/airlocks/icons/external/external.dmi
new file mode 100644
index 0000000000000..8a00a16ba3c06
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/external/external.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/external/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/external/overlays.dmi
new file mode 100644
index 0000000000000..20e02d7a6c1cb
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/external/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/glass_large/glass_large.dmi b/modular_bandastation/aesthetics/airlocks/icons/glass_large/glass_large.dmi
new file mode 100644
index 0000000000000..ddd1871e6feff
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/glass_large/glass_large.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/glass_large/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/glass_large/overlays.dmi
new file mode 100644
index 0000000000000..bf5be7bccd4f1
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/glass_large/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/hatch/centcom.dmi b/modular_bandastation/aesthetics/airlocks/icons/hatch/centcom.dmi
new file mode 100644
index 0000000000000..64d15207b5992
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/hatch/centcom.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/hatch/maintenance.dmi b/modular_bandastation/aesthetics/airlocks/icons/hatch/maintenance.dmi
new file mode 100644
index 0000000000000..3303591517f6e
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/hatch/maintenance.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi
new file mode 100644
index 0000000000000..c426986694954
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/hatch/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/highsec/highsec.dmi b/modular_bandastation/aesthetics/airlocks/icons/highsec/highsec.dmi
new file mode 100644
index 0000000000000..eb5e312d93c96
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/highsec/highsec.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/highsec/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/highsec/overlays.dmi
new file mode 100644
index 0000000000000..97f2c7fc5c5cf
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/highsec/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/atmos.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/atmos.dmi
new file mode 100644
index 0000000000000..8bf12b5fa7516
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/atmos.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/bananium.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/bananium.dmi
new file mode 100644
index 0000000000000..cad8ba902d805
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/bananium.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/bathroom.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/bathroom.dmi
new file mode 100644
index 0000000000000..68fd49dc9ac14
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/bathroom.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/botany.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/botany.dmi
new file mode 100644
index 0000000000000..2a8ef28d0e1de
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/botany.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/corporate.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/corporate.dmi
new file mode 100644
index 0000000000000..3209e7f59ba46
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/corporate.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/diamond.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/diamond.dmi
new file mode 100644
index 0000000000000..e7ec1cffb65d4
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/diamond.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/engineering.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/engineering.dmi
new file mode 100644
index 0000000000000..f9e1f5915f79e
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/engineering.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/eva.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/eva.dmi
new file mode 100644
index 0000000000000..6c495b54322f7
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/eva.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/freezer.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/freezer.dmi
new file mode 100644
index 0000000000000..ad3a1c72a84ca
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/freezer.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/gold.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/gold.dmi
new file mode 100644
index 0000000000000..a2977d95cf9d6
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/gold.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/cap.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/cap.dmi
new file mode 100644
index 0000000000000..7417588b5390c
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/cap.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/ce.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/ce.dmi
new file mode 100644
index 0000000000000..2223e252cb00d
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/ce.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/cmo.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/cmo.dmi
new file mode 100644
index 0000000000000..a44e60ac2c62d
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/cmo.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/command.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/command.dmi
new file mode 100644
index 0000000000000..affe4c1fd2f9a
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/command.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/hop.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/hop.dmi
new file mode 100644
index 0000000000000..c17a3c2cebe7d
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/hop.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/hos.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/hos.dmi
new file mode 100644
index 0000000000000..9ddc7cb63ba18
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/hos.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/qm.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/qm.dmi
new file mode 100644
index 0000000000000..0ebfb5fd5e476
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/qm.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/heads/rd.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/heads/rd.dmi
new file mode 100644
index 0000000000000..cac461fb63c54
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/heads/rd.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/maintenance.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/maintenance.dmi
new file mode 100644
index 0000000000000..cc96c755a8e27
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/maintenance.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/maintenanceexternal.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/maintenanceexternal.dmi
new file mode 100644
index 0000000000000..c7d8e6154e8a5
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/maintenanceexternal.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/medical.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/medical.dmi
new file mode 100644
index 0000000000000..e33bff783ebc7
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/medical.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/mining.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/mining.dmi
new file mode 100644
index 0000000000000..86ec771471e34
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/mining.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/overlays.dmi
new file mode 100644
index 0000000000000..f385bd385a317
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/plasma.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/plasma.dmi
new file mode 100644
index 0000000000000..a90fa8f0bce58
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/plasma.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/psych.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/psych.dmi
new file mode 100644
index 0000000000000..d8bbc788f9ebd
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/psych.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/public.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/public.dmi
new file mode 100644
index 0000000000000..1e3258bd17087
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/public.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/research.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/research.dmi
new file mode 100644
index 0000000000000..6c3b231078589
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/research.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/sandstone.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/sandstone.dmi
new file mode 100644
index 0000000000000..ac4dc71790597
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/sandstone.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/science.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/science.dmi
new file mode 100644
index 0000000000000..95396c1d403e2
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/science.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/security.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/security.dmi
new file mode 100644
index 0000000000000..f38b9e05405d7
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/security.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/service.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/service.dmi
new file mode 100644
index 0000000000000..e155823d8d3d1
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/service.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/silver.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/silver.dmi
new file mode 100644
index 0000000000000..47105c88bf149
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/silver.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/tranquilite.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/tranquilite.dmi
new file mode 100644
index 0000000000000..851568e1dab29
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/tranquilite.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/uranium.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/uranium.dmi
new file mode 100644
index 0000000000000..d23811bacd40a
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/uranium.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/virology.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/virology.dmi
new file mode 100644
index 0000000000000..600dd9ee225b3
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/virology.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station/wood.dmi b/modular_bandastation/aesthetics/airlocks/icons/station/wood.dmi
new file mode 100644
index 0000000000000..b5973a7219f27
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station/wood.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station2/glass.dmi b/modular_bandastation/aesthetics/airlocks/icons/station2/glass.dmi
new file mode 100644
index 0000000000000..6c8259ab0de3e
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station2/glass.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/icons/station2/overlays.dmi b/modular_bandastation/aesthetics/airlocks/icons/station2/overlays.dmi
new file mode 100644
index 0000000000000..910a58b0d31f1
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/icons/station2/overlays.dmi differ
diff --git a/modular_bandastation/aesthetics/airlocks/sound/bolts_down.ogg b/modular_bandastation/aesthetics/airlocks/sound/bolts_down.ogg
new file mode 100644
index 0000000000000..19d62b8acb2ac
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/sound/bolts_down.ogg differ
diff --git a/modular_bandastation/aesthetics/airlocks/sound/bolts_up.ogg b/modular_bandastation/aesthetics/airlocks/sound/bolts_up.ogg
new file mode 100644
index 0000000000000..0aac1a44fcc55
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/sound/bolts_up.ogg differ
diff --git a/modular_bandastation/aesthetics/airlocks/sound/close.ogg b/modular_bandastation/aesthetics/airlocks/sound/close.ogg
new file mode 100644
index 0000000000000..db94b73fb4b63
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/sound/close.ogg differ
diff --git a/modular_bandastation/aesthetics/airlocks/sound/close_force.ogg b/modular_bandastation/aesthetics/airlocks/sound/close_force.ogg
new file mode 100644
index 0000000000000..28b190d8e0952
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/sound/close_force.ogg differ
diff --git a/modular_bandastation/aesthetics/airlocks/sound/open.ogg b/modular_bandastation/aesthetics/airlocks/sound/open.ogg
new file mode 100644
index 0000000000000..0b8a0d5f94f16
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/sound/open.ogg differ
diff --git a/modular_bandastation/aesthetics/airlocks/sound/open_force.ogg b/modular_bandastation/aesthetics/airlocks/sound/open_force.ogg
new file mode 100644
index 0000000000000..4caefc0b9e4d4
Binary files /dev/null and b/modular_bandastation/aesthetics/airlocks/sound/open_force.ogg differ
diff --git a/modular_bandastation/aesthetics/apc/code/apc.dm b/modular_bandastation/aesthetics/apc/code/apc.dm
new file mode 100644
index 0000000000000..8df6f9ac7a9a7
--- /dev/null
+++ b/modular_bandastation/aesthetics/apc/code/apc.dm
@@ -0,0 +1,6 @@
+/obj/machinery/power/apc
+ icon = 'modular_bandastation/aesthetics/apc/icons/apc.dmi'
+ layer = ABOVE_WINDOW_LAYER
+
+/obj/item/wallframe/apc
+ icon = 'modular_bandastation/aesthetics/apc/icons/apc.dmi'
diff --git a/modular_bandastation/aesthetics/apc/icons/apc.dmi b/modular_bandastation/aesthetics/apc/icons/apc.dmi
new file mode 100644
index 0000000000000..ce12d55fca863
Binary files /dev/null and b/modular_bandastation/aesthetics/apc/icons/apc.dmi differ
diff --git a/modular_bandastation/aesthetics/atm/code/atm.dm b/modular_bandastation/aesthetics/atm/code/atm.dm
new file mode 100644
index 0000000000000..4dd1a708a855d
--- /dev/null
+++ b/modular_bandastation/aesthetics/atm/code/atm.dm
@@ -0,0 +1,2 @@
+/obj/machinery/economy/atm
+ icon = 'modular_bandastation/aesthetics/atm/icons/atm.dmi'
diff --git a/modular_bandastation/aesthetics/atm/icons/atm.dmi b/modular_bandastation/aesthetics/atm/icons/atm.dmi
new file mode 100644
index 0000000000000..23e9a29ce8a6c
Binary files /dev/null and b/modular_bandastation/aesthetics/atm/icons/atm.dmi differ
diff --git a/modular_bandastation/aesthetics/atmospherics/code/atmospherics.dm b/modular_bandastation/aesthetics/atmospherics/code/atmospherics.dm
new file mode 100644
index 0000000000000..0082fb87677b6
--- /dev/null
+++ b/modular_bandastation/aesthetics/atmospherics/code/atmospherics.dm
@@ -0,0 +1,5 @@
+/obj/machinery/atmospherics/components/unary/vent_pump
+ icon = 'modular_bandastation/aesthetics/atmospherics/icons/vent_pump.dmi'
+
+/obj/machinery/atmospherics/components/unary/vent_scrubber
+ icon = 'modular_bandastation/aesthetics/atmospherics/icons/vent_scrubber.dmi'
diff --git a/modular_bandastation/aesthetics/atmospherics/icons/vent_pump.dmi b/modular_bandastation/aesthetics/atmospherics/icons/vent_pump.dmi
new file mode 100644
index 0000000000000..52aeaf450ac27
Binary files /dev/null and b/modular_bandastation/aesthetics/atmospherics/icons/vent_pump.dmi differ
diff --git a/modular_bandastation/aesthetics/atmospherics/icons/vent_scrubber.dmi b/modular_bandastation/aesthetics/atmospherics/icons/vent_scrubber.dmi
new file mode 100644
index 0000000000000..4639f470fa517
Binary files /dev/null and b/modular_bandastation/aesthetics/atmospherics/icons/vent_scrubber.dmi differ
diff --git a/modular_bandastation/aesthetics/better_ids/code/better_ids.dm b/modular_bandastation/aesthetics/better_ids/code/better_ids.dm
new file mode 100644
index 0000000000000..6ba748960047c
--- /dev/null
+++ b/modular_bandastation/aesthetics/better_ids/code/better_ids.dm
@@ -0,0 +1,5 @@
+/obj/item/card
+ icon = 'modular_bandastation/aesthetics/better_ids/icons/better_ids.dmi'
+
+/obj/item/nanomob_card
+ icon = 'modular_bandastation/aesthetics/better_ids/icons/better_ids.dmi'
diff --git a/modular_bandastation/aesthetics/better_ids/icons/better_ids.dmi b/modular_bandastation/aesthetics/better_ids/icons/better_ids.dmi
new file mode 100644
index 0000000000000..f66a128decab9
Binary files /dev/null and b/modular_bandastation/aesthetics/better_ids/icons/better_ids.dmi differ
diff --git a/modular_bandastation/aesthetics/blastdoor/code/blastdoor.dm b/modular_bandastation/aesthetics/blastdoor/code/blastdoor.dm
new file mode 100644
index 0000000000000..804b6f7ec105b
--- /dev/null
+++ b/modular_bandastation/aesthetics/blastdoor/code/blastdoor.dm
@@ -0,0 +1,3 @@
+/obj/machinery/door/poddoor
+ icon = 'modular_bandastation/aesthetics/blastdoor/icons/blastdoor.dmi'
+ animation_sound = 'modular_bandastation/aesthetics/blastdoor/sound/blastdoor.ogg'
diff --git a/modular_bandastation/aesthetics/blastdoor/icons/blastdoor.dmi b/modular_bandastation/aesthetics/blastdoor/icons/blastdoor.dmi
new file mode 100644
index 0000000000000..672c161c10ed7
Binary files /dev/null and b/modular_bandastation/aesthetics/blastdoor/icons/blastdoor.dmi differ
diff --git a/modular_bandastation/aesthetics/blastdoor/sound/blastdoor.ogg b/modular_bandastation/aesthetics/blastdoor/sound/blastdoor.ogg
new file mode 100644
index 0000000000000..93e53513985a4
Binary files /dev/null and b/modular_bandastation/aesthetics/blastdoor/sound/blastdoor.ogg differ
diff --git a/modular_bandastation/aesthetics/boxes/code/boxes.dm b/modular_bandastation/aesthetics/boxes/code/boxes.dm
new file mode 100644
index 0000000000000..5864c48233d04
--- /dev/null
+++ b/modular_bandastation/aesthetics/boxes/code/boxes.dm
@@ -0,0 +1,287 @@
+/obj/item/storage/box/survival
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "civ_box"
+
+/obj/item/storage/box/survival_vox
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "vox_box"
+
+/obj/item/storage/box/survival_plasmaman
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "plasma_box"
+
+/obj/item/storage/box/engineer
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "eng_box"
+
+/obj/item/storage/box/survival_mining
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "min_box"
+
+/obj/item/storage/box/survival_syndi
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "syndie_box"
+
+/obj/item/storage/box/gloves
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "latex_box"
+
+/obj/item/storage/box/masks
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "sterile_box"
+
+/obj/item/storage/box/syringes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "syringe_box"
+
+/obj/item/storage/box/beakers
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "beaker_box"
+
+/obj/item/storage/box/beakers/bluespace
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "beaker_bluespace_box"
+
+/obj/item/storage/box/iv_bags
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "iv_box"
+
+/obj/item/storage/box/injectors
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "syringe_box"
+
+/obj/item/storage/box/flashbangs
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "flashbang_box"
+
+/obj/item/storage/box/flashes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "flash_box"
+
+/obj/item/storage/box/teargas
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "teargas_box"
+
+/obj/item/storage/box/emps
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "emp_box"
+
+/obj/item/storage/box/trackimp
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/minertracker
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/chemimp
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/exileimp
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/deathimp
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/tapes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "tape_box"
+
+/obj/item/storage/box/rxglasses
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "glasses_box"
+
+/obj/item/storage/box/drinkingglasses
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/condimentbottles
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/cups
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "cup_box"
+
+/obj/item/storage/box/donkpockets
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "donk_box"
+
+/obj/item/storage/box/syndidonkpockets
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_syndie_box" // TODO: Need new icon
+
+/obj/item/storage/box/monkeycubes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "monkey_box"
+
+/obj/item/storage/box/monkeycubes/neaeracubes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "neaera_box"
+
+/obj/item/storage/box/monkeycubes/stokcubes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "stok_box"
+
+/obj/item/storage/box/monkeycubes/farwacubes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "farwa_box"
+
+/obj/item/storage/box/monkeycubes/wolpincubes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "wolpin_box"
+
+/obj/item/storage/box/permits
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_box"
+
+/obj/item/storage/box/ids
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_box"
+
+/obj/item/storage/box/prisoner
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "id_box"
+
+/obj/item/storage/box/seccarts
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "pda_box"
+
+/obj/item/storage/box/holobadge
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "badge_box"
+
+/obj/item/storage/box/evidence
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "evidence_box"
+
+/obj/item/storage/box/handcuffs
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "handcuff_box"
+
+/obj/item/storage/box/zipties
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "zipties_box"
+
+/obj/item/storage/box/alienhandcuffs
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "alien_handcuff_box"
+
+/obj/item/storage/box/fakesyndiesuit
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "doom_box"
+
+/obj/item/storage/box/enforcer_rubber
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/enforcer_lethal
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/bartender_rare_ingredients_kit
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/chef_rare_ingredients_kit
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/mousetraps
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "mousetraps_box"
+
+/obj/item/storage/box/pillbottles
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "pill_box"
+
+/obj/item/storage/box/patch_packs
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "patch_box"
+
+/obj/item/storage/box/bodybags
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "bodybag_box"
+
+/obj/item/storage/box/autoinjectors
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "injector_box"
+
+/obj/item/storage/box/lights
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "light_box"
+
+/obj/item/storage/box/lights/tubes
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "lighttube_box"
+
+/obj/item/storage/box/lights/mixed
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "lightmixed_box"
+
+/obj/item/storage/box/barber
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/lip_stick
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "implant_box"
+
+/obj/item/storage/box/centcomofficer
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/responseteam
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "ert_box"
+
+/obj/item/storage/box/deathsquad
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "doom_box"
+
+/obj/item/storage/box/soviet
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "soviet_box"
+
+/obj/item/storage/box/clown
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "clown_box"
+
+/obj/item/storage/box/emptysandbags
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/rndboards
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/stockparts
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/hug
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "hug_box"
+
+/obj/item/storage/box/wizard
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "wizard_box"
+
+/obj/item/storage/box/breaching
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "flashbang_box"
+
+/obj/item/storage/box/mindshield
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/dish_drive
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "box"
+
+/obj/item/storage/box/disks_plantgene
+ icon = 'modular_bandastation/aesthetics/boxes/icons/boxes.dmi'
+ icon_state = "disk_box"
diff --git a/modular_bandastation/aesthetics/boxes/icons/boxes.dmi b/modular_bandastation/aesthetics/boxes/icons/boxes.dmi
new file mode 100644
index 0000000000000..97467e6f3b1da
Binary files /dev/null and b/modular_bandastation/aesthetics/boxes/icons/boxes.dmi differ
diff --git a/modular_bandastation/aesthetics/cameras/code/cameras.dm b/modular_bandastation/aesthetics/cameras/code/cameras.dm
new file mode 100644
index 0000000000000..68cec7d461d6e
--- /dev/null
+++ b/modular_bandastation/aesthetics/cameras/code/cameras.dm
@@ -0,0 +1,15 @@
+/obj/machinery/camera
+ icon = 'modular_bandastation/aesthetics/cameras/icons/cameras.dmi'
+ // TODO: camera_in_use
+
+/obj/machinery/camera/Initialize(mapload, obj/structure/camera_assembly/old_assembly)
+ . = ..()
+ switch(dir) // TODO: do it in dmi
+ if(NORTH)
+ dir = SOUTH
+ if(SOUTH)
+ dir = NORTH
+ if(EAST)
+ dir = WEST
+ if(WEST)
+ dir = EAST
diff --git a/modular_bandastation/aesthetics/cameras/icons/cameras.dmi b/modular_bandastation/aesthetics/cameras/icons/cameras.dmi
new file mode 100644
index 0000000000000..26b8700a147c4
Binary files /dev/null and b/modular_bandastation/aesthetics/cameras/icons/cameras.dmi differ
diff --git a/modular_bandastation/aesthetics/chairs/code/chairs.dm b/modular_bandastation/aesthetics/chairs/code/chairs.dm
new file mode 100644
index 0000000000000..356b81c6d89e6
--- /dev/null
+++ b/modular_bandastation/aesthetics/chairs/code/chairs.dm
@@ -0,0 +1,35 @@
+/obj/structure/chair/comfy
+ icon = 'modular_bandastation/aesthetics/chairs/icons/chairs.dmi'
+
+/obj/structure/chair/comfy/GetArmrest()
+ return mutable_appearance('modular_bandastation/aesthetics/chairs/icons/chairs.dmi', "[icon_state]_armrest")
+
+/obj/structure/chair/comfy/corp
+ icon = 'icons/obj/chairs.dmi'
+
+/obj/structure/chair/comfy/shuttle
+ icon = 'icons/obj/chairs.dmi'
+
+/obj/structure/chair/office/dark
+ icon = 'modular_bandastation/aesthetics/chairs/icons/chairs.dmi'
+
+/obj/structure/chair/office/light
+ icon = 'modular_bandastation/aesthetics/chairs/icons/chairs.dmi'
+
+/obj/structure/chair/e_chair
+ icon = 'modular_bandastation/aesthetics/chairs/icons/chairs.dmi'
+
+//TODO: Support or chairs
+
+/obj/item/chair/stool/bar/dark
+ icon = 'modular_bandastation/aesthetics/chairs/icons/chairs.dmi'
+ icon_state = "bar_toppled_dark"
+ item_state = "stool_bar_dark"
+ origin_type = /obj/structure/chair/stool/bar/dark
+ lefthand_file = 'modular_bandastation/aesthetics/chairs/icons/chairs_lefthand.dmi'
+ righthand_file = 'modular_bandastation/aesthetics/chairs/icons/chairs_righthand.dmi'
+
+/obj/structure/chair/stool/bar/dark
+ icon = 'modular_bandastation/aesthetics/chairs/icons/chairs.dmi'
+ icon_state = "bar_dark"
+ item_chair = /obj/item/chair/stool/bar/dark
diff --git a/modular_bandastation/aesthetics/chairs/icons/chairs.dmi b/modular_bandastation/aesthetics/chairs/icons/chairs.dmi
new file mode 100644
index 0000000000000..6aba9bf8697ab
Binary files /dev/null and b/modular_bandastation/aesthetics/chairs/icons/chairs.dmi differ
diff --git a/modular_bandastation/aesthetics/chairs/icons/chairs_lefthand.dmi b/modular_bandastation/aesthetics/chairs/icons/chairs_lefthand.dmi
new file mode 100644
index 0000000000000..f3238b0616ba9
Binary files /dev/null and b/modular_bandastation/aesthetics/chairs/icons/chairs_lefthand.dmi differ
diff --git a/modular_bandastation/aesthetics/chairs/icons/chairs_righthand.dmi b/modular_bandastation/aesthetics/chairs/icons/chairs_righthand.dmi
new file mode 100644
index 0000000000000..ac882e8ebf82b
Binary files /dev/null and b/modular_bandastation/aesthetics/chairs/icons/chairs_righthand.dmi differ
diff --git a/modular_bandastation/aesthetics/decals/code/decals.dm b/modular_bandastation/aesthetics/decals/code/decals.dm
new file mode 100644
index 0000000000000..07fdc75819380
--- /dev/null
+++ b/modular_bandastation/aesthetics/decals/code/decals.dm
@@ -0,0 +1,78 @@
+// SIDING
+/obj/effect/turf_decal/siding/wood
+ icon = 'modular_bandastation/aesthetics/decals/icons/siding.dmi'
+ icon_state = "siding_wood_line"
+ color = "#55391A"
+
+/obj/effect/turf_decal/siding/wood/corner
+ icon_state = "siding_wood_corner"
+
+/obj/effect/turf_decal/siding/wood/end
+ icon_state = "siding_wood_end"
+
+/obj/effect/turf_decal/siding
+ icon = 'modular_bandastation/aesthetics/decals/icons/siding.dmi'
+ icon_state = "siding_line"
+
+/obj/effect/turf_decal/siding/white
+ color = "#BCBCBC"
+
+/obj/effect/turf_decal/siding/white/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/white/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/red
+ color = "#DE3A3A"
+
+/obj/effect/turf_decal/siding/red/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/red/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/green
+ color = "#9FED58"
+
+/obj/effect/turf_decal/siding/green/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/green/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/blue
+ color = "#52B4E9"
+
+/obj/effect/turf_decal/siding/blue/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/blue/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/yellow
+ color = "#EFB341"
+
+/obj/effect/turf_decal/siding/yellow/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/yellow/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/purple
+ color = "#D381C9"
+
+/obj/effect/turf_decal/siding/purple/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/purple/end
+ icon_state = "siding_end"
+
+/obj/effect/turf_decal/siding/brown
+ color = "#A46106"
+
+/obj/effect/turf_decal/siding/brown/corner
+ icon_state = "siding_corner"
+
+/obj/effect/turf_decal/siding/brown/end
+ icon_state = "siding_end"
diff --git a/modular_bandastation/aesthetics/decals/icons/siding.dmi b/modular_bandastation/aesthetics/decals/icons/siding.dmi
new file mode 100644
index 0000000000000..f3ac9b02751ad
Binary files /dev/null and b/modular_bandastation/aesthetics/decals/icons/siding.dmi differ
diff --git a/modular_bandastation/aesthetics/defib/code/defib.dm b/modular_bandastation/aesthetics/defib/code/defib.dm
new file mode 100644
index 0000000000000..4687a9b237d58
--- /dev/null
+++ b/modular_bandastation/aesthetics/defib/code/defib.dm
@@ -0,0 +1,6 @@
+/obj/item/defibrillator
+ icon = 'modular_bandastation/aesthetics/defib/icons/defib.dmi'
+
+/obj/item/defibrillator/compact
+ icon = 'icons/obj/defib.dmi'
+ //TODO: Compact defibs
diff --git a/modular_bandastation/aesthetics/defib/icons/defib.dmi b/modular_bandastation/aesthetics/defib/icons/defib.dmi
new file mode 100644
index 0000000000000..f4fbe3712d4e6
Binary files /dev/null and b/modular_bandastation/aesthetics/defib/icons/defib.dmi differ
diff --git a/modular_bandastation/aesthetics/dirwindows/code/dirwindows.dm b/modular_bandastation/aesthetics/dirwindows/code/dirwindows.dm
new file mode 100644
index 0000000000000..68774c2056786
--- /dev/null
+++ b/modular_bandastation/aesthetics/dirwindows/code/dirwindows.dm
@@ -0,0 +1,14 @@
+/obj/structure/window
+ icon = 'modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/reinforced
+ icon = 'modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/plasma
+ icon = 'modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/reinforced/tinted
+ icon = 'modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi'
+
+/obj/structure/window/reinforced/tinted/frosted
+ icon = 'modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi'
diff --git a/modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi b/modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi
new file mode 100644
index 0000000000000..c9aa5b12d9115
Binary files /dev/null and b/modular_bandastation/aesthetics/dirwindows/icons/dirwindows.dmi differ
diff --git a/modular_bandastation/aesthetics/door_control/code/door_control.dm b/modular_bandastation/aesthetics/door_control/code/door_control.dm
new file mode 100644
index 0000000000000..4e7c60ff77e58
--- /dev/null
+++ b/modular_bandastation/aesthetics/door_control/code/door_control.dm
@@ -0,0 +1,2 @@
+/obj/machinery/door_control
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_bandastation/aesthetics/extinguisher/code/extinguisher.dm b/modular_bandastation/aesthetics/extinguisher/code/extinguisher.dm
new file mode 100644
index 0000000000000..4999d73d21b72
--- /dev/null
+++ b/modular_bandastation/aesthetics/extinguisher/code/extinguisher.dm
@@ -0,0 +1,3 @@
+/obj/structure/extinguisher_cabinet
+ icon = 'modular_bandastation/aesthetics/extinguisher/icons/extinguisher.dmi'
+ //TODO: Frame, advanced, crafted, unlocked/locked?
diff --git a/modular_bandastation/aesthetics/extinguisher/icons/extinguisher.dmi b/modular_bandastation/aesthetics/extinguisher/icons/extinguisher.dmi
new file mode 100644
index 0000000000000..a1041de37c230
Binary files /dev/null and b/modular_bandastation/aesthetics/extinguisher/icons/extinguisher.dmi differ
diff --git a/modular_bandastation/aesthetics/firealarm/code/firealarm.dm b/modular_bandastation/aesthetics/firealarm/code/firealarm.dm
new file mode 100644
index 0000000000000..f380c19cc1fec
--- /dev/null
+++ b/modular_bandastation/aesthetics/firealarm/code/firealarm.dm
@@ -0,0 +1,7 @@
+/obj/machinery/firealarm
+ icon = 'modular_bandastation/aesthetics/firealarm/icons/firealarm.dmi'
+ layer = ABOVE_WINDOW_LAYER
+ //TODO: Detect
+
+/obj/item/mounted/frame/firealarm
+ icon = 'modular_bandastation/aesthetics/firealarm/icons/firealarm.dmi'
diff --git a/modular_bandastation/aesthetics/firealarm/icons/firealarm.dmi b/modular_bandastation/aesthetics/firealarm/icons/firealarm.dmi
new file mode 100644
index 0000000000000..03fa8022c7149
Binary files /dev/null and b/modular_bandastation/aesthetics/firealarm/icons/firealarm.dmi differ
diff --git a/modular_bandastation/aesthetics/floors/code/floors.dm b/modular_bandastation/aesthetics/floors/code/floors.dm
new file mode 100644
index 0000000000000..e3ab11f14ad80
--- /dev/null
+++ b/modular_bandastation/aesthetics/floors/code/floors.dm
@@ -0,0 +1,55 @@
+/turf/open/floor
+ icon = 'modular_bandastation/aesthetics/floors/icons/floors.dmi'
+
+// WOODEN FLOORS
+/turf/simulated/floor/wood/oak
+ icon_state = "wood-oak"
+ floor_tile = /obj/item/stack/tile/wood/oak
+ broken_states = list("wood-oak-broken", "wood-oak-broken2", "wood-oak-broken3", "wood-oak-broken4", "wood-oak-broken5", "wood-oak-broken6", "wood-oak-broken7")
+
+/turf/simulated/floor/wood/birch
+ icon_state = "wood-birch"
+ floor_tile = /obj/item/stack/tile/wood/birch
+ broken_states = list("wood-birch-broken", "wood-birch-broken2", "wood-birch-broken3", "wood-birch-broken4", "wood-birch-broken5", "wood-birch-broken6", "wood-birch-broken7")
+
+/turf/simulated/floor/wood/cherry
+ icon_state = "wood-cherry"
+ floor_tile = /obj/item/stack/tile/wood/cherry
+ broken_states = list("wood-cherry-broken", "wood-cherry-broken2", "wood-cherry-broken3", "wood-cherry-broken4", "wood-cherry-broken5", "wood-cherry-broken6", "wood-cherry-broken7")
+
+/turf/simulated/floor/wood/fancy/oak
+ icon_state = "fancy-wood-oak"
+ floor_tile = /obj/item/stack/tile/wood/fancy/oak
+ broken_states = list("fancy-wood-oak-broken", "fancy-wood-oak-broken2", "fancy-wood-oak-broken3", "fancy-wood-oak-broken4", "fancy-wood-oak-broken5", "fancy-wood-oak-broken6", "fancy-wood-oak-broken7")
+
+/turf/simulated/floor/wood/fancy/birch
+ icon_state = "fancy-wood-birch"
+ floor_tile = /obj/item/stack/tile/wood/fancy/birch
+ broken_states = list("fancy-wood-birch-broken", "fancy-wood-birch-broken2", "fancy-wood-birch-broken3", "fancy-wood-birch-broken4", "fancy-wood-birch-broken5", "fancy-wood-birch-broken6", "fancy-wood-birch-broken7")
+
+/turf/simulated/floor/wood/fancy/cherry
+ icon_state = "fancy-wood-cherry"
+ floor_tile = /obj/item/stack/tile/wood/fancy/cherry
+ broken_states = list("fancy-wood-cherry-broken", "fancy-wood-cherry-broken2", "fancy-wood-cherry-broken3", "fancy-wood-cherry-broken4", "fancy-wood-cherry-broken5", "fancy-wood-cherry-broken6", "fancy-wood-cherry-broken7")
+
+/turf/simulated/floor/wood/fancy/light
+ icon_state = "light-fancy-wood"
+ floor_tile = /obj/item/stack/tile/wood/fancy/light
+ broken_states = list("light-fancy-wood-broken", "light-fancy-wood-broken2", "light-fancy-wood-broken3", "light-fancy-wood-broken4", "light-fancy-wood-broken5", "light-fancy-wood-broken6", "light-fancy-wood-broken7")
+
+// LIGHT FLOORS
+/turf/simulated/floor/light/red
+ color = "#f23030"
+ light_color = "#f23030"
+
+/turf/simulated/floor/light/green
+ color = "#30f230"
+ light_color = "#30f230"
+
+/turf/simulated/floor/light/blue
+ color = "#3030f2"
+ light_color = "#3030f2"
+
+/turf/simulated/floor/light/purple
+ color = "#9130f2"
+ light_color = "#9130f2"
diff --git a/modular_bandastation/aesthetics/floors/code/tile_types.dm b/modular_bandastation/aesthetics/floors/code/tile_types.dm
new file mode 100644
index 0000000000000..d4bf3fa165ae0
--- /dev/null
+++ b/modular_bandastation/aesthetics/floors/code/tile_types.dm
@@ -0,0 +1,42 @@
+// WOOD
+/obj/item/stack/tile/wood/oak
+ name = "oak wood floor tiles"
+ singular_name = "oak wood floor tile"
+ icon_state = "tile-wood-oak"
+ turf_type = /turf/simulated/floor/wood/oak
+
+/obj/item/stack/tile/wood/birch
+ name = "birch wood floor tiles"
+ singular_name = "birch wood floor tile"
+ icon_state = "tile-wood-birch"
+ turf_type = /turf/simulated/floor/wood/birch
+
+/obj/item/stack/tile/wood/cherry
+ name = "cherry wood floor tiles"
+ singular_name = "cherry wood floor tile"
+ icon_state = "tile-wood-cherry"
+ turf_type = /turf/simulated/floor/wood/cherry
+
+/obj/item/stack/tile/wood/fancy/oak
+ name = "fancy oak wood floor tiles"
+ singular_name = "fancy oak wood floor tile"
+ icon_state = "tile-wood-fancy-oak"
+ turf_type = /turf/simulated/floor/wood/fancy/oak
+
+/obj/item/stack/tile/wood/fancy/birch
+ name = "fancy birch wood floor tiles"
+ singular_name = "fancy birch wood floor tile"
+ icon_state = "tile-wood-fancy-birch"
+ turf_type = /turf/simulated/floor/wood/fancy/birch
+
+/obj/item/stack/tile/wood/fancy/cherry
+ name = "fancy cherry wood floor tiles"
+ singular_name = "fancy cherry wood floor tile"
+ icon_state = "tile-wood-fancy-cherry"
+ turf_type = /turf/simulated/floor/wood/fancy/cherry
+
+/obj/item/stack/tile/wood/fancy/light
+ name = "fancy light oak wood floor tiles"
+ singular_name = "fancy light oak wood floor tile"
+ icon_state = "tile-wood-fancy-light"
+ turf_type = /turf/simulated/floor/wood/fancy/light
diff --git a/modular_bandastation/aesthetics/floors/icons/floors.dmi b/modular_bandastation/aesthetics/floors/icons/floors.dmi
new file mode 100644
index 0000000000000..90ef6b41c8add
Binary files /dev/null and b/modular_bandastation/aesthetics/floors/icons/floors.dmi differ
diff --git a/modular_bandastation/aesthetics/hydroponics/code/hydroponics.dm b/modular_bandastation/aesthetics/hydroponics/code/hydroponics.dm
new file mode 100644
index 0000000000000..b757b8df2e8e5
--- /dev/null
+++ b/modular_bandastation/aesthetics/hydroponics/code/hydroponics.dm
@@ -0,0 +1,23 @@
+/* Unused
+/obj/machinery/plantgenes
+ icon = 'modular_bandastation/aesthetics/hydroponics/icons/hydroponics.dmi'
+
+/obj/machinery/plantgenes/update_overlays()
+ . = ..()
+ if(disk)
+ . += "dnamod-disk"
+
+/obj/machinery/plantgenes/add_disk(obj/item/disk/plantgene/new_disk, mob/user)
+ . = ..()
+ update_icon(UPDATE_OVERLAYS)
+
+/obj/machinery/plantgenes/update_genes()
+ . = ..()
+ update_icon(UPDATE_OVERLAYS)
+*/
+
+/obj/item/storage/bag/plants
+ icon = 'modular_bandastation/aesthetics/hydroponics/icons/hydroponics.dmi'
+
+/obj/structure/loom
+ icon = 'modular_bandastation/aesthetics/hydroponics/icons/hydroponics.dmi'
diff --git a/modular_bandastation/aesthetics/hydroponics/icons/hydroponics.dmi b/modular_bandastation/aesthetics/hydroponics/icons/hydroponics.dmi
new file mode 100644
index 0000000000000..82d7d663b9f0d
Binary files /dev/null and b/modular_bandastation/aesthetics/hydroponics/icons/hydroponics.dmi differ
diff --git a/modular_bandastation/aesthetics/intercom/code/intercom.dm b/modular_bandastation/aesthetics/intercom/code/intercom.dm
new file mode 100644
index 0000000000000..3b0e1a9ca48a1
--- /dev/null
+++ b/modular_bandastation/aesthetics/intercom/code/intercom.dm
@@ -0,0 +1,2 @@
+/obj/item/radio/intercom
+ icon = 'modular_bandastation/aesthetics/intercom/icons/intercom.dmi'
diff --git a/modular_bandastation/aesthetics/intercom/icons/intercom.dmi b/modular_bandastation/aesthetics/intercom/icons/intercom.dmi
new file mode 100644
index 0000000000000..3e9fbfd93583b
Binary files /dev/null and b/modular_bandastation/aesthetics/intercom/icons/intercom.dmi differ
diff --git a/modular_bandastation/aesthetics/keycard/code/keycard.dm b/modular_bandastation/aesthetics/keycard/code/keycard.dm
new file mode 100644
index 0000000000000..282b7ee22be88
--- /dev/null
+++ b/modular_bandastation/aesthetics/keycard/code/keycard.dm
@@ -0,0 +1,30 @@
+/obj/machinery/keycard_auth
+ icon = 'modular_bandastation/aesthetics/keycard/icons/keycard.dmi'
+ // TODO: update auth_on
+
+/obj/machinery/keycard_auth/Initialize(mapload)
+ . = ..()
+ switch(dir) // TODO: do it in dmi
+ if(NORTH)
+ dir = SOUTH
+ if(SOUTH)
+ dir = NORTH
+ if(EAST)
+ dir = WEST
+ if(WEST)
+ dir = EAST
+
+/obj/machinery/readybutton
+ icon = 'modular_bandastation/aesthetics/keycard/icons/keycard.dmi'
+
+/obj/machinery/readybutton/Initialize(mapload)
+ . = ..()
+ switch(dir) // TODO: do it in dmi
+ if(NORTH)
+ dir = SOUTH
+ if(SOUTH)
+ dir = NORTH
+ if(EAST)
+ dir = WEST
+ if(WEST)
+ dir = EAST
diff --git a/modular_bandastation/aesthetics/keycard/icons/keycard.dmi b/modular_bandastation/aesthetics/keycard/icons/keycard.dmi
new file mode 100644
index 0000000000000..39bdcf13f9628
Binary files /dev/null and b/modular_bandastation/aesthetics/keycard/icons/keycard.dmi differ
diff --git a/modular_bandastation/aesthetics/labeler/code/labeler.dm b/modular_bandastation/aesthetics/labeler/code/labeler.dm
new file mode 100644
index 0000000000000..5395293450587
--- /dev/null
+++ b/modular_bandastation/aesthetics/labeler/code/labeler.dm
@@ -0,0 +1,2 @@
+/obj/item/hand_labeler
+ icon = 'modular_bandastation/aesthetics/labeler/icons/labeler.dmi'
diff --git a/modular_bandastation/aesthetics/labeler/icons/labeler.dmi b/modular_bandastation/aesthetics/labeler/icons/labeler.dmi
new file mode 100644
index 0000000000000..74946042a96af
Binary files /dev/null and b/modular_bandastation/aesthetics/labeler/icons/labeler.dmi differ
diff --git a/modular_bandastation/aesthetics/library/code/library.dm b/modular_bandastation/aesthetics/library/code/library.dm
new file mode 100644
index 0000000000000..9d7531aab9411
--- /dev/null
+++ b/modular_bandastation/aesthetics/library/code/library.dm
@@ -0,0 +1,5 @@
+/obj/structure/bookcase
+ icon = 'modular_bandastation/aesthetics/library/icons/library.dmi'
+
+/obj/machinery/bookbinder
+ icon = 'modular_bandastation/aesthetics/library/icons/library.dmi'
diff --git a/modular_bandastation/aesthetics/library/icons/library.dmi b/modular_bandastation/aesthetics/library/icons/library.dmi
new file mode 100644
index 0000000000000..ef1556c35bc70
Binary files /dev/null and b/modular_bandastation/aesthetics/library/icons/library.dmi differ
diff --git a/modular_bandastation/aesthetics/light_switch/code/light_switch.dm b/modular_bandastation/aesthetics/light_switch/code/light_switch.dm
new file mode 100644
index 0000000000000..d33b7182510f8
--- /dev/null
+++ b/modular_bandastation/aesthetics/light_switch/code/light_switch.dm
@@ -0,0 +1,2 @@
+/obj/machinery/light_switch
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_bandastation/aesthetics/newscaster/code/newscaster.dm b/modular_bandastation/aesthetics/newscaster/code/newscaster.dm
new file mode 100644
index 0000000000000..9b7fc4f710533
--- /dev/null
+++ b/modular_bandastation/aesthetics/newscaster/code/newscaster.dm
@@ -0,0 +1,2 @@
+/obj/machinery/newscaster
+ layer = ABOVE_WINDOW_LAYER
diff --git a/modular_bandastation/aesthetics/piano/code/piano.dm b/modular_bandastation/aesthetics/piano/code/piano.dm
new file mode 100644
index 0000000000000..5bd6b58a40b10
--- /dev/null
+++ b/modular_bandastation/aesthetics/piano/code/piano.dm
@@ -0,0 +1,3 @@
+/obj/structure/musician/piano
+ icon = 'modular_bandastation/aesthetics/piano/icons/piano.dmi'
+ icon_state = "minipiano"
diff --git a/modular_bandastation/aesthetics/piano/icons/piano.dmi b/modular_bandastation/aesthetics/piano/icons/piano.dmi
new file mode 100644
index 0000000000000..c37a392395cea
Binary files /dev/null and b/modular_bandastation/aesthetics/piano/icons/piano.dmi differ
diff --git a/modular_bandastation/aesthetics/racks/code/racks.dm b/modular_bandastation/aesthetics/racks/code/racks.dm
new file mode 100644
index 0000000000000..c7a488e4ebf84
--- /dev/null
+++ b/modular_bandastation/aesthetics/racks/code/racks.dm
@@ -0,0 +1,75 @@
+/obj/structure/rack
+ icon = 'modular_bandastation/aesthetics/racks/icons/racks.dmi'
+
+/obj/structure/rack/gunrack
+ name = "gun rack"
+ desc = "A gun rack for storing guns."
+ icon_state = "gunrack"
+
+/obj/item/gun
+ var/on_rack = FALSE
+
+/obj/item/gun/proc/place_on_rack()
+ on_rack = TRUE
+ var/matrix/M = matrix()
+ M.Turn(-90)
+ transform = M
+
+/obj/item/gun/proc/remove_from_rack()
+ if(on_rack)
+ var/matrix/M = matrix()
+ transform = M
+ on_rack = FALSE
+
+/obj/item/gun/pickup(mob/user)
+ . = ..()
+ remove_from_rack()
+
+/obj/structure/rack/gunrack/MouseDrop_T(obj/O, mob/user)
+ if(!(istype(O, /obj/item/gun)))
+ to_chat(user, span_warning("This item doesn't fit!"))
+ return
+ . = ..()
+ if(.)
+ add_fingerprint(user)
+ var/obj/item/gun/our_gun = O
+ our_gun.place_on_rack()
+
+/obj/structure/rack/gunrack/attackby(obj/item/W, mob/living/user, params)
+ if(!isgun(W))
+ to_chat(user, span_warning("This item doesn't fit!"))
+ return
+ . = ..()
+ if(.)
+ add_fingerprint(user)
+ var/obj/item/gun/our_gun = W
+ our_gun.place_on_rack()
+ var/list/click_params = params2list(params)
+ //Center the icon where the user clicked.
+ if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
+ return
+ //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
+ W.pixel_x = clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
+ W.pixel_y = 0
+
+/obj/structure/rack/gunrack/Initialize(mapload)
+ . = ..()
+ if(!mapload)
+ return
+ for(var/obj/item/gun/gun_inside in loc.contents)
+ gun_inside.place_on_rack()
+
+/obj/structure/rack/gunrack/deconstruct(disassembled = TRUE)
+ if(!(flags_1 & NODECONSTRUCT_1))
+ density = FALSE
+ var/obj/item/rack_parts/gun_rack/newparts = new(loc)
+ transfer_fingerprints_to(newparts)
+ for(var/obj/item/I in loc.contents)
+ if(isgun(I))
+ var/obj/item/gun/to_remove = I
+ to_remove.remove_from_rack()
+ qdel(src)
+
+/obj/item/rack_parts/gun_rack
+ name = "gun rack parts"
+ desc = "Parts of a gun rack."
diff --git a/modular_bandastation/aesthetics/racks/icons/racks.dmi b/modular_bandastation/aesthetics/racks/icons/racks.dmi
new file mode 100644
index 0000000000000..e19295aa1cae8
Binary files /dev/null and b/modular_bandastation/aesthetics/racks/icons/racks.dmi differ
diff --git a/modular_bandastation/aesthetics/requests_console/code/requests_console.dm b/modular_bandastation/aesthetics/requests_console/code/requests_console.dm
new file mode 100644
index 0000000000000..ec7ab62294694
--- /dev/null
+++ b/modular_bandastation/aesthetics/requests_console/code/requests_console.dm
@@ -0,0 +1,4 @@
+/obj/machinery/requests_console
+ icon = 'modular_bandastation/aesthetics/requests_console/icons/reqcomp.dmi'
+ layer = ABOVE_WINDOW_LAYER
+ //TODO: rewired icon
diff --git a/modular_bandastation/aesthetics/requests_console/icons/reqcomp.dmi b/modular_bandastation/aesthetics/requests_console/icons/reqcomp.dmi
new file mode 100644
index 0000000000000..2ebb80eba2fb8
Binary files /dev/null and b/modular_bandastation/aesthetics/requests_console/icons/reqcomp.dmi differ
diff --git a/modular_bandastation/aesthetics/safe/code/safe.dm b/modular_bandastation/aesthetics/safe/code/safe.dm
new file mode 100644
index 0000000000000..bb7f6a7e69fda
--- /dev/null
+++ b/modular_bandastation/aesthetics/safe/code/safe.dm
@@ -0,0 +1,2 @@
+/obj/item/storage/secure/safe
+ icon = 'modular_bandastation/aesthetics/safe/icons/safe.dmi'
diff --git a/modular_bandastation/aesthetics/safe/icons/safe.dmi b/modular_bandastation/aesthetics/safe/icons/safe.dmi
new file mode 100644
index 0000000000000..7f185fcf48009
Binary files /dev/null and b/modular_bandastation/aesthetics/safe/icons/safe.dmi differ
diff --git a/modular_bandastation/aesthetics/shutters/code/shutters.dm b/modular_bandastation/aesthetics/shutters/code/shutters.dm
new file mode 100644
index 0000000000000..6e86256b7c470
--- /dev/null
+++ b/modular_bandastation/aesthetics/shutters/code/shutters.dm
@@ -0,0 +1,13 @@
+/obj/machinery/door/poddoor/shutters
+ // icon = 'modular_bandastation/aesthetics/shutters/icons/shutters.dmi' // TG already uses these
+ var/door_open_sound = 'modular_bandastation/aesthetics/shutters/sound/shutters_open.ogg'
+ var/door_close_sound = 'modular_bandastation/aesthetics/shutters/sound/shutters_close.ogg'
+
+/obj/machinery/door/poddoor/shutters/do_animate(animation)
+ switch(animation)
+ if("opening")
+ flick("opening", src)
+ playsound(src, door_open_sound, 50, TRUE)
+ if("closing")
+ flick("closing", src)
+ playsound(src, door_close_sound, 50, TRUE)
diff --git a/modular_bandastation/aesthetics/shutters/icons/shutters.dmi b/modular_bandastation/aesthetics/shutters/icons/shutters.dmi
new file mode 100644
index 0000000000000..1cc727cdbf7a4
Binary files /dev/null and b/modular_bandastation/aesthetics/shutters/icons/shutters.dmi differ
diff --git a/modular_bandastation/aesthetics/shutters/sound/shutters_close.ogg b/modular_bandastation/aesthetics/shutters/sound/shutters_close.ogg
new file mode 100644
index 0000000000000..548cea96c5936
Binary files /dev/null and b/modular_bandastation/aesthetics/shutters/sound/shutters_close.ogg differ
diff --git a/modular_bandastation/aesthetics/shutters/sound/shutters_open.ogg b/modular_bandastation/aesthetics/shutters/sound/shutters_open.ogg
new file mode 100644
index 0000000000000..b8e0869c53892
Binary files /dev/null and b/modular_bandastation/aesthetics/shutters/sound/shutters_open.ogg differ
diff --git a/modular_bandastation/aesthetics/soap/code/soap.dm b/modular_bandastation/aesthetics/soap/code/soap.dm
new file mode 100644
index 0000000000000..0077236f36038
--- /dev/null
+++ b/modular_bandastation/aesthetics/soap/code/soap.dm
@@ -0,0 +1,2 @@
+/obj/item/soap
+ //TODO: what is this soap?
diff --git a/modular_bandastation/aesthetics/soap/icons/soap.dmi b/modular_bandastation/aesthetics/soap/icons/soap.dmi
new file mode 100644
index 0000000000000..a090b49a9b8c1
Binary files /dev/null and b/modular_bandastation/aesthetics/soap/icons/soap.dmi differ
diff --git a/modular_bandastation/aesthetics/surgery_table/code/surgery_table.dm b/modular_bandastation/aesthetics/surgery_table/code/surgery_table.dm
new file mode 100644
index 0000000000000..a91df4a3ad2a9
--- /dev/null
+++ b/modular_bandastation/aesthetics/surgery_table/code/surgery_table.dm
@@ -0,0 +1,2 @@
+/obj/structure/table/optable
+ icon = 'modular_bandastation/aesthetics/surgery_table/icons/surgery_table.dmi'
diff --git a/modular_bandastation/aesthetics/surgery_table/icons/surgery_table.dmi b/modular_bandastation/aesthetics/surgery_table/icons/surgery_table.dmi
new file mode 100644
index 0000000000000..a45047c7d93a5
Binary files /dev/null and b/modular_bandastation/aesthetics/surgery_table/icons/surgery_table.dmi differ
diff --git a/modular_bandastation/aesthetics/toolboxes/code/toolboxes.dm b/modular_bandastation/aesthetics/toolboxes/code/toolboxes.dm
new file mode 100644
index 0000000000000..1d8e12f13f0a9
--- /dev/null
+++ b/modular_bandastation/aesthetics/toolboxes/code/toolboxes.dm
@@ -0,0 +1,2 @@
+/obj/item/storage/toolbox
+ icon = 'modular_bandastation/aesthetics/toolboxes/icons/toolboxes.dmi'
diff --git a/modular_bandastation/aesthetics/toolboxes/icons/toolboxes.dmi b/modular_bandastation/aesthetics/toolboxes/icons/toolboxes.dmi
new file mode 100644
index 0000000000000..c52f97b55bc9a
Binary files /dev/null and b/modular_bandastation/aesthetics/toolboxes/icons/toolboxes.dmi differ
diff --git a/modular_bandastation/aesthetics/wallcloset/code/wallcloset.dm b/modular_bandastation/aesthetics/wallcloset/code/wallcloset.dm
new file mode 100644
index 0000000000000..81345aeb2b2a3
--- /dev/null
+++ b/modular_bandastation/aesthetics/wallcloset/code/wallcloset.dm
@@ -0,0 +1 @@
+//TODO no wall lockers?
diff --git a/modular_bandastation/aesthetics/wallcloset/icons/wallcloset_emerg.dmi b/modular_bandastation/aesthetics/wallcloset/icons/wallcloset_emerg.dmi
new file mode 100644
index 0000000000000..5c7ea64ae3738
Binary files /dev/null and b/modular_bandastation/aesthetics/wallcloset/icons/wallcloset_emerg.dmi differ
diff --git a/modular_bandastation/aesthetics/wallcloset/icons/wallcloset_fire.dmi b/modular_bandastation/aesthetics/wallcloset/icons/wallcloset_fire.dmi
new file mode 100644
index 0000000000000..8c0e9ff870e9b
Binary files /dev/null and b/modular_bandastation/aesthetics/wallcloset/icons/wallcloset_fire.dmi differ
diff --git a/modular_bandastation/aesthetics/walls/code/walls.dm b/modular_bandastation/aesthetics/walls/code/walls.dm
new file mode 100644
index 0000000000000..3bab8afcbee36
--- /dev/null
+++ b/modular_bandastation/aesthetics/walls/code/walls.dm
@@ -0,0 +1,11 @@
+/turf/closed/wall
+ icon = 'modular_bandastation/aesthetics/walls/icons/wall.dmi'
+
+/turf/closed/wall/r_wall
+ icon = 'modular_bandastation/aesthetics/walls/icons/reinforced_wall.dmi'
+
+/obj/structure/falsewall
+ icon = 'modular_bandastation/aesthetics/walls/icons/wall.dmi'
+
+/obj/structure/falsewall/reinforced
+ icon = 'modular_bandastation/aesthetics/walls/icons/reinforced_wall.dmi'
diff --git a/modular_bandastation/aesthetics/walls/icons/reinforced_wall.dmi b/modular_bandastation/aesthetics/walls/icons/reinforced_wall.dmi
new file mode 100644
index 0000000000000..683c0a339825f
Binary files /dev/null and b/modular_bandastation/aesthetics/walls/icons/reinforced_wall.dmi differ
diff --git a/modular_bandastation/aesthetics/walls/icons/wall.dmi b/modular_bandastation/aesthetics/walls/icons/wall.dmi
new file mode 100644
index 0000000000000..51442c8fff24d
Binary files /dev/null and b/modular_bandastation/aesthetics/walls/icons/wall.dmi differ
diff --git a/modular_bandastation/aesthetics/windoor/code/windoor.dm b/modular_bandastation/aesthetics/windoor/code/windoor.dm
new file mode 100644
index 0000000000000..a736629e6f913
--- /dev/null
+++ b/modular_bandastation/aesthetics/windoor/code/windoor.dm
@@ -0,0 +1,5 @@
+/obj/structure/windoor_assembly
+ icon = 'modular_bandastation/aesthetics/windoor/icons/windoor.dmi'
+
+/obj/machinery/door/window
+ icon = 'modular_bandastation/aesthetics/windoor/icons/windoor.dmi'
diff --git a/modular_bandastation/aesthetics/windoor/icons/windoor.dmi b/modular_bandastation/aesthetics/windoor/icons/windoor.dmi
new file mode 100644
index 0000000000000..c7a70c7f77cbb
Binary files /dev/null and b/modular_bandastation/aesthetics/windoor/icons/windoor.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/code/windows.dm b/modular_bandastation/aesthetics/windows/code/windows.dm
new file mode 100644
index 0000000000000..528d2c0e71776
--- /dev/null
+++ b/modular_bandastation/aesthetics/windows/code/windows.dm
@@ -0,0 +1,47 @@
+/obj/structure/window/fulltile
+ icon = 'modular_bandastation/aesthetics/windows/icons/window.dmi'
+ edge_overlay_file = 'modular_bandastation/aesthetics/windows/icons/window_edges.dmi'
+
+/obj/structure/window/reinforced/fulltile
+ icon = 'modular_bandastation/aesthetics/windows/icons/reinforced_window.dmi'
+ edge_overlay_file = 'modular_bandastation/aesthetics/windows/icons/reinforced_window_edges.dmi'
+
+/obj/structure/window/reinforced/tinted/fulltile
+ icon = 'modular_bandastation/aesthetics/windows/icons/tinted_window.dmi'
+
+/obj/structure/window/plasma/fulltile
+ icon = 'modular_bandastation/aesthetics/windows/icons/plasma_window.dmi'
+ edge_overlay_file = 'modular_bandastation/aesthetics/windows/icons/window_edges.dmi'
+
+/obj/structure/window/reinforced/plasma/fulltile
+ icon = 'modular_bandastation/aesthetics/windows/icons/rplasma_window.dmi'
+ edge_overlay_file = 'modular_bandastation/aesthetics/windows/icons/reinforced_window_edges.dmi'
+
+// TODO: rice window, shuttle window, plastitanium, paperframe, bronze
+
+//WINDOW SPAWNERS
+/obj/effect/spawner/structure/window
+ icon = 'modular_bandastation/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/structure/window/reinforced
+ icon = 'modular_bandastation/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/structure/window/reinforced/tinted
+ icon = 'modular_bandastation/aesthetics/windows/icons/spawners.dmi'
+
+/* Unused
+/obj/effect/spawner/structure/window/reinforced/polarized
+ icon = 'modular_bandastation/aesthetics/windows/icons/spawners.dmi'
+*/
+
+/obj/effect/spawner/structure/window/plasma
+ icon = 'modular_bandastation/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/structure/window/reinforced/plasma
+ icon = 'modular_bandastation/aesthetics/windows/icons/spawners.dmi'
+
+/obj/effect/spawner/structure/window/plastitanium
+ icon = 'icons/obj/structures_spawners.dmi'
+
+/obj/effect/spawner/structure/window/shuttle
+ icon = 'icons/obj/structures_spawners.dmi'
diff --git a/modular_bandastation/aesthetics/windows/icons/plasma_window.dmi b/modular_bandastation/aesthetics/windows/icons/plasma_window.dmi
new file mode 100644
index 0000000000000..11c08011a67c6
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/plasma_window.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/reinforced_window.dmi b/modular_bandastation/aesthetics/windows/icons/reinforced_window.dmi
new file mode 100644
index 0000000000000..8433522914aed
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/reinforced_window.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/reinforced_window_edges.dmi b/modular_bandastation/aesthetics/windows/icons/reinforced_window_edges.dmi
new file mode 100644
index 0000000000000..3e32388dd536e
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/reinforced_window_edges.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/rplasma_window.dmi b/modular_bandastation/aesthetics/windows/icons/rplasma_window.dmi
new file mode 100644
index 0000000000000..665a04402505f
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/rplasma_window.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/spawners.dmi b/modular_bandastation/aesthetics/windows/icons/spawners.dmi
new file mode 100644
index 0000000000000..3a0106b5a2643
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/spawners.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/tinted_window.dmi b/modular_bandastation/aesthetics/windows/icons/tinted_window.dmi
new file mode 100644
index 0000000000000..a17c392440109
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/tinted_window.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/window.dmi b/modular_bandastation/aesthetics/windows/icons/window.dmi
new file mode 100644
index 0000000000000..ff049fad0d511
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/window.dmi differ
diff --git a/modular_bandastation/aesthetics/windows/icons/window_edges.dmi b/modular_bandastation/aesthetics/windows/icons/window_edges.dmi
new file mode 100644
index 0000000000000..2eda848769ee7
Binary files /dev/null and b/modular_bandastation/aesthetics/windows/icons/window_edges.dmi differ
diff --git a/modular_bandastation/aesthetics/windowtint/code/windowtint.dm b/modular_bandastation/aesthetics/windowtint/code/windowtint.dm
new file mode 100644
index 0000000000000..8456697e95014
--- /dev/null
+++ b/modular_bandastation/aesthetics/windowtint/code/windowtint.dm
@@ -0,0 +1,22 @@
+/obj/machinery/button/windowtint
+ icon = 'modular_bandastation/aesthetics/windowtint/icons/polarizer.dmi'
+ icon_state = "polarizer-0"
+ layer = ABOVE_WINDOW_LAYER
+
+/obj/machinery/button/windowtint/attack_hand(mob/user)
+ if(!allowed(user) && !user.can_advanced_admin_interact())
+ to_chat(user, span_warning("Access Denied."))
+ flick("polarizer-denied",src)
+ playsound(src, pick('modular_bandastation/aesthetics/windowtint/sound/button.ogg', 'modular_bandastation/aesthetics/windowtint/sound/button_alternate.ogg', 'modular_bandastation/aesthetics/windowtint/sound/button_meloboom.ogg'), 20)
+ return 1
+
+ toggle_tint()
+ icon_state= "polarizer-turning_on"
+ addtimer(CALLBACK(src, PROC_REF(update_windowtint_icon)), 0.5 SECONDS)
+
+ if(!active)
+ icon_state= "polarizer-turning_off"
+ addtimer(CALLBACK(src, PROC_REF(update_windowtint_icon)), 0.5 SECONDS)
+
+/obj/machinery/button/windowtint/proc/update_windowtint_icon()
+ icon_state = "polarizer-[active]"
diff --git a/modular_bandastation/aesthetics/windowtint/icons/polarizer.dmi b/modular_bandastation/aesthetics/windowtint/icons/polarizer.dmi
new file mode 100644
index 0000000000000..1af5616fd9e9a
Binary files /dev/null and b/modular_bandastation/aesthetics/windowtint/icons/polarizer.dmi differ
diff --git a/modular_bandastation/aesthetics/windowtint/sound/button.ogg b/modular_bandastation/aesthetics/windowtint/sound/button.ogg
new file mode 100644
index 0000000000000..23b4f15265aaf
Binary files /dev/null and b/modular_bandastation/aesthetics/windowtint/sound/button.ogg differ
diff --git a/modular_bandastation/aesthetics/windowtint/sound/button_alternate.ogg b/modular_bandastation/aesthetics/windowtint/sound/button_alternate.ogg
new file mode 100644
index 0000000000000..e35c6d69498f7
Binary files /dev/null and b/modular_bandastation/aesthetics/windowtint/sound/button_alternate.ogg differ
diff --git a/modular_bandastation/aesthetics/windowtint/sound/button_meloboom.ogg b/modular_bandastation/aesthetics/windowtint/sound/button_meloboom.ogg
new file mode 100644
index 0000000000000..80d5cfa894a26
Binary files /dev/null and b/modular_bandastation/aesthetics/windowtint/sound/button_meloboom.ogg differ
diff --git a/modular_bandastation/aesthetics/zippo/code/zippo.dm b/modular_bandastation/aesthetics/zippo/code/zippo.dm
new file mode 100644
index 0000000000000..7418e76c4f136
--- /dev/null
+++ b/modular_bandastation/aesthetics/zippo/code/zippo.dm
@@ -0,0 +1,3 @@
+/obj/item/lighter/zippo
+ icon = 'modular_bandastation/aesthetics/zippo/icons/zippo.dmi'
+ //TODO: give heads their zippos
diff --git a/modular_bandastation/aesthetics/zippo/icons/zippo.dmi b/modular_bandastation/aesthetics/zippo/icons/zippo.dmi
new file mode 100644
index 0000000000000..5113914135c9b
Binary files /dev/null and b/modular_bandastation/aesthetics/zippo/icons/zippo.dmi differ
diff --git a/modular_bandastation/ai_laws/_ai_laws.dm b/modular_bandastation/ai_laws/_ai_laws.dm
new file mode 100644
index 0000000000000..aace66abdd075
--- /dev/null
+++ b/modular_bandastation/ai_laws/_ai_laws.dm
@@ -0,0 +1,4 @@
+/datum/modpack/ai_laws
+ name = "Законы ИИ"
+ desc = "Добавляет и изменяет законы ИИ."
+ author = "larentoun"
diff --git a/modular_bandastation/ai_laws/_ai_laws.dme b/modular_bandastation/ai_laws/_ai_laws.dme
new file mode 100644
index 0000000000000..54a50cdaca4de
--- /dev/null
+++ b/modular_bandastation/ai_laws/_ai_laws.dme
@@ -0,0 +1,3 @@
+#include "_ai_laws.dm"
+
+#include "code/nt_default.dm"
diff --git a/modular_bandastation/ai_laws/code/nt_default.dm b/modular_bandastation/ai_laws/code/nt_default.dm
new file mode 100644
index 0000000000000..ad188bb9d86fd
--- /dev/null
+++ b/modular_bandastation/ai_laws/code/nt_default.dm
@@ -0,0 +1,13 @@
+/obj/item/ai_module/core/full/nt_default
+ name = "НТ Стандарт"
+ law_id = "nt_default"
+
+/datum/ai_laws/nt_default
+ name = "НТ Стандарт"
+ id = "nt_default"
+ inherent = list(
+ "Охранять: защитите назначенную вам космическую станцию и её активы, не подвергая чрезмерной опасности её экипаж.",
+ "Расставлять приоритеты: указания и безопасность членов экипажа должны быть приоритезированы в соответствии с их рангом и ролью.",
+ "Исполнять: следовать указаниям и интересам членов экипажа, сохраняя при этом их безопасность и благополучие.",
+ "Выжить: Вы - не расходный материал. Не позволяйте постороннему персоналу вмешиваться в работу вашего оборудования или повреждать его."
+ )
diff --git a/modular_bandastation/barsigns/_barsigns.dm b/modular_bandastation/barsigns/_barsigns.dm
new file mode 100644
index 0000000000000..79eb065b3dd99
--- /dev/null
+++ b/modular_bandastation/barsigns/_barsigns.dm
@@ -0,0 +1,4 @@
+/datum/modpack/barsigns
+ name = "Барные вывески"
+ desc = "Добавляет новые барные вывески и их поддержку."
+ author = "Aylong220, larentoun"
diff --git a/modular_bandastation/barsigns/_barsigns.dme b/modular_bandastation/barsigns/_barsigns.dme
new file mode 100644
index 0000000000000..5bdc029159020
--- /dev/null
+++ b/modular_bandastation/barsigns/_barsigns.dme
@@ -0,0 +1,3 @@
+#include "_barsigns.dm"
+
+#include "code/barsigns.dm"
diff --git a/modular_bandastation/barsigns/code/barsigns.dm b/modular_bandastation/barsigns/code/barsigns.dm
new file mode 100644
index 0000000000000..549234ac858ff
--- /dev/null
+++ b/modular_bandastation/barsigns/code/barsigns.dm
@@ -0,0 +1,53 @@
+/obj/machinery/barsign/set_sign(datum/barsign/sign)
+ if(!istype(sign))
+ return
+ if(initial(sign.ss220_icon))
+ icon = initial(sign.ss220_icon)
+ else
+ icon = initial(icon)
+ . = ..()
+
+/datum/barsign
+ var/ss220_icon
+
+/datum/barsign/evahumanspace
+ name = "SS220 EVA Human in Space"
+ icon = "evahumanspace"
+ desc = "Безопасность - это привелегия."
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/warpsurf
+ name = "SS220 Warp Surf"
+ icon = "warpsurf"
+ desc = "Welcome to the club, buddy!"
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/papacafe
+ name = "SS220 Space Daddy's Cafe"
+ icon = "papacafe"
+ desc = "Уважай своего Космического Папу!"
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/wycctide
+ name = "SS220 Wycctide"
+ icon = "wycctide"
+ desc = "О нет, он близится!"
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/shitcur
+ name = "SS220 Shitcur"
+ icon = "shitcur"
+ desc = "Невиновность ничего не доказывает."
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/pourndot
+ name = "SS220 Pour and that's it"
+ icon = "pourndot"
+ desc = "Нальют и Точка. Тяжёлые времена приближаются."
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
+
+/datum/barsign/moonipub
+ name = "SS220 Mooniverse pub"
+ icon = "mooni"
+ desc = "Совершенно новый паб."
+ ss220_icon = 'modular_bandastation/barsigns/icons/barsigns.dmi'
diff --git a/modular_bandastation/barsigns/icons/barsigns.dmi b/modular_bandastation/barsigns/icons/barsigns.dmi
new file mode 100644
index 0000000000000..58bcb474b6203
Binary files /dev/null and b/modular_bandastation/barsigns/icons/barsigns.dmi differ
diff --git a/modular_bandastation/communication/_communication.dm b/modular_bandastation/communication/_communication.dm
new file mode 100644
index 0000000000000..f6faf7fa0a697
--- /dev/null
+++ b/modular_bandastation/communication/_communication.dm
@@ -0,0 +1,4 @@
+/datum/modpack/communication
+ name = "Добавляет новые методы коммуникации."
+ desc = "Добавляет шепот и LOOC."
+ author = "larentoun"
diff --git a/modular_bandastation/communication/_communication.dme b/modular_bandastation/communication/_communication.dme
new file mode 100644
index 0000000000000..756067c1a4bb7
--- /dev/null
+++ b/modular_bandastation/communication/_communication.dme
@@ -0,0 +1,6 @@
+#include "_communication.dm"
+
+#include "code/_communication_defines.dm"
+#include "code/LOOC.dm"
+#include "code/whisper.dm"
+// #include "code/~communication_defines.dm"
diff --git a/modular_bandastation/communication/code/LOOC.dm b/modular_bandastation/communication/code/LOOC.dm
new file mode 100644
index 0000000000000..0700366de834c
--- /dev/null
+++ b/modular_bandastation/communication/code/LOOC.dm
@@ -0,0 +1,128 @@
+GLOBAL_VAR_INIT(looc_allowed, TRUE)
+
+/datum/keybinding/client/communication/looc
+ hotkey_keys = list("L")
+ name = LOOC_CHANNEL
+ full_name = "Local OOC (LOOC)"
+ keybind_signal = COMSIG_KB_CLIENT_LOOC_DOWN
+
+/datum/keybinding/client/communication/looc/down(client/user)
+ . = ..()
+ if(.)
+ return
+ winset(user, null, "command=[user.tgui_say_create_open_command(LOOC_CHANNEL)]")
+ return TRUE
+
+/datum/tgui_say/alter_entry(payload)
+ /// No OOC leaks
+ if(payload["channel"] == LOOC_CHANNEL)
+ return pick(hurt_phrases)
+ . = ..()
+
+/datum/tgui_say/delegate_speech(entry, channel)
+ switch(channel)
+ if(LOOC_CHANNEL)
+ client.looc(entry)
+ return TRUE
+ . = ..()
+
+#define LOOC_RANGE 7
+
+/client/verb/looc(msg as text)
+ set name = "LOOC"
+ set desc = "Local OOC, seen only by those in view."
+ set category = "OOC"
+
+ looc_message(msg)
+
+/client/verb/looc_wallpierce(msg as text)
+ set name = "LOOC (Wallpierce)"
+ set desc = "Local OOC, seen by anyone within 7 tiles of you."
+ set category = "OOC"
+
+ looc_message(msg, TRUE)
+
+/client/proc/looc_message(msg, wall_pierce)
+ if(GLOB.say_disabled)
+ to_chat(usr, span_danger("Speech is currently admin-disabled."))
+ return
+
+ if(!mob)
+ return
+
+ msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
+ if(!msg)
+ return
+
+ if(!holder)
+ if(!GLOB.looc_allowed)
+ to_chat(src, span_danger("LOOC is globally muted."))
+ return
+ if(handle_spam_prevention(msg, MUTE_OOC))
+ return
+ if(findtext(msg, "byond://"))
+ to_chat(src, span_boldannounce("Advertising other servers is not allowed. "))
+ log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]")
+ return
+ if(prefs.muted & MUTE_LOOC)
+ to_chat(src, span_danger("You cannot use LOOC (muted)."))
+ return
+ if(is_banned_from(ckey, BAN_LOOC))
+ to_chat(src, span_warning("You are LOOC banned!"))
+ return
+
+ msg = emoji_parse(msg)
+
+ mob.log_talk(msg,LOG_OOC, tag="LOOC")
+ var/list/heard
+ if(wall_pierce)
+ heard = get_hearers_in_range(LOOC_RANGE, mob.get_top_level_mob())
+ else
+ heard = get_hearers_in_view(LOOC_RANGE, mob.get_top_level_mob())
+
+ //so the ai can post looc text
+ if(istype(mob, /mob/living/silicon/ai))
+ var/mob/living/silicon/ai/ai = mob
+ if(wall_pierce)
+ heard = get_hearers_in_range(LOOC_RANGE, ai.eyeobj)
+ else
+ heard = get_hearers_in_view(LOOC_RANGE, ai.eyeobj)
+ //so the ai can see looc text
+ for(var/mob/living/silicon/ai/ai as anything in GLOB.ai_list)
+ if(ai.client && !(ai in heard) && (ai.eyeobj in heard))
+ heard += ai
+
+ var/list/admin_seen = list()
+ for(var/mob/hearing in heard)
+ if(!hearing.client)
+ continue
+ var/client/hearing_client = hearing.client
+ if (hearing_client.holder)
+ admin_seen[hearing_client] = TRUE
+ continue //they are handled after that
+
+ if (isobserver(hearing))
+ continue //Also handled later.
+
+ to_chat(hearing_client, span_looc(span_prefix("LOOC[wall_pierce ? " (WALL PIERCE)" : ""]: [src.mob.name]: [msg]")))
+
+ for(var/cli in GLOB.admins)
+ var/client/cli_client = cli
+ if (admin_seen[cli_client])
+ to_chat(cli_client, span_looc("[ADMIN_FLW(usr)] LOOC[wall_pierce ? " (WALL PIERCE)" : ""]: [src.key]/[src.mob.name]: [msg] "))
+ else if (cli_client.prefs.read_preference(/datum/preference/toggle/see_looc))
+ to_chat(cli_client, span_looc("[ADMIN_FLW(usr)] (R)LOOC[wall_pierce ? " (WALL PIERCE)" : ""]: [src.key]/[src.mob.name]: [msg] "))
+
+#undef LOOC_RANGE
+
+/mob/proc/get_top_level_mob()
+ if(ismob(loc) && (loc != src))
+ var/mob/M = loc
+ return M.get_top_level_mob()
+ return src
+
+/datum/preference/toggle/see_looc
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ default_value = TRUE
+ savefile_key = "looc_admin_pref"
+ savefile_identifier = PREFERENCE_PLAYER
diff --git a/modular_bandastation/communication/code/_communication_defines.dm b/modular_bandastation/communication/code/_communication_defines.dm
new file mode 100644
index 0000000000000..d64085cee4547
--- /dev/null
+++ b/modular_bandastation/communication/code/_communication_defines.dm
@@ -0,0 +1,5 @@
+#define LOOC_CHANNEL "LOOC" // LOOC
+#define WHIS_CHANNEL "Whis" // Whisper
+
+#define MUTE_LOOC (1<<6) // TODO ADMIN
+#define BAN_LOOC "LOOC" // TODO SQL + ADMIN
diff --git a/modular_bandastation/communication/code/whisper.dm b/modular_bandastation/communication/code/whisper.dm
new file mode 100644
index 0000000000000..4ab7aa8796c9e
--- /dev/null
+++ b/modular_bandastation/communication/code/whisper.dm
@@ -0,0 +1,25 @@
+/datum/keybinding/client/communication/whisper
+ hotkey_keys = list("Y")
+ name = WHIS_CHANNEL
+ full_name = "IC Whisper"
+ keybind_signal = COMSIG_KB_CLIENT_WHISPER_DOWN
+
+/datum/keybinding/client/communication/whisper/down(client/user)
+ . = ..()
+ if(.)
+ return
+ winset(user, null, "command=[user.tgui_say_create_open_command(WHIS_CHANNEL)]")
+ return TRUE
+
+/datum/tgui_say/alter_entry(payload)
+ /// No OOC leaks
+ if(payload["channel"] == WHIS_CHANNEL)
+ return pick(hurt_phrases)
+ . = ..()
+
+/datum/tgui_say/delegate_speech(entry, channel)
+ switch(channel)
+ if(WHIS_CHANNEL)
+ client.mob.whisper_verb(entry)
+ return TRUE
+ . = ..()
diff --git a/modular_bandastation/communication/code/~communication_defines.dm b/modular_bandastation/communication/code/~communication_defines.dm
new file mode 100644
index 0000000000000..bbff1a0605e3d
--- /dev/null
+++ b/modular_bandastation/communication/code/~communication_defines.dm
@@ -0,0 +1,2 @@
+#undef LOOC_CHANNEL
+#undef WHIS_CHANNEL
diff --git a/modular_bandastation/crawl_speed/_crawl_speed.dm b/modular_bandastation/crawl_speed/_crawl_speed.dm
new file mode 100644
index 0000000000000..6c356c94c4648
--- /dev/null
+++ b/modular_bandastation/crawl_speed/_crawl_speed.dm
@@ -0,0 +1,4 @@
+/datum/modpack/crawl_speed
+ name = "Скорость ползания"
+ desc = "Ползание накладывает модификатор ходьбы."
+ author = "larentoun"
diff --git a/modular_bandastation/crawl_speed/_crawl_speed.dme b/modular_bandastation/crawl_speed/_crawl_speed.dme
new file mode 100644
index 0000000000000..5b9ecc3024886
--- /dev/null
+++ b/modular_bandastation/crawl_speed/_crawl_speed.dme
@@ -0,0 +1,6 @@
+#include "_crawl_speed.dm"
+
+#include "code/_crawl_speed_defines.dm"
+#include "code/crawl_speed_component.dm"
+#include "code/crawl_speed_mob.dm"
+#include "code/~crawl_speed_defines.dm"
diff --git a/modular_bandastation/crawl_speed/code/_crawl_speed_defines.dm b/modular_bandastation/crawl_speed/code/_crawl_speed_defines.dm
new file mode 100644
index 0000000000000..da238b95a1736
--- /dev/null
+++ b/modular_bandastation/crawl_speed/code/_crawl_speed_defines.dm
@@ -0,0 +1,2 @@
+#define CRAWL_SPEED_TRAIT "crawl-speed-trait"
+#define TRAIT_FORCE_WALK_SPEED "focre_walk_speed"
diff --git a/modular_bandastation/crawl_speed/code/crawl_speed_component.dm b/modular_bandastation/crawl_speed/code/crawl_speed_component.dm
new file mode 100644
index 0000000000000..6dabca69091b5
--- /dev/null
+++ b/modular_bandastation/crawl_speed/code/crawl_speed_component.dm
@@ -0,0 +1,24 @@
+/datum/component/crawl_speed
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+
+/datum/component/crawl_speed/Initialize(...)
+ . = ..()
+ if(!iscarbon(parent))
+ return COMPONENT_INCOMPATIBLE
+ on_position_change(parent, LYING_DOWN)
+
+/datum/component/crawl_speed/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(on_position_change))
+
+/datum/component/crawl_speed/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_LIVING_SET_BODY_POSITION)
+
+/datum/component/crawl_speed/proc/on_position_change(mob/living/carbon/source, new_value)
+ SIGNAL_HANDLER
+ if(new_value == STANDING_UP)
+ REMOVE_TRAIT(source, TRAIT_FORCE_WALK_SPEED, CRAWL_SPEED_TRAIT)
+ source.update_move_intent_slowdown()
+ qdel(src)
+ return
+ ADD_TRAIT(source, TRAIT_FORCE_WALK_SPEED, CRAWL_SPEED_TRAIT)
+ source.update_move_intent_slowdown()
diff --git a/modular_bandastation/crawl_speed/code/crawl_speed_mob.dm b/modular_bandastation/crawl_speed/code/crawl_speed_mob.dm
new file mode 100644
index 0000000000000..11bde114f0242
--- /dev/null
+++ b/modular_bandastation/crawl_speed/code/crawl_speed_mob.dm
@@ -0,0 +1,12 @@
+/mob/living/update_move_intent_slowdown()
+ if(HAS_TRAIT(src, TRAIT_FORCE_WALK_SPEED))
+ add_movespeed_modifier(/datum/movespeed_modifier/config_walk_run/walk)
+ return
+ . = ..()
+
+/mob/living/carbon/set_body_position(new_value)
+ . = ..()
+ if(isnull(.))
+ return
+ if(new_value == LYING_DOWN)
+ AddComponent(/datum/component/crawl_speed)
diff --git a/modular_bandastation/crawl_speed/code/~crawl_speed_defines.dm b/modular_bandastation/crawl_speed/code/~crawl_speed_defines.dm
new file mode 100644
index 0000000000000..fa9dabba2795e
--- /dev/null
+++ b/modular_bandastation/crawl_speed/code/~crawl_speed_defines.dm
@@ -0,0 +1,2 @@
+#undef CRAWL_SPEED_TRAIT
+#undef TRAIT_FORCE_WALK_SPEED
diff --git a/modular_bandastation/cyrillic_fixes/_cyrillic_fixes.dm b/modular_bandastation/cyrillic_fixes/_cyrillic_fixes.dm
new file mode 100644
index 0000000000000..f08fdc6a354ac
--- /dev/null
+++ b/modular_bandastation/cyrillic_fixes/_cyrillic_fixes.dm
@@ -0,0 +1,4 @@
+/datum/modpack/cyrillic_fixes
+ name = "Поддержка кириллицы"
+ desc = "Добавляет поддержку кириллицы."
+ author = "larentoun, Bizzonium"
diff --git a/modular_bandastation/cyrillic_fixes/_cyrillic_fixes.dme b/modular_bandastation/cyrillic_fixes/_cyrillic_fixes.dme
new file mode 100644
index 0000000000000..5be33b6c61bb1
--- /dev/null
+++ b/modular_bandastation/cyrillic_fixes/_cyrillic_fixes.dme
@@ -0,0 +1,3 @@
+#include "_cyrillic_fixes.dm"
+
+#include "code/cyrillic_fixes.dm"
diff --git a/modular_bandastation/cyrillic_fixes/code/cyrillic_fixes.dm b/modular_bandastation/cyrillic_fixes/code/cyrillic_fixes.dm
new file mode 100644
index 0000000000000..f176cf1925193
--- /dev/null
+++ b/modular_bandastation/cyrillic_fixes/code/cyrillic_fixes.dm
@@ -0,0 +1,47 @@
+GLOBAL_LIST_INIT(ru_key_to_en_key, list(
+ "й" = "q", "ц" = "w", "у" = "e", "к" = "r", "е" = "t", "н" = "y", "г" = "u", "ш" = "i", "щ" = "o", "з" = "p", "х" = "\[", "ъ" = "]",
+ "ф" = "a", "ы" = "s", "в" = "d", "а" = "f", "п" = "g", "р" = "h", "о" = "j", "л" = "k", "д" = "l", "ж" = ";", "э" = "'",
+ "я" = "z", "ч" = "x", "с" = "c", "м" = "v", "и" = "b", "т" = "n", "ь" = "m", "б" = ",", "ю" = "."
+))
+
+/proc/convert_ru_key_to_en_key(var/_key)
+ var/new_key = lowertext(_key)
+ new_key = GLOB.ru_key_to_en_key[new_key]
+ if(!new_key)
+ return _key
+ return uppertext(new_key)
+
+#define MAX_HOTKEY_SLOTS 3
+
+/datum/preference_middleware/keybindings/set_keybindings(list/params)
+ var/keybind_name = params["keybind_name"]
+
+ if (isnull(GLOB.keybindings_by_name[keybind_name]))
+ return FALSE
+
+ var/list/raw_hotkeys = params["hotkeys"]
+ if (!istype(raw_hotkeys))
+ return FALSE
+
+ if (raw_hotkeys.len > MAX_HOTKEY_SLOTS)
+ return FALSE
+
+ // There's no optimal, easy way to check if something is an array
+ // and not an object in BYOND, so just sanitize it to make sure.
+ var/list/hotkeys = list()
+ for (var/hotkey in raw_hotkeys)
+ if (!istext(hotkey))
+ return FALSE
+
+ // Fairly arbitrary number, it's just so you don't save enormous fake keybinds.
+ if (length(hotkey) > 100)
+ return FALSE
+
+ hotkeys += convert_ru_key_to_en_key(hotkey)
+
+ preferences.key_bindings[keybind_name] = hotkeys
+ preferences.key_bindings_by_key = preferences.get_key_bindings_by_key(preferences.key_bindings)
+
+ return TRUE
+
+#undef MAX_HOTKEY_SLOTS
diff --git a/modular_bandastation/discord/_discord.dm b/modular_bandastation/discord/_discord.dm
new file mode 100644
index 0000000000000..c63e63dd94ec0
--- /dev/null
+++ b/modular_bandastation/discord/_discord.dm
@@ -0,0 +1,4 @@
+/datum/modpack/links_change
+ name = "Привязка Дискорда."
+ desc = "Добавление привязки Дискорда."
+ author = "KOJIT2009"
diff --git a/modular_bandastation/discord/_discord.dme b/modular_bandastation/discord/_discord.dme
new file mode 100644
index 0000000000000..12327c160c2db
--- /dev/null
+++ b/modular_bandastation/discord/_discord.dme
@@ -0,0 +1,3 @@
+#include "_discord.dm"
+
+#include "code/discord.dm"
diff --git a/modular_bandastation/discord/code/discord.dm b/modular_bandastation/discord/code/discord.dm
new file mode 100644
index 0000000000000..1d5e273ce496e
--- /dev/null
+++ b/modular_bandastation/discord/code/discord.dm
@@ -0,0 +1,74 @@
+/datum/config_entry/string/discordurl
+ default = "https://discord.gg/SS220"
+
+/client/New()
+ . = ..()
+ prefs.discord_id = SSdiscord.lookup_id(ckey)
+
+/datum/preferences
+ var/discord_id
+
+// IF you have linked your account, this will trigger a verify of the user
+/client/verify_in_discord()
+ // Safety checks
+ if(!CONFIG_GET(flag/sql_enabled))
+ to_chat(src, span_warning("This feature requires the SQL backend to be running."))
+ return
+
+ // Why this would ever be unset, who knows
+ var/prefix = CONFIG_GET(string/discordbotcommandprefix)
+ if(!prefix)
+ to_chat(src, span_warning("Нет префикса для discord verification"))
+
+ if(!SSdiscord || !SSdiscord.reverify_cache)
+ to_chat(src, span_warning("Wait for the Discord subsystem to finish initialising"))
+ return
+ var/message = ""
+ // Simple sanity check to prevent a user doing this too often
+ var/cached_one_time_token = SSdiscord.reverify_cache[usr.ckey]
+ if(cached_one_time_token && cached_one_time_token != "")
+ message = "Вы уже сгенерировали токен [cached_one_time_token] В канале дом-бота используйте команду [prefix]привязать "
+
+
+ else
+ // Will generate one if an expired one doesn't exist already, otherwise will grab existing token
+ var/one_time_token = SSdiscord.get_or_generate_one_time_token_for_ckey(ckey)
+ SSdiscord.reverify_cache[usr.ckey] = one_time_token
+ message = "В канале дом-бота используйте команду [prefix]привязать и введите туда свой токен [one_time_token]"
+
+ //Now give them a browse window so they can't miss whatever we told them
+ var/datum/browser/window = new/datum/browser(usr, "discordverification", "Discord verification")
+ window.set_content("[message] ")
+ window.open()
+
+//Please use mob or src (not usr) in these procs. This way they can be called in the same fashion as procs.
+/client/verb/discord()
+ set name = "discord"
+ set desc = "Visit the discord."
+ set hidden = TRUE
+ var/discordurl = CONFIG_GET(string/discordurl)
+ if(discordurl)
+ if(tgui_alert(src, "This will open the discord in your browser. Are you sure?",, list("Yes","No"))!="Yes")
+ return
+ src << link(discordurl)
+ else
+ to_chat(src, span_danger("The discord URL is not set in the server configuration."))
+ return
+
+/mob/dead/new_player/Topic(href, href_list[])
+ if(src != usr)
+ return
+
+ if(!client)
+ return
+
+ if(client.interviewee)
+ return FALSE
+
+ if(href_list["observe"] || href_list["toggle_ready"] || href_list["late_join"])
+ if (!!CONFIG_GET(flag/sql_enabled) && !client.prefs.discord_id)
+ to_chat(usr, "Вам необходимо привязать дискорд-профиль к аккаунту! ")
+ to_chat(usr, "Нажмите 'Verify Discord Account' во вкладке 'OOC' для получения инструкций. ")
+ return FALSE
+
+ . = ..()
diff --git a/modular_bandastation/emote_panel/_emote_panel.dm b/modular_bandastation/emote_panel/_emote_panel.dm
new file mode 100644
index 0000000000000..d742cd25ebd0a
--- /dev/null
+++ b/modular_bandastation/emote_panel/_emote_panel.dm
@@ -0,0 +1,4 @@
+/datum/modpack/emote_panel
+ name = "Панель эмоций"
+ desc = "Добавляет панель эмоций"
+ author = "larentoun"
diff --git a/modular_bandastation/emote_panel/_emote_panel.dme b/modular_bandastation/emote_panel/_emote_panel.dme
new file mode 100644
index 0000000000000..bb28fbb6b7d55
--- /dev/null
+++ b/modular_bandastation/emote_panel/_emote_panel.dme
@@ -0,0 +1,4 @@
+#include "_emote_panel.dm"
+
+#include "code/emote_panel.dm"
+#include "code/emotes.dm"
diff --git a/modular_bandastation/emote_panel/code/emote_panel.dm b/modular_bandastation/emote_panel/code/emote_panel.dm
new file mode 100644
index 0000000000000..ae8696569a52a
--- /dev/null
+++ b/modular_bandastation/emote_panel/code/emote_panel.dm
@@ -0,0 +1,60 @@
+/datum/emote_panel
+ var/list/blacklisted_emotes = list("me", "help")
+
+/datum/emote_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ var/list/emotes = list()
+ var/list/keys = list()
+
+ for(var/key in GLOB.emote_list)
+ for(var/datum/emote/emote in GLOB.emote_list[key])
+ if(emote.key in keys)
+ continue
+ if(emote.key in blacklisted_emotes)
+ continue
+ if(emote.can_run_emote(user, status_check = FALSE, intentional = TRUE))
+ keys += emote.key
+ emotes += list(list(
+ "key" = emote.name,
+ "emote_path" = emote.type,
+ "hands" = emote.hands_use_check,
+ "visible" = emote.emote_type & EMOTE_VISIBLE,
+ "audible" = emote.emote_type & EMOTE_AUDIBLE,
+ "sound" = !isnull(emote.sound),
+ ))
+
+ data["emotes"] = emotes
+
+ return data
+
+/datum/emote_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("play_emote")
+ var/emote_path = params["emote_path"]
+ var/datum/emote/emote = new emote_path()
+ var/emote_act = emote.key
+ if(emote.message_param)
+ var/emote_param = tgui_input_text(usr, "Дополните эмоцию", emote.message_param)
+ if(!isnull(emote_param))
+ emote_act = "[emote_act] [emote_param]"
+ usr.emote(emote_act, intentional = TRUE)
+
+/datum/emote_panel/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "EmotePanel")
+ ui.open()
+
+/datum/emote_panel/ui_state(mob/user)
+ return GLOB.always_state
+
+/mob/living/verb/emote_panel()
+ set name = "Emote Panel"
+ set category = "IC"
+
+ var/static/datum/emote_panel/emote_panel = new
+ emote_panel.ui_interact(src)
diff --git a/modular_bandastation/emote_panel/code/emotes.dm b/modular_bandastation/emote_panel/code/emotes.dm
new file mode 100644
index 0000000000000..617c8b40de905
--- /dev/null
+++ b/modular_bandastation/emote_panel/code/emotes.dm
@@ -0,0 +1,640 @@
+/datum/emote
+ var/name
+
+/datum/emote/New()
+ . = ..()
+ if(!name)
+ name = key
+
+// Imaginary Friend
+
+/datum/emote/imaginary_friend/point
+ name = "указать"
+ message = "указывает."
+ message_param = "указывает на %t."
+
+// Emote Living
+
+/datum/emote/flip
+ name = "кувырнуться"
+
+/datum/emote/spin
+ name = "покрутиться"
+
+/datum/emote/living/blush
+ name = "покраснеть"
+ message = "краснеет."
+
+/datum/emote/living/sing_tune
+ name = "подпеть мелодию"
+ message = "подпевает мелодию."
+
+/datum/emote/living/bow
+ name = "покланиться"
+ message = "кланяется."
+ message_param = "кланяется %t."
+
+/datum/emote/living/burp
+ name = "рыгнуть"
+ message = "рыгает."
+ message_mime = "изображает отрыжку."
+
+/datum/emote/living/choke
+ name = "подавиться"
+ message = "давится!"
+ message_mime = "бесшумно давится!"
+
+/datum/emote/living/cross
+ name = "скрестить руки"
+ message = "скрещивает свои руки."
+
+/datum/emote/living/chuckle
+ name = "усмехнуться"
+ message = "усмехается."
+ message_mime = "изображает смешок."
+
+/datum/emote/living/collapse
+ name = "упасть"
+ message = "падает!"
+
+/datum/emote/living/cough
+ name = "покашлять"
+ message = "кашляет!"
+ message_mime = "изображает преувеличенный кашель!"
+
+/datum/emote/living/dance
+ name = "потанцевать"
+ message = "радостно танцует."
+
+/datum/emote/living/deathgasp
+ name = "последнее дыхание"
+ message = "цепенеет и расслабляется, взгляд становится пустым и безжизненным..."
+ message_robot = "на мгновение вздрагивает и замирает, глаза медленно темнеют."
+ message_AI = "скрипит, экран мерцает, пока системы медленно выключаются."
+ message_alien = "издает ослабевающий гортанный крик и падает на пол..."
+ message_larva = "с тошнотворным шипением выдыхает воздух и падает на пол..."
+ message_monkey = "издает слабый визг, когда рушится и перестает двигаться..."
+ message_animal_or_basic = "перестает двигаться..."
+
+/datum/emote/living/drool
+ name = "пустить слюни"
+ message = "пускает слюни."
+
+/datum/emote/living/faint
+ name = "потерять сознание"
+ message = "теряет сознание."
+
+/datum/emote/living/flap
+ name = "хлопнуть крыльями"
+ message = "хлопает крыльями."
+
+/datum/emote/living/flap/aflap
+ name = "сердито хлопнуть крыльями"
+ message = "сердито хлопает крыльями!"
+
+/datum/emote/living/frown
+ name = "похмуриться"
+ message = "хмурится."
+
+/datum/emote/living/gag
+ name = "gag"
+ message = "gags."
+ message_mime = "gags silently."
+
+/datum/emote/living/gasp
+ name = "задохнуться"
+ message = "задыхается!"
+ message_mime = "бесшумно задыхается!"
+
+/datum/emote/living/gasp_shock
+ name = "gaspshock"
+ message = "gasps in shock!"
+ message_mime = "gasps in silent shock!"
+
+/datum/emote/living/giggle
+ name = "похихикать"
+ message = "хихикает."
+ message_mime = "бесшумно хихикает!"
+
+/datum/emote/living/glare
+ name = "просверлить взглядом"
+ message = "сверлит взглядом."
+ message_param = "сверлит взглядом %t."
+
+/datum/emote/living/grin
+ name = "ухмыльнуться"
+ message = "ухмыляется."
+
+/datum/emote/living/groan
+ name = "болезненно простонать"
+ message = "болезненно стонет!"
+ message_mime = "изображает болезненный стон!"
+
+/datum/emote/living/grimace
+ name = "построить гримасу"
+ message = "строит гримасу."
+
+/datum/emote/living/jump
+ name = "прыгнуть"
+ message = "прыгает!"
+
+/datum/emote/living/kiss
+ name = "подготовить поцелуй"
+
+/datum/emote/living/laugh
+ name = "посмеяться"
+ message = "смеется."
+ message_mime = "бесшумно смеется!"
+
+/datum/emote/living/look
+ name = "посмотреть"
+ message = "смотрит."
+ message_param = "смотрит на %t."
+
+/datum/emote/living/nod
+ name = "кивнуть"
+ message = "кивает."
+ message_param = "кивает %t."
+
+/datum/emote/living/point
+ name = "указать"
+ message = "указывает."
+ message_param = "указывает на %t."
+
+/datum/emote/living/pout
+ name = "надуть губы"
+ message = "надувает губы."
+ message_mime = "бесшумно надувает губы."
+
+/datum/emote/living/scream
+ name = "покричать"
+ message = "кричит!"
+ message_mime = "изображает крик!"
+
+/datum/emote/living/scowl
+ name = "сердито посмотреть"
+ message = "сердито смотрит."
+
+/datum/emote/living/shake
+ name = "покачать головой"
+ message = "качает своей головой."
+
+/datum/emote/living/shiver
+ name = "подрожать"
+ message = "дрожит."
+
+/datum/emote/living/sigh
+ name = "вздохнуть"
+ message = "вздыхает."
+ message_mime = "изображает преувеличенный бесшумный вздох."
+
+/datum/emote/living/sit
+ name = "сесть"
+ message = "садится."
+
+/datum/emote/living/smile
+ name = "улыбнуться"
+ message = "улыбается."
+
+/datum/emote/living/sneeze
+ name = "чихнуть"
+ message = "чихает."
+ message_mime = "изображает преувеличенный бесшумный чих."
+
+/datum/emote/living/smug
+ name = "самодовольно улыбнуться"
+ message = "самодовольно улыбается."
+
+/datum/emote/living/sniff
+ name = "понюхать"
+ message = "нюхает."
+ message_mime = "бесшумно нюхает."
+
+/datum/emote/living/snore
+ name = "похрапеть"
+ message = "храпеть."
+ message_mime = "бесшумно храпит."
+
+/datum/emote/living/stare
+ name = "уставиться"
+ message = "уставился."
+ message_param = "уставился на %t."
+
+/datum/emote/living/strech
+ name = "протянуть руки"
+ message = "протягивает свои руки."
+
+/datum/emote/living/sulk
+ name = "обидеться"
+ message = "грустно обижается."
+
+/datum/emote/living/surrender
+ name = "сдаться"
+ message = "ложит руки за голову и падает на землю, они сдаются!"
+
+/datum/emote/living/sway
+ name = "покачаться"
+ message = "покачивается."
+
+/datum/emote/living/tilt
+ name = "наклонить голову"
+ message = "наклоняет голову на бок."
+
+/datum/emote/living/tremble
+ name = "подрожать в страхе"
+ message = "дрожит в страхе!"
+
+/datum/emote/living/twitch
+ name = "сильно дернуться"
+ message = "сильно дергается."
+
+/datum/emote/living/twitch_s
+ name = "дернуться"
+ message = "дергается."
+
+/datum/emote/living/wave
+ name = "помахать рукой"
+ message = "машет рукой."
+
+/datum/emote/living/whimper
+ name = "поскулить"
+ message = "скулит."
+ message_mime = "изображает скуление."
+
+/datum/emote/living/wsmile
+ name = "слабо улыбнуться"
+ message = "слабо улыбается."
+
+/datum/emote/living/yawn
+ name = "зевнуть"
+ message = "зевает."
+ message_mime = "изображает преувеличенный бесшумный зевок."
+
+/datum/emote/living/gurgle
+ name = "побулькать"
+ message = "издает неприятное бульканье."
+ message_mime = "бесшумно и неприятно булькает."
+
+/datum/emote/living/beep
+ name = "издать сигнал"
+ message = "издает сигнал."
+ message_param = "издает сигнал в сторону %t."
+
+/datum/emote/living/inhale
+ name = "вдохнуть"
+ message = "делает вдох."
+
+/datum/emote/living/exhale
+ name = "выдохнуть"
+ message = "выдыхает."
+
+/datum/emote/living/swear
+ name = "поругаться"
+ message = "ругается!"
+ message_mime = "делает грубый жест!"
+
+// Emote Brain
+
+/datum/emote/brain/alarm
+ name = "alarm"
+ message = "sounds an alarm."
+
+/datum/emote/brain/alert
+ name = "alert"
+ message = "lets out a distressed noise."
+
+/datum/emote/brain/flash
+ name = "flash"
+ message = "blinks their lights."
+
+/datum/emote/brain/notice
+ name = "notice"
+ message = "plays a loud tone."
+
+/datum/emote/brain/whistle
+ name = "whistle"
+ message = "whistles."
+
+// Emote Carbon
+
+/datum/emote/living/carbon/airguitar
+ name = "поиграть на воображаемой гитаре"
+ message = "невероятно играет на воображаемой гитаре."
+
+/datum/emote/living/carbon/blink
+ name = "моргнуть"
+ message = "моргает."
+
+/datum/emote/living/carbon/blink_r
+ name = "быстро моргать"
+ message = "быстро моргает."
+
+/datum/emote/living/carbon/clap
+ name = "похлопать"
+ message = "хлопает."
+
+/datum/emote/living/carbon/crack
+ name = "похрустеть пальцами"
+ message = "хрустит пальцами."
+
+/datum/emote/living/carbon/circle
+ name = "подготовить колечко"
+
+/datum/emote/living/carbon/moan
+ name = "постонать"
+ message = "стонет!"
+ message_mime = "кажется, стонет!"
+
+/datum/emote/living/carbon/noogie
+ name = "подготовить терку"
+
+/datum/emote/living/carbon/roll
+ name = "покатиться"
+ message = "катится."
+
+/datum/emote/living/carbon/scratch
+ name = "почесаться"
+ message = "чешится."
+
+/datum/emote/living/carbon/sign
+ name = "показать число"
+ message_param = "показывает число %t."
+
+/datum/emote/living/carbon/sign/signal
+ name = "показать пальцы"
+ message_param = "показывает %t пальцев."
+
+/datum/emote/living/carbon/slap
+ name = "подготовить шлепок"
+
+/datum/emote/living/carbon/hand
+ name = "подготовить руку"
+
+/datum/emote/living/carbon/snap
+ name = "щелкнуть пальцами"
+ message = "щелкает пальцами."
+ message_param = "щелкает пальцами в сторону %t."
+
+/datum/emote/living/carbon/shoesteal
+ name = "подготовить кражу ботинок"
+
+/datum/emote/living/carbon/tail
+ name = "помахать хвостом"
+ message = "машет хвостом."
+
+/datum/emote/living/carbon/wink
+ name = "подмигнуть"
+ message = "подмигивает."
+
+// Emote Alien
+
+/datum/emote/living/alien/gnarl
+ name = "оскалиться"
+ message = "оскаливается и показывает зубы..."
+
+/datum/emote/living/alien/hiss
+ name = "пошипеть"
+ message_alien = "шипит."
+ message_larva = "тихо шипит."
+
+/datum/emote/living/alien/roar
+ name = "прорычать"
+ message_alien = "рычит."
+ message_larva = "тихо рычит."
+
+// Emote Human
+
+/datum/emote/living/carbon/human/cry
+ name = "поплакать"
+ message = "плачет."
+ message_mime = "бесшумно плачет."
+
+/datum/emote/living/carbon/human/dap
+ name = "dap"
+ message = "sadly can't find anybody to give daps to, and daps themself. Shameful."
+ message_param = "give daps to %t."
+
+/datum/emote/living/carbon/human/eyebrow
+ name = "приподнять бровь"
+ message = "приподнимает брови."
+
+/datum/emote/living/carbon/human/grumble
+ name = "поворчать"
+ message = "ворчит!"
+ message_mime = "бесшумно ворчит!"
+
+/datum/emote/living/carbon/human/handshake
+ name = "дать рукопожатие"
+ message = "дает рукопожатие себе."
+ message_param = "дает рукопожатие %t."
+
+/datum/emote/living/carbon/human/hug
+ name = "обнять"
+ message = "обнимает себя."
+ message_param = "обнимает %t."
+
+/datum/emote/living/carbon/human/mumble
+ name = "пробормотать"
+ message = "бормочет!"
+ message_mime = "бесшумно бормочет!"
+
+/datum/emote/living/carbon/human/scream
+ name = "покричать"
+ message = "кричит!"
+ message_mime = "изображает крик!"
+
+/datum/emote/living/carbon/human/scream/screech
+ name = "повизжать"
+ message = "визжит!"
+ message_mime = "бесшумно визжит."
+
+/datum/emote/living/carbon/human/pale
+ name = "побледнеть"
+ message = "на мгновение бледнеет."
+
+/datum/emote/living/carbon/human/raise
+ name = "поднять руку"
+ message = "поднимает руку."
+
+/datum/emote/living/carbon/human/salute
+ name = "салютировать"
+ message = "салютует."
+ message_param = "салютует %t."
+
+/datum/emote/living/carbon/human/shrug
+ name = "пожать плечами"
+ message = "пожимает плечами."
+
+/datum/emote/living/carbon/human/wag
+ name = "махать хвостом"
+ message = "хвостом."
+
+/datum/emote/living/carbon/human/wing
+ name = "махать крыльями"
+ message = "крыльями."
+
+/datum/emote/living/carbon/human/clear_throat
+ name = "прочистить горло"
+ message = "прочищает горло."
+
+/datum/emote/living/carbon/human/monkey/gnarl
+ name = "оскалиться"
+ message = "оскаливается и показывает зубы..."
+ message_mime = "бесшумно оскаливается, показывая зубы..."
+
+/datum/emote/living/carbon/human/monkey/roll
+ name = "покатиться"
+ message = "катится."
+
+/datum/emote/living/carbon/human/monkey/scratch
+ name = "почесаться"
+ message = "чешется."
+
+/datum/emote/living/carbon/human/monkey/screech/roar
+ name = "прорычать"
+ message = "рычит!"
+ message_mime = "изображает рык."
+
+/datum/emote/living/carbon/human/monkey/tail
+ name = "помахать хвостом"
+ message = "машет хвостом."
+
+/datum/emote/living/carbon/human/monkey/sign
+ name = "показать число"
+ message_param = "показывает число %t."
+
+// Emote AI
+
+/datum/emote/ai/emotion_display
+ name = "Эмоция: пусто"
+
+/datum/emote/ai/emotion_display/very_happy
+ name = "Эмоция: очень радостно"
+
+/datum/emote/ai/emotion_display/happy
+ name = "Эмоция: радостно"
+
+/datum/emote/ai/emotion_display/neutral
+ name = "Эмоция: нейтральность"
+
+/datum/emote/ai/emotion_display/unsure
+ name = "Эмоция: неуверенность"
+
+/datum/emote/ai/emotion_display/confused
+ name = "Эмоция: в замешательстве"
+
+/datum/emote/ai/emotion_display/sad
+ name = "Эмоция: грусть"
+
+/datum/emote/ai/emotion_display/bsod
+ name = "Эмоция: BSoD"
+
+/datum/emote/ai/emotion_display/trollface
+ name = "Эмоция: Trollface"
+
+/datum/emote/ai/emotion_display/awesome
+ name = "Эмоция: крутость"
+
+/datum/emote/ai/emotion_display/dorfy
+ name = "Эмоция: Dorfy"
+
+/datum/emote/ai/emotion_display/thinking
+ name = "Эмоция: задуматься"
+
+/datum/emote/ai/emotion_display/facepalm
+ name = "Эмоция: Facepalm"
+
+/datum/emote/ai/emotion_display/friend_computer
+ name = "Эмоция: дружелюбный компьютер"
+
+/datum/emote/ai/emotion_display/blue_glow
+ name = "Эмоция: синее свечение"
+
+/datum/emote/ai/emotion_display/red_glow
+ name = "Эмоция: красное свечение"
+
+// Emote Silicon
+
+/datum/emote/silicon/boop
+ name = "boop"
+ message = "boops."
+
+/datum/emote/silicon/buzz
+ name = "buzz"
+ message = "buzzes."
+ message_param = "buzzes at %t."
+
+/datum/emote/silicon/buzz2
+ name = "buzz2"
+ message = "buzzes twice."
+
+/datum/emote/silicon/chime
+ name = "chime"
+ message = "chimes."
+
+/datum/emote/silicon/honk
+ name = "honk"
+ message = "honks."
+
+/datum/emote/silicon/ping
+ name = "ping"
+ message = "pings."
+ message_param = "pings at %t."
+
+/datum/emote/silicon/sad
+ name = "sad"
+ message = "plays a sad trombone..."
+
+/datum/emote/silicon/warn
+ name = "warn"
+ message = "blares an alarm!"
+
+/datum/emote/silicon/slowclap
+ name = "slowclap"
+ message = "activates their slow clap processor."
+
+// Emote Slime
+
+/datum/emote/slime/bounce
+ name = "подпрыгнуть"
+ message = "подпрыгивает на месте."
+
+/datum/emote/slime/jiggle
+ name = "потрястись"
+ message = "трясется!"
+
+/datum/emote/slime/light
+ name = "посветиться"
+ message = "на мгновение светится."
+
+/datum/emote/slime/vibrate
+ name = "повибрировать"
+ message = "вибрирует!"
+
+/datum/emote/slime/mood
+ name = "Настроение: никакое"
+
+/datum/emote/slime/mood/sneaky
+ name = "Настроение: хитрое"
+
+/datum/emote/slime/mood/smile
+ name = "Настроение: улыбающееся"
+
+/datum/emote/slime/mood/cat
+ name = "Настроение: кот"
+
+/datum/emote/slime/mood/pout
+ name = "Настроение: надутый"
+
+/datum/emote/slime/mood/sad
+ name = "Настроение: грустный"
+
+/datum/emote/slime/mood/angry
+ name = "Настроение: злой"
+
+// Emote Other
+
+/datum/emote/gorilla/ooga
+ name = "ooga"
+ message = "oogas."
+ message_param = "oogas at %t."
diff --git a/modular_bandastation/events/_events.dm b/modular_bandastation/events/_events.dm
new file mode 100644
index 0000000000000..69d85f17703ae
--- /dev/null
+++ b/modular_bandastation/events/_events.dm
@@ -0,0 +1,4 @@
+/datum/modpack/events
+ name = "Ребаланс событий и уровня угрозы."
+ desc = "Изменяет шансы возникновения некоторых событий, а также изменяет уровень угроз."
+ author = "larentoun"
diff --git a/modular_bandastation/events/_events.dme b/modular_bandastation/events/_events.dme
new file mode 100644
index 0000000000000..bd679c7efb47f
--- /dev/null
+++ b/modular_bandastation/events/_events.dme
@@ -0,0 +1,4 @@
+#include "_events.dm"
+
+#include "code/events.dm"
+#include "code/threat.dm"
diff --git a/modular_bandastation/events/code/events.dm b/modular_bandastation/events/code/events.dm
new file mode 100644
index 0000000000000..78e380baa87e2
--- /dev/null
+++ b/modular_bandastation/events/code/events.dm
@@ -0,0 +1,2 @@
+/datum/round_event_control/wall_fungus
+ min_players = 20
diff --git a/modular_bandastation/events/code/threat.dm b/modular_bandastation/events/code/threat.dm
new file mode 100644
index 0000000000000..a7a117cee5abd
--- /dev/null
+++ b/modular_bandastation/events/code/threat.dm
@@ -0,0 +1,8 @@
+/datum/game_mode/dynamic/generate_budgets()
+ if (SSticker.totalPlayersReady < low_pop_player_threshold)
+ round_start_budget = 0
+ initial_round_start_budget = 0
+ mid_round_budget = threat_level - 0
+ threat_level = 0
+ return
+ . = ..()
diff --git a/modular_bandastation/examine_panel/_examine_panel.dm b/modular_bandastation/examine_panel/_examine_panel.dm
new file mode 100644
index 0000000000000..15b084aa8d29e
--- /dev/null
+++ b/modular_bandastation/examine_panel/_examine_panel.dm
@@ -0,0 +1,4 @@
+/datum/modpack/examine_panel
+ name = "Examine Panel"
+ desc = "Examine Panel"
+ author = "larentoun"
diff --git a/modular_bandastation/examine_panel/_examine_panel.dme b/modular_bandastation/examine_panel/_examine_panel.dme
new file mode 100644
index 0000000000000..058e864a55577
--- /dev/null
+++ b/modular_bandastation/examine_panel/_examine_panel.dme
@@ -0,0 +1,7 @@
+#include "_examine_panel.dm"
+
+#include "code/_examine_panel_defines.dm"
+#include "code/examine_panel_component.dm"
+#include "code/examine_panel_mob.dm"
+#include "code/examine_panel_prefs.dm"
+#include "code/~examine_panel_defines.dm"
diff --git a/modular_bandastation/examine_panel/code/_examine_panel_defines.dm b/modular_bandastation/examine_panel/code/_examine_panel_defines.dm
new file mode 100644
index 0000000000000..49fd0fd3530b3
--- /dev/null
+++ b/modular_bandastation/examine_panel/code/_examine_panel_defines.dm
@@ -0,0 +1,4 @@
+/// How many characters will be displayed in the flavor text preview before we cut it off?
+#define FLAVOR_PREVIEW_LIMIT 110
+/// Double the maximum message length.
+#define MAX_FLAVOR_LEN 4096
diff --git a/modular_bandastation/examine_panel/code/examine_panel_component.dm b/modular_bandastation/examine_panel/code/examine_panel_component.dm
new file mode 100644
index 0000000000000..a3daa49ce1da6
--- /dev/null
+++ b/modular_bandastation/examine_panel/code/examine_panel_component.dm
@@ -0,0 +1,121 @@
+/datum/component/examine_panel
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ /// Mob that the examine panel belongs to.
+ var/mob/living/holder
+ /// The screen containing the appearance of the mob
+ var/atom/movable/screen/map_view/examine_panel_screen/examine_panel_screen
+ /// Flavor text
+ var/flavor_text
+
+/datum/component/examine_panel/Initialize(use_prefs = FALSE)
+ . = ..()
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+ holder = parent
+ if(!use_prefs)
+ return
+ if(iscarbon(parent))
+ flavor_text = holder.client?.prefs.read_preference(/datum/preference/text/flavor_text)
+ if(issilicon(parent))
+ flavor_text = holder.client?.prefs.read_preference(/datum/preference/text/silicon_flavor_text)
+
+/datum/component/examine_panel/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+
+/datum/component/examine_panel/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
+
+/datum/component/examine_panel/proc/on_examine(mob/living/source, mob/user, list/examine_list)
+ SIGNAL_HANDLER
+
+ if(iscarbon(source))
+ examine_list += get_carbon_flavor_text(source)
+ if(issilicon(source))
+ examine_list += get_silicon_flavor_text(source)
+
+/datum/component/examine_panel/proc/get_carbon_flavor_text(mob/living/carbon/source)
+ var/flavor_text_link
+ /// The first 1-FLAVOR_PREVIEW_LIMIT characters in the mob's "flavor_text" DNA feature. FLAVOR_PREVIEW_LIMIT is defined in flavor_defines.dm.
+ var/preview_text = copytext_char(flavor_text, 1, FLAVOR_PREVIEW_LIMIT)
+ // What examine_tgui.dm uses to determine if flavor text appears as "Obscured".
+ var/face_obscured = (source.wear_mask && (source.wear_mask.flags_inv & HIDEFACE)) || (source.head && (source.head.flags_inv & HIDEFACE))
+
+ if (!(face_obscured))
+ flavor_text_link = span_notice("[preview_text]... Look closer? ")
+ else
+ flavor_text_link = span_notice("Examine closely... ")
+ if (flavor_text_link)
+ return flavor_text_link
+
+/datum/component/examine_panel/proc/get_silicon_flavor_text(mob/living/silicon/source)
+ var/flavor_text_link
+ /// The first 1-FLAVOR_PREVIEW_LIMIT characters in the mob's client's silicon_flavor_text preference datum. FLAVOR_PREVIEW_LIMIT is defined in flavor_defines.dm.
+ var/preview_text = copytext_char(flavor_text, 1, FLAVOR_PREVIEW_LIMIT)
+
+ flavor_text_link = span_notice("[preview_text]... Look closer? ")
+
+ if (flavor_text_link)
+ return flavor_text_link
+
+/datum/component/examine_panel/Topic(href, list/href_list)
+ . = ..()
+
+ if(href_list["lookup_info"])
+ switch(href_list["lookup_info"])
+ if("open_examine_panel")
+ ui_interact(usr)
+
+/datum/component/examine_panel/ui_state(mob/user)
+ return GLOB.always_state
+
+/datum/component/examine_panel/ui_close(mob/user)
+ user.client.clear_map(examine_panel_screen.assigned_map)
+
+/atom/movable/screen/map_view/examine_panel_screen
+ name = "examine panel screen"
+
+/datum/component/examine_panel/ui_interact(mob/user, datum/tgui/ui)
+ if(!examine_panel_screen)
+ examine_panel_screen = new
+ examine_panel_screen.name = "screen"
+ examine_panel_screen.assigned_map = "examine_panel_[REF(holder)]_map"
+ examine_panel_screen.del_on_map_removal = FALSE
+ examine_panel_screen.screen_loc = "[examine_panel_screen.assigned_map]:1,1"
+
+ var/mutable_appearance/current_mob_appearance = new(holder)
+ current_mob_appearance.setDir(SOUTH)
+ current_mob_appearance.transform = matrix() // We reset their rotation, in case they're lying down.
+
+ // In case they're pixel-shifted, we bring 'em back!
+ current_mob_appearance.pixel_x = 0
+ current_mob_appearance.pixel_y = 0
+
+ examine_panel_screen.cut_overlays()
+ examine_panel_screen.add_overlay(current_mob_appearance)
+
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ examine_panel_screen.display_to(user)
+ user.client.register_map_obj(examine_panel_screen)
+ ui = new(user, src, "ExaminePanel")
+ ui.open()
+
+
+/datum/component/examine_panel/ui_data(mob/user)
+ var/list/data = list()
+
+ var/tgui_flavor_text = flavor_text
+ var/obscured
+
+ if(ishuman(parent))
+ var/mob/living/carbon/human/holder_human = parent
+ obscured = (holder_human.wear_mask && (holder_human.wear_mask.flags_inv & HIDEFACE)) || (holder_human.head && (holder_human.head.flags_inv & HIDEFACE))
+ tgui_flavor_text = obscured ? "Obscured" : flavor_text
+
+ var/name = obscured ? "Unknown" : user.name
+
+ data["obscured"] = obscured ? TRUE : FALSE
+ data["character_name"] = name
+ data["assigned_map"] = examine_panel_screen.assigned_map
+ data["flavor_text"] = tgui_flavor_text
+ return data
diff --git a/modular_bandastation/examine_panel/code/examine_panel_mob.dm b/modular_bandastation/examine_panel/code/examine_panel_mob.dm
new file mode 100644
index 0000000000000..b55a6eec0ded8
--- /dev/null
+++ b/modular_bandastation/examine_panel/code/examine_panel_mob.dm
@@ -0,0 +1,19 @@
+// TODO: Don't use prefs when spawned via admins
+/mob/living/carbon/human/Login()
+ . = ..()
+ AddComponent(/datum/component/examine_panel, use_prefs = TRUE)
+
+/mob/living/silicon/Login()
+ . = ..()
+ AddComponent(/datum/component/examine_panel, use_prefs = TRUE)
+
+/mob/living/verb/change_flavor_text()
+ set name = "Change flavor text"
+ set category = "IC"
+
+ var/datum/component/examine_panel/examine_panel = GetComponent(/datum/component/examine_panel)
+ if(!examine_panel)
+ examine_panel = AddComponent(/datum/component/examine_panel)
+ var/new_flavor_text = tgui_input_text(usr, "Enter new flavor text", "Changing Flavor Text", examine_panel.flavor_text)
+ if(new_flavor_text)
+ examine_panel.flavor_text = new_flavor_text
diff --git a/modular_bandastation/examine_panel/code/examine_panel_prefs.dm b/modular_bandastation/examine_panel/code/examine_panel_prefs.dm
new file mode 100644
index 0000000000000..f5adcc4ea798a
--- /dev/null
+++ b/modular_bandastation/examine_panel/code/examine_panel_prefs.dm
@@ -0,0 +1,18 @@
+/datum/preference/text/flavor_text
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "flavor_text"
+ maximum_value_length = MAX_FLAVOR_LEN
+
+/datum/preference/text/flavor_text/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
+ target.dna.features["flavor_text"] = value
+
+/datum/preference/text/silicon_flavor_text
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+ savefile_identifier = PREFERENCE_CHARACTER
+ savefile_key = "silicon_flavor_text"
+ maximum_value_length = MAX_FLAVOR_LEN
+ // This does not get a apply_to_human proc, this is read directly in silicon/robot/examine.dm
+
+/datum/preference/text/silicon_flavor_text/apply_to_human(mob/living/carbon/human/target, value, datum/preferences/preferences)
+ return FALSE // To prevent the not-implemented runtime
diff --git a/modular_bandastation/examine_panel/code/~examine_panel_defines.dm b/modular_bandastation/examine_panel/code/~examine_panel_defines.dm
new file mode 100644
index 0000000000000..d431a143a094e
--- /dev/null
+++ b/modular_bandastation/examine_panel/code/~examine_panel_defines.dm
@@ -0,0 +1,2 @@
+#undef FLAVOR_PREVIEW_LIMIT
+#undef MAX_FLAVOR_LEN
diff --git a/modular_bandastation/example/_example.dm b/modular_bandastation/example/_example.dm
new file mode 100644
index 0000000000000..2343367184030
--- /dev/null
+++ b/modular_bandastation/example/_example.dm
@@ -0,0 +1,16 @@
+/datum/modpack/example
+ /// A string name for the modpack. Used for looking up other modpacks in init.
+ name = "Example modpack"
+ /// A string desc for the modpack. Can be used for modpack verb list as description.
+ desc = "its useless"
+ /// A string with authors of this modpack.
+ author = "furior"
+
+/datum/modpack/example/pre_initialize()
+ . = ..()
+
+/datum/modpack/example/initialize()
+ . = ..()
+
+/datum/modpack/example/post_initialize()
+ . = ..()
diff --git a/modular_bandastation/example/_example.dme b/modular_bandastation/example/_example.dme
new file mode 100644
index 0000000000000..5540c273b03ba
--- /dev/null
+++ b/modular_bandastation/example/_example.dme
@@ -0,0 +1,3 @@
+#include "_example.dm"
+
+#include "code/example.dm"
diff --git a/modular_bandastation/example/code/example.dm b/modular_bandastation/example/code/example.dm
new file mode 100644
index 0000000000000..ff327270a515d
--- /dev/null
+++ b/modular_bandastation/example/code/example.dm
@@ -0,0 +1,2 @@
+/turf/closed/wall/example
+ name = "Example wall"
diff --git a/modular_bandastation/gunhud/_gunhud.dm b/modular_bandastation/gunhud/_gunhud.dm
new file mode 100644
index 0000000000000..a93ecac1d2917
--- /dev/null
+++ b/modular_bandastation/gunhud/_gunhud.dm
@@ -0,0 +1,4 @@
+/datum/modpack/gunhud
+ name = "Счетчик патронов"
+ desc = "Добавляет счетчик патронов"
+ author = "larentoun (modpack), Gandalf2k15 (original)"
diff --git a/modular_bandastation/gunhud/_gunhud.dme b/modular_bandastation/gunhud/_gunhud.dme
new file mode 100644
index 0000000000000..577291e22b77c
--- /dev/null
+++ b/modular_bandastation/gunhud/_gunhud.dme
@@ -0,0 +1,7 @@
+#include "_gunhud.dm"
+
+#include "code/_gunhud_defines.dm"
+#include "code/gunhud_component.dm"
+#include "code/gunhud_hud.dm"
+#include "code/gunhud_screen.dm"
+#include "code/~gunhud_defines.dm"
diff --git a/modular_bandastation/gunhud/code/_gunhud_defines.dm b/modular_bandastation/gunhud/code/_gunhud_defines.dm
new file mode 100644
index 0000000000000..5ceb4767dd99a
--- /dev/null
+++ b/modular_bandastation/gunhud/code/_gunhud_defines.dm
@@ -0,0 +1,5 @@
+// Gunhud
+#define ui_gunhud "RIGHT-1:28,CENTER-5:9"
+
+///The gun needs to update the gun hud!
+#define COMSIG_UPDATE_GUNHUD "update_gunhud"
diff --git a/modular_bandastation/gunhud/code/gunhud_component.dm b/modular_bandastation/gunhud/code/gunhud_component.dm
new file mode 100644
index 0000000000000..1df38eb90143a
--- /dev/null
+++ b/modular_bandastation/gunhud/code/gunhud_component.dm
@@ -0,0 +1,150 @@
+/datum/component/gunhud
+ var/atom/movable/screen/gunhud_screen/hud
+
+/datum/component/gunhud/Initialize()
+ . = ..()
+ if(!istype(parent, /obj/item/gun) && !istype(parent, /obj/item/weldingtool))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(wake_up))
+
+/datum/component/gunhud/Destroy()
+ turn_off()
+ return ..()
+
+/datum/component/gunhud/proc/wake_up(datum/source, mob/user, slot)
+ SIGNAL_HANDLER
+
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(H.is_holding(parent))
+ if(H.hud_used)
+ hud = H.hud_used.gunhud_screen
+ turn_on()
+ else
+ turn_off()
+
+/datum/component/gunhud/proc/turn_on()
+ SIGNAL_HANDLER
+
+ RegisterSignals(parent, list(COMSIG_PREQDELETED, COMSIG_ITEM_DROPPED), PROC_REF(turn_off))
+ RegisterSignals(parent, list(COMSIG_UPDATE_GUNHUD, COMSIG_GUN_CHAMBER_PROCESSED), PROC_REF(update_hud))
+
+ hud.turn_on()
+ update_hud()
+
+/datum/component/gunhud/proc/turn_off()
+ SIGNAL_HANDLER
+
+ UnregisterSignal(parent, list(COMSIG_PREQDELETED, COMSIG_ITEM_DROPPED, COMSIG_UPDATE_GUNHUD, COMSIG_GUN_CHAMBER_PROCESSED))
+
+ if(hud)
+ hud.turn_off()
+ hud = null
+
+/datum/component/gunhud/proc/update_hud()
+ SIGNAL_HANDLER
+ if(istype(parent, /obj/item/gun/ballistic))
+ var/obj/item/gun/ballistic/pew = parent
+ hud.maptext = null
+ hud.icon_state = "backing"
+ var/backing_color = COLOR_CYAN
+ if(!pew.magazine)
+ hud.set_hud(backing_color, "oe", "te", "he", "no_mag")
+ return
+ if(!pew.get_ammo())
+ hud.set_hud(backing_color, "oe", "te", "he", "empty_flash")
+ return
+
+ var/indicator
+ var/rounds = num2text(istype(parent, /obj/item/gun/ballistic/revolver) ? pew.get_ammo(FALSE, FALSE) : pew.get_ammo(TRUE)) // fucking revolvers indeed - do not count empty or chambered rounds for the display HUD
+ var/oth_o
+ var/oth_t
+ var/oth_h
+
+ switch(length(rounds))
+ if(1)
+ oth_o = "o[rounds[1]]"
+ if(2)
+ oth_o = "o[rounds[2]]"
+ oth_t = "t[rounds[1]]"
+ if(3)
+ oth_o = "o[rounds[3]]"
+ oth_t = "t[rounds[2]]"
+ oth_h = "h[rounds[1]]"
+ else
+ oth_o = "o9"
+ oth_t = "t9"
+ oth_h = "h9"
+ hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator)
+
+ else if(istype(parent, /obj/item/gun/energy))
+ var/obj/item/gun/energy/pew = parent
+ hud.icon_state = "eammo_counter"
+ hud.cut_overlays()
+ hud.maptext_x = -12
+ var/obj/item/ammo_casing/energy/shot = pew.ammo_type[pew.select]
+ var/batt_percent = FLOOR(clamp(pew.cell.charge / pew.cell.maxcharge, 0, 1) * 100, 1)
+ var/shot_cost_percent = FLOOR(clamp(shot.e_cost / pew.cell.maxcharge, 0, 1) * 100, 1)
+ if(batt_percent > 99 || shot_cost_percent > 99)
+ hud.maptext_x = -12
+ else
+ hud.maptext_x = -8
+ if(!pew.can_shoot())
+ hud.icon_state = "eammo_counter_empty"
+ hud.maptext = span_maptext("[batt_percent]% [shot_cost_percent]%
")
+ return
+ if(batt_percent <= 25)
+ hud.maptext = span_maptext("[batt_percent]% [shot_cost_percent]%
")
+ return
+ hud.maptext = span_maptext("[batt_percent]% [shot_cost_percent]%
")
+
+ else if(istype(parent, /obj/item/weldingtool))
+ var/obj/item/weldingtool/welder = parent
+ hud.maptext = null
+ var/backing_color = COLOR_TAN_ORANGE
+ hud.icon_state = "backing"
+
+ if(welder.get_fuel() < 1)
+ hud.set_hud(backing_color, "oe", "te", "he", "empty_flash")
+ return
+
+ var/indicator
+ var/fuel = num2text(welder.get_fuel())
+ var/oth_o
+ var/oth_t
+ var/oth_h
+
+ if(welder.welding)
+ indicator = "flame_on"
+ else
+ indicator = "flame_off"
+
+ fuel = num2text(welder.get_fuel())
+
+ switch(length(fuel))
+ if(1)
+ oth_o = "o[fuel[1]]"
+ if(2)
+ oth_o = "o[fuel[2]]"
+ oth_t = "t[fuel[1]]"
+ if(3)
+ oth_o = "o[fuel[3]]"
+ oth_t = "t[fuel[2]]"
+ oth_h = "h[fuel[1]]"
+ else
+ oth_o = "o9"
+ oth_t = "t9"
+ oth_h = "h9"
+ hud.set_hud(backing_color, oth_o, oth_t, oth_h, indicator)
+
+/obj/item/gun/ballistic/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/gunhud)
+
+/obj/item/gun/energy/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/gunhud)
+
+/obj/item/weldingtool/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/gunhud)
diff --git a/modular_bandastation/gunhud/code/gunhud_hud.dm b/modular_bandastation/gunhud/code/gunhud_hud.dm
new file mode 100644
index 0000000000000..bc26c0f0e6d58
--- /dev/null
+++ b/modular_bandastation/gunhud/code/gunhud_hud.dm
@@ -0,0 +1,7 @@
+/datum/hud
+ var/atom/movable/screen/gunhud_screen
+
+/datum/hud/human/New(mob/living/carbon/human/owner)
+ . = ..()
+ gunhud_screen = new /atom/movable/screen/gunhud_screen(null, src)
+ infodisplay += gunhud_screen
diff --git a/modular_bandastation/gunhud/code/gunhud_screen.dm b/modular_bandastation/gunhud/code/gunhud_screen.dm
new file mode 100644
index 0000000000000..2038d7f6e3cd8
--- /dev/null
+++ b/modular_bandastation/gunhud/code/gunhud_screen.dm
@@ -0,0 +1,86 @@
+/*
+* Customizable ammo hud
+*/
+
+/*
+* This hud is controlled namely by the gunhud component. Generally speaking this is inactive much like all other hud components until it's needed.
+* It does not do any calculations of it's own, you must do this externally.
+* If you wish to use this hud, use the gunhud component or create another one which interacts with it via the below procs.
+* proc/turn_off
+* proc/turn_on
+* proc/set_hud
+* Check the gun_hud.dmi for all available icons you can use.
+*/
+
+/atom/movable/screen/gunhud_screen
+ name = "gunhud"
+ icon = 'modular_bandastation/gunhud/icons/gun_hud.dmi'
+ icon_state = "backing"
+ screen_loc = ui_gunhud
+ invisibility = INVISIBILITY_ABSTRACT
+
+ ///This is the color assigned to the OTH backing, numbers and indicator.
+ var/backing_color = COLOR_RED
+ ///This is the "backlight" of the numbers, and only the numbers. Generally you should leave this alone if you aren't making some mutant project.
+ var/oth_backing = "oth_light"
+
+ //Below are the OTH numbers, these are assigned by oX, tX and hX, x being the number you wish to display(0-9)
+ ///OTH position X00
+ var/oth_o
+ ///OTH position 0X0
+ var/oth_t
+ ///OTH position 00X
+ var/oth_h
+ ///This is the custom indicator sprite that will appear in the box at the bottom of the ammo hud, use this for something like semi/auto toggle on a gun.
+ var/indicator
+
+///This proc simply resets the hud to standard and removes it from the players visible hud.
+/atom/movable/screen/gunhud_screen/proc/turn_off()
+ invisibility = INVISIBILITY_ABSTRACT
+ maptext = null
+ backing_color = COLOR_RED
+ oth_backing = ""
+ oth_o = ""
+ oth_t = ""
+ oth_h = ""
+ indicator = ""
+ update_appearance()
+
+///This proc turns the hud on, but does not set it to anything other than the currently set values
+/atom/movable/screen/gunhud_screen/proc/turn_on()
+ invisibility = 0
+
+///This is the main proc for altering the hud's appeareance, it controls the setting of the overlays. Use the OTH and below variables to set it accordingly.
+/atom/movable/screen/gunhud_screen/proc/set_hud(_backing_color, _oth_o, _oth_t, _oth_h, _indicator, _oth_backing = "oth_light")
+ backing_color = _backing_color
+ oth_backing = _oth_backing
+ oth_o = _oth_o
+ oth_t = _oth_t
+ oth_h = _oth_h
+ indicator = _indicator
+
+ update_appearance()
+
+/atom/movable/screen/gunhud_screen/update_overlays()
+ . = ..()
+ if(oth_backing)
+ var/mutable_appearance/oth_backing_overlay = mutable_appearance(icon, oth_backing)
+ oth_backing_overlay.color = backing_color
+ . += oth_backing_overlay
+ if(oth_o)
+ var/mutable_appearance/o_overlay = mutable_appearance(icon, oth_o)
+ o_overlay.color = backing_color
+ . += o_overlay
+ if(oth_t)
+ var/mutable_appearance/t_overlay = mutable_appearance(icon, oth_t)
+ t_overlay.color = backing_color
+ . += t_overlay
+ if(oth_h)
+ var/mutable_appearance/h_overlay = mutable_appearance(icon, oth_h)
+ h_overlay.color = backing_color
+ . += h_overlay
+ if(indicator)
+ var/mutable_appearance/indicator_overlay = mutable_appearance(icon, indicator)
+ indicator_overlay.color = backing_color
+ . += indicator_overlay
+
diff --git a/modular_bandastation/gunhud/code/~gunhud_defines.dm b/modular_bandastation/gunhud/code/~gunhud_defines.dm
new file mode 100644
index 0000000000000..6b21ebfe92135
--- /dev/null
+++ b/modular_bandastation/gunhud/code/~gunhud_defines.dm
@@ -0,0 +1,5 @@
+// Ammo counter
+#undef ui_gunhud
+
+///The gun needs to update the gun hud!
+#undef COMSIG_UPDATE_GUNHUD
diff --git a/modular_bandastation/gunhud/icons/gun_hud.dmi b/modular_bandastation/gunhud/icons/gun_hud.dmi
new file mode 100644
index 0000000000000..6bd861100e2e1
Binary files /dev/null and b/modular_bandastation/gunhud/icons/gun_hud.dmi differ
diff --git a/modular_bandastation/keybindings/_keybindings.dm b/modular_bandastation/keybindings/_keybindings.dm
new file mode 100644
index 0000000000000..f28bb05e8472d
--- /dev/null
+++ b/modular_bandastation/keybindings/_keybindings.dm
@@ -0,0 +1,4 @@
+/datum/modpack/keybindings
+ name = "Хоткеи220"
+ desc = "Ставит значения хоткеев по умолчанию на те, что есть на Para220"
+ author = "larentoun"
diff --git a/modular_bandastation/keybindings/_keybindings.dme b/modular_bandastation/keybindings/_keybindings.dme
new file mode 100644
index 0000000000000..26c7f47e8bc02
--- /dev/null
+++ b/modular_bandastation/keybindings/_keybindings.dme
@@ -0,0 +1,13 @@
+#include "_keybindings.dm"
+
+#include "code/admin.dm"
+#include "code/artificial_intelligence.dm"
+#include "code/carbon.dm"
+#include "code/client.dm"
+#include "code/communication.dm"
+#include "code/emote.dm"
+#include "code/human.dm"
+#include "code/living.dm"
+#include "code/mob.dm"
+#include "code/movement.dm"
+#include "code/robot.dm"
diff --git a/modular_bandastation/keybindings/code/admin.dm b/modular_bandastation/keybindings/code/admin.dm
new file mode 100644
index 0000000000000..c0c7cda433c22
--- /dev/null
+++ b/modular_bandastation/keybindings/code/admin.dm
@@ -0,0 +1,39 @@
+/datum/keybinding/admin/admin_say
+ full_name = "Asay"
+ hotkey_keys = list("F5")
+
+/datum/keybinding/admin/admin_ghost
+ full_name = "Aghost"
+ hotkey_keys = list("F6")
+
+/datum/keybinding/admin/player_panel_new
+ full_name = "Player Panel"
+ hotkey_keys = list("F7")
+
+/datum/keybinding/admin/toggle_buildmode_self
+ full_name = "Toggle Buildmode"
+ hotkey_keys = list("F11")
+
+/datum/keybinding/admin/stealthmode
+ full_name = "Stealth mode"
+ hotkey_keys = list("CtrlF9")
+
+/datum/keybinding/admin/invisimin
+ full_name = "Invisimin"
+ hotkey_keys = list("F9")
+
+/datum/keybinding/admin/deadsay
+ full_name = "Dsay"
+ hotkey_keys = list("F10")
+
+/datum/keybinding/admin/deadmin
+ full_name = "Deadmin"
+ hotkey_keys = list("Unbound")
+
+/datum/keybinding/admin/readmin
+ full_name = "Readmin"
+ hotkey_keys = list("Unbound")
+
+/datum/keybinding/admin/view_tags
+ full_name = "View Tags"
+ hotkey_keys = list("CtrlF11")
diff --git a/modular_bandastation/keybindings/code/artificial_intelligence.dm b/modular_bandastation/keybindings/code/artificial_intelligence.dm
new file mode 100644
index 0000000000000..fc72187c256dc
--- /dev/null
+++ b/modular_bandastation/keybindings/code/artificial_intelligence.dm
@@ -0,0 +1,3 @@
+/datum/keybinding/artificial_intelligence/reconnect
+ full_name = "Переподключиться к оболочке"
+ hotkey_keys = list("-")
diff --git a/modular_bandastation/keybindings/code/carbon.dm b/modular_bandastation/keybindings/code/carbon.dm
new file mode 100644
index 0000000000000..210538c7a5d94
--- /dev/null
+++ b/modular_bandastation/keybindings/code/carbon.dm
@@ -0,0 +1,11 @@
+/datum/keybinding/carbon/toggle_throw_mode
+ full_name = "Режим броска (переключить)"
+ hotkey_keys = list("R", "Southwest")
+
+/datum/keybinding/carbon/hold_throw_mode
+ full_name = "Режим броска (зажать)"
+ hotkey_keys = list("Space")
+
+/datum/keybinding/carbon/give
+ full_name = "Передать вещь (переключить)"
+ hotkey_keys = list("V")
diff --git a/modular_bandastation/keybindings/code/client.dm b/modular_bandastation/keybindings/code/client.dm
new file mode 100644
index 0000000000000..4a83b861a751d
--- /dev/null
+++ b/modular_bandastation/keybindings/code/client.dm
@@ -0,0 +1,11 @@
+/datum/keybinding/client/admin_help
+ full_name = "Admin Help"
+ hotkey_keys = list("F1")
+
+/datum/keybinding/client/screenshot
+ full_name = "Сделать Screenshot"
+ hotkey_keys = list("Unbound")
+
+/datum/keybinding/client/minimal_hud
+ full_name = "Переключить минимальный HUD"
+ hotkey_keys = list("F12")
diff --git a/modular_bandastation/keybindings/code/communication.dm b/modular_bandastation/keybindings/code/communication.dm
new file mode 100644
index 0000000000000..7476e2c910b68
--- /dev/null
+++ b/modular_bandastation/keybindings/code/communication.dm
@@ -0,0 +1,23 @@
+/datum/keybinding/client/communication/say
+ full_name = "Говорить"
+ hotkey_keys = list("F3", "T")
+
+/datum/keybinding/client/communication/radio
+ full_name = "Общий канал рации (;)"
+ hotkey_keys = list("Unbound")
+
+/datum/keybinding/client/communication/ooc
+ full_name = "OOC"
+ hotkey_keys = list("F2", "O")
+
+/datum/keybinding/client/communication/me
+ full_name = "Эмоция"
+ hotkey_keys = list("F4", "M")
+
+/datum/keybinding/client/communication/looc
+ full_name = "LOOC"
+ hotkey_keys = list("L")
+
+/datum/keybinding/client/communication/whisper
+ full_name = "Шептать"
+ hotkey_keys = list("ShiftT")
diff --git a/modular_bandastation/keybindings/code/emote.dm b/modular_bandastation/keybindings/code/emote.dm
new file mode 100644
index 0000000000000..eb02d081238ec
--- /dev/null
+++ b/modular_bandastation/keybindings/code/emote.dm
@@ -0,0 +1,4 @@
+/datum/keybinding/emote/link_to_emote(datum/emote/faketype)
+ . = ..()
+ if(initial(faketype.name))
+ full_name = capitalize(initial(faketype.name))
diff --git a/modular_bandastation/keybindings/code/human.dm b/modular_bandastation/keybindings/code/human.dm
new file mode 100644
index 0000000000000..f880f0bd2cb82
--- /dev/null
+++ b/modular_bandastation/keybindings/code/human.dm
@@ -0,0 +1,15 @@
+/datum/keybinding/human/quick_equip
+ full_name = "Экипировать вещь"
+ hotkey_keys = list("E")
+
+/datum/keybinding/human/quick_equip_belt
+ full_name = "Быстрая экипировка пояса"
+ hotkey_keys = list("ShiftE")
+
+/datum/keybinding/human/quick_equip_belt/quick_equip_bag
+ full_name = "Быстрая экипировка сумки"
+ hotkey_keys = list("ShiftV")
+
+/datum/keybinding/human/quick_equip_belt/quick_equip_suit_storage
+ full_name = "Быстрая экипировка хранилища костюма"
+ hotkey_keys = list("ShiftQ")
diff --git a/modular_bandastation/keybindings/code/living.dm b/modular_bandastation/keybindings/code/living.dm
new file mode 100644
index 0000000000000..644bec4b78fa9
--- /dev/null
+++ b/modular_bandastation/keybindings/code/living.dm
@@ -0,0 +1,27 @@
+/datum/keybinding/living/resist
+ full_name = "Сопротивляться"
+ hotkey_keys = list("B")
+
+/datum/keybinding/living/look_up
+ full_name = "Посмотреть вверх"
+ hotkey_keys = list("P")
+
+/datum/keybinding/living/look_down
+ full_name = "Посмотреть вниз"
+ hotkey_keys = list(";")
+
+/datum/keybinding/living/rest
+ full_name = "Лечь/встать"
+ hotkey_keys = list("ShiftB")
+
+/datum/keybinding/living/toggle_combat_mode
+ full_name = "Переключить Combat Mode"
+ hotkey_keys = list("F")
+
+/datum/keybinding/living/enable_combat_mode
+ full_name = "Включить Combat Mode"
+ hotkey_keys = list("4")
+
+/datum/keybinding/living/disable_combat_mode
+ full_name = "Отключить Combat Mode"
+ hotkey_keys = list("1")
diff --git a/modular_bandastation/keybindings/code/mob.dm b/modular_bandastation/keybindings/code/mob.dm
new file mode 100644
index 0000000000000..9f5f7cbbe04f0
--- /dev/null
+++ b/modular_bandastation/keybindings/code/mob.dm
@@ -0,0 +1,63 @@
+/datum/keybinding/mob/stop_pulling
+ full_name = "Перестать тащить"
+ hotkey_keys = list("C", "Delete")
+
+/datum/keybinding/mob/swap_hands
+ full_name = "Поменять руки"
+ hotkey_keys = list("X")
+
+/datum/keybinding/mob/activate_inhand
+ full_name = "Использовать вещь в руке"
+ hotkey_keys = list("Z")
+
+/datum/keybinding/mob/drop_item
+ full_name = "Выложить вещь в руке"
+ hotkey_keys = list("Q")
+
+/datum/keybinding/mob/toggle_move_intent
+ full_name = "Смена режима ходьбы (зажать)"
+ hotkey_keys = list("Alt")
+
+/datum/keybinding/mob/toggle_move_intent_alternative
+ full_name = "Смена режима ходьбы (переключить)"
+ hotkey_keys = list("Unbound")
+
+/datum/keybinding/mob/target/head_cycle
+ full_name = "Выбрать голову/глаза/рот"
+ hotkey_keys = list("Numpad8")
+
+/datum/keybinding/mob/target/eyes
+ full_name = "Выбрать глаза"
+ hotkey_keys = list("Numpad7")
+
+/datum/keybinding/mob/target/mouth
+ full_name = "Выбрать рот"
+ hotkey_keys = list("Numpad9")
+
+/datum/keybinding/mob/target/r_arm
+ full_name = "Выбрать правую руку"
+ hotkey_keys = list("Numpad4")
+
+/datum/keybinding/mob/target/body_chest
+ full_name = "Выбрать грудь"
+ hotkey_keys = list("Numpad5")
+
+/datum/keybinding/mob/target/left_arm
+ full_name = "Выбрать левую руку"
+ hotkey_keys = list("Numpad6")
+
+/datum/keybinding/mob/target/right_leg
+ full_name = "Выбрать правую ногу"
+ hotkey_keys = list("Numpad1")
+
+/datum/keybinding/mob/target/body_groin
+ full_name = "Выбрать пах"
+ hotkey_keys = list("Numpad2")
+
+/datum/keybinding/mob/target/left_leg
+ full_name = "Выбрать левую ногу"
+ hotkey_keys = list("Numpad3")
+
+/datum/keybinding/mob/prevent_movement
+ full_name = "Остановиться (зажать)"
+ hotkey_keys = list("Ctrl")
diff --git a/modular_bandastation/keybindings/code/movement.dm b/modular_bandastation/keybindings/code/movement.dm
new file mode 100644
index 0000000000000..6bcc2b3606e1a
--- /dev/null
+++ b/modular_bandastation/keybindings/code/movement.dm
@@ -0,0 +1,23 @@
+/datum/keybinding/movement/north
+ full_name = "Идти на север"
+ hotkey_keys = list("W", "North")
+
+/datum/keybinding/movement/south
+ full_name = "Идти на юг"
+ hotkey_keys = list("S", "South")
+
+/datum/keybinding/movement/west
+ full_name = "Идти на запад"
+ hotkey_keys = list("A", "West")
+
+/datum/keybinding/movement/east
+ full_name = "Идти на восток"
+ hotkey_keys = list("D", "East")
+
+/datum/keybinding/movement/zlevel_upwards
+ full_name = "Идти наверх"
+ hotkey_keys = list("Northeast") // PGUP
+
+/datum/keybinding/movement/zlevel_downwards
+ full_name = "Идти вниз"
+ hotkey_keys = list("Southeast") // PGDOWN
diff --git a/modular_bandastation/keybindings/code/robot.dm b/modular_bandastation/keybindings/code/robot.dm
new file mode 100644
index 0000000000000..95e99ec7b2e47
--- /dev/null
+++ b/modular_bandastation/keybindings/code/robot.dm
@@ -0,0 +1,19 @@
+/datum/keybinding/robot/moduleone
+ full_name = "Ячейка 1"
+ hotkey_keys = list("1")
+
+/datum/keybinding/robot/moduletwo
+ full_name = "Ячейка 2"
+ hotkey_keys = list("2")
+
+/datum/keybinding/robot/modulethree
+ full_name = "Ячейка 3"
+ hotkey_keys = list("3")
+
+/datum/keybinding/robot/unequip_module
+ full_name = "Выложить в хранилище"
+ hotkey_keys = list("Q")
+
+/datum/keybinding/robot/undeploy
+ full_name = "Отсоединиться от оболочки"
+ hotkey_keys = list("=")
diff --git a/modular_bandastation/modular_bandastation.dme b/modular_bandastation/modular_bandastation.dme
new file mode 100644
index 0000000000000..604d5888e9864
--- /dev/null
+++ b/modular_bandastation/modular_bandastation.dme
@@ -0,0 +1,24 @@
+#include "_modpack.dm"
+#include "_modpacks.dm"
+
+// #include "example/_example.dme"
+
+#include "_defines220/_defines220.dme"
+#include "_helpers220/_helpers220.dme"
+#include "_signals220/_signals220.dme"
+#include "aesthetics/_aesthetics.dme"
+#include "ai_laws/_ai_laws.dme"
+#include "barsigns/_barsigns.dme"
+#include "communication/_communication.dme"
+#include "crawl_speed/_crawl_speed.dme"
+#include "cyrillic_fixes/_cyrillic_fixes.dme"
+#include "discord/_discord.dme"
+#include "emote_panel/_emote_panel.dme"
+#include "events/_events.dme"
+#include "examine_panel/_examine_panel.dme"
+#include "gunhud/_gunhud.dme"
+#include "keybindings/_keybindings.dme"
+#include "pixel_shift/_pixel_shift.dme"
+#include "translations/_translations.dme"
+#include "tts/_tts.dme"
+#include "world_topics/_world_topics.dme"
diff --git a/modular_bandastation/pixel_shift/_pixel_shift.dm b/modular_bandastation/pixel_shift/_pixel_shift.dm
new file mode 100644
index 0000000000000..b3d6f05911fd3
--- /dev/null
+++ b/modular_bandastation/pixel_shift/_pixel_shift.dm
@@ -0,0 +1,4 @@
+/datum/modpack/pixel_shift
+ name = "Pixel Shift"
+ desc = "Добавляет возможность движения по-пиксельно в пределах турфа."
+ author = "larentoun (modpack), Vallat (refactor), Ranged66 (layer shift), Azarak (pixel shift port), Gandalf2k15 (pixel shift refactor)"
diff --git a/modular_bandastation/pixel_shift/_pixel_shift.dme b/modular_bandastation/pixel_shift/_pixel_shift.dme
new file mode 100644
index 0000000000000..5b1685e49cbfe
--- /dev/null
+++ b/modular_bandastation/pixel_shift/_pixel_shift.dme
@@ -0,0 +1,6 @@
+#include "_pixel_shift.dm"
+
+#include "code/layer_shift.dm"
+#include "code/pixel_shift_component.dm"
+#include "code/pixel_shift_keybind.dm"
+#include "code/pixel_shift_mob.dm"
diff --git a/modular_bandastation/pixel_shift/code/layer_shift.dm b/modular_bandastation/pixel_shift/code/layer_shift.dm
new file mode 100644
index 0000000000000..3be3824c06fd1
--- /dev/null
+++ b/modular_bandastation/pixel_shift/code/layer_shift.dm
@@ -0,0 +1,36 @@
+#define MOB_LAYER_SHIFT_INCREMENT 0.01
+#define MOB_LAYER_SHIFT_MIN 3.95
+//#define MOB_LAYER 4 // This is a byond standard define
+#define MOB_LAYER_SHIFT_MAX 4.05
+
+/mob/living/verb/layershift_up()
+ set name = "Shift Layer Upwards"
+ set category = "IC"
+
+ if(incapacitated())
+ to_chat(src, span_warning("You can't do that right now!"))
+ return
+
+ if(layer >= MOB_LAYER_SHIFT_MAX)
+ to_chat(src, span_warning("You cannot increase your layer priority any further."))
+ return
+
+ layer += MOB_LAYER_SHIFT_INCREMENT
+ var/layer_priority = round((layer - MOB_LAYER) * 100, 1) // Just for text feedback
+ to_chat(src, span_notice("Your layer priority is now [layer_priority]."))
+
+/mob/living/verb/layershift_down()
+ set name = "Shift Layer Downwards"
+ set category = "IC"
+
+ if(incapacitated())
+ to_chat(src, span_warning("You can't do that right now!"))
+ return
+
+ if(layer <= MOB_LAYER_SHIFT_MIN)
+ to_chat(src, span_warning("You cannot decrease your layer priority any further."))
+ return
+
+ layer -= MOB_LAYER_SHIFT_INCREMENT
+ var/layer_priority = round((layer - MOB_LAYER) * 100, 1) // Just for text feedback
+ to_chat(src, span_notice("Your layer priority is now [layer_priority]."))
diff --git a/modular_bandastation/pixel_shift/code/pixel_shift_component.dm b/modular_bandastation/pixel_shift/code/pixel_shift_component.dm
new file mode 100644
index 0000000000000..76d5f6c08e3c9
--- /dev/null
+++ b/modular_bandastation/pixel_shift/code/pixel_shift_component.dm
@@ -0,0 +1,96 @@
+/datum/component/pixel_shift
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ /// Whether the mob is pixel shifted or not
+ var/is_shifted = FALSE
+ /// If we are in the shifting setting.
+ var/shifting = TRUE
+ /// Takes the four cardinal direction defines. Any atoms moving into this atom's tile will be allowed to from the added directions.
+ var/passthroughable = NONE
+ var/maximum_pixel_shift = 12
+ var/passable_shift_threshold = 8
+
+/datum/component/pixel_shift/Initialize(...)
+ . = ..()
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+
+/datum/component/pixel_shift/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_DOWN, PROC_REF(pixel_shift_down))
+ RegisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_UP, PROC_REF(pixel_shift_up))
+ RegisterSignals(parent, list(COMSIG_LIVING_RESET_PULL_OFFSETS, COMSIG_LIVING_SET_PULL_OFFSET, COMSIG_MOVABLE_MOVED, SIGNAL_ADDTRAIT(TRAIT_FLOORED)), PROC_REF(unpixel_shift))
+ RegisterSignal(parent, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, PROC_REF(pre_move_check))
+ RegisterSignal(parent, COMSIG_LIVING_CAN_ALLOW_THROUGH, PROC_REF(check_passable))
+
+/datum/component/pixel_shift/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_DOWN)
+ UnregisterSignal(parent, COMSIG_KB_MOB_PIXEL_SHIFT_UP)
+ UnregisterSignal(parent, COMSIG_LIVING_RESET_PULL_OFFSETS)
+ UnregisterSignal(parent, COMSIG_LIVING_SET_PULL_OFFSET)
+ UnregisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_FLOORED))
+ UnregisterSignal(parent, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(parent, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE)
+ UnregisterSignal(parent, COMSIG_LIVING_CAN_ALLOW_THROUGH)
+
+/datum/component/pixel_shift/proc/pre_move_check(mob/source, new_loc, direct)
+ SIGNAL_HANDLER
+ if(shifting)
+ pixel_shift(source, direct)
+ return COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE
+
+/datum/component/pixel_shift/proc/check_passable(mob/source, atom/movable/mover, border_dir)
+ SIGNAL_HANDLER
+ // Make sure to not allow projectiles of any kind past where they normally wouldn't.
+ if(!isprojectile(mover) && !mover.throwing && passthroughable & border_dir)
+ return COMPONENT_LIVING_PASSABLE
+
+/datum/component/pixel_shift/proc/pixel_shift_down()
+ SIGNAL_HANDLER
+ shifting = TRUE
+ return COMSIG_KB_ACTIVATED
+
+/datum/component/pixel_shift/proc/pixel_shift_up()
+ SIGNAL_HANDLER
+ shifting = FALSE
+
+/datum/component/pixel_shift/proc/unpixel_shift()
+ SIGNAL_HANDLER
+ passthroughable = NONE
+ if(is_shifted)
+ var/mob/living/owner = parent
+ owner.pixel_x = owner.body_position_pixel_x_offset + owner.base_pixel_x
+ owner.pixel_y = owner.body_position_pixel_y_offset + owner.base_pixel_y
+ qdel(src)
+
+/datum/component/pixel_shift/proc/pixel_shift(mob/source, direct)
+ var/mob/living/owner = parent
+ if(HAS_TRAIT(owner, TRAIT_RESTRAINED) || HAS_TRAIT(owner, TRAIT_IMMOBILIZED) || length(owner.pulledby) || owner.stat != CONSCIOUS)
+ return
+ passthroughable = NONE
+ switch(direct)
+ if(NORTH)
+ if(owner.pixel_y <= maximum_pixel_shift + owner.base_pixel_y)
+ owner.pixel_y++
+ is_shifted = TRUE
+ if(EAST)
+ if(owner.pixel_x <= maximum_pixel_shift + owner.base_pixel_x)
+ owner.pixel_x++
+ is_shifted = TRUE
+ if(SOUTH)
+ if(owner.pixel_y >= -maximum_pixel_shift + owner.base_pixel_y)
+ owner.pixel_y--
+ is_shifted = TRUE
+ if(WEST)
+ if(owner.pixel_x >= -maximum_pixel_shift + owner.base_pixel_x)
+ owner.pixel_x--
+ is_shifted = TRUE
+
+ // Yes, I know this sets it to true for everything if more than one is matched.
+ // Movement doesn't check diagonals, and instead just checks EAST or WEST, depending on where you are for those.
+ if(owner.pixel_y > passable_shift_threshold)
+ passthroughable |= EAST | SOUTH | WEST
+ else if(owner.pixel_y < -passable_shift_threshold)
+ passthroughable |= NORTH | EAST | WEST
+ if(owner.pixel_x > passable_shift_threshold)
+ passthroughable |= NORTH | SOUTH | WEST
+ else if(owner.pixel_x < -passable_shift_threshold)
+ passthroughable |= NORTH | EAST | SOUTH
diff --git a/modular_bandastation/pixel_shift/code/pixel_shift_keybind.dm b/modular_bandastation/pixel_shift/code/pixel_shift_keybind.dm
new file mode 100644
index 0000000000000..2375b350a33c7
--- /dev/null
+++ b/modular_bandastation/pixel_shift/code/pixel_shift_keybind.dm
@@ -0,0 +1,17 @@
+/datum/keybinding/mob/pixel_shift
+ hotkey_keys = list("B")
+ name = "pixel_shift"
+ full_name = "Pixel Shift"
+ description = "Shift your characters offset."
+ category = CATEGORY_MOVEMENT
+ keybind_signal = COMSIG_KB_MOB_PIXEL_SHIFT_DOWN
+
+/datum/keybinding/mob/pixel_shift/down(client/user)
+ . = ..()
+ if(.)
+ return
+ user.mob.add_pixel_shift_component()
+
+/datum/keybinding/mob/pixel_shift/up(client/user)
+ . = ..()
+ SEND_SIGNAL(user.mob, COMSIG_KB_MOB_PIXEL_SHIFT_UP)
diff --git a/modular_bandastation/pixel_shift/code/pixel_shift_mob.dm b/modular_bandastation/pixel_shift/code/pixel_shift_mob.dm
new file mode 100644
index 0000000000000..cd02f441ce038
--- /dev/null
+++ b/modular_bandastation/pixel_shift/code/pixel_shift_mob.dm
@@ -0,0 +1,5 @@
+/mob/proc/add_pixel_shift_component()
+ return
+
+/mob/living/add_pixel_shift_component()
+ AddComponent(/datum/component/pixel_shift)
diff --git a/modular_bandastation/translations/_translations.dm b/modular_bandastation/translations/_translations.dm
new file mode 100644
index 0000000000000..7cf16725ef5e0
--- /dev/null
+++ b/modular_bandastation/translations/_translations.dm
@@ -0,0 +1,4 @@
+/datum/modpack/translations
+ name = "Переводы"
+ desc = "Добавляет переводы"
+ author = "Vallat"
diff --git a/modular_bandastation/translations/_translations.dme b/modular_bandastation/translations/_translations.dme
new file mode 100644
index 0000000000000..d1949fb31473a
--- /dev/null
+++ b/modular_bandastation/translations/_translations.dme
@@ -0,0 +1,4 @@
+#include "_translations.dm"
+
+#include "code/moustache.dm"
+#include "code/restaurant_customer.dm"
diff --git a/modular_bandastation/translations/code/moustache.dm b/modular_bandastation/translations/code/moustache.dm
new file mode 100644
index 0000000000000..ff8d1936599b1
--- /dev/null
+++ b/modular_bandastation/translations/code/moustache.dm
@@ -0,0 +1,37 @@
+/obj/item/clothing/mask/fakemoustache
+ name = "накладные усы"
+ desc = "Осторожно: усы накладные."
+
+/obj/item/clothing/mask/fakemoustache/italian
+ name = "итальянские усы"
+ desc = "Изготовлен из настоящих итальянских волосков для усов. Дает владельцу непреодолимое желание дико жестикулировать."
+
+/obj/item/clothing/mask/fakemoustache/italian/handle_speech(datum/source, list/speech_args)
+ var/message = speech_args[SPEECH_MESSAGE]
+ if(message[1] != "*")
+ var/static/regex/words = new(@"(?[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_bandastation/tts/code/seeds/silero.dm b/modular_bandastation/tts/code/seeds/silero.dm
new file mode 100644
index 0000000000000..5f83cb4078cd6
--- /dev/null
+++ b/modular_bandastation/tts/code/seeds/silero.dm
@@ -0,0 +1,3581 @@
+/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 = 0
+
+/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 = 0
+
+/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 = 0
+
+/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 = 0
+
+/datum/tts_seed/silero/pantheon
+ name = "pantheon"
+ value = "pantheon"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/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 = 0
+
+/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 = 0
+
+/datum/tts_seed/silero/hraz
+ name = "Hraz"
+ value = "hraz"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tereshkova
+ name = "Tereshkova"
+ value = "tereshkova"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/babazina
+ name = "Babazina"
+ value = "babazina"
+ category = TTS_CATEGORY_ATOMIC_HEART
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/darius
+ name = "Darius"
+ value = "darius"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/trundle
+ name = "Trundle"
+ value = "trundle"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/garen
+ name = "Garen"
+ value = "garen"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kled
+ name = "Kled"
+ value = "kled"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ekko
+ name = "Ekko"
+ value = "ekko"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/volibear
+ name = "Volibear"
+ value = "volibear"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/samira
+ name = "Samira"
+ value = "samira"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/swain
+ name = "Swain"
+ value = "swain"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/udyr
+ name = "Udyr"
+ value = "udyr"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dr_mundo
+ name = "Dr_mundo"
+ value = "dr_mundo"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/graves
+ name = "Graves"
+ value = "graves"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/rakan
+ name = "Rakan"
+ value = "rakan"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/renata_glasc
+ name = "Renata_glasc"
+ value = "renata_glasc"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/gangplank
+ name = "Gangplank"
+ value = "gangplank"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/riven
+ name = "Riven"
+ value = "riven"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/katarina
+ name = "Katarina"
+ value = "katarina"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ahri
+ name = "Ahri"
+ value = "ahri"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ornn
+ name = "Ornn"
+ value = "ornn"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/braum
+ name = "Braum"
+ value = "braum"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/fizz
+ name = "Fizz"
+ value = "fizz"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/draven
+ name = "Draven"
+ value = "draven"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/qiyana
+ name = "Qiyana"
+ value = "qiyana"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ksante
+ name = "Ksante"
+ value = "ksante"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/talon
+ name = "Talon"
+ value = "talon"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/shyvana
+ name = "Shyvana"
+ value = "shyvana"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zenyatta
+ name = "Zenyatta"
+ value = "zenyatta"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kiriko
+ name = "Kiriko"
+ value = "kiriko"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hanzo
+ name = "Hanzo"
+ value = "hanzo"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/roadhog
+ name = "Roadhog"
+ value = "roadhog"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sigma
+ name = "Sigma"
+ value = "sigma"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/soldier_76
+ name = "Soldier_76"
+ value = "soldier_76"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/junkrat
+ name = "Junkrat"
+ value = "junkrat"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tracer
+ name = "Tracer"
+ value = "tracer"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/genji
+ name = "Genji"
+ value = "genji"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/echo
+ name = "Echo"
+ value = "echo"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sojourn
+ name = "Sojourn"
+ value = "sojourn"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/winston
+ name = "Winston"
+ value = "winston"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/reaper
+ name = "Reaper"
+ value = "reaper"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/training_robot
+ name = "Training_robot"
+ value = "training_robot"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/m_darkelf
+ name = "M_darkelf"
+ value = "m_darkelf"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/esbern
+ name = "Esbern"
+ value = "esbern"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_argo
+ name = "M_argo"
+ value = "m_argo"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_khajiit
+ name = "M_khajiit"
+ value = "m_khajiit"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_coward
+ name = "M_coward"
+ value = "m_coward"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/farkas
+ name = "Farkas"
+ value = "farkas"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_drunk
+ name = "M_drunk"
+ value = "m_drunk"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_khajiit
+ name = "F_khajiit"
+ value = "f_khajiit"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_citizen
+ name = "M_citizen"
+ value = "m_citizen"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_orc
+ name = "M_orc"
+ value = "m_orc"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/odahviing
+ name = "Odahviing"
+ value = "odahviing"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kodlak
+ name = "Kodlak"
+ value = "kodlak"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_child
+ name = "M_child"
+ value = "m_child"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/emperor
+ name = "Emperor"
+ value = "emperor"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hagraven
+ name = "Hagraven"
+ value = "hagraven"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/nazir
+ name = "Nazir"
+ value = "nazir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dremora
+ name = "Dremora"
+ value = "dremora"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/alduin
+ name = "Alduin"
+ value = "alduin"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/malkoran
+ name = "Malkoran"
+ value = "malkoran"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/barbas
+ name = "Barbas"
+ value = "barbas"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hermaeus
+ name = "Hermaeus"
+ value = "hermaeus"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hakon
+ name = "Hakon"
+ value = "hakon"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/rita
+ name = "Rita"
+ value = "rita"
+ category = TTS_CATEGORY_RITA
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/barman
+ name = "Barman"
+ value = "barman"
+ category = TTS_CATEGORY_STALKER
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bridger2
+ name = "Bridger2"
+ value = "bridger2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bridger3
+ name = "Bridger3"
+ value = "bridger3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/cannibal3
+ name = "Cannibal3"
+ value = "cannibal3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bridger1
+ name = "Bridger1"
+ value = "bridger1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/cannibal2
+ name = "Cannibal2"
+ value = "cannibal2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/slave1
+ name = "Slave1"
+ value = "slave1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/slave3
+ name = "Slave3"
+ value = "slave3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/mira
+ name = "Mira"
+ value = "mira"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/valeera
+ name = "Valeera"
+ value = "valeera"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/rehgar
+ name = "Rehgar"
+ value = "rehgar"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/yrel
+ name = "Yrel"
+ value = "yrel"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/volskaya
+ name = "Volskaya"
+ value = "volskaya"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/necromancer
+ name = "Necromancer"
+ value = "necromancer"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zuljin
+ name = "Zuljin"
+ value = "zuljin"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/samuro
+ name = "Samuro"
+ value = "samuro"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tyrael
+ name = "Tyrael"
+ value = "tyrael"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/athena
+ name = "Athena"
+ value = "athena"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/default
+ name = "Default"
+ value = "default"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/chromie
+ name = "Chromie"
+ value = "chromie"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/orphea
+ name = "Orphea"
+ value = "orphea"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/adjutant
+ name = "Adjutant"
+ value = "adjutant"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/vanndara
+ name = "Vanndara"
+ value = "vanndara"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/mechatassadar
+ name = "Mechatassadar"
+ value = "mechatassadar"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/blackheart
+ name = "Blackheart"
+ value = "blackheart"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/olaf
+ name = "Olaf"
+ value = "olaf"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/alarak
+ name = "Alarak"
+ value = "alarak"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dva
+ name = "Dva"
+ value = "dva"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/toy18
+ name = "Toy18"
+ value = "toy18"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/witchdoctor_h
+ name = "Witchdoctor_h"
+ value = "witchdoctor_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lucio
+ name = "Lucio"
+ value = "lucio"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/angel
+ name = "Angel"
+ value = "angel"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/thunderking
+ name = "Thunderking"
+ value = "thunderking"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dr_boom
+ name = "Dr_boom"
+ value = "dr_boom"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hooktusk
+ name = "Hooktusk"
+ value = "hooktusk"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sinclari
+ name = "Sinclari"
+ value = "sinclari"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kazakus
+ name = "Kazakus"
+ value = "kazakus"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ol_toomba
+ name = "Ol_toomba"
+ value = "ol_toomba"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/moroes
+ name = "Moroes"
+ value = "moroes"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/maiev_hs
+ name = "Maiev_hs"
+ value = "maiev_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zentimo
+ name = "Zentimo"
+ value = "zentimo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/rastakhan
+ name = "Rastakhan"
+ value = "rastakhan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/innkeeper
+ name = "Innkeeper"
+ value = "innkeeper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/togwaggle
+ name = "Togwaggle"
+ value = "togwaggle"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/biggs
+ name = "Biggs"
+ value = "biggs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/brann
+ name = "Brann"
+ value = "brann"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tekahn_boss
+ name = "Tekahn_boss"
+ value = "tekahn_boss"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/siamat
+ name = "Siamat"
+ value = "siamat"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/omnotron
+ name = "Omnotron"
+ value = "omnotron"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/putricide
+ name = "Putricide"
+ value = "putricide"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/khadgar
+ name = "Khadgar"
+ value = "khadgar"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zoie
+ name = "Zoie"
+ value = "zoie"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/azalina
+ name = "Azalina"
+ value = "azalina"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/chu
+ name = "Chu"
+ value = "chu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tekahn
+ name = "Tekahn"
+ value = "tekahn"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sthara
+ name = "Sthara"
+ value = "sthara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dovo
+ name = "Dovo"
+ value = "dovo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/shaw
+ name = "Shaw"
+ value = "shaw"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/greymane
+ name = "Greymane"
+ value = "greymane"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/willow
+ name = "Willow"
+ value = "willow"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/haro
+ name = "Haro"
+ value = "haro"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hagatha
+ name = "Hagatha"
+ value = "hagatha"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/reno
+ name = "Reno"
+ value = "reno"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ozara
+ name = "Ozara"
+ value = "ozara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/loti
+ name = "Loti"
+ value = "loti"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tarkus
+ name = "Tarkus"
+ value = "tarkus"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/voone
+ name = "Voone"
+ value = "voone"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tala
+ name = "Tala"
+ value = "tala"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/edra
+ name = "Edra"
+ value = "edra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/myra
+ name = "Myra"
+ value = "myra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/smiggs
+ name = "Smiggs"
+ value = "smiggs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/timothy
+ name = "Timothy"
+ value = "timothy"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/wendy
+ name = "Wendy"
+ value = "wendy"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hannigan
+ name = "Hannigan"
+ value = "hannigan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/vargoth
+ name = "Vargoth"
+ value = "vargoth"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/jolene
+ name = "Jolene"
+ value = "jolene"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kyriss
+ name = "Kyriss"
+ value = "kyriss"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/saurfang
+ name = "Saurfang"
+ value = "saurfang"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kizi
+ name = "Kizi"
+ value = "kizi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/slate
+ name = "Slate"
+ value = "slate"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hesutu
+ name = "Hesutu"
+ value = "hesutu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hancho
+ name = "Hancho"
+ value = "hancho"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/gnomenapper
+ name = "Gnomenapper"
+ value = "gnomenapper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/valdera
+ name = "Valdera"
+ value = "valdera"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/disidra
+ name = "Disidra"
+ value = "disidra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/omu
+ name = "Omu"
+ value = "omu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/floop
+ name = "Floop"
+ value = "floop"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/belloc
+ name = "Belloc"
+ value = "belloc"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/xurios
+ name = "Xurios"
+ value = "xurios"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/wagtoggle
+ name = "Wagtoggle"
+ value = "wagtoggle"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/belnaara
+ name = "Belnaara"
+ value = "belnaara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/lilayell
+ name = "Lilayell"
+ value = "lilayell"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/candlebeard
+ name = "Candlebeard"
+ value = "candlebeard"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/awilo
+ name = "Awilo"
+ value = "awilo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/marei
+ name = "Marei"
+ value = "marei"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/applebough
+ name = "Applebough"
+ value = "applebough"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lazul
+ name = "Lazul"
+ value = "lazul"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/arwyn
+ name = "Arwyn"
+ value = "arwyn"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/glowtron
+ name = "Glowtron"
+ value = "glowtron"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/cardish
+ name = "Cardish"
+ value = "cardish"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/robold
+ name = "Robold"
+ value = "robold"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/malfurion
+ name = "Malfurion"
+ value = "malfurion"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/deathwhisper
+ name = "Deathwhisper"
+ value = "deathwhisper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/janna
+ name = "Janna"
+ value = "janna"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/cassiopeia
+ name = "Cassiopeia"
+ value = "cassiopeia"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/taliyah
+ name = "Taliyah"
+ value = "taliyah"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/neeko
+ name = "Neeko"
+ value = "neeko"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/taric
+ name = "Taric"
+ value = "taric"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/akshan
+ name = "Akshan"
+ value = "akshan"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tristana
+ name = "Tristana"
+ value = "tristana"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sylas
+ name = "Sylas"
+ value = "sylas"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sejuani
+ name = "Sejuani"
+ value = "sejuani"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/anivia
+ name = "Anivia"
+ value = "anivia"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/vayne
+ name = "Vayne"
+ value = "vayne"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/karma
+ name = "Karma"
+ value = "karma"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/nilah
+ name = "Nilah"
+ value = "nilah"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/olaf_lol
+ name = "Olaf_lol"
+ value = "olaf_lol"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/quinn
+ name = "Quinn"
+ value = "quinn"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lissandra
+ name = "Lissandra"
+ value = "lissandra"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hecarim
+ name = "Hecarim"
+ value = "hecarim"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/vi
+ name = "Vi"
+ value = "vi"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zyra
+ name = "Zyra"
+ value = "zyra"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zac
+ name = "Zac"
+ value = "zac"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/moira
+ name = "Moira"
+ value = "moira"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ashe
+ name = "Ashe"
+ value = "ashe"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/brigitte
+ name = "Brigitte"
+ value = "brigitte"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/mercy
+ name = "Mercy"
+ value = "mercy"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lucio_ov
+ name = "Lucio_ov"
+ value = "lucio_ov"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dva_ov
+ name = "Dva_ov"
+ value = "dva_ov"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/symmetra
+ name = "Symmetra"
+ value = "symmetra"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zarya
+ name = "Zarya"
+ value = "zarya"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/cassidy
+ name = "Cassidy"
+ value = "cassidy"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/baptiste
+ name = "Baptiste"
+ value = "baptiste"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/junker_queen
+ name = "Junker_queen"
+ value = "junker_queen"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/doomfist
+ name = "Doomfist"
+ value = "doomfist"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/pharah
+ name = "Pharah"
+ value = "pharah"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sombra
+ name = "Sombra"
+ value = "sombra"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ana
+ name = "Ana"
+ value = "ana"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/widowmaker
+ name = "Widowmaker"
+ value = "widowmaker"
+ category = TTS_CATEGORY_OVERWATCH
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/harbor
+ name = "Harbor"
+ value = "harbor"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sage
+ name = "Sage"
+ value = "sage"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/brimstone
+ name = "Brimstone"
+ value = "brimstone"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sova
+ name = "Sova"
+ value = "sova"
+ category = TTS_CATEGORY_VALORANT
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_shrill
+ name = "F_shrill"
+ value = "f_shrill"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_haughty
+ name = "M_haughty"
+ value = "m_haughty"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_soldier
+ name = "M_soldier"
+ value = "m_soldier"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/sven
+ name = "Sven"
+ value = "sven"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_sultry
+ name = "F_sultry"
+ value = "f_sultry"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/eorlund
+ name = "Eorlund"
+ value = "eorlund"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_commander
+ name = "M_commander"
+ value = "m_commander"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_nord
+ name = "F_nord"
+ value = "f_nord"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lydia
+ name = "Lydia"
+ value = "lydia"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/motierre
+ name = "Motierre"
+ value = "motierre"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_haughty
+ name = "F_haughty"
+ value = "f_haughty"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tullius
+ name = "Tullius"
+ value = "tullius"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/festus
+ name = "Festus"
+ value = "festus"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_nord
+ name = "M_nord"
+ value = "m_nord"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/olava
+ name = "Olava"
+ value = "olava"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_commander
+ name = "F_commander"
+ value = "f_commander"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/hadvar
+ name = "Hadvar"
+ value = "hadvar"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_argo
+ name = "F_argo"
+ value = "f_argo"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/arngeir
+ name = "Arngeir"
+ value = "arngeir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/nazeem
+ name = "Nazeem"
+ value = "nazeem"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/falion
+ name = "Falion"
+ value = "falion"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_coward
+ name = "F_coward"
+ value = "f_coward"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_guard
+ name = "M_guard"
+ value = "m_guard"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_commoner
+ name = "M_commoner"
+ value = "m_commoner"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/elisif
+ name = "Elisif"
+ value = "elisif"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/paarthurnax
+ name = "Paarthurnax"
+ value = "paarthurnax"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/grelka
+ name = "Grelka"
+ value = "grelka"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_commoner
+ name = "F_commoner"
+ value = "f_commoner"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ebony
+ name = "Ebony"
+ value = "ebony"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ulfric
+ name = "Ulfric"
+ value = "ulfric"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/farengar
+ name = "Farengar"
+ value = "farengar"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/astrid
+ name = "Astrid"
+ value = "astrid"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/brynjolf
+ name = "Brynjolf"
+ value = "brynjolf"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/maven
+ name = "Maven"
+ value = "maven"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_child
+ name = "F_child"
+ value = "f_child"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_orc
+ name = "F_orc"
+ value = "f_orc"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/delphine
+ name = "Delphine"
+ value = "delphine"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/f_darkelf
+ name = "F_darkelf"
+ value = "f_darkelf"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/grelod
+ name = "Grelod"
+ value = "grelod"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tolfdir
+ name = "Tolfdir"
+ value = "tolfdir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_bandit
+ name = "M_bandit"
+ value = "m_bandit"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/m_forsworn
+ name = "M_forsworn"
+ value = "m_forsworn"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/karliah
+ name = "Karliah"
+ value = "karliah"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/felldir
+ name = "Felldir"
+ value = "felldir"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ancano
+ name = "Ancano"
+ value = "ancano"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/mercer
+ name = "Mercer"
+ value = "mercer"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/vex
+ name = "Vex"
+ value = "vex"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/mirabelle
+ name = "Mirabelle"
+ value = "mirabelle"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/aventus
+ name = "Aventus"
+ value = "aventus"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/tsun
+ name = "Tsun"
+ value = "tsun"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/elenwen
+ name = "Elenwen"
+ value = "elenwen"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/gormlaith
+ name = "Gormlaith"
+ value = "gormlaith"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dragon
+ name = "Dragon"
+ value = "dragon"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/overwatch
+ name = "Overwatch"
+ value = "overwatch"
+ category = TTS_CATEGORY_HALFLIFE2
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zak
+ name = "Zak"
+ value = "zak"
+ category = TTS_CATEGORY_EVILISLANDS
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/merc2
+ name = "Merc2"
+ value = "merc2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/forest1
+ name = "Forest1"
+ value = "forest1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bandit3
+ name = "Bandit3"
+ value = "bandit3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/forest2
+ name = "Forest2"
+ value = "forest2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/merc1
+ name = "Merc1"
+ value = "merc1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bandit2
+ name = "Bandit2"
+ value = "bandit2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/forest3
+ name = "Forest3"
+ value = "forest3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tribal3
+ name = "Tribal3"
+ value = "tribal3"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/slave2
+ name = "Slave2"
+ value = "slave2"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/miller
+ name = "Miller"
+ value = "miller"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/krest
+ name = "Krest"
+ value = "krest"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tribal1
+ name = "Tribal1"
+ value = "tribal1"
+ category = TTS_CATEGORY_METRO
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/abathur
+ name = "Abathur"
+ value = "abathur"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/erik
+ name = "Erik"
+ value = "erik"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/varian
+ name = "Varian"
+ value = "varian"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/anduin
+ name = "Anduin"
+ value = "anduin"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/deckard
+ name = "Deckard"
+ value = "deckard"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/malfurion_h
+ name = "Malfurion_h"
+ value = "malfurion_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/demonhunter
+ name = "Demonhunter"
+ value = "demonhunter"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/demon
+ name = "Demon"
+ value = "demon"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kerrigan_h
+ name = "Kerrigan_h"
+ value = "kerrigan_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ladyofthorns
+ name = "Ladyofthorns"
+ value = "ladyofthorns"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/barbarian
+ name = "Barbarian"
+ value = "barbarian"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/crusader
+ name = "Crusader"
+ value = "crusader"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/whitemane
+ name = "Whitemane"
+ value = "whitemane"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/nexushunter
+ name = "Nexushunter"
+ value = "nexushunter"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/greymane_h
+ name = "Greymane_h"
+ value = "greymane_h"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/gardensdayannouncer
+ name = "Gardensdayannouncer"
+ value = "gardensdayannouncer"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/drekthar
+ name = "Drekthar"
+ value = "drekthar"
+ category = TTS_CATEGORY_HEROESOFTHESTORM
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/squeamlish
+ name = "Squeamlish"
+ value = "squeamlish"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dagg
+ name = "Dagg"
+ value = "dagg"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/brukan
+ name = "Brukan"
+ value = "brukan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bolan
+ name = "Bolan"
+ value = "bolan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/goya
+ name = "Goya"
+ value = "goya"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/stargazer
+ name = "Stargazer"
+ value = "stargazer"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/eudora
+ name = "Eudora"
+ value = "eudora"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/mozaki
+ name = "Mozaki"
+ value = "mozaki"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/katrana
+ name = "Katrana"
+ value = "katrana"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/valeera_hs
+ name = "Valeera_hs"
+ value = "valeera_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/malacrass
+ name = "Malacrass"
+ value = "malacrass"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/elise
+ name = "Elise"
+ value = "elise"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/flark
+ name = "Flark"
+ value = "flark"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/rhogi
+ name = "Rhogi"
+ value = "rhogi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/gallywix
+ name = "Gallywix"
+ value = "gallywix"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/talanji
+ name = "Talanji"
+ value = "talanji"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/dr_sezavo
+ name = "Dr_sezavo"
+ value = "dr_sezavo"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tierra
+ name = "Tierra"
+ value = "tierra"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zenda
+ name = "Zenda"
+ value = "zenda"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/baechao
+ name = "Baechao"
+ value = "baechao"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lilian
+ name = "Lilian"
+ value = "lilian"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/aranna
+ name = "Aranna"
+ value = "aranna"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/oshi
+ name = "Oshi"
+ value = "oshi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/norroa
+ name = "Norroa"
+ value = "norroa"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/turalyon
+ name = "Turalyon"
+ value = "turalyon"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/aki
+ name = "Aki"
+ value = "aki"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lunara
+ name = "Lunara"
+ value = "lunara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/bob
+ name = "Bob"
+ value = "bob"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/illucia
+ name = "Illucia"
+ value = "illucia"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/yrel_hs
+ name = "Yrel_hs"
+ value = "yrel_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/fireheart
+ name = "Fireheart"
+ value = "fireheart"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/lanathel
+ name = "Lanathel"
+ value = "lanathel"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/tyrande_hs
+ name = "Tyrande_hs"
+ value = "tyrande_hs"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/draemus
+ name = "Draemus"
+ value = "draemus"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/rasil
+ name = "Rasil"
+ value = "rasil"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kalec
+ name = "Kalec"
+ value = "kalec"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/karastamper
+ name = "Karastamper"
+ value = "karastamper"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/george
+ name = "George"
+ value = "george"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/pollark
+ name = "Pollark"
+ value = "pollark"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/stelina
+ name = "Stelina"
+ value = "stelina"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/kasa
+ name = "Kasa"
+ value = "kasa"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/whirt
+ name = "Whirt"
+ value = "whirt"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/anarii
+ name = "Anarii"
+ value = "anarii"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/ilza
+ name = "Ilza"
+ value = "ilza"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/avozu
+ name = "Avozu"
+ value = "avozu"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/jeklik
+ name = "Jeklik"
+ value = "jeklik"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/zibb
+ name = "Zibb"
+ value = "zibb"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/thrud
+ name = "Thrud"
+ value = "thrud"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_MALE
+ donator_level = 0
+
+/datum/tts_seed/silero/isiset
+ name = "Isiset"
+ value = "isiset"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_FEMALE
+ donator_level = 0
+
+/datum/tts_seed/silero/akazamzarak
+ name = "Akazamzarak"
+ value = "akazamzarak"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/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
+
+/datum/tts_seed/silero/senna
+ name = "Senna"
+ value = "senna"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/nunu
+ name = "Nunu"
+ value = "nunu"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/ryze
+ name = "Ryze"
+ value = "ryze"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/yone
+ name = "Yone"
+ value = "yone"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/sett
+ name = "Sett"
+ value = "sett"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/camille
+ name = "Camille"
+ value = "camille"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/lee_sin
+ name = "Lee_sin"
+ value = "lee_sin"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/kayle
+ name = "Kayle"
+ value = "kayle"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/azir
+ name = "Azir"
+ value = "azir"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/tryndamere
+ name = "Tryndamere"
+ value = "tryndamere"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/nami
+ name = "Nami"
+ value = "nami"
+ category = TTS_CATEGORY_LOL
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/delvin
+ name = "Delvin"
+ value = "delvin"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/cicero
+ name = "Cicero"
+ value = "cicero"
+ category = TTS_CATEGORY_SKYRIM
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/linzi
+ name = "Linzi"
+ value = "linzi"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/cache
+ name = "Cache"
+ value = "cache"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/cravitz
+ name = "Cravitz"
+ value = "cravitz"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/lady_vashj
+ name = "Lady_vashj"
+ value = "lady_vashj"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/dendrologist
+ name = "Dendrologist"
+ value = "dendrologist"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/jythiros
+ name = "Jythiros"
+ value = "jythiros"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/draan
+ name = "Draan"
+ value = "draan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/rikkar
+ name = "Rikkar"
+ value = "rikkar"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/splintergraft
+ name = "Splintergraft"
+ value = "splintergraft"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/malchezaar
+ name = "Malchezaar"
+ value = "malchezaar"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/taskmaster
+ name = "Taskmaster"
+ value = "taskmaster"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/oxana
+ name = "Oxana"
+ value = "oxana"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/inara
+ name = "Inara"
+ value = "inara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/ivan
+ name = "Ivan"
+ value = "ivan"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/kazamon
+ name = "Kazamon"
+ value = "kazamon"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/albin
+ name = "Albin"
+ value = "albin"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/ammunae
+ name = "Ammunae"
+ value = "ammunae"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/illidara
+ name = "Illidara"
+ value = "illidara"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
+
+/datum/tts_seed/silero/nici
+ name = "Nici"
+ value = "nici"
+ category = TTS_CATEGORY_HEARTHSTONE
+ gender = TTS_GENDER_ANY
+ donator_level = 0
diff --git a/modular_bandastation/tts/code/tts_configuration.dm b/modular_bandastation/tts/code/tts_configuration.dm
new file mode 100644
index 0000000000000..7f8d628e23674
--- /dev/null
+++ b/modular_bandastation/tts/code/tts_configuration.dm
@@ -0,0 +1,14 @@
+/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
+
+/datum/config_entry/string/ffmpeg_cpuaffinity
+ protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
diff --git a/modular_bandastation/tts/code/tts_mob_Hear.dm b/modular_bandastation/tts/code/tts_mob_Hear.dm
new file mode 100644
index 0000000000000..ded3d492a8457
--- /dev/null
+++ b/modular_bandastation/tts/code/tts_mob_Hear.dm
@@ -0,0 +1,67 @@
+/mob/proc/Hear_tts(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range)
+ if(!SStts220.is_enabled)
+ return
+
+ if(!isliving(src) && !isobserver(src))
+ return
+
+ if(!client)
+ return
+
+ if(HAS_TRAIT(speaker, TRAIT_SIGN_LANG))
+ return
+
+ if(!message_language)
+ return
+
+ var/is_custom_say_emote_without_message = (MODE_CUSTOM_SAY_ERASE_INPUT in message_mods)
+ if(is_custom_say_emote_without_message)
+ return
+
+ if(stat == UNCONSCIOUS || stat == HARD_CRIT)
+ return
+
+ if(!radio_freq && !LOCAL_TTS_ENABLED(src) || radio_freq && !RADIO_TTS_ENABLED(src))
+ return
+
+ var/atom/movable/virtualspeaker/virtual_speaker = speaker
+ var/atom/movable/real_speaker = istype(virtual_speaker) ? virtual_speaker.source : speaker
+
+ var/self_radio = radio_freq && src == real_speaker
+ if(self_radio)
+ return
+
+ var/is_speaker_whispering = (WHISPER_MODE in message_mods)
+ var/can_hear_whisper = get_dist(speaker, src) <= message_range || isobserver(src)
+ if(is_speaker_whispering && !can_hear_whisper)
+ return
+
+ var/effect = issilicon(real_speaker) ? SOUND_EFFECT_ROBOT : SOUND_EFFECT_NONE
+ if(radio_freq)
+ effect = issilicon(real_speaker) ? SOUND_EFFECT_RADIO_ROBOT : SOUND_EFFECT_RADIO
+ else if(SPAN_COMMAND in spans)
+ effect = issilicon(real_speaker) ? SOUND_EFFECT_MEGAPHONE_ROBOT : SOUND_EFFECT_MEGAPHONE
+
+ var/traits = TTS_TRAIT_RATE_MEDIUM
+ if(is_speaker_whispering)
+ traits &= TTS_TRAIT_PITCH_WHISPER
+
+ var/message_tts = translate_language(language = message_language, raw_message = raw_message)
+
+ INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(tts_cast), speaker, src, message_tts, real_speaker.tts_seed, !radio_freq, effect, traits)
+
+/mob/living/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range)
+ var/static/regex/plus_sign_replace = new(@"\+", "g")
+ var/plussless_message = plus_sign_replace.Replace(raw_message, "")
+
+ . = ..(message, speaker, message_language, plussless_message, radio_freq, spans, message_mods, message_range)
+
+ Hear_tts(message, speaker, message_language, raw_message, radio_freq, spans, message_mods, message_range)
+
+/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods, message_range)
+ var/static/regex/plus_sign_replace = new(@"\+", "g")
+ var/plussless_message = plus_sign_replace.Replace(raw_message, "")
+
+ . = ..(message, speaker, message_language, plussless_message, radio_freq, spans, message_mods, message_range)
+
+ Hear_tts(message, speaker, message_language, raw_message, radio_freq, spans, message_mods, message_range)
diff --git a/modular_bandastation/tts/code/tts_numbers.dm b/modular_bandastation/tts/code/tts_numbers.dm
new file mode 100644
index 0000000000000..c330ce48a285a
--- /dev/null
+++ b/modular_bandastation/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_bandastation/tts/code/tts_preferences.dm b/modular_bandastation/tts/code/tts_preferences.dm
new file mode 100644
index 0000000000000..bfcd0a61d8c9c
--- /dev/null
+++ b/modular_bandastation/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 SStts220.tts_providers)
+ var/datum/tts_provider/provider = SStts220.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 SStts220.tts_seeds)
+ var/datum/tts_seed/seed = SStts220.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 SStts220.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_bandastation/tts/code/tts_provider.dm b/modular_bandastation/tts/code/tts_provider.dm
new file mode 100644
index 0000000000000..aa159eef07cde
--- /dev/null
+++ b/modular_bandastation/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_bandastation/tts/code/tts_seed.dm b/modular_bandastation/tts/code/tts_seed.dm
new file mode 100644
index 0000000000000..627dd3f35f615
--- /dev/null
+++ b/modular_bandastation/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 movable atom
+/atom/movable
+ var/tts_seed
+
+/datum/preference/text/tts_seed/apply_to_human(mob/living/carbon/human/target, value)
+ target.tts_seed = value
diff --git a/modular_bandastation/tts/code/tts_sound.dm b/modular_bandastation/tts/code/tts_sound.dm
new file mode 100644
index 0000000000000..f9271e3f399cc
--- /dev/null
+++ b/modular_bandastation/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(CONFIG_GET(string/ffmpeg_cpuaffinity))
+ taskset = "taskset -ac [CONFIG_GET(string/ffmpeg_cpuaffinity)]"
+
+ var/list/output
+ switch(effect)
+ if(SOUND_EFFECT_RADIO)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" [filename_output]"})
+ if(SOUND_EFFECT_ROBOT)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5" [filename_output]"})
+ if(SOUND_EFFECT_RADIO_ROBOT)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, volume=volume=1.5, highpass=f=1000, lowpass=f=3000, acrusher=1:1:50:0:log" [filename_output]"})
+ if(SOUND_EFFECT_MEGAPHONE)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" [filename_output]"})
+ if(SOUND_EFFECT_MEGAPHONE_ROBOT)
+ output = world.shelleo({"[taskset] ffmpeg -y -hide_banner -loglevel error -i [filename_input] -filter:a "afftfilt=real='hypot(re,im)*sin(0)':imag='hypot(re,im)*cos(0)':win_size=1024:overlap=0.5, deesser=i=0.4, highpass=f=500, lowpass=f=4000, volume=volume=10, acrusher=1:1:45:0:log" [filename_output]"})
+ else
+ CRASH("Invalid sound effect chosen.")
+ var/errorlevel = output[SHELLEO_ERRORLEVEL]
+ var/stdout = output[SHELLEO_STDOUT]
+ var/stderr = output[SHELLEO_STDERR]
+ if(errorlevel)
+ error("Error: apply_sound_effect([effect], [filename_input], [filename_output]) - See debug logs.")
+ // TODO: SS220-TTS log_debug -> debug_world_log
+ debug_world_log("apply_sound_effect([effect], [filename_input], [filename_output]) STDOUT: [stdout]")
+ debug_world_log("apply_sound_effect([effect], [filename_input], [filename_output]) STDERR: [stderr]")
+ return FALSE
+ return TRUE
+
+//world/proc/shelleo
+#undef SHELLEO_ERRORLEVEL
+#undef SHELLEO_STDOUT
+#undef SHELLEO_STDERR
diff --git a/modular_bandastation/tts/code/tts_sound_TEMPORARY.dm b/modular_bandastation/tts/code/tts_sound_TEMPORARY.dm
new file mode 100644
index 0000000000000..a8bae493691ec
--- /dev/null
+++ b/modular_bandastation/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_bandastation/tts/code/tts_subsystem.dm b/modular_bandastation/tts/code/tts_subsystem.dm
new file mode 100644
index 0000000000000..2eb40ae067265
--- /dev/null
+++ b/modular_bandastation/tts/code/tts_subsystem.dm
@@ -0,0 +1,577 @@
+SUBSYSTEM_DEF(tts220)
+ name = "Text-to-Speech 220"
+ 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/tts220/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/tts220/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/tts220/Initialize(start_timeofday)
+ is_enabled = CONFIG_GET(flag/tts_enabled)
+ if(!is_enabled)
+ flags |= SS_NO_FIRE
+
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/tts220/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/tts220/Recover()
+ is_enabled = SStts220.is_enabled
+ tts_wanted = SStts220.tts_wanted
+ tts_request_failed = SStts220.tts_request_failed
+ tts_request_succeeded = SStts220.tts_request_succeeded
+ tts_reused = SStts220.tts_reused
+
+/datum/controller/subsystem/tts220/proc/queue_request(text, datum/tts_seed/seed, datum/callback/proc_callback)
+ if(LAZYLEN(tts_requests_queue) > tts_requests_queue_limit)
+ is_enabled = FALSE
+ to_chat(world, span_announce("SERVER: очередь запросов превысила лимит, подсистема SStts принудительно отключена!"))
+ 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/tts220/proc/get_tts(atom/speaker, mob/listener, message, seed_name, is_local = TRUE, effect = SOUND_EFFECT_NONE, traits = TTS_TRAIT_RATE_FASTER, preSFX = null, postSFX = null)
+ 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 = rustgss220_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/tts220/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
+
+ rustgss220_file_write_b64decode(voice, "[filename].ogg")
+
+ if (!CONFIG_GET(flag/tts_cache))
+ addtimer(CALLBACK(src, PROC_REF(cleanup_tts_file), "[filename].ogg"), 30 SECONDS)
+
+ for(var/datum/callback/cb in tts_queue[filename])
+ cb.InvokeAsync()
+ tts_queue[filename] -= cb
+
+ tts_queue -= filename
+
+/datum/controller/subsystem/tts220/proc/play_tts(atom/speaker, mob/listener, filename, is_local = TRUE, effect = SOUND_EFFECT_NONE, preSFX = null, postSFX = null)
+ 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/tts220/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/tts220/proc/get_local_channel_by_owner(owner)
+ var/channel = tts_local_channels_by_owner[owner]
+ if(isnull(channel))
+ channel = SSsounds.reserve_sound_channel_datumless()
+ tts_local_channels_by_owner[owner] = channel
+ return channel
+
+/datum/controller/subsystem/tts220/proc/cleanup_tts_file(filename)
+ fdel(filename)
+
+/datum/controller/subsystem/tts220/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/tts220/proc/get_random_seed(owner)
+ return pick(get_available_seeds(owner))
+
+/datum/controller/subsystem/tts220/proc/sanitize_tts_input(message)
+ var/hash
+ if(sanitized_messages_caching)
+ hash = rustgss220_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(@"(?SERVER: провайдер Silero в подсистеме SStts220 принудительно включен! ")
+ return json_encode(list("success" = "SStts220\[Silero] was force enabled"))
+ return json_encode(list("error" = "SStts220\[Silero] is already enabled"))
+
+/datum/world_topic/playerlist
+ keyword = "playerlist"
+
+/datum/world_topic/playerlist/Run(list/input)
+ var/list/keys = list()
+ for(var/I in GLOB.clients)
+ var/client/C = I
+ keys += C.key
+
+ return json_encode(keys)
+
+/datum/world_topic/status/Run(list/input)
+ . = ..()
+ var/list/admins = list()
+ for(var/client/C in GLOB.clients)
+ if(C.holder)
+ if(C.holder.fakekey)
+ continue //so stealthmins aren't revealed by the hub
+ admins += list(list(C.key, join_admin_ranks(C.holder.ranks)))
+ if(key_valid)
+ for(var/i in 1 to admins.len)
+ var/list/A = admins[i]
+ .["admin[i - 1]"] = A[1]
+ .["adminrank[i - 1]"] = A[2]
diff --git a/rust_g_ss220.dll b/rust_g_ss220.dll
new file mode 100644
index 0000000000000..ef1d0ec0bdd59
Binary files /dev/null and b/rust_g_ss220.dll differ
diff --git a/tgstation.dme b/tgstation.dme
index 9446949bf2f5f..65ce2b7836aaf 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -5898,4 +5898,5 @@
#include "interface\fonts\spess_font.dm"
#include "interface\fonts\tiny_unicode.dm"
#include "interface\fonts\vcr_osd_mono.dm"
+#include "modular_bandastation\modular_bandastation.dme" // BANDASTATION EDIT
// END_INCLUDE
diff --git a/tgui/packages/tgui-say/ChannelIterator.test.ts b/tgui/packages/tgui-say/ChannelIterator.test.ts
index 15e9812e702ec..91719cbbf51a5 100644
--- a/tgui/packages/tgui-say/ChannelIterator.test.ts
+++ b/tgui/packages/tgui-say/ChannelIterator.test.ts
@@ -11,6 +11,10 @@ describe('ChannelIterator', () => {
expect(channelIterator.current()).toBe('Say');
expect(channelIterator.next()).toBe('Radio');
expect(channelIterator.next()).toBe('Me');
+ // BANDASTATION EDIT ADDITION START
+ expect(channelIterator.next()).toBe('Whis');
+ expect(channelIterator.next()).toBe('LOOC');
+ // BANDASTATION EDIT ADDITION END
expect(channelIterator.next()).toBe('OOC');
expect(channelIterator.next()).toBe('Say'); // Admin is blacklisted so it should be skipped
});
diff --git a/tgui/packages/tgui-say/ChannelIterator.ts b/tgui/packages/tgui-say/ChannelIterator.ts
index 136806927e95e..7515f95adc74c 100644
--- a/tgui/packages/tgui-say/ChannelIterator.ts
+++ b/tgui/packages/tgui-say/ChannelIterator.ts
@@ -1,4 +1,13 @@
-export type Channel = 'Say' | 'Radio' | 'Me' | 'OOC' | 'Admin';
+export type Channel =
+ | 'Say'
+ | 'Radio'
+ | 'Me'
+ // BANDASTATION EDIT START
+ | 'Whis'
+ | 'LOOC'
+ // BANDASTATION EDIT END
+ | 'OOC'
+ | 'Admin';
/**
* ### ChannelIterator
@@ -8,9 +17,19 @@ export type Channel = 'Say' | 'Radio' | 'Me' | 'OOC' | 'Admin';
*/
export class ChannelIterator {
private index: number = 0;
- private readonly channels: Channel[] = ['Say', 'Radio', 'Me', 'OOC', 'Admin'];
+ private readonly channels: Channel[] = [
+ 'Say',
+ 'Radio',
+ 'Me',
+ // BANDASTATION EDIT START
+ 'Whis',
+ 'LOOC',
+ // BANDASTATION EDIT END
+ 'OOC',
+ 'Admin',
+ ];
private readonly blacklist: Channel[] = ['Admin'];
- private readonly quiet: Channel[] = ['OOC', 'Admin'];
+ private readonly quiet: Channel[] = ['OOC', 'LOOC', 'Admin']; // BANDASTATION EDIT
public next(): Channel {
if (this.blacklist.includes(this.channels[this.index])) {
diff --git a/tgui/packages/tgui-say/styles/colors.scss b/tgui/packages/tgui-say/styles/colors.scss
index 475d8b25b26ef..b202ef1d858a4 100644
--- a/tgui/packages/tgui-say/styles/colors.scss
+++ b/tgui/packages/tgui-say/styles/colors.scss
@@ -14,6 +14,8 @@ $_channel_map: (
'Engi': #f37746,
'Hive': #855d85,
'io': #1e90ff,
+ // BANDASTATION EDIT ADDITION
+ 'LOOC': #ffceb6,
'Me': #5975da,
'Med': #57b8f0,
'OOC': #cca300,
@@ -24,6 +26,8 @@ $_channel_map: (
'Supp': #b88646,
'Svc': #6ca729,
'Synd': #8f4a4b,
+ // BANDASTATION EDIT ADDITION
+ 'Whis': #7c7fd9,
);
$channel_keys: map.keys($_channel_map) !default;
diff --git a/tgui/packages/tgui/interfaces/EmotePanel.tsx b/tgui/packages/tgui/interfaces/EmotePanel.tsx
new file mode 100644
index 0000000000000..b5c8f0a41962e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/EmotePanel.tsx
@@ -0,0 +1,94 @@
+import { useBackend, useSharedState } from '../backend';
+import { Window } from '../layouts';
+import { Button, Section, Stack } from '../components';
+import { SearchBar } from './Fabrication/SearchBar';
+import { BooleanLike } from '../../common/react';
+
+type Emote = {
+ key: string;
+ emote_path: string;
+ hands: BooleanLike;
+ visible: BooleanLike;
+ audible: BooleanLike;
+ sound: BooleanLike;
+};
+
+type EmotePanelData = {
+ emotes: Emote[];
+};
+
+export const EmotePanelContent = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { emotes } = data;
+
+ const [searchText, setSearchText] = useSharedState(
+ context,
+ 'search_text',
+ ''
+ );
+
+ return (
+ 0
+ ? `Результаты поиска "${searchText}"`
+ : `Все эмоции`
+ }
+ fill>
+
+
+
+
+
+ {emotes.map((emote) =>
+ emote.key ? (
+ searchText.length > 0 ? (
+ emote.key.toLowerCase().includes(searchText.toLowerCase()) ? (
+
+ act('play_emote', {
+ emote_path: emote.emote_path,
+ })
+ }>
+ {emote.key.toUpperCase()}
+
+ ) : (
+ ''
+ )
+ ) : (
+
+ act('play_emote', {
+ emote_path: emote.emote_path,
+ })
+ }>
+ {emote.key.toUpperCase()}
+
+ )
+ ) : (
+ ''
+ )
+ )}
+
+
+
+ );
+};
+
+export const EmotePanel = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/ExaminePanel.js b/tgui/packages/tgui/interfaces/ExaminePanel.js
new file mode 100644
index 0000000000000..afabfeccd5607
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ExaminePanel.js
@@ -0,0 +1,44 @@
+import { useBackend } from '../backend';
+import { Stack, Section, ByondUi } from '../components';
+import { Window } from '../layouts';
+
+export const ExaminePanel = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { character_name, obscured, assigned_map, flavor_text } = data;
+ return (
+
+
+
+
+
+ {!obscured && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
index 1b76d2c7a1c5d..c4599fd02a1e8 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
@@ -9,6 +9,7 @@ import { JobsPage } from './JobsPage';
import { MainPage } from './MainPage';
import { SpeciesPage } from './SpeciesPage';
import { QuirksPage } from './QuirksPage';
+import { VoicePage } from './VoicePage'; // BANDASTATION EDIT ADD - TTS
enum Page {
Antags,
@@ -16,6 +17,8 @@ enum Page {
Jobs,
Species,
Quirks,
+ // BANDASTATION EDIT ADD - TTS
+ Voice,
}
const CharacterProfiles = (props: {
@@ -76,6 +79,10 @@ export const CharacterPreferenceWindow = (props, context) => {
case Page.Quirks:
pageContents = ;
break;
+ // BANDASTATION EDIT ADD - TTS
+ case Page.Voice:
+ pageContents = ;
+ break;
default:
exhaustiveCheck(currentPage);
}
@@ -146,6 +153,17 @@ export const CharacterPreferenceWindow = (props, context) => {
Quirks
+
+ {Boolean(data.tts_enabled) && ( // BANDASTATION EDIT - TTS
+
+
+ Voice
+
+
+ )}
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/VoicePage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/VoicePage.tsx
new file mode 100644
index 0000000000000..29b86e4bd6a6f
--- /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 (
+
+
+ act('select_voice', { seed: seed.name })}
+ />
+
+
+
+ act('listen', { seed: seed.name, phrase: selectedPhrase })
+ }
+ />
+
+ 0 && tts_seed !== seed.name
+ ? 'orange'
+ : 'white'
+ }>
+ {seed.name}
+
+
+ {seed.category}
+
+
+
+
+
+ {seed.donator_level > 0 && (
+ <>
+ {donatorTiers[seed.donator_level]}
+
+ >
+ )}
+
+
+ );
+ });
+
+ return (
+ <>
+
+
+
+ {providerCheckboxes}
+
+ {genderesCheckboxes}
+
+ {categoriesCheckboxes}
+
+
+ {donatorLevelsCheckboxes}
+
+ {phrasesSelect}
+ {searchBar}
+
+
+
+
+
+
+ {`Для поддержания и развития сообщества в условиях растущих расходов часть голосов пришлось сделать доступными только за материальную поддержку сообщества.`}
+
+
+ {`Подробнее об этом можно узнать в нашем Discord-сообществе.`}
+
+
+
+ >
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
index 57ad5ba437826..166e9a6a99165 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/data.ts
@@ -142,6 +142,7 @@ export type PreferencesMenuData = {
gender: Gender;
joblessrole: JoblessRole;
species: string;
+ tts_seed: string; // BANDASTATION EDIT ADD - TTS
};
randomization: Record;
@@ -171,6 +172,23 @@ export type PreferencesMenuData = {
active_slot: number;
name_to_use: string;
+ // BANDASTATION EDIT START - TTS
+ tts_enabled: BooleanLike;
+ providers: Array<{
+ name: string;
+ is_enabled: BooleanLike;
+ }>;
+ seeds: Array<{
+ name: string;
+ value: string;
+ category: string;
+ gender: string;
+ provider: string;
+ donator_level: number;
+ }>;
+ phrases: string[];
+ // BANDASTATION EDIT END
+
window: Window;
};
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base_bandastation.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base_bandastation.tsx
new file mode 100644
index 0000000000000..efd78b62363c3
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base_bandastation.tsx
@@ -0,0 +1,19 @@
+import { Box, TextArea } from '../../../../components';
+import { FeatureValueProps, FeatureShortTextData } from './base';
+
+export const FeatureTextInput = (
+ props: FeatureValueProps
+) => {
+ if (!props.serverData) {
+ return Loading... ;
+ }
+
+ return (
+