diff --git a/code/__defines/materials.dm b/code/__defines/materials.dm
new file mode 100644
index 00000000000..499c974b475
--- /dev/null
+++ b/code/__defines/materials.dm
@@ -0,0 +1,63 @@
+#define DEFAULT_TABLE_MATERIAL "plastic"
+#define DEFAULT_WALL_MATERIAL "steel"
+
+#define MAT_IRON "iron"
+#define MAT_MARBLE "marble"
+#define MAT_STEEL "steel"
+#define MAT_PLASTIC "plastic"
+#define MAT_GLASS "glass"
+#define MAT_SILVER "silver"
+#define MAT_GOLD "gold"
+#define MAT_URANIUM "uranium"
+#define MAT_TITANIUM "titanium"
+#define MAT_PHORON "phoron"
+#define MAT_DIAMOND "diamond"
+#define MAT_SNOW "snow"
+#define MAT_SNOWBRICK "packed snow"
+#define MAT_WOOD "wood"
+#define MAT_LOG "log"
+#define MAT_SIFWOOD "alien wood"
+#define MAT_SIFLOG "alien log"
+#define MAT_STEELHULL "steel hull"
+#define MAT_PLASTEEL "plasteel"
+#define MAT_PLASTEELHULL "plasteel hull"
+#define MAT_DURASTEEL "durasteel"
+#define MAT_DURASTEELHULL "durasteel hull"
+#define MAT_TITANIUMHULL "titanium hull"
+#define MAT_VERDANTIUM "verdantium"
+#define MAT_MORPHIUM "morphium"
+#define MAT_MORPHIUMHULL "morphium hull"
+#define MAT_VALHOLLIDE "valhollide"
+#define MAT_LEAD "lead"
+#define MAT_SUPERMATTER "supermatter"
+#define MAT_METALHYDROGEN "mhydrogen"
+#define MAT_OSMIUM "osmium"
+#define MAT_GRAPHITE "graphite"
+#define MAT_LEATHER "leather"
+#define MAT_CHITIN "chitin"
+#define MAT_CLOTH "cloth"
+#define MAT_SYNCLOTH "syncloth"
+#define MAT_COPPER "copper"
+#define MAT_QUARTZ "quartz"
+#define MAT_TIN "tin"
+#define MAT_VOPAL "void opal"
+#define MAT_ALUMINIUM "aluminium"
+#define MAT_BRONZE "bronze"
+#define MAT_PAINITE "painite"
+#define MAT_BOROSILICATE "borosilicate glass"
+#define MAT_SANDSTONE "sandstone"
+
+#define SHARD_SHARD "shard"
+#define SHARD_SHRAPNEL "shrapnel"
+#define SHARD_STONE_PIECE "piece"
+#define SHARD_SPLINTER "splinters"
+#define SHARD_NONE ""
+
+#define MATERIAL_UNMELTABLE 0x1
+#define MATERIAL_BRITTLE 0x2
+#define MATERIAL_PADDING 0x4
+
+#define DEFAULT_TABLE_MATERIAL "plastic"
+#define DEFAULT_WALL_MATERIAL "steel"
+
+#define TABLE_BRITTLE_MATERIAL_MULTIPLIER 4 // Amount table damage is multiplied by if it is made of a brittle material (e.g. glass)
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index e9e7126dbe6..bb0387ab4ee 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -121,65 +121,6 @@
#define WALL_CAN_OPEN 1
#define WALL_OPENING 2
-#define DEFAULT_TABLE_MATERIAL "plastic"
-#define DEFAULT_WALL_MATERIAL "steel"
-
-#define MAT_IRON "iron"
-#define MAT_MARBLE "marble"
-#define MAT_STEEL "steel"
-#define MAT_PLASTIC "plastic"
-#define MAT_GLASS "glass"
-#define MAT_SILVER "silver"
-#define MAT_GOLD "gold"
-#define MAT_URANIUM "uranium" //Did it
-#define MAT_TITANIUM "titanium"
-#define MAT_PHORON "phoron"
-#define MAT_DIAMOND "diamond"
-#define MAT_SNOW "snow"
-#define MAT_WOOD "wood"
-#define MAT_LOG "log"
-#define MAT_SIFWOOD "alien wood"
-#define MAT_SIFLOG "alien log"
-#define MAT_STEELHULL "steel hull"
-#define MAT_PLASTEEL "plasteel"
-#define MAT_PLASTEELHULL "plasteel hull"
-#define MAT_DURASTEEL "durasteel"
-#define MAT_DURASTEELHULL "durasteel hull"
-#define MAT_TITANIUMHULL "titanium hull"
-#define MAT_VERDANTIUM "verdantium"
-#define MAT_MORPHIUM "morphium"
-#define MAT_MORPHIUMHULL "morphium hull"
-#define MAT_VALHOLLIDE "valhollide"
-#define MAT_LEAD "lead"
-#define MAT_SUPERMATTER "supermatter"
-#define MAT_METALHYDROGEN "mhydrogen"
-#define MAT_OSMIUM "osmium"
-#define MAT_GRAPHITE "graphite"
-#define MAT_LEATHER "leather"
-#define MAT_CHITIN "chitin"
-#define MAT_CLOTH "cloth"
-#define MAT_SYNCLOTH "syncloth"
-#define MAT_COPPER "copper"
-#define MAT_QUARTZ "quartz"
-#define MAT_TIN "tin"
-#define MAT_VOPAL "void opal"
-#define MAT_ALUMINIUM "aluminium"
-#define MAT_BRONZE "bronze"
-#define MAT_PAINITE "painite"
-#define MAT_BOROSILICATE "borosilicate glass"
-
-#define SHARD_SHARD "shard"
-#define SHARD_SHRAPNEL "shrapnel"
-#define SHARD_STONE_PIECE "piece"
-#define SHARD_SPLINTER "splinters"
-#define SHARD_NONE ""
-
-#define MATERIAL_UNMELTABLE 0x1
-#define MATERIAL_BRITTLE 0x2
-#define MATERIAL_PADDING 0x4
-
-#define TABLE_BRITTLE_MATERIAL_MULTIPLIER 4 // Amount table damage is multiplied by if it is made of a brittle material (e.g. glass)
-
#define BOMBCAP_DVSTN_RADIUS (max_explosion_range/4)
#define BOMBCAP_HEAVY_RADIUS (max_explosion_range/2)
#define BOMBCAP_LIGHT_RADIUS max_explosion_range
@@ -504,3 +445,7 @@ GLOBAL_LIST_INIT(all_volume_channels, list(
#define APPEARANCECHANGER_CHANGED_EYES "Eye Color"
#define GET_DECL(D) (ispath(D, /decl) ? (decls_repository.fetched_decls[D] || decls_repository.get_decl(D)) : null)
+
+#define LOADOUT_WHITELIST_OFF 0
+#define LOADOUT_WHITELIST_LAX 1
+#define LOADOUT_WHITELIST_STRICT 2
\ No newline at end of file
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 67d14623cf6..1f82939221a 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -284,7 +284,10 @@ var/list/gamemode_cache = list()
var/static/disable_cid_warn_popup = FALSE
// whether or not to use the nightshift subsystem to perform lighting changes
- var/static/enable_night_shifts = FALSE
+ var/static/enable_night_shifts = FALSE
+
+ // How strictly the loadout enforces object species whitelists
+ var/loadout_whitelist = LOADOUT_WHITELIST_LAX
var/static/vgs_access_identifier = null // VOREStation Edit - VGS
var/static/vgs_server_port = null // VOREStation Edit - VGS
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
deleted file mode 100644
index c2535ec3eb8..00000000000
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Glass stack types
- * Contains:
- * Glass sheets
- * Reinforced glass sheets
- * Phoron Glass Sheets
- * Reinforced Phoron Glass Sheets (AKA Holy fuck strong windows)
- * Glass shards - TODO: Move this into code/game/object/item/weapons
- */
-
-/*
- * Glass sheets
- */
-/obj/item/stack/material/glass
- name = "glass"
- singular_name = "glass sheet"
- icon_state = "sheet-glass"
- var/is_reinforced = 0
- default_type = "glass"
- drop_sound = 'sound/items/drop/glass.ogg'
- pickup_sound = 'sound/items/pickup/glass.ogg'
-
-/obj/item/stack/material/glass/attack_self(mob/user as mob)
- construct_window(user)
-
-/obj/item/stack/material/glass/attackby(obj/item/W, mob/user)
- ..()
- if(!is_reinforced)
- if(istype(W,/obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/CC = W
- if (get_amount() < 1 || CC.get_amount() < 5)
- to_chat(user, "You need five lengths of coil and one sheet of glass to make wired glass.")
- return
-
- CC.use(5)
- use(1)
- to_chat(user, "You attach wire to the [name].")
- new /obj/item/stack/light_w(user.loc)
- else if(istype(W, /obj/item/stack/rods))
- var/obj/item/stack/rods/V = W
- if (V.get_amount() < 1 || get_amount() < 1)
- to_chat(user, "You need one rod and one sheet of glass to make reinforced glass.")
- return
-
- var/obj/item/stack/material/glass/reinforced/RG = new (user.loc)
- RG.add_fingerprint(user)
- RG.add_to_stacks(user)
- var/obj/item/stack/material/glass/G = src
- src = null
- var/replace = (user.get_inactive_hand()==G)
- V.use(1)
- G.use(1)
- if (!G && replace)
- user.put_in_hands(RG)
-
-
-
-
-/*
- * Reinforced glass sheets
- */
-/obj/item/stack/material/glass/reinforced
- name = "reinforced glass"
- singular_name = "reinforced glass sheet"
- icon_state = "sheet-rglass"
- default_type = "reinforced glass"
- is_reinforced = 1
-
-/*
- * Phoron Glass sheets
- */
-/obj/item/stack/material/glass/phoronglass
- name = "phoron glass"
- singular_name = "phoron glass sheet"
- icon_state = "sheet-phoronglass"
- default_type = "phoron glass"
-
-/obj/item/stack/material/glass/phoronglass/attackby(obj/item/W, mob/user)
- ..()
- if( istype(W, /obj/item/stack/rods) )
- var/obj/item/stack/rods/V = W
- var/obj/item/stack/material/glass/phoronrglass/RG = new (user.loc)
- RG.add_fingerprint(user)
- RG.add_to_stacks(user)
- V.use(1)
- var/obj/item/stack/material/glass/G = src
- src = null
- var/replace = (user.get_inactive_hand()==G)
- G.use(1)
- if (!G && !RG && replace)
- user.put_in_hands(RG)
- else
- return ..()
-
-/*
- * Reinforced phoron glass sheets
- */
-/obj/item/stack/material/glass/phoronrglass
- name = "reinforced phoron glass"
- singular_name = "reinforced phoron glass sheet"
- icon_state = "sheet-phoronrglass"
- default_type = "reinforced phoron glass"
- is_reinforced = 1
diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm
deleted file mode 100644
index c48f57e693a..00000000000
--- a/code/game/objects/items/stacks/sheets/leather.dm
+++ /dev/null
@@ -1,286 +0,0 @@
-/obj/item/stack/animalhide
- name = "hide"
- desc = "The hide of some creature."
- description_info = "Use something sharp, like a knife, to scrape the hairs/feathers/etc off this hide to prepare it for tanning."
- icon_state = "sheet-hide"
- drop_sound = 'sound/items/drop/cloth.ogg'
- pickup_sound = 'sound/items/pickup/cloth.ogg'
- amount = 1
- max_amount = 20
- stacktype = "hide"
- no_variants = TRUE
-// This needs to be very clearly documented for players. Whether it should stay in the main description is up for debate.
-/obj/item/stack/animalhide/examine(var/mob/user)
- . = ..()
- . += description_info
-
-/obj/item/stack/animalhide/human
- name = "skin"
- desc = "The by-product of sapient farming."
- singular_name = "skin piece"
- icon_state = "sheet-hide"
- no_variants = FALSE
- drop_sound = 'sound/items/drop/leather.ogg'
- pickup_sound = 'sound/items/pickup/leather.ogg'
- stacktype = "hide-human"
-
-/obj/item/stack/animalhide/corgi
- name = "corgi hide"
- desc = "The by-product of corgi farming."
- singular_name = "corgi hide piece"
- icon_state = "sheet-corgi"
- stacktype = "hide-corgi"
-
-/obj/item/stack/animalhide/cat
- name = "cat hide"
- desc = "The by-product of cat farming."
- singular_name = "cat hide piece"
- icon_state = "sheet-cat"
- stacktype = "hide-cat"
-
-/obj/item/stack/animalhide/monkey
- name = "monkey hide"
- desc = "The by-product of monkey farming."
- singular_name = "monkey hide piece"
- icon_state = "sheet-monkey"
- stacktype = "hide-monkey"
-
-/obj/item/stack/animalhide/lizard
- name = "lizard skin"
- desc = "Sssssss..."
- singular_name = "lizard skin piece"
- icon_state = "sheet-lizard"
- stacktype = "hide-lizard"
-
-/obj/item/stack/animalhide/xeno
- name = "alien hide"
- desc = "The skin of a terrible creature."
- singular_name = "alien hide piece"
- icon_state = "sheet-xeno"
- stacktype = "hide-xeno"
-
-//don't see anywhere else to put these, maybe together they could be used to make the xenos suit?
-/obj/item/stack/xenochitin
- name = "alien chitin"
- desc = "A piece of the hide of a terrible creature."
- singular_name = "alien chitin piece"
- icon = 'icons/mob/alien.dmi'
- icon_state = "chitin"
- stacktype = "hide-chitin"
-
-/obj/item/xenos_claw
- name = "alien claw"
- desc = "The claw of a terrible creature."
- icon = 'icons/mob/alien.dmi'
- icon_state = "claw"
-
-/obj/item/weed_extract
- name = "weed extract"
- desc = "A piece of slimy, purplish weed."
- icon = 'icons/mob/alien.dmi'
- icon_state = "weed_extract"
-
-//Step one - dehairing.
-/obj/item/stack/animalhide/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(has_edge(W) || is_sharp(W))
- //visible message on mobs is defined as visible_message(var/message, var/self_message, var/blind_message)
- user.visible_message("\The [user] starts cutting hair off \the [src]", "You start cutting the hair off \the [src]", "You hear the sound of a knife rubbing against flesh")
- var/scraped = 0
- while(amount > 0 && do_after(user, 2.5 SECONDS)) // 2.5s per hide
- //Try locating an exisitng stack on the tile and add to there if possible
- var/obj/item/stack/hairlesshide/H = null
- for(var/obj/item/stack/hairlesshide/HS in user.loc) // Could be scraping something inside a locker, hence the .loc, not get_turf
- if(HS.amount < HS.max_amount)
- H = HS
- break
-
- // Either we found a valid stack, in which case increment amount,
- // Or we need to make a new stack
- if(istype(H))
- H.amount++
- else
- H = new /obj/item/stack/hairlesshide(user.loc)
-
- // Increment the amount
- src.use(1)
- scraped++
-
- if(scraped)
- to_chat(user, SPAN_NOTICE("You scrape the hair off [scraped] hide\s."))
- else
- ..()
-
-
-//Step two - washing..... it's actually in washing machine code, and ere.
-
-/obj/item/stack/hairlesshide
- name = "hairless hide"
- desc = "This hide was stripped of it's hair, but still needs tanning."
- description_info = "Get it wet to continue tanning this into leather.
\
- You could set it in a river, wash it with a sink, or just splash water on it with a bucket."
- singular_name = "hairless hide piece"
- icon_state = "sheet-hairlesshide"
- no_variants = FALSE
- max_amount = 20
- stacktype = "hairlesshide"
-
-/obj/item/stack/hairlesshide/examine(var/mob/user)
- . = ..()
- . += description_info
-
-/obj/item/stack/hairlesshide/water_act(var/wateramount)
- . = ..()
- wateramount = min(amount, round(wateramount))
- for(var/i in 1 to wateramount)
- var/obj/item/stack/wetleather/H = null
- for(var/obj/item/stack/wetleather/HS in get_turf(src)) // Doesn't have a user, can't just use their loc
- if(HS.amount < HS.max_amount)
- H = HS
- break
-
- // Either we found a valid stack, in which case increment amount,
- // Or we need to make a new stack
- if(istype(H))
- H.amount++
- else
- H = new /obj/item/stack/wetleather(get_turf(src))
-
- // Increment the amount
- src.use(1)
-
-/obj/item/stack/hairlesshide/proc/rapidcure(var/stacknum = 1)
- stacknum = min(stacknum, amount)
-
- while(stacknum)
- var/obj/item/stack/wetleather/I = new /obj/item/stack/wetleather(get_turf(src))
-
- if(istype(I))
- I.dry()
-
- use(1)
- stacknum -= 1
-
-//Step three - drying
-/obj/item/stack/wetleather
- name = "wet leather"
- desc = "This leather has been cleaned but still needs to be dried."
- description_info = "To finish tanning the leather, you need to dry it. \
- You could place it under a fire, \
- put it in a drying rack, \
- or build a tanning rack from steel or wooden boards."
- singular_name = "wet leather piece"
- icon_state = "sheet-wetleather"
- var/wetness = 30 //Reduced when exposed to high temperautres
- var/drying_threshold_temperature = 500 //Kelvin to start drying
- no_variants = FALSE
- max_amount = 20
- stacktype = "wetleather"
-
- var/dry_type = /obj/item/stack/material/leather
-
-/obj/item/stack/wetleather/examine(var/mob/user)
- . = ..()
- . += description_info
- . += "\The [src] is [get_dryness_text()]."
-
-/obj/item/stack/wetleather/proc/get_dryness_text()
- if(wetness > 20)
- return "wet"
- if(wetness > 10)
- return "damp"
- if(wetness)
- return "almost dry"
- return "dry"
-
-/obj/item/stack/wetleather/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- ..()
- if(exposed_temperature >= drying_threshold_temperature)
- wetness--
- if(wetness == 0)
- dry()
-
-/obj/item/stack/wetleather/proc/dry()
- var/obj/item/stack/material/leather/L = new(src.loc)
- L.amount = amount
- use(amount)
- return L
-
-/obj/item/stack/wetleather/transfer_to(obj/item/stack/S, var/tamount=null, var/type_verified)
- . = ..()
- if(.) // If it transfers any, do a weighted average of the wetness
- var/obj/item/stack/wetleather/W = S
- var/oldamt = W.amount - .
- W.wetness = round(((oldamt * W.wetness) + (. * wetness)) / W.amount)
-
-
-
-/obj/structure/tanning_rack
- name = "tanning rack"
- desc = "A rack used to stretch leather out and hold it taut during the tanning process."
- icon = 'icons/obj/kitchen.dmi'
- icon_state = "spike"
-
- var/obj/item/stack/wetleather/drying = null
-
-/obj/structure/tanning_rack/Initialize()
- . = ..()
- START_PROCESSING(SSobj, src) // SSObj fires ~every 2s , starting from wetness 30 takes ~1m
-
-/obj/structure/tanning_rack/Destroy()
- STOP_PROCESSING(SSobj, src)
- return ..()
-
-/obj/structure/tanning_rack/process()
- if(drying && drying.wetness)
- drying.wetness = max(drying.wetness - 1, 0)
- if(!drying.wetness)
- visible_message("The [drying] is dry!")
- update_icon()
-
-/obj/structure/tanning_rack/examine(var/mob/user)
- . = ..()
- if(drying)
- . += "\The [drying] is [drying.get_dryness_text()]."
-
-/obj/structure/tanning_rack/update_icon()
- overlays.Cut()
- if(drying)
- var/image/I
- if(drying.wetness)
- I = image(icon, "leather_wet")
- else
- I = image(icon, "leather_dry")
- add_overlay(I)
-
-/obj/structure/tanning_rack/attackby(var/atom/A, var/mob/user)
- if(istype(A, /obj/item/stack/wetleather))
- if(!drying) // If not drying anything, start drying the thing
- if(user.unEquip(A, target = src))
- drying = A
- else // Drying something, add if possible
- var/obj/item/stack/wetleather/W = A
- W.transfer_to(drying, W.amount, TRUE)
- update_icon()
- return TRUE
- return ..()
-
-/obj/structure/tanning_rack/attack_hand(var/mob/user)
- if(drying)
- var/obj/item/stack/S = drying
- if(!drying.wetness) // If it's dry, make a stack of dry leather and prepare to put that in their hands
- var/obj/item/stack/material/leather/L = new(src)
- L.amount = drying.amount
- drying.use(drying.amount)
- S = L
-
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- if(!H.put_in_any_hand_if_possible(S))
- S.forceMove(get_turf(src))
- else
- S.forceMove(get_turf(src))
- drying = null
- update_icon()
-
-/obj/structure/tanning_rack/attack_robot(var/mob/user)
- attack_hand(user) // That has checks to
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/general/02_language.dm b/code/modules/client/preference_setup/general/02_language.dm
index 673e273af0b..783358d692b 100644
--- a/code/modules/client/preference_setup/general/02_language.dm
+++ b/code/modules/client/preference_setup/general/02_language.dm
@@ -15,8 +15,18 @@
if(!islist(pref.alternate_languages)) pref.alternate_languages = list()
if(pref.species)
var/datum/species/S = GLOB.all_species[pref.species]
- if(S && pref.alternate_languages.len > S.num_alternate_languages)
+ if(!istype(S))
+ return
+
+ if(pref.alternate_languages.len > S.num_alternate_languages)
pref.alternate_languages.len = S.num_alternate_languages // Truncate to allowed length
+
+ // Sanitize illegal languages
+ for(var/language in pref.alternate_languages)
+ var/datum/language/L = GLOB.all_languages[language]
+ if((L.flags & RESTRICTED) || (!(language in S.secondary_langs) && !is_lang_whitelisted(pref.client, L)))
+ pref.alternate_languages -= language
+
if(isnull(pref.language_prefixes) || !pref.language_prefixes.len)
pref.language_prefixes = config.language_prefixes.Copy()
for(var/prefix in pref.language_prefixes)
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 949d613b6b5..88085aa97cc 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -228,21 +228,12 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
pref.r_wing3 = sanitize_integer(pref.r_wing3, 0, 255, initial(pref.r_wing3))
pref.g_wing3 = sanitize_integer(pref.g_wing3, 0, 255, initial(pref.g_wing3))
pref.b_wing3 = sanitize_integer(pref.b_wing3, 0, 255, initial(pref.b_wing3))
- if(pref.ear_style)
- pref.ear_style = sanitize_inlist(pref.ear_style, ear_styles_list, initial(pref.ear_style))
- var/datum/sprite_accessory/temp_ear_style = ear_styles_list[pref.ear_style]
- if(temp_ear_style.apply_restrictions && (!(pref.species in temp_ear_style.species_allowed)))
- pref.ear_style = initial(pref.ear_style)
- if(pref.tail_style)
- pref.tail_style = sanitize_inlist(pref.tail_style, tail_styles_list, initial(pref.tail_style))
- var/datum/sprite_accessory/temp_tail_style = tail_styles_list[pref.tail_style]
- if(temp_tail_style.apply_restrictions && (!(pref.species in temp_tail_style.species_allowed)))
- pref.tail_style = initial(pref.tail_style)
- if(pref.wing_style)
- pref.wing_style = sanitize_inlist(pref.wing_style, wing_styles_list, initial(pref.wing_style))
- var/datum/sprite_accessory/temp_wing_style = wing_styles_list[pref.wing_style]
- if(temp_wing_style.apply_restrictions && (!(pref.species in temp_wing_style.species_allowed)))
- pref.wing_style = initial(pref.wing_style)
+ if(!(pref.ear_style in get_ear_styles()))
+ pref.ear_style = initial(pref.ear_style)
+ if(!(pref.wing_style in get_wing_styles()))
+ pref.wing_style = initial(pref.wing_style)
+ if(!(pref.tail_style in get_tail_styles()))
+ pref.tail_style = initial(pref.tail_style)
// Moved from /datum/preferences/proc/copy_to()
/datum/category_item/player_setup_item/general/body/copy_to_mob(var/mob/living/carbon/human/character)
@@ -669,7 +660,15 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
reset_limbs() // Safety for species with incompatible manufacturers; easier than trying to do it case by case.
pref.body_markings.Cut() // Basically same as above.
-
+
+ // Sanitize ear/wing/tail styles
+ if(!(pref.ear_style in get_ear_styles()))
+ pref.ear_style = initial(pref.ear_style)
+ if(!(pref.wing_style in get_wing_styles()))
+ pref.wing_style = initial(pref.wing_style)
+ if(!(pref.tail_style in get_tail_styles()))
+ pref.tail_style = initial(pref.tail_style)
+
var/min_age = get_min_age()
var/max_age = get_max_age()
pref.age = max(min(pref.age, max_age), min_age)
@@ -1086,11 +1085,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["ear_style"])
// Construct the list of names allowed for this user.
- var/list/pretty_ear_styles = list("Normal" = null)
- for(var/path in ear_styles_list)
- var/datum/sprite_accessory/ears/instance = ear_styles_list[path]
- if(((!instance.ckeys_allowed) || (usr.ckey in instance.ckeys_allowed)) && ((!instance.apply_restrictions) || (pref.species in instance.species_allowed)) || check_rights(R_ADMIN | R_EVENT | R_FUN, 0, user)) //VOREStation Edit
- pretty_ear_styles[instance.name] = path
+ var/list/pretty_ear_styles = get_ear_styles()
// Present choice to user
var/new_ear_style = input(user, "Pick ears", "Character Preference", pref.ear_style) as null|anything in pretty_ear_styles
@@ -1128,11 +1123,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["tail_style"])
// Construct the list of names allowed for this user.
- var/list/pretty_tail_styles = list("Normal" = null)
- for(var/path in tail_styles_list)
- var/datum/sprite_accessory/tail/instance = tail_styles_list[path]
- if(((!instance.ckeys_allowed) || (usr.ckey in instance.ckeys_allowed)) && ((!instance.apply_restrictions) || (pref.species in instance.species_allowed)) || check_rights(R_ADMIN | R_EVENT | R_FUN, 0, user)) //VOREStation Edit
- pretty_tail_styles[instance.name] = path
+ var/list/pretty_tail_styles = get_tail_styles()
// Present choice to user
var/new_tail_style = input(user, "Pick tails", "Character Preference", pref.tail_style) as null|anything in pretty_tail_styles
@@ -1170,11 +1161,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
else if(href_list["wing_style"])
// Construct the list of names allowed for this user.
- var/list/pretty_wing_styles = list("Normal" = null)
- for(var/path in wing_styles_list)
- var/datum/sprite_accessory/wing/instance = wing_styles_list[path]
- if(((!instance.ckeys_allowed) || (usr.ckey in instance.ckeys_allowed)) && ((!instance.apply_restrictions) || (pref.species in instance.species_allowed)) || check_rights(R_ADMIN | R_EVENT | R_FUN, 0, user)) //VOREStation Edit
- pretty_wing_styles[instance.name] = path
+ var/list/pretty_wing_styles = get_wing_styles()
// Present choice to user
var/new_wing_style = input(user, "Pick wings", "Character Preference", pref.wing_style) as null|anything in pretty_wing_styles
@@ -1304,3 +1291,27 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
dat += "