diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index 05338f347020..5f1fe7ce4731 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -98,7 +98,7 @@
#define DNA_HAIR_STYLE_BLOCK 7
/// This number needs to equal the total number of DNA blocks
-#define DNA_FEATURE_BLOCKS 22
+#define DNA_FEATURE_BLOCKS 28
#define DNA_MUTANT_COLOR_BLOCK 1
#define DNA_ETHEREAL_COLOR_BLOCK 2
@@ -123,6 +123,12 @@
#define DNA_PRETERNIS_WEATHERING_BLOCK 20
#define DNA_PRETERNIS_ANTENNA_BLOCK 21
#define DNA_PRETERNIS_EYE_BLOCK 22
+#define DNA_VOX_QUILLS_BLOCK 23
+#define DNA_VOX_FACIAL_QUILLS_BLOCK 24
+#define DNA_VOX_TAIL_MARKINGS_BLOCK 25
+#define DNA_VOX_BODY_MARKINGS_BLOCK 26
+#define DNA_VOX_SKIN_TONE_BLOCK 27
+#define DNA_MUTANT_COLOR_SECONDARY 28
#define DNA_SEQUENCE_LENGTH 4
#define DNA_MUTATION_BLOCKS 8
@@ -183,6 +189,9 @@
/// has a tail
#define HAS_TAIL 24
#define NONANITES 25
+#define HAIRCOLOR 26
+#define FACEHAIRCOLOR 27
+#define MUTCOLORS_SECONDARY 28
//organ slots
#define ORGAN_SLOT_BRAIN "brain"
diff --git a/code/__DEFINES/species_clothing_paths.dm b/code/__DEFINES/species_clothing_paths.dm
new file mode 100644
index 000000000000..b504e8594a80
--- /dev/null
+++ b/code/__DEFINES/species_clothing_paths.dm
@@ -0,0 +1,7 @@
+//HUMAN PATHS
+///The dmi for humanoid uniforms
+#define DEFAULT_UNIFORM_FILE 'icons/mob/clothing/uniform/uniform.dmi'
+///The dmi for humanoid shoes
+#define DEFAULT_SHOES_FILE 'icons/mob/clothing/feet/feet.dmi'
+///The dmi for humanoid oversuits
+#define DEFAULT_SUIT_FILE 'icons/mob/clothing/suit/suit.dmi'
diff --git a/code/__DEFINES/{yogs_defines}/colors.dm b/code/__DEFINES/{yogs_defines}/colors.dm
new file mode 100644
index 000000000000..c8312ec5ccc9
--- /dev/null
+++ b/code/__DEFINES/{yogs_defines}/colors.dm
@@ -0,0 +1,10 @@
+//Vox blood color
+#define COLOR_BLOOD_VOX "#2299FC"
+
+//Color blending modes
+#define COLOR_BLEND_MULTIPLY "multiply"
+#define COLOR_BLEND_ADD "add"
+
+//Color matrix
+#define COLOR_MATRIX_ADD(color) list(COLOR_RED, COLOR_VIBRANT_LIME, COLOR_BLUE, color)
+#define COLOR_MATRIX_OVERLAY(color) list(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK, color)
diff --git a/code/__DEFINES/{yogs_defines}/is_helpers.dm b/code/__DEFINES/{yogs_defines}/is_helpers.dm
index c7f9e2042008..f16192ad5669 100644
--- a/code/__DEFINES/{yogs_defines}/is_helpers.dm
+++ b/code/__DEFINES/{yogs_defines}/is_helpers.dm
@@ -1,3 +1,5 @@
+#define isvox(A) (is_species(A, /datum/species/vox))
+
#define isdarkspawn(A) (A?.mind?.has_antag_datum(/datum/antagonist/darkspawn))
#define isthrall(A) (A?.mind?.has_antag_datum(/datum/antagonist/thrall))
#define ispsyche(A) (A?.mind?.has_antag_datum(/datum/antagonist/psyche)) //non thrall teammates
diff --git a/code/__DEFINES/{yogs_defines}/mobs.dm b/code/__DEFINES/{yogs_defines}/mobs.dm
index 816023c8161a..dc4c11e90278 100644
--- a/code/__DEFINES/{yogs_defines}/mobs.dm
+++ b/code/__DEFINES/{yogs_defines}/mobs.dm
@@ -8,3 +8,7 @@
#define REGEN_BLOOD_REQUIREMENT 40 // The amount of "blood" that a slimeperson consumes when regenerating a single limb.
#define MONKIFY_BLOOD_COEFFICIENT (BLOOD_VOLUME_MONKEY/BLOOD_VOLUME_GENERIC) //the ratio of monkey to human blood volume so a 100% blood volume monkey will not instantly die when you turn it into a human with ~58% blood volume
+
+#define SPECIES_VOX "vox"
+
+#define BUTT_SPRITE_VOX "vox"
diff --git a/code/__DEFINES/{yogs_defines}/species_clothing_paths.dm b/code/__DEFINES/{yogs_defines}/species_clothing_paths.dm
new file mode 100644
index 000000000000..cb02eb5bf268
--- /dev/null
+++ b/code/__DEFINES/{yogs_defines}/species_clothing_paths.dm
@@ -0,0 +1,11 @@
+//VOX PATHS
+#define VOX_MASK_FILE 'icons/mob/clothing/species/vox/mask.dmi'
+#define VOX_HEAD_FILE 'icons/mob/clothing/species/vox/head.dmi'
+#define VOX_BACK_FILE 'icons/mob/clothing/species/vox/back.dmi'
+#define VOX_EARS_FILE 'icons/mob/clothing/species/vox/ears.dmi'
+#define VOX_EYES_FILE 'icons/mob/clothing/species/vox/eyes.dmi'
+#define VOX_SHOES_FILE 'icons/mob/clothing/species/vox/shoes.dmi'
+#define VOX_GLOVES_FILE 'icons/mob/clothing/species/vox/gloves.dmi'
+#define VOX_HELMET_FILE 'icons/mob/clothing/species/vox/helmet.dmi'
+#define VOX_SUIT_FILE 'icons/mob/clothing/species/vox/suit.dmi'
+#define VOX_UNIFORM_FILE 'icons/mob/clothing/species/vox/uniform.dmi'
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index ab1c0a2c5ed8..3b538f1b8d9a 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -45,6 +45,15 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_screens, GLOB.ipc_screens_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_antennas, GLOB.ipc_antennas_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_chassis, GLOB.ipc_chassis_list)
+ // Vox bodyparts
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_quills, GLOB.vox_quills_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_facial_quills, GLOB.vox_facial_quills_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_tails, GLOB.vox_tails_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_body_markings, GLOB.vox_body_markings_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_tail_markings, GLOB.vox_tail_markings_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/vox, GLOB.animated_vox_tails_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_tail_markings_animated, GLOB.animated_vox_tail_markings_list)
+
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index f9f710948257..773eab6e1cee 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -228,10 +228,13 @@ world
/icon/proc/Greyify()
MapColors(0.75,0.3,0.3, 0.6,0.75,0.6, 0.10,0.10,0.50, 0,0,0)
-/icon/proc/ColorTone(tone)
- Greyify()
+/icon/proc/ColorTone(tone, greyify = FALSE)
+ if(greyify)
+ Greyify()
+ else
+ GrayScale()
- var/list/TONE = ReadRGB(tone)
+ var/list/TONE = rgb2num(tone)
var/gray = round(TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11, 1)
var/icon/upper = (255-gray) ? new(src) : null
@@ -922,7 +925,7 @@ world
/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created.
var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon.
- flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish.
+ flat_icon.ColorTone(rgb(125,180,225), greyify = TRUE)//Let's make it bluish.
flat_icon.ChangeOpacity(0.5)//Make it half transparent.
var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect.
flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect.
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 1ab59833e0f0..936ddf1b2e1f 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -95,10 +95,21 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_antennas, GLOB.ipc_antennas_list)
if(!GLOB.ipc_chassis_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_chassis, GLOB.ipc_chassis_list)
+ if(!GLOB.vox_quills_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_quills, GLOB.vox_quills_list)
+ if(!GLOB.vox_facial_quills_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_facial_quills, GLOB.vox_facial_quills_list)
+ if(!GLOB.vox_tails_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_tails, GLOB.vox_tails_list)
+ if(!GLOB.vox_body_markings_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_body_markings, GLOB.vox_body_markings_list)
+ if(!GLOB.vox_tail_markings_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/vox_tail_markings, GLOB.vox_tail_markings_list)
//For now we will always return none for tail_human and ears. this shit was unreadable if you do somethign like this make it at least readable
return(list(
"mcolor" = "#[pick("7F","FF")][pick("7F","FF")][pick("7F","FF")]",
+ "mcolor_secondary" = "#[pick("7F","FF")][pick("7F","FF")][pick("7F","FF")]",
"gradientstyle" = random_hair_gradient_style(10),
"gradientcolor" = "#[pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")]",
"tail_lizard" = pick(GLOB.tails_list_lizard),
@@ -126,7 +137,12 @@
"pod_hair" = pick(GLOB.pod_hair_list),
"ipc_screen" = pick(GLOB.ipc_screens_list),
"ipc_antenna" = pick(GLOB.ipc_antennas_list),
- "ipc_chassis" = pick(GLOB.ipc_chassis_list)
+ "ipc_chassis" = pick(GLOB.ipc_chassis_list),
+ "vox_skin_tone" = pick(GLOB.vox_skin_tones),
+ "vox_quills" = pick(GLOB.vox_quills_list),
+ "vox_facial_quills" = pick(GLOB.vox_facial_quills_list),
+ "vox_body_markings" = pick(GLOB.vox_body_markings_list),
+ "vox_tail_markings" = pick(GLOB.vox_tail_markings_list)
))
/proc/random_hair_style(gender)
diff --git a/code/_globalvars/lists/maintenance_loot.dm b/code/_globalvars/lists/maintenance_loot.dm
index 0bda2b22d1bc..0a879c263264 100644
--- a/code/_globalvars/lists/maintenance_loot.dm
+++ b/code/_globalvars/lists/maintenance_loot.dm
@@ -80,12 +80,14 @@ GLOBAL_LIST_INIT(maintenance_loot_traditional,list(
/obj/item/clothing/mask/breath = W_UNCOMMON,
/obj/item/tank/internals/air = W_COMMON,
/obj/item/tank/internals/anesthetic = W_RARE,
+ /obj/item/tank/internals/nitrogen = W_RARE,
/obj/item/tank/internals/emergency_oxygen = W_UNCOMMON,
/obj/item/tank/internals/emergency_oxygen/empty = W_UNCOMMON,
/obj/item/tank/internals/emergency_oxygen/engi = W_RARE,
/obj/item/tank/internals/emergency_oxygen/engi/empty = W_RARE,
/obj/item/tank/internals/emergency_oxygen/double = W_MYTHICAL,
/obj/item/tank/internals/emergency_oxygen/double/empty = W_MYTHICAL,
+ /obj/item/tank/internals/emergency_oxygen/vox = W_MYTHICAL,
/obj/item/tank/internals/ipc_coolant = W_RARE,
/obj/item/tank/internals/ipc_coolant/empty = W_RARE,
/obj/item/tank/internals/plasma = W_LEGENDARY,
@@ -365,6 +367,7 @@ GLOBAL_LIST_INIT(maintenance_loot_traditional,list(
/obj/item/stack/sheet/animalhide/gorilla = W_RARE,
/obj/item/stack/sheet/animalhide/human = W_RARE,
/obj/item/stack/sheet/animalhide/lizard = W_RARE,
+ /obj/item/stack/sheet/animalhide/vox = W_RARE,
/obj/item/stack/sheet/animalhide/monkey = W_RARE,
/obj/item/stack/sheet/animalhide/xeno = W_RARE,
/obj/item/storage/bag/money = W_RARE,
@@ -581,6 +584,7 @@ GLOBAL_LIST_INIT(maintenance_loot_makeshift,list(
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/golem/adamantine = W_MYTHICAL,
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/gorilla = W_MYTHICAL,
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/lizard = W_MYTHICAL,
+ /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/vox = W_MYTHICAL,
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/moth = W_MYTHICAL,
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/plant = W_MYTHICAL,
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/shadow = W_MYTHICAL,
diff --git a/code/controllers/subsystem/traumas.dm b/code/controllers/subsystem/traumas.dm
index 5f3f0c4c1f0f..e668b1bcf95e 100644
--- a/code/controllers/subsystem/traumas.dm
+++ b/code/controllers/subsystem/traumas.dm
@@ -139,7 +139,10 @@ SUBSYSTEM_DEF(traumas)
/obj/item/clothing/suit/chickensuit, /obj/item/clothing/head/chicken,
/obj/item/clothing/suit/toggle/owlwings, /obj/item/clothing/under/costume/owl, /obj/item/clothing/mask/gas/owl_mask,
/obj/item/clothing/under/costume/griffin, /obj/item/clothing/shoes/griffin, /obj/item/clothing/head/griffin,
- /obj/item/clothing/head/helmet/space/freedom, /obj/item/clothing/suit/space/freedom)),
+ /obj/item/clothing/head/helmet/space/freedom, /obj/item/clothing/suit/space/freedom, /obj/item/toy/plush/voxplushie,
+ /obj/item/clothing/mask/breath/vox, /obj/item/clothing/suit/space/vox, /obj/item/clothing/head/helmet/space/vox,
+ /obj/item/clothing/shoes/magboots/vox, /obj/item/clothing/gloves/color/yellow/vox, /obj/item/flag/species/vox, /obj/item/stack/sheet/animalhide/vox,
+ /obj/item/organ/tail/vox)),
"anime" = typecacheof(list(/obj/item/clothing/under/costume/schoolgirl, /obj/item/katana, /obj/item/reagent_containers/food/snacks/sashimi, /obj/item/reagent_containers/food/snacks/chawanmushi,
/obj/item/reagent_containers/food/drinks/bottle/sake, /obj/item/throwing_star, /obj/item/clothing/head/kitty/genuine, /obj/item/clothing/suit/space/space_ninja,
@@ -162,7 +165,8 @@ SUBSYSTEM_DEF(traumas)
"the supernatural" = typecacheof(list(/datum/species/golem/clockwork, /datum/species/golem/runic)),
"aliens" = typecacheof(list(/datum/species/abductor, /datum/species/jelly, /datum/species/pod,
/datum/species/shadow, /datum/species/polysmorph)),
- "anime" = typecacheof(list(/datum/species/human/felinid))
+ "anime" = typecacheof(list(/datum/species/human/felinid)),
+ "birds" = typecacheof(list(/datum/species/vox))
)
return SS_INIT_SUCCESS
diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm
index 77a842eed651..261266e8cc48 100644
--- a/code/datums/components/bloodysoles.dm
+++ b/code/datums/components/bloodysoles.dm
@@ -215,7 +215,7 @@ Like its parent but can be applied to carbon mobs instead of clothing items
*/
/datum/component/bloodysoles/feet
- var/static/mutable_appearance/bloody_feet
+ var/mutable_appearance/bloody_feet
/datum/component/bloodysoles/feet/Initialize()
if(!iscarbon(parent))
@@ -236,6 +236,11 @@ Like its parent but can be applied to carbon mobs instead of clothing items
/datum/component/bloodysoles/feet/update_icon()
. = list()
if(ishuman(wielder))// Monkeys get no bloody feet :(
+ var/obj/item/bodypart/l_leg/left_leg = wielder.get_bodypart(BODY_ZONE_L_LEG)
+ var/obj/item/bodypart/r_leg/right_leg = wielder.get_bodypart(BODY_ZONE_R_LEG)
+ if(left_leg?.species_id == right_leg?.species_id)
+ if(icon_exists(bloody_feet.icon, "shoeblood_[left_leg.species_id]"))
+ bloody_feet.icon_state = "shoeblood_[left_leg.species_id]"
if(HAS_BLOOD_DNA(wielder))
bloody_feet.color = get_blood_dna_color(wielder.return_blood_DNA())
. += bloody_feet
diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm
index 5b667d4a6d54..588181d5b52a 100644
--- a/code/datums/diseases/advance/symptoms/shedding.dm
+++ b/code/datums/diseases/advance/symptoms/shedding.dm
@@ -44,14 +44,13 @@ BONUS
to_chat(H, span_warning("Your hair starts to fall out in clumps..."))
addtimer(CALLBACK(src, PROC_REF(Shed), H, FALSE), 50)
if(5)
- if(!(H.facial_hair_style == "Shaved") || !(H.hair_style == "Bald"))
+ if(!(H.facial_hair_style == "Shaved") || !(H.hair_style == "Bald") || !(H.hair_style == "None"))
to_chat(H, span_warning("Your hair starts to fall out in clumps..."))
addtimer(CALLBACK(src, PROC_REF(Shed), H, TRUE), 50)
/datum/symptom/shedding/proc/Shed(mob/living/carbon/human/H, fullbald)
if(fullbald)
- H.facial_hair_style = "Shaved"
- H.hair_style = "Bald"
+ H.dna.species.go_bald()
else
H.hair_style = "Balding Hair"
H.update_hair()
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 64884ff14bf3..5759cb356131 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -191,6 +191,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(features["mcolor"])
L[DNA_MUTANT_COLOR_BLOCK] = sanitize_hexcolor(features["mcolor"], include_crunch = FALSE)
+ if(features["mcolor_secondary"])
+ L[DNA_MUTANT_COLOR_SECONDARY] = sanitize_hexcolor(features["mcolor_secondary"], include_crunch = FALSE)
if(features["body_markings"])
L[DNA_LIZARD_MARKINGS_BLOCK] = construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len)
if(features["tail_lizard"])
@@ -231,6 +233,16 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
L[DNA_POD_HAIR_BLOCK] = construct_block(GLOB.pod_hair_list.Find(features["pod_hair"]), GLOB.pod_hair_list.len)
if(features["pod_flower"])
L[DNA_POD_FLOWER_BLOCK] = construct_block(GLOB.pod_flower_list.Find(features["pod_flower"]), GLOB.pod_flower_list.len)
+ if(features["vox_quills"])
+ L[DNA_VOX_QUILLS_BLOCK] = construct_block(GLOB.vox_quills_list.Find(features["vox_quills"]), GLOB.vox_quills_list.len)
+ if(features["vox_facial_quills"])
+ L[DNA_VOX_FACIAL_QUILLS_BLOCK] = construct_block(GLOB.vox_facial_quills_list.Find(features["vox_facial_quills"]), GLOB.vox_facial_quills_list.len)
+ if(features["vox_tail_markings"])
+ L[DNA_VOX_TAIL_MARKINGS_BLOCK] = construct_block(GLOB.vox_tail_markings_list.Find(features["vox_tail_markings"]), GLOB.vox_tail_markings_list.len)
+ if(features["vox_body_markings"])
+ L[DNA_VOX_BODY_MARKINGS_BLOCK] = construct_block(GLOB.vox_body_markings_list.Find(features["vox_body_markings"]), GLOB.vox_body_markings_list.len)
+ if(features["vox_skin_tone"])
+ L[DNA_VOX_SKIN_TONE_BLOCK] = construct_block(GLOB.vox_skin_tones.Find(features["vox_skin_tone"]), GLOB.vox_skin_tones.len)
for(var/blocknum in 1 to DNA_FEATURE_BLOCKS)
. += L[blocknum] || random_string(GET_UI_BLOCK_LEN(blocknum), GLOB.hex_characters)
@@ -348,6 +360,8 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
switch(blocknumber)
if(DNA_MUTANT_COLOR_BLOCK)
set_uni_feature_block(blocknumber, sanitize_hexcolor(features["mcolor"], include_crunch = FALSE))
+ if(DNA_MUTANT_COLOR_SECONDARY)
+ set_uni_feature_block(blocknumber, sanitize_hexcolor(features["mcolor_secondary"], include_crunch = FALSE))
if(DNA_LIZARD_MARKINGS_BLOCK)
set_uni_feature_block(blocknumber, construct_block(GLOB.body_markings_list.Find(features["body_markings"]), GLOB.body_markings_list.len))
if(DNA_LIZARD_TAIL_BLOCK)
@@ -388,6 +402,16 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
set_uni_feature_block(blocknumber, construct_block(GLOB.pod_hair_list.Find(features["pod_hair"]), GLOB.pod_hair_list.len))
if(DNA_POD_FLOWER_BLOCK)
set_uni_feature_block(blocknumber, construct_block(GLOB.pod_flower_list.Find(features["pod_flower"]), GLOB.pod_flower_list.len))
+ if(DNA_VOX_QUILLS_BLOCK)
+ set_uni_feature_block(blocknumber, construct_block(GLOB.vox_quills_list.Find(features["vox_quills"]), GLOB.vox_quills_list.len))
+ if(DNA_VOX_FACIAL_QUILLS_BLOCK)
+ set_uni_feature_block(blocknumber, construct_block(GLOB.vox_facial_quills_list.Find(features["vox_facial_quills"]), GLOB.vox_facial_quills_list.len))
+ if(DNA_VOX_TAIL_MARKINGS_BLOCK)
+ set_uni_feature_block(blocknumber, construct_block(GLOB.vox_tail_markings_list.Find(features["vox_tail_markings"]), GLOB.vox_tail_markings_list.len))
+ if(DNA_VOX_BODY_MARKINGS_BLOCK)
+ set_uni_feature_block(blocknumber, construct_block(GLOB.vox_body_markings_list.Find(features["vox_body_markings"]), GLOB.vox_body_markings_list.len))
+ if(DNA_VOX_SKIN_TONE_BLOCK)
+ set_uni_feature_block(blocknumber, construct_block(GLOB.vox_skin_tones.Find(features["vox_skin_tone"]), GLOB.vox_skin_tones.len))
//Please use add_mutation or activate_mutation instead
/datum/dna/proc/force_give(datum/mutation/human/HM)
@@ -596,12 +620,18 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
eye_color = sanitize_hexcolor(get_uni_identity_block(structure, DNA_EYE_COLOR_BLOCK))
facial_hair_style = GLOB.facial_hair_styles_list[deconstruct_block(get_uni_identity_block(structure, DNA_FACIAL_HAIR_STYLE_BLOCK), GLOB.facial_hair_styles_list.len)]
if(HAS_TRAIT(src, TRAIT_BALD))
- hair_style = "Bald"
+ if(isvox(src))
+ dna.features["vox_quills"] = "None"
+ dna.update_uf_block(DNA_VOX_QUILLS_BLOCK)
+ else
+ hair_style = "Bald"
else
hair_style = GLOB.hair_styles_list[deconstruct_block(get_uni_identity_block(structure, DNA_HAIR_STYLE_BLOCK), GLOB.hair_styles_list.len)]
var/features = dna.unique_features
if(dna.features["mcolor"])
dna.features["mcolor"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_MUTANT_COLOR_BLOCK))
+ if(dna.features["mcolor_secondary"])
+ dna.features["mcolor_secondary"] = sanitize_hexcolor(get_uni_feature_block(features, DNA_MUTANT_COLOR_SECONDARY))
if(dna.features["body_markings"])
dna.features["body_markings"] = GLOB.body_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_LIZARD_MARKINGS_BLOCK), GLOB.body_markings_list.len)]
if(dna.features["tail_lizard"])
@@ -645,6 +675,16 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
dna.features["pod_hair"] = GLOB.pod_hair_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_HAIR_BLOCK), GLOB.pod_hair_list.len)]
if(dna.features["pod_flower"])
dna.features["pod_flower"] = GLOB.pod_flower_list[deconstruct_block(get_uni_feature_block(features, DNA_POD_FLOWER_BLOCK), GLOB.pod_flower_list.len)]
+ if(dna.features["vox_quills"])
+ dna.features["vox_quills"] = GLOB.vox_quills_list[deconstruct_block(get_uni_feature_block(features, DNA_VOX_QUILLS_BLOCK), GLOB.vox_quills_list.len)]
+ if(dna.features["vox_facial_quills"])
+ dna.features["vox_facial_quills"] = GLOB.vox_facial_quills_list[deconstruct_block(get_uni_feature_block(features, DNA_VOX_FACIAL_QUILLS_BLOCK), GLOB.vox_facial_quills_list.len)]
+ if(dna.features["vox_tail_markings"])
+ dna.features["vox_tail_markings"] = GLOB.vox_tail_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_VOX_TAIL_MARKINGS_BLOCK), GLOB.vox_tail_markings_list.len)]
+ if(dna.features["vox_body_markings"])
+ dna.features["vox_body_markings"] = GLOB.vox_body_markings_list[deconstruct_block(get_uni_feature_block(features, DNA_VOX_BODY_MARKINGS_BLOCK), GLOB.vox_body_markings_list.len)]
+ if(dna.features["vox_skin_tone"])
+ dna.features["vox_skin_tone"] = GLOB.vox_skin_tones[deconstruct_block(get_uni_feature_block(features, DNA_VOX_SKIN_TONE_BLOCK), GLOB.vox_skin_tones.len)]
if(icon_update)
dna.species.handle_body(src) // We want 'update_body_parts()' to be called only if mutcolor_update is TRUE, so no 'update_body()' here.
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 5ff180ddfe0f..2ea07ea48d0e 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -146,6 +146,8 @@
. = message_monkey
else if(isipc(user) && message_ipc)
. = message_ipc
+ else if(isvox(user) && message_vox)
+ . = message_vox
else if(isanimal(user) && message_simple)
. = message_simple
diff --git a/code/datums/greyscale/json_configs/jumpsuit_prison_worn.json b/code/datums/greyscale/json_configs/jumpsuit_prison_worn.json
index 0096c95e812a..abe6be92981d 100644
--- a/code/datums/greyscale/json_configs/jumpsuit_prison_worn.json
+++ b/code/datums/greyscale/json_configs/jumpsuit_prison_worn.json
@@ -12,6 +12,19 @@
"blend_mode": "overlay"
}
],
+ "jumpsuit_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_prison_vox",
+ "blend_mode": "overlay"
+ }
+ ],
"jumpsuit_d" : [
{
"type": "icon_state",
@@ -25,6 +38,19 @@
"blend_mode": "overlay"
}
],
+ "jumpsuit_d_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_d_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_d_prison_vox",
+ "blend_mode": "overlay"
+ }
+ ],
"jumpskirt": [
{
"type": "icon_state",
@@ -38,6 +64,19 @@
"blend_mode": "overlay"
}
],
+ "jumpskirt_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_prison_vox",
+ "blend_mode": "overlay"
+ }
+ ],
"jumpskirt_d" : [
{
"type": "icon_state",
@@ -50,5 +89,18 @@
"icon_state": "jumpskirt_d_prison",
"blend_mode": "overlay"
}
+ ],
+ "jumpskirt_d_vox" : [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_d_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_d_prison_vox",
+ "blend_mode": "overlay"
+ }
]
}
diff --git a/code/datums/greyscale/json_configs/jumpsuit_worn.json b/code/datums/greyscale/json_configs/jumpsuit_worn.json
index 99bc3fc19874..04eb78e8dac1 100644
--- a/code/datums/greyscale/json_configs/jumpsuit_worn.json
+++ b/code/datums/greyscale/json_configs/jumpsuit_worn.json
@@ -12,6 +12,19 @@
"blend_mode": "overlay"
}
],
+ "jumpsuit_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_accessories_vox",
+ "blend_mode": "overlay"
+ }
+ ],
"jumpsuit_d" : [
{
"type": "icon_state",
@@ -20,6 +33,14 @@
"color_ids": [ 1 ]
}
],
+ "jumpsuit_d_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpsuit_d_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
"jumpsuit_l": [
{
"type": "icon_state",
@@ -54,6 +75,19 @@
"blend_mode": "overlay"
}
],
+ "jumpskirt_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_accessories_vox",
+ "blend_mode": "overlay"
+ }
+ ],
"jumpskirt_d" : [
{
"type": "icon_state",
@@ -61,5 +95,13 @@
"blend_mode": "overlay",
"color_ids": [ 1 ]
}
+ ],
+ "jumpskirt_d_vox" : [
+ {
+ "type": "icon_state",
+ "icon_state": "jumpskirt_d_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
]
}
diff --git a/code/datums/greyscale/json_configs/sneakers_orange_worn.json b/code/datums/greyscale/json_configs/sneakers_orange_worn.json
index fb0a07ff1969..d28b36812f6e 100644
--- a/code/datums/greyscale/json_configs/sneakers_orange_worn.json
+++ b/code/datums/greyscale/json_configs/sneakers_orange_worn.json
@@ -13,6 +13,20 @@
"color_ids": [ 2 ]
}
],
+ "sneakers_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_back_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_front_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ }
+ ],
"sneakers_chained": [
{
"type": "icon_state",
@@ -31,5 +45,24 @@
"icon_state": "sneakers_chained",
"blend_mode": "overlay"
}
+ ],
+ "sneakers_chained_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_back_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_front_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_chained_vox",
+ "blend_mode": "overlay"
+ }
]
}
diff --git a/code/datums/greyscale/json_configs/sneakers_worn.json b/code/datums/greyscale/json_configs/sneakers_worn.json
index 50a064bb02b6..4d808820ec05 100644
--- a/code/datums/greyscale/json_configs/sneakers_worn.json
+++ b/code/datums/greyscale/json_configs/sneakers_worn.json
@@ -12,5 +12,19 @@
"blend_mode": "overlay",
"color_ids": [ 2 ]
}
+ ],
+ "sneakers_vox": [
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_back_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "sneakers_front_vox",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ }
]
}
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index 6f1bc7dcac91..ce82580ff2a6 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -356,10 +356,12 @@
/datum/quirk/telomeres_long/check_quirk(datum/preferences/prefs)
var/datum/species/species_type = prefs.read_preference(/datum/preference/choiced/species)
- var/disallowed_trait = (NO_DNA_COPY in initial(species_type.species_traits)) //Can't pick if you have no DNA bruv.
-
- if(disallowed_trait)
+ var/no_dna = (NO_DNA_COPY in initial(species_type.species_traits)) //Can't pick if you have no DNA bruv.
+ var/no_clone = (TRAIT_NOCLONE in initial(species_type.inherent_traits))
+ if(no_dna)
return "You have no DNA!"
+ else if(no_clone)
+ return "Your species cannot be cloned!"
return FALSE
/datum/quirk/marine
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 5c9498f595fd..2e025359558e 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -828,10 +828,12 @@
/datum/quirk/telomeres_short/check_quirk(datum/preferences/prefs)
var/datum/species/species_type = prefs.read_preference(/datum/preference/choiced/species)
- var/disallowed_trait = (NO_DNA_COPY in initial(species_type.species_traits)) //Can't pick if you have no DNA bruv.
-
- if(disallowed_trait)
+ var/no_dna = (NO_DNA_COPY in initial(species_type.species_traits)) //Can't pick if you have no DNA bruv.
+ var/no_clone = (TRAIT_NOCLONE in initial(species_type.inherent_traits))
+ if(no_dna)
return "You have no DNA!"
+ else if(no_clone)
+ return "Your species cannot be cloned!"
return FALSE
/datum/quirk/body_purist
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index bcfea380f601..8cbb39d5c893 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -174,7 +174,7 @@
/datum/quirk/colorist/check_quirk(datum/preferences/prefs)
var/datum/species/species_type = prefs.read_preference(/datum/preference/choiced/species)
- var/disallowed_trait = (HAIR in initial(species_type.species_traits)) // No Hair
+ var/disallowed_trait = (HAIR in initial(species_type.species_traits)) || ("vox_quills" in initial(species_type.mutant_bodyparts)) // No Hair
if(!disallowed_trait)
return "You don't have hair!"
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index bdfdd5ff63a9..ba228387fefb 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -219,6 +219,7 @@ GLOBAL_VAR(changeling_team_objective_type)
new_flesh_item.item_state = chosen_prof.inhand_icon_state_list[slot]
new_flesh_item.worn_icon = chosen_prof.worn_icon_list[slot]
new_flesh_item.worn_icon_state = chosen_prof.worn_icon_state_list[slot]
+ new_flesh_item.sprite_sheets = chosen_prof.sprite_sheets_list[slot]
if(equip)
user.equip_to_slot_or_del(new_flesh_item, GLOB.slot2slot[slot])
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index 6086a909e646..321b34a75505 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -53,7 +53,8 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
/obj/item/storage/box/heretic_box = 1,
/obj/item/toy/plush/mothplushie = 2,
/obj/item/toy/plush/abductor = 2,
- /obj/item/toy/plush/abductor/agent = 1))
+ /obj/item/toy/plush/abductor/agent = 1,
+ /obj/item/toy/plush/voxplushie = 2))
/obj/machinery/computer/arcade
name = "random arcade"
diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm
index 7fac837c3ec3..c49b87da94c4 100644
--- a/code/game/machinery/limbgrower.dm
+++ b/code/game/machinery/limbgrower.dm
@@ -24,7 +24,7 @@
/// Our internal techweb for limbgrower designs.
var/datum/techweb/stored_research
/// All the categories of organs we can print.
- var/list/categories = list("human", "lizard", "moth", "plasmaman", "ethereal", "polysmorph", "other")
+ var/list/categories = list("human", "lizard", "moth", "plasmaman", "ethereal", "polysmorph", "vox", "other")
//yogs grower a little different because we're going to allow meats to be converted to synthflesh because hugbox
var/list/accepted_biomass = list(
/obj/item/reagent_containers/food/snacks/meat/slab/monkey = 25,
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index 39962577f6f6..4dc99243424b 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -84,12 +84,15 @@
///obj/item/organ/tongue/ethereal,
/obj/item/organ/tongue/robot,
/obj/item/organ/tongue/zombie,
+ /obj/item/organ/tongue/vox,
/obj/item/organ/appendix,
/obj/item/organ/liver/fly,
/obj/item/organ/lungs/plasmaman,
/obj/item/organ/lungs/ethereal,
+ /obj/item/organ/lungs/vox,
/obj/item/organ/tail/cat,
/obj/item/organ/tail/lizard,
+ /obj/item/organ/tail/vox
)
/obj/effect/spawner/lootdrop/plushies
@@ -115,7 +118,8 @@
/obj/item/toy/plush/inorixplushie,
/obj/item/toy/plush/flowerbunch,
/obj/item/toy/plush/goatplushie,
- /obj/item/toy/plush/realgoat
+ /obj/item/toy/plush/realgoat,
+ /obj/item/toy/plush/voxplushie
)
/obj/effect/spawner/lootdrop/techshell
diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm
index 60e4073b12dc..120a3366f170 100644
--- a/code/game/objects/items/cosmetics.dm
+++ b/code/game/objects/items/cosmetics.dm
@@ -118,10 +118,18 @@
return BRUTELOSS
/obj/item/razor/proc/shave(mob/living/carbon/human/H, location = BODY_ZONE_PRECISE_MOUTH)
- if(location == BODY_ZONE_PRECISE_MOUTH)
- H.facial_hair_style = "Shaved"
+ if(isvox(H))//both hair and bodyparts need refactors to do this cleanly
+ if(location == BODY_ZONE_PRECISE_MOUTH)
+ H.dna.features["vox_facial_quills"] = "None"
+ H.dna.update_uf_block(DNA_VOX_FACIAL_QUILLS_BLOCK)
+ else
+ H.dna.features["vox_quills"] = "None"
+ H.dna.update_uf_block(DNA_VOX_QUILLS_BLOCK)
else
- H.hair_style = "Skinhead"
+ if(location == BODY_ZONE_PRECISE_MOUTH)
+ H.facial_hair_style = "Shaved"
+ else
+ H.hair_style = "Skinhead"
H.update_hair()
playsound(loc, 'sound/items/welder2.ogg', 20, 1)
@@ -134,85 +142,117 @@
if((location in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_HEAD)) && !H.get_bodypart(BODY_ZONE_HEAD))
to_chat(user, span_warning("[H] doesn't have a head!"))
return
+ var/hair_name = "hair"
+ if(isvox(H))
+ hair_name = "quills"
if(location == BODY_ZONE_PRECISE_MOUTH)
if(!user.combat_mode)
if(H.gender == MALE)
if (H == user)
- to_chat(user, span_warning("You need a mirror to properly style your own facial hair!"))
+ to_chat(user, span_warning("You need a mirror to properly style your own facial [hair_name]!"))
return
if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
- var/new_style = input(user, "Select a facial hair style", "Grooming") as null|anything in GLOB.facial_hair_styles_list
+ var/list/hair_list = GLOB.facial_hair_styles_list
+ if(isvox(H))
+ hair_list = GLOB.vox_facial_quills_list
+ var/new_style = input(user, "Select a facial [hair_name] style", "Grooming") as null|anything in hair_list
if(!get_location_accessible(H, location))
to_chat(user, span_warning("The mask is in the way!"))
return
- user.visible_message(span_notice("[user] tries to change [H]'s facial hair style using [src]."), span_notice("You try to change [H]'s facial hair style using [src]."))
+ user.visible_message(span_notice("[user] tries to change [H]'s facial [hair_name] style using [src]."), span_notice("You try to change [H]'s facial [hair_name] style using [src]."))
if(new_style && do_after(user, 6 SECONDS, H))
- user.visible_message(span_notice("[user] successfully changes [H]'s facial hair style using [src]."), span_notice("You successfully change [H]'s facial hair style using [src]."))
- H.facial_hair_style = new_style
+ user.visible_message(span_notice("[user] successfully changes [H]'s facial [hair_name] style using [src]."), span_notice("You successfully change [H]'s facial [hair_name] style using [src]."))
+ if(isvox(H))
+ H.dna.features["vox_facial_quills"] = new_style
+ H.dna.update_uf_block(DNA_VOX_FACIAL_QUILLS_BLOCK)
+ else
+ H.facial_hair_style = new_style
H.update_hair()
return
else
return
else
- if(!(FACEHAIR in H.dna.species.species_traits))
- to_chat(user, span_warning("There is no facial hair to shave!"))
- return
if(!get_location_accessible(H, location))
to_chat(user, span_warning("The mask is in the way!"))
return
- if(H.facial_hair_style == "Shaved")
- to_chat(user, span_warning("Already clean-shaven!"))
- return
-
+ if(!isvox(H))
+ if(!(FACEHAIR in H.dna.species.species_traits))
+ to_chat(user, span_warning("There is no facial hair to shave!"))
+ return
+ if(H.facial_hair_style == "Shaved")
+ to_chat(user, span_warning("Already clean-shaven!"))
+ return
+ else
+ if(!("vox_facial_quills" in H.dna.species.mutant_bodyparts))
+ to_chat(user, span_warning("There are no facial quills to shave!"))
+ return
+ if(H.dna.species.mutant_bodyparts["vox_facial_quills"] == "None")
+ to_chat(user, span_warning("Already clean-shaven!"))
+ return
if(H == user) //shaving yourself
- user.visible_message("[user] starts to shave [user.p_their()] facial hair with [src].", \
- span_notice("You take a moment to shave your facial hair with [src]..."))
+ user.visible_message("[user] starts to shave [user.p_their()] facial [hair_name] with [src].", \
+ span_notice("You take a moment to shave your facial [hair_name] with [src]..."))
if(do_after(user, 5 SECONDS, H))
- user.visible_message("[user] shaves [user.p_their()] facial hair clean with [src].", \
+ user.visible_message("[user] shaves [user.p_their()] facial [hair_name] clean with [src].", \
span_notice("You finish shaving with [src]. Fast and clean!"))
shave(H, location)
else
- user.visible_message(span_warning("[user] tries to shave [H]'s facial hair with [src]."), \
- span_notice("You start shaving [H]'s facial hair..."))
+ user.visible_message(span_warning("[user] tries to shave [H]'s facial [hair_name] with [src]."), \
+ span_notice("You start shaving [H]'s facial [hair_name]..."))
if(do_after(user, 5 SECONDS, target = H))
- user.visible_message(span_warning("[user] shaves off [H]'s facial hair with [src]."), \
- span_notice("You shave [H]'s facial hair clean off."))
+ user.visible_message(span_warning("[user] shaves off [H]'s facial [hair_name] with [src]."), \
+ span_notice("You shave [H]'s facial [hair_name] clean off."))
shave(H, location)
else if(location == BODY_ZONE_HEAD)
if(!user.combat_mode)
if (H == user)
- to_chat(user, span_warning("You need a mirror to properly style your own hair!"))
+ to_chat(user, span_warning("You need a mirror to properly style your own [hair_name]!"))
return
if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
- var/new_style = input(user, "Select a hair style", "Grooming") as null|anything in GLOB.hair_styles_list
+ var/hair_list = GLOB.hair_styles_list
+ if(isvox(H))
+ hair_list = GLOB.vox_quills_list
+ var/new_style = input(user, "Select a [hair_name] style", "Grooming") as null|anything in hair_list
if(!get_location_accessible(H, location))
to_chat(user, span_warning("The headgear is in the way!"))
return
if(HAS_TRAIT(H, TRAIT_BALD))
to_chat(user, span_warning("[H] is just way too bald. Like, really really bald."))
return
- user.visible_message(span_notice("[user] tries to change [H]'s hairstyle using [src]."), span_notice("You try to change [H]'s hairstyle using [src]."))
+ user.visible_message(span_notice("[user] tries to change [H]'s [hair_name]style using [src]."), span_notice("You try to change [H]'s [hair_name]style using [src]."))
if(new_style && do_after(user, 6 SECONDS, H))
- user.visible_message(span_notice("[user] successfully changes [H]'s hairstyle using [src]."), span_notice("You successfully change [H]'s hairstyle using [src]."))
- H.hair_style = new_style
+ user.visible_message(span_notice("[user] successfully changes [H]'s [hair_name]style using [src]."), span_notice("You successfully change [H]'s [hair_name]style using [src]."))
+ if(isvox(H))
+ H.dna.features["vox_quills"] = new_style
+ H.dna.update_uf_block(DNA_VOX_QUILLS_BLOCK)
+ else
+ H.hair_style = new_style
H.update_hair()
return
else
- if(!(HAIR in H.dna.species.species_traits))
- to_chat(user, span_warning("There is no hair to shave!"))
- return
if(!get_location_accessible(H, location))
to_chat(user, span_warning("The headgear is in the way!"))
return
- if(H.hair_style == "Bald" || H.hair_style == "Balding Hair" || H.hair_style == "Skinhead")
- to_chat(user, span_warning("There is not enough hair left to shave!"))
- return
-
+ if(!isvox(H))
+ if(!(HAIR in H.dna.species.species_traits))
+ to_chat(user, span_warning("There is no hair to shave!"))
+ return
+
+ if(H.hair_style == "Bald" || H.hair_style == "Balding Hair" || H.hair_style == "Skinhead")
+ to_chat(user, span_warning("There is not enough hair left to shave!"))
+ return
+ else
+ if(!("vox_quills" in H.dna.species.mutant_bodyparts))
+ to_chat(user, span_warning("There are no quills to shave!"))
+ return
+ if(H.dna.species.mutant_bodyparts["vox_quills"] == "None")
+ to_chat(user, span_warning("There are not enough quills left to shave!"))
+ return
if(H == user) //shaving yourself
user.visible_message("[user] starts to shave [user.p_their()] head with [src].", \
span_notice("You start to shave your head with [src]..."))
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index ea46fd2a4d7c..2785da73c02d 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -35,7 +35,7 @@
var/drawtype
var/text_buffer = ""
- var/static/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","star","poseur tag","prolizard","antilizard")
+ var/static/list/graffiti = list("amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","body","cyka","star","poseur tag","prolizard","antilizard","voxpox")
var/static/list/symbols = list("danger","firedanger","electricdanger","biohazard","radiation","safe","evac","space","med","trade","shop","food","peace","like","skull","nay","heart","credit")
var/static/list/drawings = list("smallbrush","brush","largebrush","splatter","snake","stickman","carp","ghost","clown","taser","disk","fireaxe","toolbox","corgi","cat","toilet","blueprint","beepsky","scroll","bottle","shotgun")
var/static/list/oriented = list("arrow","line","thinline","shortline","body","chevron","footprint","clawprint","pawprint") // These turn to face the same way as the drawer
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index ce024afeadc7..c61caed09844 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -264,6 +264,7 @@
languages = list(
/datum/language/bonespeak,
/datum/language/draconic,
+ /datum/language/vox,
/datum/language/english,
/datum/language/etherean,
/datum/language/felinid,
@@ -280,6 +281,7 @@
languages = list(
/datum/language/bonespeak,
/datum/language/draconic,
+ /datum/language/vox,
/datum/language/english,
/datum/language/etherean,
/datum/language/felinid,
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 2458430625da..364f4a9640bf 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -146,13 +146,16 @@
new /obj/item/radio/off(src)
/obj/item/storage/box/survival/proc/wardrobe_removal()
- if(!isplasmaman(loc)) //We need to specially fill the box with plasmaman gear, since it's intended for one
+ if(!ishuman(loc))
return
- var/obj/item/mask = locate(/obj/item/clothing/mask/breath) in src
- var/obj/item/internals = locate(/obj/item/tank/internals/emergency_oxygen) in src
- new /obj/item/tank/internals/plasmaman/belt(src)
- qdel(mask) // Get rid of the items that shouldn't be
- qdel(internals)
+ var/mob/living/carbon/human/box_owner = loc
+ if(!length(box_owner.dna?.species?.survival_box_replacements))
+ return
+ var/list/survival_box_replacements_delete = box_owner.dna.species.survival_box_replacements["items_to_delete"]
+ var/list/survival_box_replacements_add = box_owner.dna.species.survival_box_replacements["new_items"]
+ var/list/items_to_delete = survival_box_replacements_delete?.Copy() || list()
+ var/list/new_items = survival_box_replacements_add?.Copy() || list()
+ box_owner.dna.species.survival_box_replacement(box_owner, src, items_to_delete, new_items)
/obj/item/storage/box/survival/mining
mask_type = /obj/item/clothing/mask/gas/explorer
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index e16f595fed15..fe85b6ecc311 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -134,7 +134,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF))
resistance_flags |= ON_FIRE
SSfire_burning.processing[src] = src
- add_overlay(GLOB.fire_overlay, TRUE)
+ add_overlay(custom_fire_overlay ? custom_fire_overlay : GLOB.fire_overlay)
return 1
///called when the obj is destroyed by fire
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 1043e7bafec1..f3707d937dd7 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -156,6 +156,7 @@
new /obj/item/reagent_containers/blood/OMinus(src)
new /obj/item/reagent_containers/blood/OPlus(src)
new /obj/item/reagent_containers/blood/lizard(src)
+ new /obj/item/reagent_containers/blood/vox(src)
new /obj/item/reagent_containers/blood/gorilla(src) // yogs -- gorilla people
new /obj/item/reagent_containers/blood/ethereal(src)
for(var/i in 1 to 3)
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index c056418554c9..8c032d362b99 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -28,11 +28,16 @@
/obj/structure/mirror/proc/get_choices(mob/living/carbon/human/H)
. = list()
var/datum/species/S = H.dna.species
- if((FACEHAIR in S.species_traits))
- . += list(FACIAL_HAIR = list("select a facial hair style", GLOB.facial_hair_styles_list))
+ var/list/facial_hair_list = GLOB.facial_hair_styles_list
+ var/list/hair_list = GLOB.hair_styles_list
+ if(isvox(H))
+ facial_hair_list = GLOB.vox_facial_quills_list
+ hair_list = GLOB.vox_quills_list
+ if((FACEHAIR in S.species_traits) || (FACEHAIRCOLOR in S.species_traits))
+ . += list(FACIAL_HAIR = list("select a facial hair style", facial_hair_list))
. += list(FACE_HAIR_COLOR)
- if((HAIR in S.species_traits))
- . += list(HEAD_HAIR = list("select a hair style", GLOB.hair_styles_list))
+ if((HAIR in S.species_traits) || (HAIRCOLOR in S.species_traits))
+ . += list(HEAD_HAIR = list("select a hair style", hair_list))
. += list(HAIR_COLOR)
// for things that dont use a list style syntax
@@ -60,14 +65,22 @@
return
switch(selectiontype)
if(FACIAL_HAIR)
- H.facial_hair_style = selection
+ if(isvox(H))
+ H.dna.features["vox_facial_quills"] = selection
+ H.dna.update_uf_block(DNA_VOX_FACIAL_QUILLS_BLOCK)
+ else
+ H.facial_hair_style = selection
H.update_hair()
return TRUE
if(HEAD_HAIR)
- if(HAS_TRAIT(H, TRAIT_BALD) && selection != "Bald")
+ if(HAS_TRAIT(H, TRAIT_BALD) && !((selection == "Bald") || (selection == ("None"))))
to_chat(H, span_notice("If only growing back hair were that easy for you..."))
return TRUE
- H.hair_style = selection
+ if(isvox(H))
+ H.dna.features["vox_quills"] = selection
+ H.dna.update_uf_block(DNA_VOX_QUILLS_BLOCK)
+ else
+ H.hair_style = selection
H.update_hair()
return TRUE
@@ -190,6 +203,8 @@
. += list(SKIN_COLOR = list("select a new skintone", GLOB.skin_tones))
if((MUTCOLORS in S.species_traits) && !(NOCOLORCHANGE in S.species_traits))
. += list(MUTANT_COLOR)
+ if((MUTCOLORS_SECONDARY in S.species_traits))
+ . += list("Secondary mutant color")
. += list(NAME)
/obj/structure/mirror/magic/preapply_choices(selectiontype, mob/living/carbon/human/H)
@@ -228,6 +243,16 @@
H.update_hair()
H.update_body_parts()
H.update_mutations_overlay() // no hulk lizard
+ if("Secondary mutant color")
+ var/new_mutantcolor = input(H, "Choose your secondary mutant color:", "Race change",H.dna.features["mcolor_secondary"]) as color|null
+ if(!new_mutantcolor)
+ return TRUE
+ H.dna.features["mcolor_secondary"] = sanitize_hexcolor(new_mutantcolor)
+ H.dna.update_uf_block(DNA_MUTANT_COLOR_SECONDARY)
+ H.update_body()
+ H.update_hair()
+ H.update_body_parts()
+ H.update_mutations_overlay()
return TRUE
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index 49c1f9927fdf..700d5182b9dc 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -25,6 +25,7 @@
// Mutant randomizing, doesn't affect the mob appearance unless it's the specific mutant.
human.dna.features["mcolor"] = "#[random_color()]"
+ human.dna.features["mcolor_secondary"] = "#[random_color()]"
human.dna.features["pretcolor"] = GLOB.color_list_preternis[pick(GLOB.color_list_preternis)]
human.dna.features["tail_lizard"] = pick(GLOB.tails_list_lizard)
human.dna.features["tail_polysmorph"] = pick(GLOB.tails_list_polysmorph)
@@ -44,6 +45,11 @@
human.dna.features["preternis_core"] = pick(GLOB.preternis_core_list)
human.dna.features["pod_hair"] = pick(GLOB.pod_hair_list)
human.dna.features["pod_flower"] = GLOB.pod_flower_list[human.dna.features["pod_hair"]]
+ human.dna.features["vox_skin_tone"] = pick(GLOB.vox_skin_tones)
+ human.dna.features["vox_quills"] = pick(GLOB.vox_quills_list)
+ human.dna.features["vox_facial_quills"] = pick(GLOB.vox_facial_quills_list)
+ human.dna.features["vox_body_markings"] = pick(GLOB.vox_body_markings_list)
+ human.dna.features["vox_tail_markings"] = pick(GLOB.vox_tail_markings_list)
human.update_body()
human.update_hair()
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index eb305df0c8dd..6834a5b0ab36 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -345,6 +345,7 @@
prof.righthand_file_list[slot] = I.righthand_file
prof.worn_icon_list[slot] = I.worn_icon
prof.worn_icon_state_list[slot] = I.worn_icon_state
+ prof.sprite_sheets_list[slot] = I.sprite_sheets
prof.exists_list[slot] = 1
else
continue
@@ -676,6 +677,7 @@
new_profile.socks = socks
new_profile.worn_icon_list = worn_icon_list.Copy()
new_profile.worn_icon_state_list = worn_icon_state_list.Copy()
+ new_profile.sprite_sheets_list = sprite_sheets_list.Copy()
new_profile.stored_scars = stored_scars.Copy()
/datum/antagonist/changeling/xenobio
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 09fe1626b5a9..b31ac8c0e8ce 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -48,6 +48,10 @@
if(!isplasmaman(H)) //no killing plasmies
H.equip_to_slot_or_del(new /obj/item/clothing/under/costume/kilt/highlander(H), ITEM_SLOT_ICLOTHING)
H.equip_to_slot_or_del(new /obj/item/clothing/head/beret/highlander(H), ITEM_SLOT_HEAD)
+ if(isvox(H))
+ H.equip_to_slot_or_del(new /obj/item/tank/internals/emergency_oxygen/vox(H), ITEM_SLOT_BELT)
+ H.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(H), ITEM_SLOT_MASK)
+ H.open_internals(H.get_item_by_slot(ITEM_SLOT_BELT))
else
H.equip_to_slot_or_del(new /obj/item/clothing/under/plasmaman(H), ITEM_SLOT_ICLOTHING)
H.equip_to_slot_or_del(new /obj/item/tank/internals/plasmaman/belt/full(H), ITEM_SLOT_BELT)
diff --git a/code/modules/atmospherics/auxgm/gas_types.dm b/code/modules/atmospherics/auxgm/gas_types.dm
index 0c559d9d8e3a..616d26f9978a 100644
--- a/code/modules/atmospherics/auxgm/gas_types.dm
+++ b/code/modules/atmospherics/auxgm/gas_types.dm
@@ -3,6 +3,16 @@
specific_heat = 20
name = "Oxygen"
label = "O₂"
+ breath_alert_info = list(
+ not_enough_alert = list(
+ alert_category = "not_enough_oxy",
+ alert_type = /atom/movable/screen/alert/not_enough_oxy
+ ),
+ too_much_alert = list(
+ alert_category = "too_much_oxy",
+ alert_type = /atom/movable/screen/alert/too_much_oxy
+ )
+ )
ui_color = "blue"
/datum/gas/nitrogen
@@ -47,6 +57,16 @@
gas_overlay = "plasma"
moles_visible = MOLES_GAS_VISIBLE
flags = GAS_FLAG_DANGEROUS
+ breath_alert_info = list(
+ not_enough_alert = list(
+ alert_category = "not_enough_tox",
+ alert_type = /atom/movable/screen/alert/not_enough_tox,
+ ),
+ too_much_alert = list(
+ alert_category = "too_much_tox",
+ alert_type = /atom/movable/screen/alert/too_much_tox,
+ )
+ )
ui_color = "orange"
/datum/gas/water_vapor
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 2c5d754fd67b..be718754a097 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -1428,7 +1428,7 @@
/datum/supply_pack/medical/bloodpacks
name = "Blood Pack Variety Crate"
- desc = "Contains eight different blood packs for reintroducing blood to patients."
+ desc = "Contains many different blood packs for reintroducing blood to patients."
cost = 3500
contains = list(/obj/item/reagent_containers/blood,
/obj/item/reagent_containers/blood,
@@ -1439,6 +1439,7 @@
/obj/item/reagent_containers/blood/OPlus,
/obj/item/reagent_containers/blood/OMinus,
/obj/item/reagent_containers/blood/lizard,
+ /obj/item/reagent_containers/blood/vox,
/obj/item/reagent_containers/blood/ethereal)
crate_name = "blood freezer"
crate_type = /obj/structure/closet/crate/freezer
diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm
index f2ed776c0e41..884667cb24b5 100644
--- a/code/modules/client/preferences/_preference.dm
+++ b/code/modules/client/preferences/_preference.dm
@@ -317,12 +317,12 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
/datum/preference/proc/is_accessible(datum/preferences/preferences)
SHOULD_CALL_PARENT(TRUE)
SHOULD_NOT_SLEEP(TRUE)
-
- if (!isnull(relevant_mutant_bodypart) || !isnull(relevant_species_trait))
- var/species_type = preferences.read_preference(/datum/preference/choiced/species)
-
- var/datum/species/species = new species_type
- if (!(savefile_key in species.get_features()))
+ var/species_type = preferences.read_preference(/datum/preference/choiced/species)
+ var/datum/species/species = new species_type
+ if(species_type in blacklisted_species)
+ return FALSE
+ if(!isnull(relevant_mutant_bodypart) || !isnull(relevant_species_trait))
+ if(!(savefile_key in species.get_features()))
return FALSE
if (!should_show_on_page(preferences.current_window))
diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm
index 44e72c844861..1b110178739e 100644
--- a/code/modules/client/preferences/clothing.dm
+++ b/code/modules/client/preferences/clothing.dm
@@ -1,16 +1,22 @@
-/proc/generate_values_for_underwear(icon_file, list/accessory_list, list/icons)
+/proc/generate_values_for_underwear(icon_file, list/accessory_list, list/icons, bodyparts_file = 'icons/mob/human_parts_greyscale.dmi')
var/icon/lower_half = icon('icons/blanks/32x32.dmi', "nothing")
for (var/icon in icons)
- lower_half.Blend(icon('icons/mob/human_parts_greyscale.dmi', icon), ICON_OVERLAY)
+ lower_half.Blend(icon(bodyparts_file, icon), ICON_OVERLAY)
var/list/values = list()
-
- for (var/accessory_name in accessory_list)
+ var/list/undergarment_list = accessory_list.Copy()
+ for(var/undergarment_accessory in undergarment_list)
+ if(undergarment_accessory == "Nude")
+ continue
+ var/datum/sprite_accessory/undergarment = undergarment_list[undergarment_accessory]
+ if(!icon_exists(icon_file, undergarment.icon_state))
+ undergarment_list -= undergarment_accessory
+ for (var/accessory_name in undergarment_list)
var/icon/icon_with_socks = new(lower_half)
if (accessory_name != "Nude")
- var/datum/sprite_accessory/accessory = accessory_list[accessory_name]
+ var/datum/sprite_accessory/accessory = undergarment_list[accessory_name]
var/icon/accessory_icon = icon(icon_file, accessory.icon_state)
icon_with_socks.Blend(accessory_icon, ICON_OVERLAY)
diff --git a/code/modules/client/preferences/middleware/species.dm b/code/modules/client/preferences/middleware/species.dm
index 05fffbb32811..ed33227c8b03 100644
--- a/code/modules/client/preferences/middleware/species.dm
+++ b/code/modules/client/preferences/middleware/species.dm
@@ -22,9 +22,10 @@
dummy.equipOutfit(/datum/outfit/job/assistant/consistent, visualsOnly = TRUE)
dummy.dna.species.prepare_human_for_preview(dummy)
var/icon/dummy_icon = getFlatIcon(dummy)
- if(ismoth(dummy))
+ var/list/lazy_icons_species = list("moth", SPECIES_VOX)//no idea why it fails to render properly the normal way
+ if(dummy.dna.species.id in lazy_icons_species)
dummy_icon = null
- dummy_icon = icon('icons/mob/human.dmi', "moth")
+ dummy_icon = icon('icons/mob/human.dmi', "[dummy.dna.species.id]")
dummy_icon.Scale(64, 64)
dummy_icon.Crop(15, 64, 15 + 31, 64 - 31)
dummy_icon.Scale(64, 64)
diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm
index 1378543606a8..31e78911e304 100644
--- a/code/modules/client/verbs/suicide.dm
+++ b/code/modules/client/verbs/suicide.dm
@@ -1,18 +1,18 @@
-#define SUICIDE_MESSAGE(p_their, p_theyre, p_them) pick( \
- "[src] is attempting to push [p_their] own head off [p_their] shoulders! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is pushing [p_their] thumbs into [p_their] eye sockets! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is ripping [p_their] own arms off! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is attempting to pull [p_their] own head off! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is aggressively grabbing [p_their] own neck! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is pulling [p_their] eyes out of their sockets! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is hugging [p_them]self to death! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is high-fiving [p_them]self to death! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is getting too high on life! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is attempting to bite [p_their] tongue off! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is jamming [p_their] thumbs into [p_their] eye sockets! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is twisting [p_their] own neck! It looks like [p_theyre] trying to commit suicide.", \
- "[src] is holding [p_their] breath! It looks like [p_theyre] trying to commit suicide.", \
-)
+GLOBAL_LIST_INIT(human_suicide_messages, list( \
+ "%%SUICIDER%% is attempting to push %%P_THEIR%% own head off %%P_THEIR%% shoulders! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is pushing %%P_THEIR%% thumbs into %%P_THEIR%% eye sockets! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is ripping %%P_THEIR%% own arms off! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is attempting to pull %%P_THEIR%% own head off! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is aggressively grabbing %%P_THEIR%% own neck! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is pulling %%P_THEIR%% eyes out of their sockets! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is hugging %%P_THEM%%self to death! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is high-fiving %%P_THEM%%self to death! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is getting too high on life! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is attempting to bite %%P_THEIR%% tongue off! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is jamming %%P_THEIR%% thumbs into %%P_THEIR%% eye sockets! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is twisting %%P_THEIR%% own neck! It looks like %%P_THEYRE%% trying to commit suicide.", \
+ "%%SUICIDER%% is holding %%P_THEIR%% breath! It looks like %%P_THEYRE%% trying to commit suicide.", \
+))
/mob/var/suiciding = 0
@@ -101,9 +101,13 @@
return
- var/suicide_message = SUICIDE_MESSAGE(p_their(), p_theyre(), p_them())
-
- visible_message(span_danger("[suicide_message]"), span_userdanger("[suicide_message]"))
+ var/list/suicide_messages = GLOB.human_suicide_messages.Copy()
+ suicide_messages |= dna.species.suicide_messages
+ var/chosen_message = pick(suicide_messages)
+ var/list/text_replacements = list("%%SUICIDER%%" = "[src]", "%%P_THEIR%%" = "[p_their()]", "%%P_THEM%%" = "[p_them()]", "%%P_THEYRE%%" = "[p_theyre()]")
+ for(var/find_text in text_replacements)
+ chosen_message = replacetext(chosen_message, find_text, text_replacements[find_text])
+ visible_message(span_danger(chosen_message), span_userdanger(chosen_message))
suicide_log()
@@ -278,5 +282,3 @@
to_chat(src, "Something inside your head stops your action!")
return
return TRUE
-
-#undef SUICIDE_MESSAGE
diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm
index 28563976c467..d3eafcd6e8a9 100644
--- a/code/modules/clothing/chameleon.dm
+++ b/code/modules/clothing/chameleon.dm
@@ -252,6 +252,7 @@
item_target.worn_icon_state = initial(picked_item.worn_icon_state)
item_target.item_state = initial(picked_item.item_state)
+ item_target.sprite_sheets = initial(picked_item.sprite_sheets)
if(isclothing(item_target) && ispath(picked_item, /obj/item/clothing))
var/obj/item/clothing/CL = item_target
var/obj/item/clothing/PCL = picked_item
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index a382e315bca8..d48ba7b96b3a 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -33,6 +33,8 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves")
if(HAS_BLOOD_DNA(src))
var/mutable_appearance/bloody_hands = mutable_appearance('icons/effects/blood.dmi', "bloodyhands")
+ if(species_fitted && icon_exists(bloody_hands.icon, "bloodyhands_[species_fitted]"))
+ bloody_hands.icon_state = "bloodyhands_[species_fitted]"
bloody_hands.color = get_blood_dna_color(return_blood_DNA())
. += bloody_hands
diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm
index bcf9327ed800..72dc63c19166 100644
--- a/code/modules/clothing/head/_head.dm
+++ b/code/modules/clothing/head/_head.dm
@@ -33,6 +33,8 @@
bloody_helmet = mutable_appearance('icons/effects/64x64.dmi', "helmetblood_large")
else
bloody_helmet = mutable_appearance('icons/effects/blood.dmi', "helmetblood")
+ if(species_fitted && icon_exists(bloody_helmet.icon, "helmetblood_[species_fitted]"))
+ bloody_helmet.icon_state = "helmetblood_[species_fitted]"
bloody_helmet.color = get_blood_dna_color(return_blood_DNA())
. += bloody_helmet
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 0bb43ccddeea..771569397c8c 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -38,6 +38,8 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask")
if(HAS_BLOOD_DNA(src))
var/mutable_appearance/bloody_mask = mutable_appearance('icons/effects/blood.dmi', "maskblood")
+ if(species_fitted && icon_exists(bloody_mask.icon, "maskblood_[species_fitted]"))
+ bloody_mask.icon_state = "maskblood_[species_fitted]"
bloody_mask.color = get_blood_dna_color(return_blood_DNA())
. += bloody_mask
diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm
index 8e9af7a6a789..9b5b599d1d1a 100644
--- a/code/modules/clothing/masks/breath.dm
+++ b/code/modules/clothing/masks/breath.dm
@@ -42,7 +42,8 @@
/obj/item/clothing/mask/breath/examine(mob/user)
. = ..()
- . += span_notice("Alt-click [src] to adjust it.")
+ if(length(actions_types))
+ . += span_notice("Alt-click [src] to adjust it.")
/obj/item/clothing/mask/breath/medical
desc = "A close-fitting sterile mask that can be connected to an air supply."
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 8916c9fc230a..f83ba0f70d3d 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -16,6 +16,8 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask")
if(HAS_BLOOD_DNA(src))
var/mutable_appearance/bloody_mask = mutable_appearance('icons/effects/blood.dmi', "maskblood")
+ if(species_fitted && icon_exists(bloody_mask.icon, "maskblood_[species_fitted]"))
+ bloody_mask.icon_state = "maskblood_[species_fitted]"
bloody_mask.color = get_blood_dna_color(return_blood_DNA())
. += bloody_mask
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index a9aa009a16a4..d8560612d8e5 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -54,6 +54,8 @@
bloody_shoes = mutable_appearance('icons/effects/64x64.dmi', "shoeblood_large")
else
bloody_shoes = mutable_appearance('icons/effects/blood.dmi', "shoeblood")
+ if(species_fitted && icon_exists(bloody_shoes.icon, "shoeblood_[species_fitted]"))
+ bloody_shoes.icon_state = "shoeblood_[species_fitted]"
bloody_shoes.color = get_blood_dna_color(return_blood_DNA())
. += bloody_shoes
diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm
index 112d2a0dc2b5..14f7f33bc0d0 100644
--- a/code/modules/clothing/suits/_suits.dm
+++ b/code/modules/clothing/suits/_suits.dm
@@ -30,6 +30,8 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
if(HAS_BLOOD_DNA(src))
var/mutable_appearance/bloody_armor = mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
+ if(species_fitted && icon_exists(bloody_armor.icon, "[bloody_armor.icon_state]_[species_fitted]"))
+ bloody_armor.icon_state = "[bloody_armor.icon_state]_[species_fitted]"
bloody_armor.color = get_blood_dna_color(return_blood_DNA())
. += bloody_armor
var/mob/living/carbon/human/M = loc
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 1bebc3b9dff3..a909f3deb696 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -372,7 +372,7 @@
/obj/item/clothing/head/hooded/human_head
name = "bloated human head"
desc = "A horribly bloated and mismatched human head."
- icon_state = "lingspacehelmet"
+ icon_state = "humanheadhat"
body_parts_covered = HEAD
flags_cover = HEADCOVERSEYES
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 66ef8c2e4c66..25aa84df63d1 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -33,6 +33,8 @@
. += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform")
if(HAS_BLOOD_DNA(src))
var/mutable_appearance/bloody_uniform = mutable_appearance('icons/effects/blood.dmi', "uniformblood")
+ if(species_fitted && icon_exists(bloody_uniform.icon, "uniformblood_[species_fitted]"))
+ bloody_uniform.icon_state = "uniformblood_[species_fitted]"
bloody_uniform.color = get_blood_dna_color(return_blood_DNA())
. += bloody_uniform
if(accessory_overlay)
diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm
index 31298dfc77ce..c5d616b1a3af 100644
--- a/code/modules/food_and_drinks/food/snacks_pie.dm
+++ b/code/modules/food_and_drinks/food/snacks_pie.dm
@@ -43,10 +43,7 @@
if(ishuman(hit_atom))
var/mob/living/carbon/human/H = hit_atom
var/mutable_appearance/creamoverlay = mutable_appearance('icons/effects/creampie.dmi')
- if(H.dna.species.limbs_id == "lizard")
- creamoverlay.icon_state = "creampie_lizard"
- else
- creamoverlay.icon_state = "creampie_human"
+ creamoverlay.icon_state = H.dna.species.creampie_id
if(stunning)
H.Paralyze(20) //splat!
H.adjust_eye_blur(1)
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm
index c7fac4fd301d..38677ad30da7 100644
--- a/code/modules/language/language_holder.dm
+++ b/code/modules/language/language_holder.dm
@@ -392,6 +392,7 @@ Key procs
/datum/language/bonespeak = list(LANGUAGE_ATOM),
/datum/language/sylvan = list(LANGUAGE_ATOM),
/datum/language/draconic = list(LANGUAGE_ATOM),
+ /datum/language/vox = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/felinid = list(LANGUAGE_ATOM),
@@ -402,6 +403,7 @@ Key procs
/datum/language/bonespeak = list(LANGUAGE_ATOM),
/datum/language/sylvan = list(LANGUAGE_ATOM),
/datum/language/draconic = list(LANGUAGE_ATOM),
+ /datum/language/vox = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM))
/datum/language_holder/empty
diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm
index 99171e324ffb..71a46b48d508 100644
--- a/code/modules/mob/living/brain/MMI.dm
+++ b/code/modules/mob/living/brain/MMI.dm
@@ -34,11 +34,10 @@
if(!brain)
icon_state = "mmi_off"
return
+ icon_state = brain.get_mmi_brain_sprite()
if(istype(brain, /obj/item/organ/brain/alien))
- icon_state = "mmi_brain_alien"
braintype = "Xenoborg" //HISS....Beep.
else
- icon_state = "mmi_brain"
braintype = "Cyborg"
/obj/item/mmi/update_overlays()
@@ -73,7 +72,7 @@
return
var/mob/living/brain/B = newbrain.brainmob
if(!B.key)
- B.notify_ghost_cloning("Someone has put your brain in a MMI!", source = src)
+ B.notify_ghost_cloning("Someone has put your [newbrain.brain_name] in an MMI!", source = src)
user.visible_message("[user] sticks \a [newbrain] into [src].", span_notice("[src]'s indicator light turn on as you insert [newbrain]."))
brainmob = newbrain.brainmob
@@ -130,14 +129,15 @@
/obj/item/mmi/attack_self(mob/user)
if(brain)
- user.visible_message(span_notice("[user] begins to remove the brain from [src]."), span_danger("You begin to pry the brain out of [src], ripping out the wires and probes."))
+ var/brain_flavor_name = brain.brain_name
+ user.visible_message(span_notice("[user] begins to remove the [brain_flavor_name] from [src]."), span_danger("You begin to pry the [brain_flavor_name] out of [src], ripping out the wires and probes."))
to_chat(brainmob, span_userdanger("You feel your mind failing as you are slowly ripped from the [src]."))
if(do_after(user, remove_time, src))
- to_chat(brainmob, span_userdanger("Due to the traumatic danger of your removal, all memories of the events leading to your brain being removed are lost[rebooting ? ", along with all memories of the events leading to your death as a cyborg." : ""]."))
+ to_chat(brainmob, span_userdanger("Due to the traumatic danger of your removal, all memories of the events leading to your [brain_flavor_name] being removed are lost[rebooting ? ", along with all memories of the events leading to your death as a cyborg." : ""]."))
eject_brain(user)
update_appearance(UPDATE_ICON)
name = initial(name)
- user.visible_message(span_notice("[user] rips the brain out of [src]."), span_danger("You successfully remove the brain from the [src][rebooting ? ", interrupting the reboot process." : ""]."))
+ user.visible_message(span_notice("[user] rips the [brain_flavor_name] out of [src]."), span_danger("You successfully remove the [brain_flavor_name] from the [src][rebooting ? ", interrupting the reboot process." : ""]."))
if(rebooting)
rebooting = FALSE
deltimer(reboot_timer)
@@ -192,7 +192,7 @@
brain = newbrain
else if(!brain)
brain = new(src)
- brain.name = "[L.real_name]'s brain"
+ brain.name = "[L.real_name]'s [brain.brain_name]"
name = "[initial(name)]: [brainmob.real_name]"
to_chat(brainmob, welcome_message)
@@ -262,13 +262,13 @@
if(brainmob)
var/mob/living/brain/B = brainmob
if(!B.key || !B.mind || B.stat == DEAD)
- . += span_warning("The MMI indicates the brain is completely unresponsive.")
+ . += span_warning("The MMI indicates the [brain.brain_name] is completely unresponsive.")
else if(!B.client)
- . += span_warning("The MMI indicates the brain is currently inactive; it might change.")
+ . += span_warning("The MMI indicates the [brain.brain_name] is currently inactive; it might change.")
else
- . += span_notice("The MMI indicates the brain is active.")
+ . += span_notice("The MMI indicates the [brain.brain_name] is active.")
. += span_notice("It has a port for reading AI law modules.")
if(laws)
. += span_notice("Any AI created using this MMI will use these uploaded laws:")
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index ec755053510e..f3dd1fa5f7eb 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -35,7 +35,7 @@
if(C.mind && C.mind.has_antag_datum(/datum/antagonist/changeling) && !no_id_transfer) //congrats, you're trapped in a body you don't control
if(brainmob && !(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_DEATHCOMA))))
- to_chat(brainmob, "You can't feel your body! You're still just a brain!")
+ to_chat(brainmob, span_danger("You can't feel your body! You're still just a [brain_name]!"))
forceMove(C)
C.update_hair()
return
@@ -90,7 +90,7 @@
..()
/obj/item/organ/brain/proc/transfer_identity(mob/living/L)
- name = "[L.name]'s brain"
+ name = "[L.name]'s [brain_name]"
if(brainmob || decoy_override)
return
if(!L.mind)
@@ -115,7 +115,7 @@
brainmob.set_species(ZI.old_species) //For if the brain is cloned
if(L.mind && L.mind.current)
L.mind.transfer_to(brainmob)
- to_chat(brainmob, span_notice("You feel slightly disoriented. That's normal when you're just a brain."))
+ to_chat(brainmob, span_notice("You feel slightly disoriented. That's normal when you're just a [brain_name]."))
/obj/item/organ/brain/attackby(obj/item/O, mob/user, params)
user.changeNext_move(CLICK_CD_MELEE)
@@ -138,7 +138,7 @@
to_chat(user, span_warning("You failed to pour [O] onto [src]!"))
return
- user.visible_message("[user] pours the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade of pink.", span_notice("You pour the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade of pink."))
+ user.visible_message("[user] pours the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade[brain_name == "brain" ? " of pink." : "."]", span_notice("You pour the contents of [O] onto [src], causing it to reform its original shape and turn a slightly brighter shade[brain_name == "brain" ? " of pink." : "."]"))
setOrganDamage(damage - (0.05 * maxHealth)) //heals a small amount, and by using "setorgandamage", we clear the failing variable if that was up
O.reagents.clear_reagents()
@@ -193,7 +193,7 @@
// This should be a better check but this covers 99.9% of cases
if(!(compatible_biotypes & C.mob_biotypes))
- to_chat(user, span_warner("This brain is incompatiable with this beings biology!"))
+ to_chat(user, span_warner("This brain is incompatible with this being's biology!"))
return
if(!target_has_brain && C.is_eyes_covered() && user.zone_selected == BODY_ZONE_HEAD)
@@ -226,7 +226,7 @@
/obj/item/organ/brain/on_life()
if(damage >= BRAIN_DAMAGE_DEATH) //rip
- to_chat(owner, span_userdanger("The last spark of life in your brain fizzles out..."))
+ to_chat(owner, span_userdanger("The last spark of life in your [brain_name] fizzles out..."))
owner.death()
brain_death = TRUE
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 664adf0832a2..0a2970f76794 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -30,10 +30,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/grad_style
///The gradient color used to color the gradient.
var/grad_color
-
/// does it use skintones or not? (spoiler alert this is only used by humans)
var/use_skintones = FALSE
-
+ var/icon_husk
var/forced_skintone
/// What genders can this race be?
@@ -135,6 +134,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/grab_sound
///yogs - audio of a species' scream
var/screamsound //yogs - grabs scream from screamsound list or string
+ var/husk_color = "#A6A6A6"
+ var/creampie_id = "creampie_human"
/// The visual effect of the attack.
var/attack_effect = ATTACK_EFFECT_PUNCH
///is a flying species, just a check for some things
@@ -148,7 +149,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/species_gibs = "human"
/// Can this species use numbers in its name?
var/allow_numbers_in_name = FALSE
-
+ ///Does this species have different sprites according to sex? A species may have sexes, but only one kind of bodypart sprite like chests
+ var/is_dimorphic = TRUE
/// species-only traits. Can be found in DNA.dm
var/list/species_traits = list()
/// generic traits tied to having the species
@@ -385,6 +387,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/obj/item/organ/tail/lizard/new_lizard_tail = neworgan
new_lizard_tail.tail_type = C.dna.features["tail_lizard"]
new_lizard_tail.spines = C.dna.features["spines"]
+ if(isvox(C))
+ var/obj/item/organ/tail/vox/new_vox_tail = neworgan
+ new_vox_tail.tail_type = C.dna.features["vox_skin_tone"]
+ new_vox_tail.tail_markings = C.dna.features["vox_tail_markings"]
// if(tail && (!should_have_tail || replace_current))
// tail.Remove(C,1)
@@ -497,7 +503,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
fly.Grant(C)
C.add_movespeed_modifier(MOVESPEED_ID_SPECIES, TRUE, 100, override=TRUE, multiplicative_slowdown=speedmod, movetypes=(~FLYING))
-
+ C.regenerate_icons()
SEND_SIGNAL(C, COMSIG_SPECIES_GAIN, src, old_species)
@@ -581,6 +587,17 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(M.flags_inv & HIDEFACIALHAIR)
facialhair_hidden = TRUE
+ if(("vox_facial_quills" in H.dna.species.mutant_bodyparts) && !facialhair_hidden)
+ S = GLOB.vox_facial_quills_list[H.dna.features["vox_facial_quills"]]
+ if(S)
+ var/mutable_appearance/facial_quills_overlay = mutable_appearance(layer = -HAIR_LAYER, appearance_flags = KEEP_TOGETHER)
+ var/mutable_appearance/facial_quills_base = mutable_appearance(S.icon, S.icon_state)
+ facial_quills_base.color = forced_colour || H.facial_hair_color
+ if(S.color_blend_mode == COLOR_BLEND_ADD)
+ facial_quills_base.color = COLOR_MATRIX_ADD(facial_quills_base.color)
+ facial_quills_overlay.overlays += facial_quills_base
+ facial_quills_overlay.alpha = hair_alpha
+ standing += facial_quills_overlay
if(H.facial_hair_style && (FACEHAIR in species_traits) && (!facialhair_hidden || dynamic_fhair_suffix))
S = GLOB.facial_hair_styles_list[H.facial_hair_style]
if(S)
@@ -726,7 +743,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
hair_overlay.color = H.hair_color
hair_overlay.alpha = hair_alpha
standing+=hair_overlay
- //var/mutable_appearance/pod_flower = mutable_appearance(GLOB.pod_flower_list[H.dna.features["pod_flower"]].icon, GLOB.pod_flower_list[H.dna.features["pod_flower"]].icon_state, -HAIR_LAYER)
+ //var/mutable_appearance/pod_flower = mutable_appearance(GLOB.pod_flower_list[H.dna.features["pod_flower"]].icon, GLOB.pod_flower_list[H.dna.features["pod_flower"]].icon_state, -HAIR_LAYER)
S = GLOB.pod_flower_list[H.dna.features["pod_flower"]]
if(S)
var/flower_state = S.icon_state
@@ -746,6 +763,26 @@ GLOBAL_LIST_EMPTY(features_by_species)
flower_overlay.color = H.facial_hair_color
flower_overlay.alpha = hair_alpha
standing += flower_overlay
+ if(("vox_quills" in H.dna.species.mutant_bodyparts) && !hair_hidden)
+ S = GLOB.vox_quills_list[H.dna.features["vox_quills"]]
+ if(S)
+ var/mutable_appearance/quills_overlay = mutable_appearance(layer = -HAIR_LAYER, appearance_flags = KEEP_TOGETHER)
+ var/mutable_appearance/quills_base = mutable_appearance(S.icon, S.icon_state)
+ quills_base.color = forced_colour || H.hair_color
+ if(S.color_blend_mode == COLOR_BLEND_ADD)
+ quills_base.color = COLOR_MATRIX_ADD(quills_base.color)
+ quills_overlay.overlays += quills_base
+ //Gradients
+ grad_style = H.grad_style
+ grad_color = H.grad_color
+ if(grad_style)
+ var/datum/sprite_accessory/gradient = GLOB.hair_gradients_list[grad_style]
+ var/mutable_appearance/gradient_quills = mutable_appearance(gradient.icon, gradient.icon_state)
+ gradient_quills.color = COLOR_MATRIX_OVERLAY(grad_color)
+ gradient_quills.blend_mode = BLEND_INSET_OVERLAY
+ quills_overlay.overlays += gradient_quills
+ quills_overlay.alpha = hair_alpha
+ standing += quills_overlay
if(standing.len)
H.overlays_standing[HAIR_LAYER] = standing
@@ -777,7 +814,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(parent_eyes)
eye_overlay += parent_eyes.generate_body_overlay(H)
else
- var/mutable_appearance/missing_eyes = mutable_appearance('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER)
+ var/mutable_appearance/missing_eyes = mutable_appearance(HD.eyes_icon, "eyes_missing", -BODY_LAYER)
if(OFFSET_FACE in offset_features)
missing_eyes.pixel_x += offset_features[OFFSET_FACE][1]
missing_eyes.pixel_y += offset_features[OFFSET_FACE][2]
@@ -794,22 +831,34 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(HAS_TRAIT(H, TRAIT_SKINNY))
standing += wear_skinny_version(underwear.icon_state, underwear.icon, BODY_LAYER) //Neat, this works
else
- standing += mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
+ var/mutable_appearance/underwear_overlay = mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
+ if(H.dna.species.id in underwear.sprite_sheets)
+ if(icon_exists(underwear.sprite_sheets[H.dna.species.id], underwear.icon_state))
+ underwear_overlay.icon = underwear.sprite_sheets[H.dna.species.id]
+ standing += underwear_overlay
if(H.undershirt)
var/datum/sprite_accessory/undershirt/undershirt = GLOB.undershirt_list[H.undershirt]
if(undershirt)
if(HAS_TRAIT(H, TRAIT_SKINNY)) //Check for skinny first
standing += wear_skinny_version(undershirt.icon_state, undershirt.icon, BODY_LAYER)
- else if(H.gender == FEMALE && (FEMALE in possible_genders))
+ else if((H.gender == FEMALE && (FEMALE in possible_genders)) && H.dna.species.is_dimorphic)
standing += wear_female_version(undershirt.icon_state, undershirt.icon, BODY_LAYER)
else
- standing += mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
+ var/mutable_appearance/undershirt_overlay = mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
+ if(H.dna.species.id in undershirt.sprite_sheets)
+ if(icon_exists(undershirt.sprite_sheets[H.dna.species.id], undershirt.icon_state))
+ undershirt_overlay.icon = undershirt.sprite_sheets[H.dna.species.id]
+ standing += undershirt_overlay
if(H.socks && H.get_num_legs(FALSE) >= 2 && !(DIGITIGRADE in species_traits))
var/datum/sprite_accessory/socks/socks = GLOB.socks_list[H.socks]
if(socks)
- standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
+ var/mutable_appearance/socks_overlay = mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
+ if(H.dna.species.id in socks.sprite_sheets)
+ if(icon_exists(socks.sprite_sheets[H.dna.species.id], socks.icon_state))
+ socks_overlay.icon = socks.sprite_sheets[H.dna.species.id]
+ standing += socks_overlay
if(standing.len)
H.overlays_standing[BODY_LAYER] = standing
@@ -930,10 +979,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEEYES)) || (H.head && (H.head.flags_inv & HIDEEYES)) || !HD)
bodyparts_to_add -= "preternis_eye"
+
if("preternis_core" in mutant_bodyparts)
if(!get_location_accessible(H, BODY_ZONE_CHEST))
bodyparts_to_add -= "preternis_core"
-
if("pod_hair" in mutant_bodyparts)
if((H.wear_mask && (H.wear_mask.flags_inv & HIDEHAIR)) || (H.head && (H.head.flags_inv & HIDEHAIR)) || !HD || HD.status == BODYPART_ROBOTIC)
bodyparts_to_add -= "pod_hair"
@@ -944,6 +993,26 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(H.dna.features["pod_flower"] != H.dna.features["pod_hair"])
H.dna.features["pod_flower"] = H.dna.features["pod_hair"]
+ if("vox_tail" in mutant_bodyparts)
+ if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
+ bodyparts_to_add -= "vox_tail"
+
+ if("wagging_vox_tail" in mutant_bodyparts)
+ if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
+ bodyparts_to_add -= "wagging_vox_tail"
+ else if ("vox_tail" in mutant_bodyparts)
+ bodyparts_to_add -= "wagging_vox_tail"
+
+ if("vox_tail_markings" in mutant_bodyparts)
+ if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
+ bodyparts_to_add -= "vox_tail_markings"
+
+ if("wagging_vox_tail_markings" in mutant_bodyparts)
+ if(!H.dna.features["vox_tail_markings"] || H.dna.features["vox_tail_markings"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
+ bodyparts_to_add -= "vox_tail_markings"
+ else if ("vox_tail" in mutant_bodyparts)
+ bodyparts_to_add -= "wagging_vox_tail_markings"
+
//Digitigrade legs are stuck in the phantom zone between true limbs and mutant bodyparts. Mainly it just needs more agressive updating than most limbs.
var/update_needed = FALSE
var/not_digitigrade = TRUE
@@ -982,7 +1051,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
H.update_body_parts()
if(not_digitigrade && (DIGITIGRADE in species_traits)) //Curse is lifted
species_traits -= DIGITIGRADE
-
if(!bodyparts_to_add)
return
@@ -1056,15 +1124,33 @@ GLOBAL_LIST_EMPTY(features_by_species)
S = GLOB.ipc_antennas_list[H.dna.features["ipc_antenna"]]
if("ipc_chassis")
S = GLOB.ipc_chassis_list[H.dna.features["ipc_chassis"]]
+ if("vox_tail")
+ var/obj/item/organ/tail/vox/vox_tail = H.getorganslot(ORGAN_SLOT_TAIL)
+ if(vox_tail && istype(vox_tail))
+ S = GLOB.vox_tails_list[vox_tail.tail_type]
+ if("wagging_vox_tail")
+ var/obj/item/organ/tail/vox/vox_tail = H.getorganslot(ORGAN_SLOT_TAIL)
+ if(vox_tail && istype(vox_tail))
+ S = GLOB.animated_vox_tails_list[vox_tail.tail_type]
+ if("vox_body_markings")
+ S = GLOB.vox_body_markings_list[H.dna.features["vox_body_markings"]]
+ if("vox_tail_markings")
+ var/obj/item/organ/tail/vox/vox_tail = H.getorganslot(ORGAN_SLOT_TAIL)
+ if(vox_tail && istype(vox_tail))
+ S = GLOB.vox_tail_markings_list[vox_tail.tail_markings]
+ if("wagging_vox_tail_markings")
+ var/obj/item/organ/tail/vox/vox_tail = H.getorganslot(ORGAN_SLOT_TAIL)
+ if(vox_tail && istype(vox_tail))
+ S = GLOB.animated_vox_tail_markings_list[vox_tail.tail_markings]
if(!S || S.icon_state == "none")
continue
var/mutable_appearance/accessory_overlay = mutable_appearance(S.icon, layer = -layer)
//A little rename so we don't have to use tail_lizard or tail_human when naming the sprites.
- if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "tail_polysmorph")
+ if(bodypart == "tail_lizard" || bodypart == "tail_human" || bodypart == "tail_polysmorph" || bodypart == "vox_tail")
bodypart = "tail"
- else if(bodypart == "waggingtail_lizard" || bodypart == "waggingtail_human")
+ else if(bodypart == "waggingtail_lizard" || bodypart == "waggingtail_human" || bodypart == "wagging_vox_tail")
bodypart = "waggingtail"
if(S.gender_specific)
@@ -1085,6 +1171,11 @@ GLOBAL_LIST_EMPTY(features_by_species)
accessory_overlay.color = fixed_mut_color
else //Then snowflake color
accessory_overlay.color = H.dna.features["mcolor"]
+ if(MUTCOLORS_SECONDARY)
+ if(fixed_mut_color)
+ accessory_overlay.color = fixed_mut_color
+ else
+ accessory_overlay.color = H.dna.features["mcolor_secondary"]
if(HAIR)
if(hair_color == "mutcolor")
accessory_overlay.color = H.dna.features["mcolor"]
@@ -1098,6 +1189,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
accessory_overlay.color = H.eye_color
else
accessory_overlay.color = forced_colour
+ if(S.color_blend_mode == COLOR_BLEND_ADD)
+ accessory_overlay.color = COLOR_MATRIX_ADD(accessory_overlay.color)
standing += accessory_overlay
if(S.emissive && !(HAS_TRAIT(H, TRAIT_HUSK)) && !istype(H, /mob/living/carbon/human/dummy))//don't put emissives on dummy mobs as they're used for the preference menu, which doesn't draw emissives properly
@@ -1136,6 +1229,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
emissive_accessory_overlay.color = forced_colour
standing += emissive_accessory_overlay
+ if(length(S.body_slots) || length(S.external_slots))
+ standing += return_accessory_layer(layer, S, H, accessory_overlay.color)
if(S.hasinner)
var/mutable_appearance/inner_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
if(S.gender_specific)
@@ -1147,7 +1242,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
inner_accessory_overlay = center_image(inner_accessory_overlay, S.dimension_x, S.dimension_y)
standing += inner_accessory_overlay
-
+ if(HAS_TRAIT(H, TRAIT_HUSK))
+ for(var/image/sprite_image as anything in standing)
+ huskify_image(sprite_image, H, draw_blood = FALSE)
+ sprite_image.color = H.dna.species.husk_color
H.overlays_standing[layer] = standing.Copy()
standing = list()
@@ -1155,7 +1253,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
H.apply_overlay(BODY_ADJ_LAYER)
H.apply_overlay(BODY_FRONT_LAYER)
-
//This exists so sprite accessories can still be per-layer without having to include that layer's
//number in their sprite name, which causes issues when those numbers change.
/datum/species/proc/mutant_bodyparts_layertext(layer)
@@ -2215,10 +2312,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(H.IsParalyzed() || H.IsStun())
return FALSE
// var/obj/item/organ/tail = H.getorganslot(ORGAN_SLOT_TAIL)
- return ("tail_human" in mutant_bodyparts) || ("waggingtail_human" in mutant_bodyparts) || ("tail_lizard" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts)
+ return ("tail_human" in mutant_bodyparts) || ("waggingtail_human" in mutant_bodyparts) || ("tail_lizard" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts) || ("vox_tail" in mutant_bodyparts) || ("wagging_vox_tail" in mutant_bodyparts)
/datum/species/proc/is_wagging_tail(mob/living/carbon/human/H)
- return ("waggingtail_human" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts)
+ return ("waggingtail_human" in mutant_bodyparts) || ("waggingtail_lizard" in mutant_bodyparts) || ("wagging_vox_tail" in mutant_bodyparts)
/datum/species/proc/start_wagging_tail(mob/living/carbon/human/H)
if("tail_human" in mutant_bodyparts)
@@ -2229,6 +2326,11 @@ GLOBAL_LIST_EMPTY(features_by_species)
mutant_bodyparts -= "spines"
mutant_bodyparts |= "waggingtail_lizard"
mutant_bodyparts |= "waggingspines"
+ if("vox_tail" in mutant_bodyparts)
+ mutant_bodyparts -= "vox_tail"
+ mutant_bodyparts -= "vox_tail_markings"
+ mutant_bodyparts |= "wagging_vox_tail"
+ mutant_bodyparts |= "wagging_vox_tail_markings"
H.update_body()
/datum/species/proc/stop_wagging_tail(mob/living/carbon/human/H)
@@ -2240,6 +2342,11 @@ GLOBAL_LIST_EMPTY(features_by_species)
mutant_bodyparts -= "waggingspines"
mutant_bodyparts |= "tail_lizard"
mutant_bodyparts |= "spines"
+ if("wagging_vox_tail" in mutant_bodyparts)
+ mutant_bodyparts |= "vox_tail"
+ mutant_bodyparts |= "vox_tail_markings"
+ mutant_bodyparts -= "wagging_vox_tail"
+ mutant_bodyparts -= "wagging_vox_tail_markings"
H.update_body()
///////////////
@@ -2362,6 +2469,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(HAS_BONE in species_traits)
. |= BIO_JUST_BONE
+/datum/species/proc/get_footprint_sprite()
+ return null
+
/datum/species/proc/eat_text(fullness, eatverb, obj/O, mob/living/carbon/C, mob/user)
. = TRUE
if(C == user)
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index f4b38a0eb575..8736b752f2a7 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -80,6 +80,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
target.dna.features["frills"] = "None"
target.dna.features["horns"] = "None"
target.dna.features["mcolor"] = COLOR_VIBRANT_LIME
+ target.dna.features["mcolor_secondary"] = COLOR_RED
target.dna.features["moth_antennae"] = "Plain"
target.dna.features["moth_markings"] = "None"
target.dna.features["moth_wings"] = "Plain"
@@ -88,6 +89,11 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
target.dna.features["tail_cat"] = "None"
target.dna.features["tail_lizard"] = "Smooth"
target.dna.features["pod_hair"] = "Ivy"
+ target.dna.features["vox_quills"] = "None"
+ target.dna.features["vox_facial_quills"] = "None"
+ target.dna.features["vox_skin_tone"] = "lime"
+ target.dna.features["vox_tail_markings"] = "None"
+ target.dna.features["vox_body_markings"] = "None"
/// Provides a dummy that is consistently bald, white, naked, etc.
/mob/living/carbon/human/dummy/consistent
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 9c9526e2924c..af39fdd088d5 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -420,7 +420,14 @@
/mob/living/carbon/human/get_footprint_sprite()
var/obj/item/bodypart/l_leg/left_leg = get_bodypart(BODY_ZONE_L_LEG)
var/obj/item/bodypart/r_leg/right_leg = get_bodypart(BODY_ZONE_R_LEG)
- return shoes?.footprint_sprite || left_leg?.footprint_sprite || right_leg?.footprint_sprite
+ var/species_id
+ var/datum/species/species
+ if(left_leg?.species_id == right_leg?.species_id)
+ species_id = left_leg.species_id
+ var/species_type = GLOB.species_list[species_id]
+ if(species_type)
+ species = new species_type()
+ return species?.get_footprint_sprite() || shoes?.footprint_sprite || left_leg?.footprint_sprite || right_leg?.footprint_sprite
/mob/living/carbon/human/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
if(judgement_criteria & JUDGE_EMAGGED)
@@ -685,6 +692,7 @@
if(creamed) //clean both to prevent a rare bug
cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_lizard"))
cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_human"))
+ cut_overlay(mutable_appearance('icons/effects/creampie.dmi', "creampie_vox"))
creamed = FALSE
//Turns a mob black, flashes a skeleton overlay
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 7ca856b5669c..7a676d6667e6 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -20,6 +20,7 @@
attack_verbs = list("slash", "scratch", "claw")
attack_effect = ATTACK_EFFECT_CLAW
barefoot_step_sound = FOOTSTEP_MOB_CLAW
+ creampie_id = "creampie_lizard"
attack_sound = 'sound/weapons/slash.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/lizard
diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
index 792a3f98f72c..acc4dde95a25 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -28,7 +28,8 @@
liked_food = DAIRY
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC
species_language_holder = /datum/language_holder/plasmaman
-
+ survival_box_replacements = list(items_to_delete = list(/obj/item/clothing/mask/breath, /obj/item/tank/internals/emergency_oxygen),\
+ new_items = list(/obj/item/tank/internals/plasmaman/belt))
screamsound = list('sound/voice/plasmaman/plasmeme_scream_1.ogg', 'sound/voice/plasmaman/plasmeme_scream_2.ogg', 'sound/voice/plasmaman/plasmeme_scream_3.ogg')
smells_like = "plasma-caked calcium"
diff --git a/code/modules/mob/living/carbon/human/status_procs.dm b/code/modules/mob/living/carbon/human/status_procs.dm
index d00c37605965..de86f33fcdcb 100644
--- a/code/modules/mob/living/carbon/human/status_procs.dm
+++ b/code/modules/mob/living/carbon/human/status_procs.dm
@@ -32,7 +32,7 @@
update_hair()
/mob/living/carbon/human/become_husk(source)
- if(NOHUSK in dna.species.species_traits)
+ if(NOHUSK in dna?.species?.species_traits)
cure_husk()
return
. = ..()
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 2991dbe2b0cc..a892565fa24f 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -155,17 +155,36 @@ There are several things that need to be remembered:
//Friendly reminder that icon_exists(file, state, scream = TRUE) is your friend when debugging this code.
var/icon_file
var/target_overlay = RESOLVE_ICON_STATE(uniform) //Selects proper icon from the vars the clothing has (Search define for more.)
-
+ var/obj/item/bodypart/l_leg = get_bodypart(BODY_ZONE_L_LEG)
+ var/obj/item/bodypart/r_leg = get_bodypart(BODY_ZONE_R_LEG)
+ var/obj/item/bodypart/chest/chest = get_bodypart(BODY_ZONE_CHEST)
+ uniform.species_fitted = null
if(uniform.adjusted == ALT_STYLE)
target_overlay = "[target_overlay]_d"
else if(uniform.adjusted == DIGITIGRADE_STYLE) // yogs - digitigrade alt sprites
target_overlay = "[target_overlay]_l"
else if(uniform.adjusted == DIGIALT_STYLE)
target_overlay = "[target_overlay]_d_l" // yogs end
+ //Checks for GAGS
+ if(uniform.greyscale_config && uniform.greyscale_colors)
+ if("GAGS_sprite" in uniform.sprite_sheets)
+ var/list/GAGS_species = uniform.sprite_sheets["GAGS_sprite"]
+ if((SPECIES_VOX in GAGS_species) && l_leg?.species_id == SPECIES_VOX && r_leg?.species_id == SPECIES_VOX)
+ target_overlay += "_vox"
+ uniform.species_fitted = SPECIES_VOX
+ if(l_leg?.species_id == SPECIES_VOX && (r_leg?.species_id == SPECIES_VOX))//for Vox, it's the Vox legs that make regular sprites not fit
+ if(SPECIES_VOX in uniform.sprite_sheets)
+ if(icon_exists(uniform.sprite_sheets[SPECIES_VOX], uniform.icon_state))
+ icon_file = uniform.sprite_sheets[SPECIES_VOX]
+ uniform.species_fitted = SPECIES_VOX
+ else if(chest?.species_id in uniform.sprite_sheets)
+ if(icon_exists(uniform.sprite_sheets[chest.species_id], uniform.icon_state))
+ icon_file = uniform.sprite_sheets[chest.species_id]
+ uniform.species_fitted = chest.species_id
var/mutable_appearance/uniform_overlay
- if(gender == FEMALE && uniform.fitted != NO_FEMALE_UNIFORM)
+ if((gender == FEMALE && dna.species.is_dimorphic) && uniform.fitted != NO_FEMALE_UNIFORM)
uniform_overlay = uniform.build_worn_icon(
default_layer = UNIFORM_LAYER,
default_icon_file = icon_file,
@@ -173,6 +192,7 @@ There are several things that need to be remembered:
femaleuniform = uniform.fitted,
override_state = target_overlay,
)
+
else
uniform_overlay = uniform.build_worn_icon(
default_layer = UNIFORM_LAYER,
@@ -223,13 +243,21 @@ There are several things that need to be remembered:
var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_GLOVES) + 1]
inv.update_appearance(UPDATE_ICON)
+ var/obj/item/bodypart/l_arm = get_bodypart(BODY_ZONE_L_ARM)
+ var/obj/item/bodypart/r_arm = get_bodypart(BODY_ZONE_R_ARM)
if(!gloves && blood_in_hands)
var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER)
+ if(icon_exists(bloody_overlay.icon, "[bloody_overlay.icon_state]_[l_arm?.species_id]"))
+ bloody_overlay.icon_state = "bloodyhands_[l_arm.species_id]"
if(get_num_arms(FALSE) < 2)
if(has_left_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_left"
+ if(icon_exists(bloody_overlay.icon, "[bloody_overlay.icon_state]_[l_arm?.species_id]"))
+ bloody_overlay.icon_state += "_[l_arm.species_id]"
else if(has_right_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_right"
+ if(icon_exists(bloody_overlay.icon, "[bloody_overlay.icon_state]_[r_arm?.species_id]"))
+ bloody_overlay.icon_state += "_[r_arm.species_id]"
bloody_overlay.color = get_blood_dna_color(return_blood_DNA())
overlays_standing[GLOVES_LAYER] = bloody_overlay
@@ -241,7 +269,14 @@ There are several things that need to be remembered:
if(hud_used.inventory_shown)
client.screen += gloves
update_observer_view(gloves,1)
- overlays_standing[GLOVES_LAYER] = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = 'icons/mob/clothing/hands/hands.dmi')
+ var/icon_to_use = 'icons/mob/clothing/hands/hands.dmi'
+ gloves.species_fitted = null
+ if(l_arm?.species_id == r_arm?.species_id)
+ if(l_arm.species_id in gloves.sprite_sheets)
+ if(icon_exists(gloves.sprite_sheets[l_arm.species_id], gloves.icon_state))
+ icon_to_use = gloves.sprite_sheets[l_arm.species_id]
+ gloves.species_fitted = l_arm.species_id
+ overlays_standing[GLOVES_LAYER] = gloves.build_worn_icon(default_layer = GLOVES_LAYER, default_icon_file = icon_to_use)
gloves_overlay = overlays_standing[GLOVES_LAYER]
if(OFFSET_GLOVES in dna.species.offset_features)
gloves_overlay.pixel_x += dna.species.offset_features[OFFSET_GLOVES][1]
@@ -253,7 +288,8 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_glasses()
remove_overlay(GLASSES_LAYER)
- if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
+ var/obj/item/bodypart/head/head = get_bodypart(BODY_ZONE_HEAD)
+ if(!head) //decapitated
return
if(client && hud_used)
@@ -267,7 +303,13 @@ There are several things that need to be remembered:
client.screen += glasses //Either way, add the item to the HUD
update_observer_view(glasses,1)
if(!(head && (head.flags_inv & HIDEEYES)) && !(wear_mask && (wear_mask.flags_inv & HIDEEYES)))
- overlays_standing[GLASSES_LAYER] = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = 'icons/mob/clothing/eyes/eyes.dmi')
+ var/icon_to_use = 'icons/mob/clothing/eyes/eyes.dmi'
+ glasses.species_fitted = null
+ if(head.species_id in glasses.sprite_sheets)
+ if(icon_exists(glasses.sprite_sheets[head.species_id], glasses.icon_state))
+ icon_to_use = glasses.sprite_sheets[head.species_id]
+ glasses.species_fitted = head.species_id
+ overlays_standing[GLASSES_LAYER] = glasses.build_worn_icon(default_layer = GLASSES_LAYER, default_icon_file = icon_to_use)
var/mutable_appearance/glasses_overlay = overlays_standing[GLASSES_LAYER]
if(glasses_overlay)
@@ -281,7 +323,8 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_ears()
remove_overlay(EARS_LAYER)
- if(!get_bodypart(BODY_ZONE_HEAD)) //decapitated
+ var/obj/item/bodypart/head/head = get_bodypart(BODY_ZONE_HEAD)
+ if(!head) //decapitated
return
if(client && hud_used)
@@ -294,7 +337,13 @@ There are several things that need to be remembered:
if(hud_used.inventory_shown) //if the inventory is open
client.screen += ears //add it to the client's screen
update_observer_view(ears,1)
- overlays_standing[EARS_LAYER] = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = 'icons/mob/clothing/ears/ears.dmi')
+ var/icon_to_use = 'icons/mob/clothing/ears/ears.dmi'
+ ears.species_fitted = null
+ if(head.species_id in ears.sprite_sheets)
+ if(icon_exists(ears.sprite_sheets[head.species_id], ears.icon_state))
+ icon_to_use = ears.sprite_sheets[head.species_id]
+ ears.species_fitted = head.species_id
+ overlays_standing[EARS_LAYER] = ears.build_worn_icon(default_layer = EARS_LAYER, default_icon_file = icon_to_use)
var/mutable_appearance/ears_overlay = overlays_standing[EARS_LAYER]
if(OFFSET_EARS in dna.species.offset_features)
ears_overlay.pixel_x += dna.species.offset_features[OFFSET_EARS][1]
@@ -336,16 +385,29 @@ There are several things that need to be remembered:
if(shoes)
var/target_overlay = RESOLVE_ICON_STATE(shoes)
+ var/obj/item/bodypart/l_leg = get_bodypart(BODY_ZONE_L_LEG)
+ var/obj/item/bodypart/r_leg = get_bodypart(BODY_ZONE_R_LEG)
+ shoes.species_fitted = null
if(istype(shoes, /obj/item/clothing/shoes))
var/obj/item/clothing/shoes/S = shoes
if(S.adjusted == DIGITIGRADE_STYLE)
target_overlay = "[target_overlay]_l"
+ if("GAGS_sprite" in S.sprite_sheets)
+ var/list/GAGS_species = S.sprite_sheets["GAGS_sprite"]
+ if((l_leg?.species_id == r_leg?.species_id) && (l_leg.species_id in GAGS_species))
+ target_overlay += "_[l_leg.species_id]"
+ S.species_fitted = l_leg.species_id
shoes.screen_loc = ui_shoes //move the item to the appropriate screen loc
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown) //if the inventory is open
client.screen += shoes //add it to client's screen
update_observer_view(shoes,1)
- overlays_standing[SHOES_LAYER] = shoes.build_worn_icon(default_layer = SHOES_LAYER, default_icon_file = 'icons/mob/clothing/feet/feet.dmi', override_state = target_overlay)
+ var/icon_to_use = DEFAULT_SHOES_FILE
+ if((l_leg?.species_id == r_leg?.species_id) && (l_leg.species_id in shoes.sprite_sheets))
+ if(icon_exists(shoes.sprite_sheets[l_leg.species_id], shoes.icon_state))
+ icon_to_use = shoes.sprite_sheets[l_leg.species_id]
+ shoes.species_fitted = l_leg.species_id
+ overlays_standing[SHOES_LAYER] = shoes.build_worn_icon(default_layer = SHOES_LAYER, default_icon_file = icon_to_use, override_state = target_overlay)
var/mutable_appearance/shoes_overlay = overlays_standing[SHOES_LAYER]
if(OFFSET_SHOES in dna.species.offset_features)
shoes_overlay.pixel_x += dna.species.offset_features[OFFSET_SHOES][1]
@@ -380,18 +442,25 @@ There are several things that need to be remembered:
/mob/living/carbon/human/update_inv_head()
- ..()
- update_mutant_bodyparts()
+ remove_overlay(HEAD_LAYER)
+ if(client && hud_used?.inv_slots[TOBITSHIFT(ITEM_SLOT_BACK) + 1])
+ var/atom/movable/screen/inventory/inv = hud_used.inv_slots[TOBITSHIFT(ITEM_SLOT_HEAD) + 1]
+ inv.update_appearance(UPDATE_ICON)
if(head)
update_hud_head(head)
- overlays_standing[HEAD_LAYER] = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = 'icons/mob/clothing/head/head.dmi')
- var/mutable_appearance/head_overlay = overlays_standing[HEAD_LAYER]
- if(head_overlay)
- remove_overlay(HEAD_LAYER)
+ var/obj/item/bodypart/head/head_bodypart = get_bodypart(BODY_ZONE_HEAD)
+ var/icon_to_use = 'icons/mob/clothing/head/head.dmi'
+ head.species_fitted = null
+ if(head_bodypart?.species_id in head.sprite_sheets)
+ if(icon_exists(head.sprite_sheets[head_bodypart.species_id], head.icon_state))
+ icon_to_use = head.sprite_sheets[head_bodypart.species_id]
+ head.species_fitted = head_bodypart.species_id
+ overlays_standing[HEAD_LAYER] = head.build_worn_icon(default_layer = HEAD_LAYER, default_icon_file = icon_to_use)
+ var/mutable_appearance/head_overlay = overlays_standing[HEAD_LAYER]
if(OFFSET_HEAD in dna.species.offset_features)
head_overlay.pixel_x += dna.species.offset_features[OFFSET_HEAD][1]
head_overlay.pixel_y += dna.species.offset_features[OFFSET_HEAD][2]
- overlays_standing[HEAD_LAYER] = head_overlay
+ update_mutant_bodyparts()
apply_overlay(HEAD_LAYER)
/mob/living/carbon/human/update_inv_belt()
@@ -406,7 +475,14 @@ There are several things that need to be remembered:
if(client && hud_used && hud_used.hud_shown)
client.screen += belt
update_observer_view(belt)
- overlays_standing[BELT_LAYER] = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = 'icons/mob/clothing/belt.dmi')
+ var/obj/item/bodypart/chest/chest = get_bodypart(BODY_ZONE_CHEST)
+ var/icon_to_use = 'icons/mob/clothing/belt.dmi'
+ belt.species_fitted = null
+ if(chest?.species_id in belt.sprite_sheets)
+ if(icon_exists(belt.sprite_sheets[chest.species_id], belt.icon_state))
+ icon_to_use = belt.sprite_sheets[chest.species_id]
+ belt.species_fitted = chest.species_id
+ overlays_standing[BELT_LAYER] = belt.build_worn_icon(default_layer = BELT_LAYER, default_icon_file = icon_to_use)
var/mutable_appearance/belt_overlay = overlays_standing[BELT_LAYER]
if(OFFSET_BELT in dna.species.offset_features)
belt_overlay.pixel_x += dna.species.offset_features[OFFSET_BELT][1]
@@ -433,7 +509,20 @@ There are several things that need to be remembered:
if(client && hud_used && hud_used.hud_shown)
if(hud_used.inventory_shown)
client.screen += wear_suit
- overlays_standing[SUIT_LAYER] = wear_suit.build_worn_icon(default_layer = SUIT_LAYER, default_icon_file = 'icons/mob/clothing/suit/suit.dmi', override_state = worn_suit_icon)
+ var/obj/item/bodypart/chest/chest = get_bodypart(BODY_ZONE_CHEST)
+ var/icon_to_use = DEFAULT_SUIT_FILE
+ S.species_fitted = null
+ var/obj/item/bodypart/l_leg = get_bodypart(BODY_ZONE_L_LEG)
+ var/obj/item/bodypart/r_leg = get_bodypart(BODY_ZONE_R_LEG)
+ if(l_leg?.species_id == r_leg?.species_id == SPECIES_VOX)//for Vox, it's the Vox legs that make regular sprites not fit
+ if(icon_exists(S.sprite_sheets[l_leg.species_id], S.icon_state))
+ icon_to_use = S.sprite_sheets[l_leg.species_id]
+ S.species_fitted = l_leg.species_id
+ else if(chest?.species_id in S.sprite_sheets)
+ if(icon_exists(S.sprite_sheets[chest.species_id], S.icon_state))
+ icon_to_use = S.sprite_sheets[chest.species_id]
+ S.species_fitted = chest.species_id
+ overlays_standing[SUIT_LAYER] = wear_suit.build_worn_icon(default_layer = SUIT_LAYER, default_icon_file = icon_to_use, override_state = worn_suit_icon)
var/mutable_appearance/suit_overlay = overlays_standing[SUIT_LAYER]
if(OFFSET_SUIT in dna.species.offset_features)
suit_overlay.pixel_x += dna.species.offset_features[OFFSET_SUIT][1]
@@ -487,7 +576,14 @@ There are several things that need to be remembered:
target_overlay = "[target_overlay]_l"
update_hud_wear_mask(wear_mask)
if(!(head && (head.flags_inv & HIDEMASK)))
- overlays_standing[FACEMASK_LAYER] = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = 'icons/mob/clothing/mask/mask.dmi', override_state = target_overlay)
+ var/obj/item/bodypart/head/head_bodypart = get_bodypart(BODY_ZONE_HEAD)
+ var/icon_to_use = 'icons/mob/clothing/mask/mask.dmi'
+ wear_mask.species_fitted = null
+ if(head_bodypart.species_id in wear_mask.sprite_sheets)
+ if(icon_exists(wear_mask.sprite_sheets[head_bodypart.species_id], wear_mask.icon_state))
+ icon_to_use = wear_mask.sprite_sheets[head_bodypart.species_id]
+ wear_mask.species_fitted = head_bodypart.species_id
+ overlays_standing[FACEMASK_LAYER] = wear_mask.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = icon_to_use, override_state = target_overlay)
var/mutable_appearance/mask_overlay = overlays_standing[FACEMASK_LAYER]
if(mask_overlay)
remove_overlay(FACEMASK_LAYER)
@@ -507,7 +603,14 @@ There are several things that need to be remembered:
if(back)
update_hud_back(back)
- overlays_standing[BACK_LAYER] = back.build_worn_icon(default_layer = BACK_LAYER, default_icon_file = 'icons/mob/clothing/back.dmi')
+ var/obj/item/bodypart/chest/chest = get_bodypart(BODY_ZONE_CHEST)
+ var/icon_to_use = 'icons/mob/clothing/back.dmi'
+ back.species_fitted = null
+ if(chest?.species_id in back.sprite_sheets)
+ if(icon_exists(back.sprite_sheets[chest.species_id], back.icon_state))
+ icon_to_use = back.sprite_sheets[chest.species_id]
+ back.species_fitted = chest.species_id
+ overlays_standing[BACK_LAYER] = back.build_worn_icon(default_layer = BACK_LAYER, default_icon_file = icon_to_use)
var/mutable_appearance/back_overlay = overlays_standing[BACK_LAYER]
if(back_overlay)
remove_overlay(BACK_LAYER)
@@ -614,12 +717,16 @@ generate/load female uniform sprites matching all previously decided variables
t_state = !isinhands ? (worn_icon_state ? worn_icon_state : icon_state) : (item_state ? item_state : icon_state)
//Find a valid icon file from variables+arguments
- var/file2use = !isinhands ? (worn_icon ? worn_icon : default_icon_file) : default_icon_file
+ var/file2use = default_icon_file
+ if(!isinhands && worn_icon)
+ if(!species_fitted || (species_fitted && greyscale_config))
+ file2use = worn_icon
//Find a valid layer from variables+arguments
var/layer2use = alternate_worn_layer ? alternate_worn_layer : default_layer
var/mob/living/carbon/human/H = loc
+
var/mutable_appearance/standing
if(femaleuniform)
if(HAS_TRAIT(H, TRAIT_SKINNY) && (H.underwear == "Nude"))
@@ -680,12 +787,15 @@ generate/load female uniform sprites matching all previously decided variables
. += "-coloured-[dna.species.forced_skintone]"
else if(dna.species.fixed_mut_color)
. += "-coloured-[dna.species.fixed_mut_color]"
+ else if(dna.species.get_icon_variant(src))
+ . += "-limb-variant-[dna.species.get_icon_variant(src)]"
else if(dna.features["mcolor"])
. += "-coloured-[dna.features["mcolor"]]"
else
. += "-not_coloured"
- . += "-[gender]"
+ if(dna.species.is_dimorphic)
+ . += "-[gender]"
for(var/X in bodyparts)
var/obj/item/bodypart/BP = X
@@ -701,6 +811,11 @@ generate/load female uniform sprites matching all previously decided variables
. += "-digitigrade[BP.use_digitigrade]"
if(BP.dmg_overlay_type)
. += "-[BP.dmg_overlay_type]"
+ if(BP.has_static_sprite_part)
+ var/static_text = "-static"
+ if(BP.limb_icon_variant in dna.species.get_special_statics())
+ static_text += "-special-[BP.limb_icon_variant]"
+ . += static_text
if(HAS_TRAIT(src, TRAIT_HUSK))
. += "-husk"
@@ -772,3 +887,4 @@ generate/load female uniform sprites matching all previously decided variables
update_inv_wear_mask()
#undef RESOLVE_ICON_STATE
+
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index 7bcec196672f..2e1cfeaeb9c4 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -102,7 +102,7 @@
/obj/item/reagent_containers/blood/random/Initialize(mapload)
icon_state = "bloodpack"
- blood_type = pick("A+", "A-", "B+", "B-", "O+", "O-", "L")
+ blood_type = pick("A+", "A-", "B+", "B-", "O+", "O-", "L", "V")
return ..()
/obj/item/reagent_containers/blood/APlus
diff --git a/code/modules/research/designs/limbgrower_designs.dm b/code/modules/research/designs/limbgrower_designs.dm
index ce5690cd7e5e..9ea17d900168 100644
--- a/code/modules/research/designs/limbgrower_designs.dm
+++ b/code/modules/research/designs/limbgrower_designs.dm
@@ -8,7 +8,7 @@
build_type = LIMBGROWER
reagents_list = list(/datum/reagent/medicine/synthflesh = 25)
build_path = /obj/item/bodypart/l_arm
- category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph")
+ category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph","vox")
/datum/design/rightarm
name = "Right Arm"
@@ -16,7 +16,7 @@
build_type = LIMBGROWER
reagents_list = list(/datum/reagent/medicine/synthflesh = 25)
build_path = /obj/item/bodypart/r_arm
- category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph")
+ category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph","vox")
/datum/design/leftleg
name = "Left Leg"
@@ -24,7 +24,7 @@
build_type = LIMBGROWER
reagents_list = list(/datum/reagent/medicine/synthflesh = 25)
build_path = /obj/item/bodypart/l_leg
- category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph")
+ category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph","vox")
/datum/design/rightleg
name = "Right Leg"
@@ -32,7 +32,7 @@
build_type = LIMBGROWER
reagents_list = list(/datum/reagent/medicine/synthflesh = 25)
build_path = /obj/item/bodypart/r_leg
- category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph")
+ category = list("initial","human","lizard","fly","moth","plasmaman","polysmorph","vox")
/datum/design/digi_leftleg
name = "Digitigrade Left Leg"
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 326707ee031c..85cd95a2eb98 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -78,7 +78,7 @@
display_name = "Xeno-organ Biology"
description = "Plasmaman, Ethereals, Lizardpeople... What makes our non-human crewmembers tick?"
prereq_ids = list("adv_biotech")
- design_ids = list("limbdesign_felinid", "limbdesign_lizard", "limbdesign_plasmaman", "limbdesign_ethereal", "limbdesign_polysmorph")
+ design_ids = list("limbdesign_felinid", "limbdesign_lizard", "limbdesign_plasmaman", "limbdesign_ethereal", "limbdesign_polysmorph", "limbdesign_vox")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
/datum/techweb_node/bio_process
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 579543f086c6..d284bd5e9632 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -824,13 +824,18 @@
no_update = TRUE
else
no_update = FALSE
-
if(HAS_TRAIT(C, TRAIT_HUSK) && is_organic_limb())
if(ishuman(C))
var/mob/living/carbon/human/S = C
if(isszlachta(S))
return
- species_id = "husk" //overrides species_id
+ var/datum/species/id_to_species
+ var/species_type = GLOB.species_list[species_id]
+ if(species_type)
+ id_to_species = new species_type()
+ if(id_to_species && !id_to_species.generate_husk_icon)
+ species_id = "husk" //overrides species_id
+
dmg_overlay_type = "" //no damage overlay shown when husked
should_draw_gender = FALSE
should_draw_greyscale = FALSE
@@ -846,6 +851,14 @@
var/datum/species/S = H.dna.species
if(!limb_override)
species_id = S.limbs_id
+ var/datum/species/id_to_species
+ var/species_type = GLOB.species_list[species_id]
+ if(species_type)
+ id_to_species = new species_type()
+ if(id_to_species && (body_zone in id_to_species.static_part_body_zones))
+ has_static_sprite_part = TRUE
+ else
+ has_static_sprite_part = FALSE
species_flags_list = S.species_traits
if(S.use_skintones)
@@ -859,10 +872,13 @@
body_gender = H.gender
- should_draw_gender = (FEMALE in S.possible_genders)
+ if(S.is_dimorphic)
+ should_draw_gender = (FEMALE in S.possible_genders)
+ limb_icon_variant = S.get_icon_variant(H)
+ limb_icon_file = S.limb_icon_file
use_damage_color = S.use_damage_color
- if((MUTCOLORS in S.species_traits) || (DYNCOLORS in S.species_traits))
+ if((MUTCOLORS in S.species_traits) && !limb_icon_variant || (DYNCOLORS in S.species_traits))
if(S.fixed_mut_color)
species_color = S.fixed_mut_color
else
@@ -918,6 +934,7 @@
. += image('icons/mob/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_0[burnstate]", -DAMAGE_LAYER, image_dir)
var/image/limb = image(layer = -BODYPARTS_LAYER, dir = image_dir)
+ var/image/limb_static
var/image/aux
. += limb
@@ -934,8 +951,11 @@
return
var/icon_gender = (body_gender == FEMALE) ? "f" : "m" //gender of the icon, if applicable
-
- if((body_zone != BODY_ZONE_HEAD && body_zone != BODY_ZONE_CHEST))
+ var/datum/species/id_to_species
+ var/species_type = GLOB.species_list[species_id]
+ if(species_type)
+ id_to_species = new species_type()
+ if(((body_zone != BODY_ZONE_HEAD && body_zone != BODY_ZONE_CHEST )) || (id_to_species && !id_to_species.is_dimorphic))
should_draw_gender = FALSE
if(status == BODYPART_ORGANIC || (status == BODYPART_ROBOTIC && render_like_organic == TRUE)) // So IPC augments can be colorful without disrupting normal BODYPART_ROBOTIC render code.
@@ -953,15 +973,25 @@
else
limb.icon_state = "[species_id]_[body_zone]"
else
- limb.icon = 'yogstation/icons/mob/human_parts.dmi' // yogs -- use yogs icon instead of tg, gorilla people
+ limb.icon = limb_icon_file || 'yogstation/icons/mob/human_parts.dmi' // yogs -- use yogs icon instead of tg, gorilla people
if(should_draw_gender)
limb.icon_state = "[species_id]_[body_zone]_[icon_gender]"
else
limb.icon_state = "[species_id]_[body_zone]"
+ if(limb_icon_variant)
+ limb.icon_state += "_[limb_icon_variant]"
if(aux_zone)
- aux = image(limb.icon, "[species_id]_[aux_zone]", -aux_layer, image_dir)
+ var/aux_icon_name = "[species_id]_[aux_zone]_[limb_icon_variant]"
+ if(!icon_exists(limb.icon, "[aux_icon_name]"))
+ aux_icon_name = "[species_id]_[aux_zone]"
+ aux = image(limb.icon, aux_icon_name, -aux_layer, image_dir)
. += aux
-
+ if(has_static_sprite_part)
+ var/limb_static_icon_name = "[species_id]_[body_zone]_static"
+ if(id_to_species && (limb_icon_variant in id_to_species.get_special_statics()))
+ limb_static_icon_name += "_[limb_icon_variant]"
+ limb_static = image(limb.icon, limb_static_icon_name, limb.layer, limb.dir)
+ . += limb_static
else
limb.icon = icon
if(should_draw_gender)
@@ -977,13 +1007,35 @@
. += aux
return
-
+ var/draw_color
if(should_draw_greyscale)
- var/draw_color = mutation_color || species_color || (skin_tone && skintone2hex(skin_tone))
- if(draw_color)
- limb.color = "[draw_color]"
- if(aux_zone)
- aux.color = "[draw_color]"
+ draw_color = mutation_color || species_color || (skin_tone && skintone2hex(skin_tone))
+
+ if(status == BODYPART_ORGANIC || (status == BODYPART_ROBOTIC && render_like_organic == TRUE))
+ if(id_to_species && id_to_species.generate_husk_icon && owner && HAS_TRAIT(owner, TRAIT_HUSK))
+ huskify_image(limb, owner, TRUE, id_to_species)
+ if(aux)
+ huskify_image(aux, owner, TRUE, id_to_species)
+ if(limb_static)
+ huskify_image(limb_static, owner, TRUE, id_to_species)
+ draw_color = id_to_species.husk_color
+
+ if(draw_color)
+ limb.color = "[draw_color]"
+ if(aux_zone)
+ aux.color = "[draw_color]"
+
+/proc/huskify_image(image/thing_to_husk, mob/living/carbon/husked_guy, draw_blood = TRUE, datum/species/passed_species)
+ var/husk_color_mod = rgb(96, 88, 80)
+ var/icon/husk_icon = new(thing_to_husk.icon)
+ husk_icon.ColorTone(husk_color_mod)
+ thing_to_husk.icon = husk_icon
+ var/icon_of_husk = husked_guy?.dna?.species?.icon_husk || passed_species?.icon_husk
+ if(draw_blood)
+ var/mutable_appearance/husk_blood = mutable_appearance(icon_of_husk || 'yogstation/icons/mob/human_parts.dmi', "overlay_[husked_guy?.dna?.species?.id || passed_species?.id]husk", appearance_flags = RESET_COLOR)
+ husk_blood.blend_mode = BLEND_INSET_OVERLAY
+ husk_blood.dir = thing_to_husk.dir
+ thing_to_husk.add_overlay(husk_blood)
/obj/item/bodypart/deconstruct(disassembled = TRUE)
drop_organs()
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index a4de79fd173b..99e86f6a7b66 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -22,7 +22,7 @@
var/obj/item/organ/eyes/eyes
var/obj/item/organ/ears/ears
var/obj/item/organ/tongue/tongue
-
+ var/eyes_icon = 'icons/mob/human_face.dmi'
//Limb appearance info:
var/real_name = "" //Replacement name
//Hair colour and style
@@ -190,6 +190,10 @@
else
hair_color = "000"
hair_alpha = initial(hair_alpha)
+ if(HAIRCOLOR in S.species_traits)
+ hair_color = H.hair_color
+ if(FACEHAIRCOLOR in S.species_traits)
+ facial_hair_color = H.facial_hair_color
// lipstick
if(H.lip_style && (LIPS in S.species_traits))
lip_style = H.lip_style
@@ -197,6 +201,9 @@
else
lip_style = null
lip_color = "white"
+ if(S.eyes_icon)
+ eyes_icon = S.eyes_icon
+ eyes_static = S.get_eyes_static(H)
..()
/obj/item/bodypart/head/update_icon_dropped()
@@ -253,13 +260,16 @@
. += lips_overlay
// eyes
- var/image/eyes_overlay = image('icons/mob/human_face.dmi', "eyes_missing", -BODY_LAYER, SOUTH)
+ var/image/eyes_overlay = image(eyes_icon, "eyes_missing", -BODY_LAYER, SOUTH)
. += eyes_overlay
if(eyes)
eyes_overlay.icon_state = eyes.eye_icon_state
-
if(eyes.eye_color)
eyes_overlay.color = eyes.eye_color
+ if(eyes_static)
+ var/mutable_appearance/eyes_static_sprite = mutable_appearance(eyes_overlay.icon, "[eyes_overlay.icon_state]_static_[eyes_static]", eyes_overlay.layer, appearance_flags = RESET_COLOR)
+ eyes_static_sprite.dir = eyes_overlay.dir
+ eyes_overlay.add_overlay(eyes_static_sprite)
/obj/item/bodypart/head/monkey
icon = 'icons/mob/animal_parts.dmi'
diff --git a/code/modules/surgery/organs/appendix.dm b/code/modules/surgery/organs/appendix.dm
index e45511648e94..8c0baea181ce 100644
--- a/code/modules/surgery/organs/appendix.dm
+++ b/code/modules/surgery/organs/appendix.dm
@@ -13,16 +13,16 @@
/obj/item/organ/appendix/update_name(updates=ALL)
. = ..()
if(inflamed)
- name = "inflamed appendix"
+ name = "inflamed [initial(name)]"
else
- name = "appendix"
+ name = initial(name)
/obj/item/organ/appendix/update_icon_state()
. = ..()
if(inflamed)
- icon_state = "appendixinflamed"
+ icon_state = "[initial(icon_state)]inflamed"
else
- icon_state = "appendix"
+ icon_state = initial(icon_state)
/obj/item/organ/appendix/on_life()
..()
diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm
index 468ccea83bf2..7c8af8cdc31a 100644
--- a/code/modules/surgery/organs/eyes.dm
+++ b/code/modules/surgery/organs/eyes.dm
@@ -94,16 +94,20 @@
/obj/item/organ/eyes/proc/generate_body_overlay(mob/living/carbon/human/parent)
if(!istype(parent) || parent.getorgan(/obj/item/organ/eyes) != src)
CRASH("Generating a body overlay for [src] targeting an invalid parent '[parent]'.")
-
- var/mutable_appearance/eye_overlay = mutable_appearance('icons/mob/human_face.dmi', eye_icon_state, -BODY_LAYER)
+ var/obj/item/bodypart/head/head = parent.get_bodypart(BODY_ZONE_HEAD)
+ var/mutable_appearance/eye_overlay = mutable_appearance(head.eyes_icon, eye_icon_state, -BODY_LAYER)
var/list/overlays = list(eye_overlay)
if((EYECOLOR in parent.dna.species.species_traits))
eye_overlay.color = eye_color
+ if(head.eyes_static)
+ var/mutable_appearance/eyes_static_sprite = mutable_appearance(eye_overlay.icon, "[eye_overlay.icon_state]_static_[head.eyes_static]", eye_overlay.layer, appearance_flags = RESET_COLOR)
+ eye_overlay.add_overlay(eyes_static_sprite)
+
// Cry emote overlay
- if (HAS_TRAIT(parent, TRAIT_CRYING)) // Caused by the *cry emote
- var/mutable_appearance/tears_overlay = mutable_appearance('icons/mob/human_face.dmi', "tears", -BODY_ADJ_LAYER)
+ if(HAS_TRAIT(parent, TRAIT_CRYING)) // Caused by the *cry emote
+ var/mutable_appearance/tears_overlay = mutable_appearance(head.eyes_icon, "tears", -BODY_ADJ_LAYER)
tears_overlay.color = COLOR_DARK_CYAN
overlays += tears_overlay
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index 35d93c8863de..526b2cb6046f 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -117,6 +117,17 @@
alert_type = alert["alert_type"]
if(alert_category)
H.throw_alert(alert_category, alert_type)
+ var/list/too_much_gas_alerts = list()
+ for(var/gas in gas_max)
+ var/gas_alert_category
+ if(ispath(gas))
+ var/datum/breathing_class/breathclass = gas
+ gas_alert_category = breathclass.high_alert_category
+ else
+ gas_alert_category = GLOB.gas_data.breath_alert_info[gas]["too_much_alert"]["alert_category"]
+ too_much_gas_alerts += gas_alert_category
+ for(var/alert as anything in too_much_gas_alerts)
+ H.clear_alert(alert)
return FALSE
#define PP_MOLES(X) ((X / total_moles) * pressure)
diff --git a/code/modules/surgery/organs/tongue.dm b/code/modules/surgery/organs/tongue.dm
index 92b50110451d..3db5fb0b582a 100644
--- a/code/modules/surgery/organs/tongue.dm
+++ b/code/modules/surgery/organs/tongue.dm
@@ -29,6 +29,7 @@
/datum/language/japanese,
/datum/language/machine, //yogs
/datum/language/darkspawn, //also yogs
+ /datum/language/vox,
/datum/language/encrypted,
/datum/language/felinid,
/datum/language/english,
diff --git a/config/game_options.txt b/config/game_options.txt
index 3d919ac80891..e8d5a9a5c87b 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -19,10 +19,10 @@ REVIVAL_BRAIN_LIFE -1
JOB_SPECIES_WHITELIST /datum/job/captain human
JOB_SPECIES_WHITELIST /datum/job/hop human,lizard,plasmaman,ipc
-JOB_SPECIES_WHITELIST /datum/job/hos human,lizard,pod,preternis,polysmorph
-JOB_SPECIES_WHITELIST /datum/job/chief_engineer human,lizard,pod,plasmaman,moth,ethereal,preternis,polysmorph,ipc
-JOB_SPECIES_WHITELIST /datum/job/rd human,lizard,pod,plasmaman,moth,ethereal,preternis,polysmorph,ipc
-JOB_SPECIES_WHITELIST /datum/job/cmo human,lizard,pod,plasmaman,moth,ethereal,preternis,polysmorph,ipc
+JOB_SPECIES_WHITELIST /datum/job/hos human,lizard,pod,preternis,polysmorph,vox
+JOB_SPECIES_WHITELIST /datum/job/chief_engineer human,lizard,pod,plasmaman,moth,ethereal,preternis,polysmorph,ipc,vox
+JOB_SPECIES_WHITELIST /datum/job/rd human,lizard,pod,plasmaman,moth,ethereal,preternis,polysmorph,ipc,vox
+JOB_SPECIES_WHITELIST /datum/job/cmo human,lizard,pod,plasmaman,moth,ethereal,preternis,polysmorph,ipc,vox
## OOC DURING ROUND ###
@@ -562,6 +562,7 @@ ROUNDSTART_RACES preternis
ROUNDSTART_RACES polysmorph
#ROUNDSTART_RACES snail
ROUNDSTART_RACES ipc
+ROUNDSTART_RACES vox
## Races that are better than humans in some ways, but worse in others
ROUNDSTART_RACES ethereal
diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi
index 483ab0444233..63b8a39a1190 100644
Binary files a/icons/effects/blood.dmi and b/icons/effects/blood.dmi differ
diff --git a/icons/effects/crayondecal.dmi b/icons/effects/crayondecal.dmi
index c9e7f880c809..d1f9461a2043 100755
Binary files a/icons/effects/crayondecal.dmi and b/icons/effects/crayondecal.dmi differ
diff --git a/icons/effects/creampie.dmi b/icons/effects/creampie.dmi
index 6caecf7d767c..bdb39e5698ff 100644
Binary files a/icons/effects/creampie.dmi and b/icons/effects/creampie.dmi differ
diff --git a/icons/misc/language.dmi b/icons/misc/language.dmi
index 86e8a0aeacb4..188d9b834227 100644
Binary files a/icons/misc/language.dmi and b/icons/misc/language.dmi differ
diff --git a/icons/mob/butts.dmi b/icons/mob/butts.dmi
index ae4b41961a1c..fb14886ba10a 100644
Binary files a/icons/mob/butts.dmi and b/icons/mob/butts.dmi differ
diff --git a/icons/mob/clothing/feet/feet.dmi b/icons/mob/clothing/feet/feet.dmi
index b6a4e7dc80fd..44cd673724a9 100644
Binary files a/icons/mob/clothing/feet/feet.dmi and b/icons/mob/clothing/feet/feet.dmi differ
diff --git a/icons/mob/clothing/head/head.dmi b/icons/mob/clothing/head/head.dmi
index 40c420838730..6710eb3d925f 100644
Binary files a/icons/mob/clothing/head/head.dmi and b/icons/mob/clothing/head/head.dmi differ
diff --git a/icons/mob/clothing/species/vox/back.dmi b/icons/mob/clothing/species/vox/back.dmi
new file mode 100644
index 000000000000..9288778584d1
Binary files /dev/null and b/icons/mob/clothing/species/vox/back.dmi differ
diff --git a/icons/mob/clothing/species/vox/collar.dmi b/icons/mob/clothing/species/vox/collar.dmi
new file mode 100644
index 000000000000..6c442c62f775
Binary files /dev/null and b/icons/mob/clothing/species/vox/collar.dmi differ
diff --git a/icons/mob/clothing/species/vox/ears.dmi b/icons/mob/clothing/species/vox/ears.dmi
new file mode 100644
index 000000000000..cd43d1a1893a
Binary files /dev/null and b/icons/mob/clothing/species/vox/ears.dmi differ
diff --git a/icons/mob/clothing/species/vox/eyes.dmi b/icons/mob/clothing/species/vox/eyes.dmi
new file mode 100644
index 000000000000..5962060f0d23
Binary files /dev/null and b/icons/mob/clothing/species/vox/eyes.dmi differ
diff --git a/icons/mob/clothing/species/vox/gloves.dmi b/icons/mob/clothing/species/vox/gloves.dmi
new file mode 100644
index 000000000000..4ac7a044f2f0
Binary files /dev/null and b/icons/mob/clothing/species/vox/gloves.dmi differ
diff --git a/icons/mob/clothing/species/vox/head.dmi b/icons/mob/clothing/species/vox/head.dmi
new file mode 100644
index 000000000000..7e4d425a948e
Binary files /dev/null and b/icons/mob/clothing/species/vox/head.dmi differ
diff --git a/icons/mob/clothing/species/vox/held.dmi b/icons/mob/clothing/species/vox/held.dmi
new file mode 100644
index 000000000000..973f8aa94ccc
Binary files /dev/null and b/icons/mob/clothing/species/vox/held.dmi differ
diff --git a/icons/mob/clothing/species/vox/helmet.dmi b/icons/mob/clothing/species/vox/helmet.dmi
new file mode 100644
index 000000000000..a36767b34a28
Binary files /dev/null and b/icons/mob/clothing/species/vox/helmet.dmi differ
diff --git a/icons/mob/clothing/species/vox/mask.dmi b/icons/mob/clothing/species/vox/mask.dmi
new file mode 100644
index 000000000000..465394780642
Binary files /dev/null and b/icons/mob/clothing/species/vox/mask.dmi differ
diff --git a/icons/mob/clothing/species/vox/shoes.dmi b/icons/mob/clothing/species/vox/shoes.dmi
new file mode 100644
index 000000000000..eba8a11aa2ee
Binary files /dev/null and b/icons/mob/clothing/species/vox/shoes.dmi differ
diff --git a/icons/mob/clothing/species/vox/socks.dmi b/icons/mob/clothing/species/vox/socks.dmi
new file mode 100644
index 000000000000..e9e433a82db6
Binary files /dev/null and b/icons/mob/clothing/species/vox/socks.dmi differ
diff --git a/icons/mob/clothing/species/vox/suit.dmi b/icons/mob/clothing/species/vox/suit.dmi
new file mode 100644
index 000000000000..ccf234e740ea
Binary files /dev/null and b/icons/mob/clothing/species/vox/suit.dmi differ
diff --git a/icons/mob/clothing/species/vox/undershirt.dmi b/icons/mob/clothing/species/vox/undershirt.dmi
new file mode 100644
index 000000000000..b43f2ed8cd12
Binary files /dev/null and b/icons/mob/clothing/species/vox/undershirt.dmi differ
diff --git a/icons/mob/clothing/species/vox/underwear.dmi b/icons/mob/clothing/species/vox/underwear.dmi
new file mode 100644
index 000000000000..81fb752d12e2
Binary files /dev/null and b/icons/mob/clothing/species/vox/underwear.dmi differ
diff --git a/icons/mob/clothing/species/vox/uniform.dmi b/icons/mob/clothing/species/vox/uniform.dmi
new file mode 100644
index 000000000000..cd8bb9bee358
Binary files /dev/null and b/icons/mob/clothing/species/vox/uniform.dmi differ
diff --git a/icons/mob/clothing/suit/suit.dmi b/icons/mob/clothing/suit/suit.dmi
index e796e64a1e66..a41781c79674 100644
Binary files a/icons/mob/clothing/suit/suit.dmi and b/icons/mob/clothing/suit/suit.dmi differ
diff --git a/icons/mob/clothing/uniform/color.dmi b/icons/mob/clothing/uniform/color.dmi
index 9b06725cbba7..4e63fcd41719 100644
Binary files a/icons/mob/clothing/uniform/color.dmi and b/icons/mob/clothing/uniform/color.dmi differ
diff --git a/icons/mob/corgi_head.dmi b/icons/mob/corgi_head.dmi
index a525a36c2d46..7c610b800776 100644
Binary files a/icons/mob/corgi_head.dmi and b/icons/mob/corgi_head.dmi differ
diff --git a/icons/mob/human.dmi b/icons/mob/human.dmi
index 63284a0012f2..2a5ee78d27cb 100644
Binary files a/icons/mob/human.dmi and b/icons/mob/human.dmi differ
diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi
index dd22f1dc5bc4..dcc27abd3c09 100644
Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ
diff --git a/icons/mob/inhands/equipment/tanks_lefthand.dmi b/icons/mob/inhands/equipment/tanks_lefthand.dmi
index 47f9e4be647b..b07ed1e5d530 100644
Binary files a/icons/mob/inhands/equipment/tanks_lefthand.dmi and b/icons/mob/inhands/equipment/tanks_lefthand.dmi differ
diff --git a/icons/mob/inhands/equipment/tanks_righthand.dmi b/icons/mob/inhands/equipment/tanks_righthand.dmi
index 2de83e274a25..84ffef493ade 100644
Binary files a/icons/mob/inhands/equipment/tanks_righthand.dmi and b/icons/mob/inhands/equipment/tanks_righthand.dmi differ
diff --git a/icons/mob/inhands/flags_lefthand.dmi b/icons/mob/inhands/flags_lefthand.dmi
new file mode 100644
index 000000000000..6b5c936dcc8d
Binary files /dev/null and b/icons/mob/inhands/flags_lefthand.dmi differ
diff --git a/icons/mob/inhands/flags_righthand.dmi b/icons/mob/inhands/flags_righthand.dmi
new file mode 100644
index 000000000000..85088c7a8df5
Binary files /dev/null and b/icons/mob/inhands/flags_righthand.dmi differ
diff --git a/icons/mob/inhands/misc/food_lefthand.dmi b/icons/mob/inhands/misc/food_lefthand.dmi
index c58a87284eb3..02281580dd52 100644
Binary files a/icons/mob/inhands/misc/food_lefthand.dmi and b/icons/mob/inhands/misc/food_lefthand.dmi differ
diff --git a/icons/mob/inhands/misc/food_righthand.dmi b/icons/mob/inhands/misc/food_righthand.dmi
index 186dc8d55537..a7690f04eed8 100644
Binary files a/icons/mob/inhands/misc/food_righthand.dmi and b/icons/mob/inhands/misc/food_righthand.dmi differ
diff --git a/icons/mob/species/vox/body_markings.dmi b/icons/mob/species/vox/body_markings.dmi
new file mode 100644
index 000000000000..0a6b23ed2e1e
Binary files /dev/null and b/icons/mob/species/vox/body_markings.dmi differ
diff --git a/icons/mob/species/vox/bodyparts.dmi b/icons/mob/species/vox/bodyparts.dmi
new file mode 100644
index 000000000000..e53b018ee089
Binary files /dev/null and b/icons/mob/species/vox/bodyparts.dmi differ
diff --git a/icons/mob/species/vox/eyes.dmi b/icons/mob/species/vox/eyes.dmi
new file mode 100644
index 000000000000..4fb2c4bb63ad
Binary files /dev/null and b/icons/mob/species/vox/eyes.dmi differ
diff --git a/icons/mob/species/vox/facial_quills.dmi b/icons/mob/species/vox/facial_quills.dmi
new file mode 100644
index 000000000000..b4a79df754ba
Binary files /dev/null and b/icons/mob/species/vox/facial_quills.dmi differ
diff --git a/icons/mob/species/vox/quills.dmi b/icons/mob/species/vox/quills.dmi
new file mode 100644
index 000000000000..da5af684da14
Binary files /dev/null and b/icons/mob/species/vox/quills.dmi differ
diff --git a/icons/mob/species/vox/tail_markings.dmi b/icons/mob/species/vox/tail_markings.dmi
new file mode 100644
index 000000000000..69f372ddcb9e
Binary files /dev/null and b/icons/mob/species/vox/tail_markings.dmi differ
diff --git a/icons/mob/species/vox/tails.dmi b/icons/mob/species/vox/tails.dmi
new file mode 100644
index 000000000000..ae23a1fae9ae
Binary files /dev/null and b/icons/mob/species/vox/tails.dmi differ
diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi
index 25d4a2a9cac0..c66dba622827 100644
Binary files a/icons/obj/assemblies.dmi and b/icons/obj/assemblies.dmi differ
diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi
index 01d73a179eec..e5bb9f997b55 100644
Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ
diff --git a/icons/obj/clothing/species/vox/gloves.dmi b/icons/obj/clothing/species/vox/gloves.dmi
new file mode 100644
index 000000000000..eeca4b63f30b
Binary files /dev/null and b/icons/obj/clothing/species/vox/gloves.dmi differ
diff --git a/icons/obj/clothing/species/vox/hats.dmi b/icons/obj/clothing/species/vox/hats.dmi
new file mode 100644
index 000000000000..922ede57f1af
Binary files /dev/null and b/icons/obj/clothing/species/vox/hats.dmi differ
diff --git a/icons/obj/clothing/species/vox/shoes.dmi b/icons/obj/clothing/species/vox/shoes.dmi
new file mode 100644
index 000000000000..a1dde383ff72
Binary files /dev/null and b/icons/obj/clothing/species/vox/shoes.dmi differ
diff --git a/icons/obj/clothing/species/vox/suits.dmi b/icons/obj/clothing/species/vox/suits.dmi
new file mode 100644
index 000000000000..4dc0ade4fb89
Binary files /dev/null and b/icons/obj/clothing/species/vox/suits.dmi differ
diff --git a/icons/obj/clothing/species/vox/uniforms.dmi b/icons/obj/clothing/species/vox/uniforms.dmi
new file mode 100644
index 000000000000..4bf1b92308d0
Binary files /dev/null and b/icons/obj/clothing/species/vox/uniforms.dmi differ
diff --git a/icons/obj/clothing/suits/suits.dmi b/icons/obj/clothing/suits/suits.dmi
index bfe2f6a535d3..e646f0eb2016 100644
Binary files a/icons/obj/clothing/suits/suits.dmi and b/icons/obj/clothing/suits/suits.dmi differ
diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi
index 8ed868f984c6..8f069529592b 100644
Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ
diff --git a/icons/obj/flag.dmi b/icons/obj/flag.dmi
new file mode 100644
index 000000000000..a311c47f8840
Binary files /dev/null and b/icons/obj/flag.dmi differ
diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi
index 310033272db2..495105e65e45 100644
Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ
diff --git a/icons/obj/plushes.dmi b/icons/obj/plushes.dmi
index 82665c089b15..a66158ffde33 100644
Binary files a/icons/obj/plushes.dmi and b/icons/obj/plushes.dmi differ
diff --git a/icons/obj/stack_objects.dmi b/icons/obj/stack_objects.dmi
index ce2743612a4d..d88ae0e3504d 100644
Binary files a/icons/obj/stack_objects.dmi and b/icons/obj/stack_objects.dmi differ
diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi
index a728c79817a9..e779a3f2e0b7 100755
Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ
diff --git a/sound/effects/voxrustle.ogg b/sound/effects/voxrustle.ogg
new file mode 100644
index 000000000000..fe400fc5dd18
Binary files /dev/null and b/sound/effects/voxrustle.ogg differ
diff --git a/sound/voice/vox/ashriek.ogg b/sound/voice/vox/ashriek.ogg
new file mode 100644
index 000000000000..125ba868fdbf
Binary files /dev/null and b/sound/voice/vox/ashriek.ogg differ
diff --git a/sound/voice/vox/shriek1.ogg b/sound/voice/vox/shriek1.ogg
new file mode 100644
index 000000000000..bb13db1ca0ee
Binary files /dev/null and b/sound/voice/vox/shriek1.ogg differ
diff --git a/sound/voice/vox/shriekcough.ogg b/sound/voice/vox/shriekcough.ogg
new file mode 100644
index 000000000000..15dc2e3873cb
Binary files /dev/null and b/sound/voice/vox/shriekcough.ogg differ
diff --git a/sound/voice/vox/shrieksneeze.ogg b/sound/voice/vox/shrieksneeze.ogg
new file mode 100644
index 000000000000..9b1a5d1cca73
Binary files /dev/null and b/sound/voice/vox/shrieksneeze.ogg differ
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
index e25bec52472c..f4a6ae3e5fbe 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferenceWindow.tsx
@@ -78,7 +78,7 @@ export const CharacterPreferenceWindow = (props, context) => {
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
index e53f1b3ee5c8..9fd6b472df2e 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx
@@ -341,7 +341,11 @@ const createSetRandomization = (
const sortPreferences = sortBy<[string, unknown]>(
([featureId, _]) => {
const feature = features[featureId];
- return feature?.name;
+ if (feature?.sortingPrefix) {
+ return feature.sortingPrefix + feature.name;
+ } else {
+ return feature?.name;
+ }
});
const PreferenceList = (props: {
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx
index 0800d809c5fd..75487daff179 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx
@@ -22,6 +22,7 @@ export type Feature<
>;
category?: string;
description?: string;
+ sortingPrefix?: string;
};
/**
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx
index 3ee93eb571bd..e6b47438e036 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/skin_tone.tsx
@@ -2,17 +2,17 @@ import { sortBy } from "common/collections";
import { Box, Stack } from "../../../../../components";
import { Feature, FeatureChoicedServerData, FeatureValueProps, StandardizedDropdown } from "../base";
-type HexValue = {
+export type HexValue = {
lightness: number,
value: string,
};
-type SkinToneServerData = FeatureChoicedServerData & {
+export type SkinToneServerData = FeatureChoicedServerData & {
display_names: NonNullable,
to_hex: Record,
};
-const sortHexValues
+export const sortHexValues
= sortBy<[string, HexValue]>(([_, hexValue]) => -hexValue.lightness);
export const skin_tone: Feature = {
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
index 51171da67598..8a8fc54c1987 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
@@ -1,7 +1,11 @@
-import { FeatureColorInput, Feature, FeatureChoiced, FeatureDropdownInput } from "./base";
+import { Box, Stack } from "../../../../components";
+import { FeatureColorInput, FeatureChoicedServerData, FeatureValueProps, StandardizedDropdown, Feature, FeatureChoiced, FeatureDropdownInput } from "./base";
+import { SkinToneServerData } from "./character_preferences/skin_tone";
+import { sortHexValues } from "./character_preferences/skin_tone";
export const eye_color: Feature = {
- name: "Eye color",
+ name: "Eye Color",
+ sortingPrefix: "aaaa",
component: FeatureColorInput,
};
@@ -50,6 +54,45 @@ export const feature_mcolor: Feature = {
component: FeatureColorInput,
};
+export const feature_mcolor_secondary: Feature = {
+ name: "Secondary Mutant color",
+ component: FeatureColorInput,
+};
+
+export const feature_quill_color: Feature = {
+ name: "Quill Color",
+ component: FeatureColorInput,
+};
+
+export const feature_facial_quill_color: Feature = {
+ name: "Facial Quill Color",
+ component: FeatureColorInput,
+};
+
+export const feature_quill_gradientstyle: FeatureChoiced = {
+ name: "Quill Gradient",
+ sortingPrefix: "v1",
+ component: FeatureDropdownInput,
+};
+
+export const feature_quill_gradientcolor: Feature = {
+ name: "Quill Gradient Color",
+ sortingPrefix: "v2",
+ component: FeatureColorInput,
+};
+
+export const feature_body_markings_color: Feature = {
+ name: "Body Markings Color",
+ sortingPrefix: "v6",
+ component: FeatureColorInput,
+};
+
+export const feature_tail_markings_color: Feature = {
+ name: "Tail Markings Color",
+ sortingPrefix: "v4",
+ component: FeatureColorInput,
+};
+
export const feature_ipc_screen: FeatureChoiced = {
name: "Screen",
component: FeatureDropdownInput,
@@ -134,3 +177,100 @@ export const feature_preternis_eye: FeatureChoiced = {
name: "Eye",
component: FeatureDropdownInput,
};
+
+export const feature_vox_quills: FeatureChoiced = {
+ name: 'Quillstyle',
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_facial_quills: FeatureChoiced = {
+ name: 'Facial Quillstyle',
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_tail_markings: FeatureChoiced = {
+ name: 'Tail Markings',
+ sortingPrefix: "v3",
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_body_markings: FeatureChoiced = {
+ name: 'Body Markings',
+ sortingPrefix: "v5",
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_skin_tone: Feature = {
+ name: "Skin Tone",
+ sortingPrefix: "a",
+ component: (props: FeatureValueProps) => {
+ const {
+ handleSetValue,
+ serverData,
+ value,
+ } = props;
+
+ if (!serverData) {
+ return null;
+ }
+
+ return (
+ key)}
+ displayNames={Object.fromEntries(
+ Object.entries(serverData.display_names)
+ .map(([key, displayName]) => {
+ const hexColor = serverData.to_hex[key];
+
+ return [key, (
+
+
+
+
+
+
+ {displayName}
+
+
+ )];
+ })
+ )}
+ onSetValue={handleSetValue}
+ value={value}
+ />
+ );
+ },
+};
+
+export const feature_vox_underwear: FeatureChoiced = {
+ name: 'Underwear',
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_socks: FeatureChoiced = {
+ name: 'Socks',
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_undershirt: FeatureChoiced = {
+ name: 'Undershirt',
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_tank_type: FeatureChoiced = {
+ name: 'N² Tank',
+ component: FeatureDropdownInput,
+};
+
+export const feature_vox_mask: FeatureChoiced = {
+ name: 'Mask',
+ component: FeatureDropdownInput,
+};
diff --git a/yogstation.dme b/yogstation.dme
index 85a7eceac8ff..468cd57fc5cc 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -143,6 +143,7 @@
#include "code\__DEFINES\space.dm"
#include "code\__DEFINES\spaceman_dmm.dm"
#include "code\__DEFINES\span.dm"
+#include "code\__DEFINES\species_clothing_paths.dm"
#include "code\__DEFINES\speech_channels.dm"
#include "code\__DEFINES\stat.dm"
#include "code\__DEFINES\stat_tracking.dm"
@@ -226,6 +227,7 @@
#include "code\__DEFINES\{yogs_defines}\admin.dm"
#include "code\__DEFINES\{yogs_defines}\antagonists.dm"
#include "code\__DEFINES\{yogs_defines}\atmospherics.dm"
+#include "code\__DEFINES\{yogs_defines}\colors.dm"
#include "code\__DEFINES\{yogs_defines}\components.dm"
#include "code\__DEFINES\{yogs_defines}\darkspawn.dm"
#include "code\__DEFINES\{yogs_defines}\DNA.dm"
@@ -244,6 +246,7 @@
#include "code\__DEFINES\{yogs_defines}\reactions.dm"
#include "code\__DEFINES\{yogs_defines}\shuttles.dm"
#include "code\__DEFINES\{yogs_defines}\spacepods.dm"
+#include "code\__DEFINES\{yogs_defines}\species_clothing_paths.dm"
#include "code\__DEFINES\{yogs_defines}\status_effects.dm"
#include "code\__DEFINES\{yogs_defines}\telecomms.dm"
#include "code\__DEFINES\{yogs_defines}\traits.dm"
@@ -4011,6 +4014,7 @@
#include "yogstation\code\__HELPERS\unsorted.dm"
#include "yogstation\code\_globalvars\configuration.dm"
#include "yogstation\code\_globalvars\logging.dm"
+#include "yogstation\code\_globalvars\lists\flavor_misc.dm"
#include "yogstation\code\_globalvars\lists\game.dm"
#include "yogstation\code\_globalvars\lists\mentors.dm"
#include "yogstation\code\_globalvars\lists\names.dm"
@@ -4027,6 +4031,9 @@
#include "yogstation\code\controllers\subsystem\yogs.dm"
#include "yogstation\code\controllers\subsystem\processing\quirks.dm"
#include "yogstation\code\datums\action.dm"
+#include "yogstation\code\datums\blood_types.dm"
+#include "yogstation\code\datums\dog_fashion.dm"
+#include "yogstation\code\datums\emotes.dm"
#include "yogstation\code\datums\mind.dm"
#include "yogstation\code\datums\mutations.dm"
#include "yogstation\code\datums\shuttles.dm"
@@ -4065,6 +4072,7 @@
#include "yogstation\code\datums\ruins\station.dm"
#include "yogstation\code\datums\status_effects\buffs.dm"
#include "yogstation\code\datums\status_effects\neutral.dm"
+#include "yogstation\code\datums\traits\good.dm"
#include "yogstation\code\datums\wires\disposals.dm"
#include "yogstation\code\datums\wires\smartfridge.dm"
#include "yogstation\code\game\communications.dm"
@@ -4120,6 +4128,8 @@
#include "yogstation\code\game\mecha\mecha_wreckage.dm"
#include "yogstation\code\game\mecha\makeshift\lockermech.dm"
#include "yogstation\code\game\mecha\makeshift\makeshift_tools.dm"
+#include "yogstation\code\game\objects\items.dm"
+#include "yogstation\code\game\objects\objs.dm"
#include "yogstation\code\game\objects\effects\contraband.dm"
#include "yogstation\code\game\objects\effects\countdown.dm"
#include "yogstation\code\game\objects\effects\landmarks.dm"
@@ -4138,6 +4148,7 @@
#include "yogstation\code\game\objects\items\crayons.dm"
#include "yogstation\code\game\objects\items\dna_injector.dm"
#include "yogstation\code\game\objects\items\extinguisher.dm"
+#include "yogstation\code\game\objects\items\flag.dm"
#include "yogstation\code\game\objects\items\fryingpan.dm"
#include "yogstation\code\game\objects\items\manuals.dm"
#include "yogstation\code\game\objects\items\plushes.dm"
@@ -4146,6 +4157,7 @@
#include "yogstation\code\game\objects\items\tool_switcher.dm"
#include "yogstation\code\game\objects\items\tools.dm"
#include "yogstation\code\game\objects\items\toys.dm"
+#include "yogstation\code\game\objects\items\trash.dm"
#include "yogstation\code\game\objects\items\weaponry.dm"
#include "yogstation\code\game\objects\items\circuitboards\computer_circuitboards.dm"
#include "yogstation\code\game\objects\items\circuitboards\machine_circuitboards.dm"
@@ -4180,6 +4192,7 @@
#include "yogstation\code\game\objects\items\storage\mre.dm"
#include "yogstation\code\game\objects\items\storage\toolbox.dm"
#include "yogstation\code\game\objects\items\storage\uplink_kits.dm"
+#include "yogstation\code\game\objects\items\tanks\tank_types.dm"
#include "yogstation\code\game\objects\items\wielded\big_spoon.dm"
#include "yogstation\code\game\objects\items\wielded\sledgehammer.dm"
#include "yogstation\code\game\objects\items\wielded\vxtvulhammer.dm"
@@ -4196,8 +4209,10 @@
#include "yogstation\code\game\objects\structures\bar_stuff\bar_stuff.dm"
#include "yogstation\code\game\objects\structures\beds_chairs\chair.dm"
#include "yogstation\code\game\objects\structures\beds_chairs\electric_bed.dm"
+#include "yogstation\code\game\objects\structures\signs\_signs.dm"
#include "yogstation\code\game\objects\structures\signs\signs_plaques.dm"
#include "yogstation\code\game\turfs\change_turf.dm"
+#include "yogstation\code\game\turfs\open\floor\plating\asteroid.dm"
#include "yogstation\code\game\turfs\simulated\ballpit.dm"
#include "yogstation\code\game\turfs\simulated\minerals.dm"
#include "yogstation\code\game\turfs\simulated\floor\fancy_floor.dm"
@@ -4224,6 +4239,7 @@
#include "yogstation\code\modules\antagonists\_common\antag_menu.dm"
#include "yogstation\code\modules\antagonists\abductor\equipment\abduction_outfits.dm"
#include "yogstation\code\modules\antagonists\blob\blob\blobs\core.dm"
+#include "yogstation\code\modules\antagonists\changeling\changeling.dm"
#include "yogstation\code\modules\antagonists\darkspawn\_psi_web.dm"
#include "yogstation\code\modules\antagonists\darkspawn\darkspawn_antag.dm"
#include "yogstation\code\modules\antagonists\darkspawn\darkspawn_illusions.dm"
@@ -4278,14 +4294,21 @@
#include "yogstation\code\modules\antagonists\traitor\backstory\traitor_factions.dm"
#include "yogstation\code\modules\assembly\signaler.dm"
#include "yogstation\code\modules\atmospherics\airalarm.dm"
+#include "yogstation\code\modules\atmospherics\auxgm\breathing_classes.dm"
#include "yogstation\code\modules\atmospherics\machinery\pipes\bluespace.dm"
#include "yogstation\code\modules\atmospherics\unary_devices\vent_pump.dm"
#include "yogstation\code\modules\cargo\cargo_packs.dm"
+#include "yogstation\code\modules\cargo\bounties\medical.dm"
+#include "yogstation\code\modules\cargo\exports\organs.dm"
#include "yogstation\code\modules\cargo\exports\sheets.dm"
#include "yogstation\code\modules\cargo\exports\weapons.dm"
#include "yogstation\code\modules\client\client_defines.dm"
#include "yogstation\code\modules\client\client_procs.dm"
#include "yogstation\code\modules\client\preferences_savefile.dm"
+#include "yogstation\code\modules\client\preferences\_preference.dm"
+#include "yogstation\code\modules\client\preferences\clothing.dm"
+#include "yogstation\code\modules\client\preferences\species_features\mutants.dm"
+#include "yogstation\code\modules\client\preferences\species_features\vox.dm"
#include "yogstation\code\modules\client\verbs\afk.dm"
#include "yogstation\code\modules\client\verbs\antag_token.dm"
#include "yogstation\code\modules\client\verbs\looc.dm"
@@ -4298,24 +4321,36 @@
#include "yogstation\code\modules\clothing\mask.dm"
#include "yogstation\code\modules\clothing\shoe.dm"
#include "yogstation\code\modules\clothing\under.dm"
+#include "yogstation\code\modules\clothing\ears\_ears.dm"
#include "yogstation\code\modules\clothing\glasses\_glasses.dm"
+#include "yogstation\code\modules\clothing\gloves\_gloves.dm"
#include "yogstation\code\modules\clothing\gloves\miscellaneous.dm"
+#include "yogstation\code\modules\clothing\head\_head.dm"
#include "yogstation\code\modules\clothing\head\helmet.dm"
#include "yogstation\code\modules\clothing\head\jobs.dm"
#include "yogstation\code\modules\clothing\head\misc.dm"
#include "yogstation\code\modules\clothing\head\misc_special.dm"
+#include "yogstation\code\modules\clothing\masks\_masks.dm"
+#include "yogstation\code\modules\clothing\masks\breath.dm"
#include "yogstation\code\modules\clothing\masks\hailer.dm"
#include "yogstation\code\modules\clothing\neck\_neck.dm"
#include "yogstation\code\modules\clothing\outfits\event.dm"
+#include "yogstation\code\modules\clothing\shoes\_shoes.dm"
+#include "yogstation\code\modules\clothing\shoes\colour.dm"
+#include "yogstation\code\modules\clothing\shoes\magboots.dm"
#include "yogstation\code\modules\clothing\shoes\miscellaneous.dm"
#include "yogstation\code\modules\clothing\shoes\special_shoes.dm"
+#include "yogstation\code\modules\clothing\spacesuits\alien.dm"
#include "yogstation\code\modules\clothing\spacesuits\hardsuit.dm"
+#include "yogstation\code\modules\clothing\suits\_suits.dm"
#include "yogstation\code\modules\clothing\suits\armor.dm"
#include "yogstation\code\modules\clothing\suits\explorer_gear.dm"
#include "yogstation\code\modules\clothing\suits\labcoat.dm"
#include "yogstation\code\modules\clothing\suits\miscellaneous.dm"
#include "yogstation\code\modules\clothing\suits\nerd.dm"
#include "yogstation\code\modules\clothing\suits\wiz_robe.dm"
+#include "yogstation\code\modules\clothing\under\_under.dm"
+#include "yogstation\code\modules\clothing\under\color.dm"
#include "yogstation\code\modules\clothing\under\miscellaneous.dm"
#include "yogstation\code\modules\clothing\under\jobs\civilian.dm"
#include "yogstation\code\modules\clothing\under\jobs\engineering.dm"
@@ -4337,6 +4372,8 @@
#include "yogstation\code\modules\events\probabilistic_anomaly.dm"
#include "yogstation\code\modules\events\weightless.dm"
#include "yogstation\code\modules\food_and_drinks\food\condiment.dm"
+#include "yogstation\code\modules\food_and_drinks\food\snacks_meat.dm"
+#include "yogstation\code\modules\food_and_drinks\food\recipes\tablecraft\recipes_meat.dm"
#include "yogstation\code\modules\food_and_drinks\food\snacks\meat.dm"
#include "yogstation\code\modules\goals\station_goals\bluespace_tap.dm"
#include "yogstation\code\modules\guardian\guardian.dm"
@@ -4385,6 +4422,8 @@
#include "yogstation\code\modules\jungleland\kinetic_javelin.dm"
#include "yogstation\code\modules\language\darkspeak.dm"
#include "yogstation\code\modules\language\japanese.dm"
+#include "yogstation\code\modules\language\language_holder.dm"
+#include "yogstation\code\modules\language\voxpidgin.dm"
#include "yogstation\code\modules\mentor\follow.dm"
#include "yogstation\code\modules\mentor\mentor.dm"
#include "yogstation\code\modules\mentor\mentor_memo.dm"
@@ -4412,6 +4451,7 @@
#include "yogstation\code\modules\mob\living\carbon\alien\humanoid\humanoid.dm"
#include "yogstation\code\modules\mob\living\carbon\alien\humanoid\humanoid_defense.dm"
#include "yogstation\code\modules\mob\living\carbon\alien\humanoid\queen.dm"
+#include "yogstation\code\modules\mob\living\carbon\human\emote.dm"
#include "yogstation\code\modules\mob\living\carbon\human\human.dm"
#include "yogstation\code\modules\mob\living\carbon\human\human_defense.dm"
#include "yogstation\code\modules\mob\living\carbon\human\human_defines.dm"
@@ -4424,6 +4464,7 @@
#include "yogstation\code\modules\mob\living\carbon\human\species_types\lizard.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\plantpeople.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\szlachta.dm"
+#include "yogstation\code\modules\mob\living\carbon\human\species_types\vox.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\organs.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\power_suck.dm"
#include "yogstation\code\modules\mob\living\carbon\human\species_types\preternis\preternis.dm"
@@ -4477,6 +4518,7 @@
#include "yogstation\code\modules\reagents\chemistry\reagents\food_reagents.dm"
#include "yogstation\code\modules\reagents\chemistry\reagents\other_reagents.dm"
#include "yogstation\code\modules\reagents\chemistry\recipes\slime_extracts.dm"
+#include "yogstation\code\modules\reagents\reagent_containers\blood_pack.dm"
#include "yogstation\code\modules\reagents\reagent_containers\bottle.dm"
#include "yogstation\code\modules\reagents\reagent_containers\gummies.dm"
#include "yogstation\code\modules\reagents\reagent_containers\hypospray.dm"
@@ -4486,6 +4528,7 @@
#include "yogstation\code\modules\research\rdconsole.dm"
#include "yogstation\code\modules\research\designs\bluespace_designs.dm"
#include "yogstation\code\modules\research\designs\comp_board_designs.dm"
+#include "yogstation\code\modules\research\designs\limbgrower_designs.dm"
#include "yogstation\code\modules\research\designs\medical_designs.dm"
#include "yogstation\code\modules\research\designs\misc_designs.dm"
#include "yogstation\code\modules\research\designs\spacepod_designs.dm"
@@ -4529,10 +4572,18 @@
#include "yogstation\code\modules\spells\spell_types\projectile\animation.dm"
#include "yogstation\code\modules\spells\spell_types\self\cauterize.dm"
#include "yogstation\code\modules\spells\spell_types\shapeshift\mouse.dm"
-#include "yogstation\code\modules\surgery\bodypart.dm"
#include "yogstation\code\modules\surgery\gender_reassignment.dm"
+#include "yogstation\code\modules\surgery\bodyparts\_bodyparts.dm"
+#include "yogstation\code\modules\surgery\bodyparts\head.dm"
+#include "yogstation\code\modules\surgery\organs\appendix.dm"
+#include "yogstation\code\modules\surgery\organs\ears.dm"
+#include "yogstation\code\modules\surgery\organs\eyes.dm"
#include "yogstation\code\modules\surgery\organs\heart.dm"
+#include "yogstation\code\modules\surgery\organs\liver.dm"
#include "yogstation\code\modules\surgery\organs\lungs.dm"
+#include "yogstation\code\modules\surgery\organs\stomach.dm"
+#include "yogstation\code\modules\surgery\organs\tails.dm"
+#include "yogstation\code\modules\surgery\organs\tongue.dm"
#include "yogstation\code\modules\uplink\uplink_item.dm"
#include "yogstation\code\modules\vending\fishing.dm"
#include "yogstation\code\modules\vending\gift.dm"
diff --git a/yogstation/code/__HELPERS/mobs.dm b/yogstation/code/__HELPERS/mobs.dm
index 2f69fc04ac15..051949f87976 100644
--- a/yogstation/code/__HELPERS/mobs.dm
+++ b/yogstation/code/__HELPERS/mobs.dm
@@ -5,6 +5,25 @@
if(!findname(.))
break
+/proc/random_unique_vox_name(attempts_to_find_unique_name=10)
+ for(var/i in 1 to attempts_to_find_unique_name)
+ . = capitalize(vox_name())
+
+ if(!findname(.))
+ break
+
+GLOBAL_LIST_INIT(vox_skin_tones, sortList(list(
+ "lime",
+ "crimson",
+ "nebula",
+ "azure",
+ "emerald",
+ "brown",
+ "plum",
+ "grey",
+ "mossy"
+ )))
+
/proc/is_admin(user)
if(ismob(user))
var/mob/temp = user
diff --git a/yogstation/code/__HELPERS/names.dm b/yogstation/code/__HELPERS/names.dm
index fe3263046599..23861e1403c5 100644
--- a/yogstation/code/__HELPERS/names.dm
+++ b/yogstation/code/__HELPERS/names.dm
@@ -3,3 +3,10 @@
return "[pick(GLOB.gorilla_names_male)] [pick(GLOB.last_names)]"
else
return "[pick(GLOB.gorilla_names_female)] [pick(GLOB.last_names)]"
+
+/proc/vox_name()
+ var/sounds = rand(2,8)
+ var/vox_name = ""
+ for(var/sound in 1 to sounds)
+ vox_name += pick("ti","hi","ki","ya","ta","ha","ka","yi","chi","cha","kah")
+ return vox_name
diff --git a/yogstation/code/_globalvars/lists/flavor_misc.dm b/yogstation/code/_globalvars/lists/flavor_misc.dm
new file mode 100644
index 000000000000..920f3c028b5b
--- /dev/null
+++ b/yogstation/code/_globalvars/lists/flavor_misc.dm
@@ -0,0 +1,8 @@
+//Vox prefs and sprite accessories
+GLOBAL_LIST_EMPTY(vox_quills_list)
+GLOBAL_LIST_EMPTY(vox_facial_quills_list)
+GLOBAL_LIST_EMPTY(vox_tails_list)
+GLOBAL_LIST_EMPTY(vox_body_markings_list)
+GLOBAL_LIST_EMPTY(vox_tail_markings_list)
+GLOBAL_LIST_EMPTY(animated_vox_tails_list)
+GLOBAL_LIST_EMPTY(animated_vox_tail_markings_list)
diff --git a/yogstation/code/datums/blood_types.dm b/yogstation/code/datums/blood_types.dm
new file mode 100644
index 000000000000..720f16bdbabb
--- /dev/null
+++ b/yogstation/code/datums/blood_types.dm
@@ -0,0 +1,4 @@
+/datum/blood_type/vox
+ name = "V"
+ color = COLOR_BLOOD_VOX
+ compatible_types = list(/datum/blood_type/vox)
diff --git a/yogstation/code/datums/dog_fashion.dm b/yogstation/code/datums/dog_fashion.dm
new file mode 100644
index 000000000000..9ab978ec2221
--- /dev/null
+++ b/yogstation/code/datums/dog_fashion.dm
@@ -0,0 +1,3 @@
+/datum/dog_fashion/head/fried_vox_empty
+ name = "Colonel REAL_NAME"
+ desc = "Keep away from live Vox."
diff --git a/yogstation/code/datums/emotes.dm b/yogstation/code/datums/emotes.dm
new file mode 100644
index 000000000000..c295343cfe40
--- /dev/null
+++ b/yogstation/code/datums/emotes.dm
@@ -0,0 +1,3 @@
+/datum/emote
+ /// Message to display if the user is a vox
+ var/message_vox = ""
diff --git a/yogstation/code/datums/traits/good.dm b/yogstation/code/datums/traits/good.dm
new file mode 100644
index 000000000000..06779e2aae93
--- /dev/null
+++ b/yogstation/code/datums/traits/good.dm
@@ -0,0 +1,6 @@
+/datum/quirk/multilingual/voxpidgin
+ name = "Multilingual (Vox-pidgin)"
+ desc = "You spent a portion of your life learning to understand Vox-pidgin. You may or may not be able to speak it based on your anatomy."
+ specific = /datum/language/vox
+ gain_text = span_notice("You have learned to understand Vox-pidgin.")
+ lose_text = span_notice("You have forgotten how to understand Vox-pidgin.")
diff --git a/yogstation/code/game/objects/items.dm b/yogstation/code/game/objects/items.dm
new file mode 100644
index 000000000000..f5b158c92218
--- /dev/null
+++ b/yogstation/code/game/objects/items.dm
@@ -0,0 +1,3 @@
+/obj/item
+ var/list/sprite_sheets = null
+ var/species_fitted = null
diff --git a/yogstation/code/game/objects/items/flag.dm b/yogstation/code/game/objects/items/flag.dm
new file mode 100644
index 000000000000..c56520a5f54b
--- /dev/null
+++ b/yogstation/code/game/objects/items/flag.dm
@@ -0,0 +1,299 @@
+/obj/item/flag
+ name = "flag"
+ desc = "It's a flag."
+ icon = 'icons/obj/flag.dmi'
+ icon_state = "ntflag"
+ lefthand_file = 'icons/mob/inhands/flags_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/flags_righthand.dmi'
+ w_class = WEIGHT_CLASS_BULKY
+ max_integrity = 40
+ resistance_flags = FLAMMABLE
+ custom_fire_overlay = "fire"
+ var/rolled = FALSE
+
+/obj/item/flag/attackby(obj/item/item, mob/user, params)
+ . = ..()
+ if(item.is_hot() && !(resistance_flags & ON_FIRE))
+ user.visible_message(span_notice("[user] lights [src] with [item]."), span_notice("You light [src] with [item]."), span_warning("You hear a low whoosh."))
+ fire_act()
+
+/obj/item/flag/attack_self(mob/user)
+ rolled = !rolled
+ user.visible_message(span_notice("[user] [rolled ? "rolls up" : "unfurls"] [src]."), span_notice("You [rolled ? "roll up" : "unfurl"] [src]."), span_warning("You hear fabric rustling."))
+ update_icon()
+
+/obj/item/flag/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = FALSE)
+ ..()
+ update_icon()
+
+/obj/item/flag/extinguish()
+ ..()
+ update_icon()
+
+/obj/item/flag/update_icon_state()
+ . = ..()
+ icon_state = initial(icon_state)
+ item_state = icon_state
+ if(rolled)
+ icon_state = "[icon_state]_rolled"
+ custom_fire_overlay = "fire_rolled"
+ else
+ custom_fire_overlay = initial(custom_fire_overlay)
+ if(resistance_flags & ON_FIRE)
+ item_state = "[item_state]_fire"
+ if(ismob(loc))
+ var/mob/M = loc
+ M.update_inv_hands()
+
+/obj/item/flag/nt
+ name = "\improper Nanotrasen flag"
+ desc = "A flag proudly boasting the logo of NT."
+ icon_state = "ntflag"
+
+/obj/item/flag/clown
+ name = "\improper Clown Planet flag"
+ desc = "The banner of His Majesty, King Squiggles the Eighth."
+ icon_state = "clownflag"
+
+/obj/item/flag/mime
+ name = "\improper Mime Revolution flag"
+ desc = "The banner of the glorious revolutionary forces fighting the oppressors on Clown Planet."
+ icon_state = "mimeflag"
+
+/obj/item/flag/ian
+ name = "\improper Ian flag"
+ desc = "The banner of Ian, because SQUEEEEE."
+ icon_state = "ianflag"
+
+
+//Species flags
+
+/obj/item/flag/species/slime
+ name = "\improper Slime People flag"
+ desc = "A flag proudly proclaiming the superior heritage of Slime People."
+ icon_state = "slimeflag"
+
+/obj/item/flag/species/skrell
+ //name = "\improper Skrell flag"
+ //desc = "A flag proudly proclaiming the superior heritage of Skrell."
+ icon_state = "skrellflag"
+
+/obj/item/flag/species/vox
+ name = "\improper Vox flag"
+ desc = "A flag proudly proclaiming the superior heritage of Vox."
+ icon_state = "voxflag"
+
+/obj/item/flag/species/machine
+ name = "\improper Synthetics flag"
+ desc = "A flag proudly proclaiming the superior heritage of Synthetics."
+ icon_state = "machineflag"
+
+/obj/item/flag/species/diona
+ //name = "\improper Diona flag" //phytosians maybe?
+ //desc = "A flag proudly proclaiming the superior heritage of Dionae."
+ icon_state = "dionaflag"
+
+/obj/item/flag/species/human
+ name = "\improper Human flag"
+ desc = "A flag proudly proclaiming the superior heritage of Humans."
+ icon_state = "humanflag"
+
+/obj/item/flag/species/greys
+ //name = "\improper Greys flag"
+ //desc = "A flag proudly proclaiming the superior heritage of Greys."
+ icon_state = "greysflag"
+
+/obj/item/flag/species/kidan
+ //name = "\improper Kidan flag" //preternis maybe?
+ //desc = "A flag proudly proclaiming the superior heritage of Kidan."
+ icon_state = "kidanflag"
+
+/obj/item/flag/species/taj
+ //name = "\improper Tajaran flag"
+ //desc = "A flag proudly proclaiming the superior heritage of Tajaran."
+ icon_state = "tajflag"
+
+/obj/item/flag/species/lizard
+ name = "\improper Vuulek flag"
+ desc = "A flag proudly proclaiming the superior heritage of Vuulek."
+ icon_state = "lizardflag"
+
+/obj/item/flag/species/vulp
+ //name = "\improper Vulpkanin flag"
+ //desc = "A flag proudly proclaiming the superior heritage of Vulpkanin."
+ icon_state = "vulpflag"
+
+/obj/item/flag/species/drask
+ //name = "\improper Drask flag"
+ //desc = "A flag proudly proclaiming the superior heritage of Drask."
+ icon_state = "draskflag"
+
+/obj/item/flag/species/plasma
+ name = "\improper Plasmaman flag"
+ desc = "A flag proudly proclaiming the superior heritage of Plasmamen."
+ icon_state = "plasmaflag"
+
+/obj/item/flag/species/moth
+ name ="\improper Ex'hai flag"
+ desc = "An eccentric handmade standard, luxuriously soft due to exotic silks and embossed with lustrous gold. Although inspired by the pride that Ex'hai take in their baubles, it ultimately feels melancholic. Beauty knows no pain, afterall."
+ icon_state = "mothflag"
+
+//Department Flags
+
+/obj/item/flag/cargo
+ name = "\improper Cargonia flag"
+ desc = "The flag of the independent, sovereign nation of Cargonia."
+ icon_state = "cargoflag"
+
+/obj/item/flag/med
+ name = "\improper Medistan flag"
+ desc = "The flag of the independent, sovereign nation of Medistan."
+ icon_state = "medflag"
+
+/obj/item/flag/sec
+ name = "\improper Brigston flag"
+ desc = "The flag of the independent, sovereign nation of Brigston."
+ icon_state = "secflag"
+
+/obj/item/flag/rnd
+ name = "\improper Scientopia flag"
+ desc = "The flag of the independent, sovereign nation of Scientopia."
+ icon_state = "rndflag"
+
+/obj/item/flag/atmos
+ name = "\improper Atmosia flag"
+ desc = "The flag of the independent, sovereign nation of Atmosia."
+ icon_state = "atmosflag"
+
+/obj/item/flag/command
+ name = "\improper Command flag"
+ desc = "The flag of the independent, sovereign nation of Command."
+ icon_state = "ntflag"
+
+//Antags
+
+/obj/item/flag/grey
+ name = "\improper Greytide flag"
+ desc = "A banner made from an old grey jumpsuit."
+ icon_state = "greyflag"
+
+/obj/item/flag/syndi
+ name = "\improper Syndicate flag"
+ desc = "A flag proudly boasting the logo of the Syndicate, in defiance of NT."
+ icon_state = "syndiflag"
+
+/obj/item/flag/wiz
+ name = "\improper Wizard Federation flag"
+ desc = "A flag proudly boasting the logo of the Wizard Federation, sworn enemies of NT."
+ icon_state = "wizflag"
+
+/obj/item/flag/cult
+ name = "\improper Nar'Sie Cultist flag"
+ desc = "A flag proudly boasting the logo of the cultists, sworn enemies of NT."
+ icon_state = "cultflag"
+
+/obj/item/flag/ussp
+ name = "\improper USSP flag"
+ desc = "A flag proudly boasting the logo of the USSP, a noticeable faction in the galaxy."
+ icon_state = "usspflag"
+
+/obj/item/flag/solgov
+ name = "\improper Trans-Solar Federation flag"
+ desc = "A flag proudly boasting the logo of the SolGov, allied to NT government originated from Earth."
+ icon_state = "solgovflag"
+
+//Chameleon
+/*
+/obj/item/flag/chameleon
+ name = "chameleon flag"
+ desc = "A poor recreation of the official NT flag. It seems to shimmer a little."
+ icon_state = "ntflag"
+ origin_tech = "syndicate=1;magnets=4"
+ var/updated_icon_state = null
+ var/used = FALSE
+ var/obj/item/grenade/boobytrap = null
+ var/mob/trapper = null
+
+/obj/item/flag/chameleon/New()
+ updated_icon_state = icon_state
+ ..()
+
+/obj/item/flag/chameleon/attack_self(mob/user)
+ if(used)
+ return ..()
+
+ var/list/flag_types = typesof(/obj/item/flag) - list(/obj/item/flag, /obj/item/flag/chameleon, /obj/item/flag/chameleon/depot)
+ var/list/flag = list()
+
+ for(var/flag_type in flag_types)
+ var/obj/item/flag/F = new flag_type
+ flag[capitalize(F.name)] = F
+
+ var/list/show_flag = list("EXIT" = null) + sortList(flag)
+
+ var/input_flag = input(user, "Choose a flag to disguise as.", "Choose a flag.") in show_flag
+
+ if(user && (src in user.contents))
+
+ var/obj/item/flag/chosen_flag = flag[input_flag]
+
+ if(chosen_flag && !used)
+ name = chosen_flag.name
+ icon_state = chosen_flag.icon_state
+ updated_icon_state = icon_state
+ desc = chosen_flag.desc
+ used = TRUE
+
+/obj/item/flag/chameleon/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/grenade) && !boobytrap)
+ if(user.drop_item())
+ boobytrap = I
+ trapper = user
+ I.forceMove(src)
+ to_chat(user, "You hide [I] in [src]. It will detonate some time after the flag is lit on fire.")
+ var/turf/bombturf = get_turf(src)
+ var/area/A = get_area(bombturf)
+ log_game("[key_name(user)] has hidden [I] in [src] ready for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).")
+ investigate_log("[key_name(user)] has hidden [I] in [src] ready for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).", INVESTIGATE_BOMB)
+ add_attack_logs(user, src, "has hidden [I] ready for detonation in", ATKLOG_MOST)
+ else if(I.get_heat() && !(resistance_flags & ON_FIRE) && boobytrap && trapper)
+ var/turf/bombturf = get_turf(src)
+ var/area/A = get_area(bombturf)
+ log_game("[key_name_admin(user)] has lit [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).")
+ investigate_log("[key_name_admin(user)] has lit [src] trapped with [boobytrap] by [key_name_admin(trapper)] at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]).", INVESTIGATE_BOMB)
+ add_attack_logs(user, src, "has lit (booby trapped with [boobytrap]", ATKLOG_FEW)
+ burn()
+ else
+ return ..()
+
+/obj/item/flag/chameleon/screwdriver_act(mob/user, obj/item/I)
+ if(!boobytrap || user != trapper)
+ return
+ . = TRUE
+ if(!I.use_tool(src, user, 0, volume = I.tool_volume))
+ return
+ to_chat(user, "You remove [boobytrap] from [src].")
+ boobytrap.forceMove(get_turf(src))
+ boobytrap = null
+ trapper = null
+
+/obj/item/flag/chameleon/burn()
+ if(boobytrap)
+ fire_act()
+ addtimer(CALLBACK(src, PROC_REF(prime_boobytrap)), boobytrap.det_time)
+ else
+ ..()
+
+/obj/item/flag/chameleon/proc/prime_boobytrap()
+ boobytrap.forceMove(get_turf(loc))
+ boobytrap.prime()
+ boobytrap = null
+ burn()
+
+/obj/item/flag/chameleon/updateFlagIcon()
+ icon_state = updated_icon_state
+
+/obj/item/flag/chameleon/depot/New()
+ ..()
+ boobytrap = new /obj/item/grenade/gas/plasma(src)
+*/
diff --git a/yogstation/code/game/objects/items/plushes.dm b/yogstation/code/game/objects/items/plushes.dm
index e8948ea04e3c..1f72cd81668b 100644
--- a/yogstation/code/game/objects/items/plushes.dm
+++ b/yogstation/code/game/objects/items/plushes.dm
@@ -176,4 +176,11 @@
squeak_override = list(
'sound/weapons/egloves.ogg' = 2,
'sound/weapons/cablecuff.ogg' = 1
- )
\ No newline at end of file
+ )
+
+/obj/item/toy/plush/voxplushie
+ name = "vox plushie"
+ desc = "A stitched-together Vox, fresh from the skipjack. Press its belly to hear it skree!"
+ icon_state = "plushie_vox"
+ item_state = "plushie_vox"
+ squeak_override = list('sound/voice/vox/shriek1.ogg' = 1)
diff --git a/yogstation/code/game/objects/items/stacks/sheets/leather.dm b/yogstation/code/game/objects/items/stacks/sheets/leather.dm
index 61b7a43e7ed2..87e7c2d6f964 100644
--- a/yogstation/code/game/objects/items/stacks/sheets/leather.dm
+++ b/yogstation/code/game/objects/items/stacks/sheets/leather.dm
@@ -10,4 +10,10 @@
desc = "This would make a nice rug."
singular_name = "gorilla skin piece"
icon_state = "sheet-gorilla"
- item_state = "sheet-gorilla"
\ No newline at end of file
+ item_state = "sheet-gorilla"
+
+/obj/item/stack/sheet/animalhide/vox
+ name = "vox hide"
+ desc = "SKREE!"
+ singular_name = "vox hide piece"
+ icon_state = "sheet-vox"
diff --git a/yogstation/code/game/objects/items/storage/backpack.dm b/yogstation/code/game/objects/items/storage/backpack.dm
index b79d23c42753..87a0f08483e9 100644
--- a/yogstation/code/game/objects/items/storage/backpack.dm
+++ b/yogstation/code/game/objects/items/storage/backpack.dm
@@ -1,3 +1,6 @@
+/obj/item/storage/backpack
+ sprite_sheets = list(SPECIES_VOX = VOX_BACK_FILE)
+
/obj/item/storage/backpack/holding
icon = 'yogstation/icons/obj/storage.dmi'
icon_state = "holdingpack"
diff --git a/yogstation/code/game/objects/items/tanks/tank_types.dm b/yogstation/code/game/objects/items/tanks/tank_types.dm
new file mode 100644
index 000000000000..da9c60b792b0
--- /dev/null
+++ b/yogstation/code/game/objects/items/tanks/tank_types.dm
@@ -0,0 +1,26 @@
+/obj/item/tank/internals/nitrogen
+ name = "nitrogen tank"
+ desc = "A tank of nitrogen."
+ icon_state = "oxygen_fr"
+ force = 10
+ distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE
+
+/obj/item/tank/internals/nitrogen/populate_gas()
+ air_contents.set_moles(GAS_N2, (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C))
+
+/obj/item/tank/internals/emergency_oxygen/nitrogen
+ name = "emergency nitrogen tank"
+ desc = "An emergency tank designed specifically for Vox."
+ icon_state = "emergency_nitrogen"
+
+/obj/item/tank/internals/emergency_oxygen/nitrogen/populate_gas()
+ air_contents.set_moles(GAS_N2, (10*ONE_ATMOSPHERE)* volume/(R_IDEAL_GAS_EQUATION*T20C))
+
+/obj/item/tank/internals/emergency_oxygen/vox
+ name = "vox specialized nitrogen tank"
+ desc = "A high-tech nitrogen tank designed specifically for Vox."
+ icon_state = "emergency_vox"
+ volume = 35
+
+/obj/item/tank/internals/emergency_oxygen/vox/populate_gas()
+ air_contents.set_moles(GAS_N2, (10*ONE_ATMOSPHERE)* volume/(R_IDEAL_GAS_EQUATION*T20C))
diff --git a/yogstation/code/game/objects/items/trash.dm b/yogstation/code/game/objects/items/trash.dm
new file mode 100644
index 000000000000..88a6ffaa1b23
--- /dev/null
+++ b/yogstation/code/game/objects/items/trash.dm
@@ -0,0 +1,6 @@
+/obj/item/trash/fried_vox
+ name = "Kentucky Fried Vox"
+ icon_state = "fried_vox_empty"
+ item_state = "fried_vox_empty"
+ slot_flags = ITEM_SLOT_HEAD
+ dog_fashion = /datum/dog_fashion/head/fried_vox_empty
diff --git a/yogstation/code/game/objects/objs.dm b/yogstation/code/game/objects/objs.dm
new file mode 100644
index 000000000000..d5e06d66cc20
--- /dev/null
+++ b/yogstation/code/game/objects/objs.dm
@@ -0,0 +1,2 @@
+/obj
+ var/custom_fire_overlay // Update_fire_overlay will check if a different icon state should be used
diff --git a/yogstation/code/game/objects/structures/signs/_signs.dm b/yogstation/code/game/objects/structures/signs/_signs.dm
new file mode 100644
index 000000000000..d1351d3b841f
--- /dev/null
+++ b/yogstation/code/game/objects/structures/signs/_signs.dm
@@ -0,0 +1,19 @@
+/obj/structure/sign/pox
+ name = "NO VOX ALLOWED"
+ icon_state = "novox1-b"
+ desc = "A sign which reads 'NO VOX ALLOWED'."
+
+/obj/structure/sign/pox/no_cross
+ icon_state = "novox2-b"
+
+/obj/structure/sign/pox/red
+ icon_state = "novox1-r"
+
+/obj/structure/sign/pox/red/no_cross
+ icon_state = "novox2-r"
+
+/obj/structure/sign/pox/red/cicle
+ icon_state = "novox_circle1"
+
+/obj/structure/sign/pox/red/cicle/no_cross
+ icon_state = "novox_circle2"
diff --git a/yogstation/code/game/turfs/open/floor/plating/asteroid.dm b/yogstation/code/game/turfs/open/floor/plating/asteroid.dm
new file mode 100644
index 000000000000..a042e9517957
--- /dev/null
+++ b/yogstation/code/game/turfs/open/floor/plating/asteroid.dm
@@ -0,0 +1 @@
+#define BREATH_VOX /datum/breathing_class/vox
diff --git a/yogstation/code/modules/antagonists/changeling/changeling.dm b/yogstation/code/modules/antagonists/changeling/changeling.dm
new file mode 100644
index 000000000000..4cc43fb18adc
--- /dev/null
+++ b/yogstation/code/modules/antagonists/changeling/changeling.dm
@@ -0,0 +1,2 @@
+/datum/changelingprofile
+ var/list/sprite_sheets_list = list()
diff --git a/yogstation/code/modules/atmospherics/auxgm/breathing_classes.dm b/yogstation/code/modules/atmospherics/auxgm/breathing_classes.dm
new file mode 100644
index 000000000000..dda45a896de5
--- /dev/null
+++ b/yogstation/code/modules/atmospherics/auxgm/breathing_classes.dm
@@ -0,0 +1,12 @@
+/datum/breathing_class/vox
+ gases = list(
+ GAS_N2 = 1,
+ GAS_CO2 = -0.7,
+ )
+ products = list(
+ GAS_CO2 = 1
+ )
+ low_alert_category = "not_enough_nitro"
+ low_alert_datum = /atom/movable/screen/alert/not_enough_nitro
+ high_alert_category = "too_much_nitro"
+ high_alert_datum = /atom/movable/screen/alert/too_much_nitro
diff --git a/yogstation/code/modules/cargo/bounties/medical.dm b/yogstation/code/modules/cargo/bounties/medical.dm
new file mode 100644
index 000000000000..1916f7a4aba7
--- /dev/null
+++ b/yogstation/code/modules/cargo/bounties/medical.dm
@@ -0,0 +1,5 @@
+/datum/bounty/item/medical/vox_tail
+ name = "Vox Tail"
+ description = "Negotiations have broken down between Central Command and a group of Vox raiders. Ship us a vox tail so we can show them we mean business."
+ reward = 3000
+ wanted_types = list(/obj/item/organ/tail/vox)
diff --git a/yogstation/code/modules/cargo/cargo_packs.dm b/yogstation/code/modules/cargo/cargo_packs.dm
index 638d4b90c0ea..68905411b5c2 100644
--- a/yogstation/code/modules/cargo/cargo_packs.dm
+++ b/yogstation/code/modules/cargo/cargo_packs.dm
@@ -106,6 +106,7 @@
/obj/item/toy/plush/inorixplushie,
/obj/item/toy/plush/beeplushie,
/obj/item/toy/plush/slimeplushie,
+ /obj/item/toy/plush/voxplushie,
/obj/item/toy/plush/cdragon)
crate_name = "plush crate"
crate_type = /obj/structure/closet/crate/wooden
diff --git a/yogstation/code/modules/cargo/exports/organs.dm b/yogstation/code/modules/cargo/exports/organs.dm
new file mode 100644
index 000000000000..5440334e34b8
--- /dev/null
+++ b/yogstation/code/modules/cargo/exports/organs.dm
@@ -0,0 +1,4 @@
+/datum/export/organ/mutant/vox_tail
+ cost = 300
+ unit_name = "vox tail"
+ export_types = list(/obj/item/organ/tail/vox)
diff --git a/yogstation/code/modules/cargo/exports/sheets.dm b/yogstation/code/modules/cargo/exports/sheets.dm
index e2718f56ca14..b2a65d50088d 100644
--- a/yogstation/code/modules/cargo/exports/sheets.dm
+++ b/yogstation/code/modules/cargo/exports/sheets.dm
@@ -1,4 +1,10 @@
/datum/export/stack/skin/gorilla
cost = 150
unit_name = "gorilla hide"
- export_types = list(/obj/item/stack/sheet/animalhide/gorilla)
\ No newline at end of file
+ export_types = list(/obj/item/stack/sheet/animalhide/gorilla)
+
+/datum/export/stack/skin/vox
+ cost = 2500
+ export_limit = 200
+ unit_name = "vox hide"
+ export_types = list(/obj/item/stack/sheet/animalhide/vox)
diff --git a/yogstation/code/modules/client/preferences/_preference.dm b/yogstation/code/modules/client/preferences/_preference.dm
new file mode 100644
index 000000000000..0df2a785aeb1
--- /dev/null
+++ b/yogstation/code/modules/client/preferences/_preference.dm
@@ -0,0 +1,2 @@
+/datum/preference
+ var/list/blacklisted_species = list()
diff --git a/yogstation/code/modules/client/preferences/clothing.dm b/yogstation/code/modules/client/preferences/clothing.dm
new file mode 100644
index 000000000000..9eebd30d1d6a
--- /dev/null
+++ b/yogstation/code/modules/client/preferences/clothing.dm
@@ -0,0 +1,8 @@
+/datum/preference/choiced/socks
+ blacklisted_species = list(/datum/species/vox)
+
+/datum/preference/choiced/undershirt
+ blacklisted_species = list(/datum/species/vox)
+
+/datum/preference/choiced/underwear
+ blacklisted_species = list(/datum/species/vox)
diff --git a/yogstation/code/modules/client/preferences/species_features/mutants.dm b/yogstation/code/modules/client/preferences/species_features/mutants.dm
new file mode 100644
index 000000000000..d9db94b95fbb
--- /dev/null
+++ b/yogstation/code/modules/client/preferences/species_features/mutants.dm
@@ -0,0 +1,15 @@
+/datum/preference/color/mutant_color
+ blacklisted_species = list(/datum/species/vox)
+
+/datum/preference/color/mutant_color_secondary
+ savefile_key = "feature_mcolor_secondary"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ relevant_species_trait = MUTCOLORS_SECONDARY
+ blacklisted_species = list(/datum/species/vox)
+
+/datum/preference/color/mutant_color_secondary/create_default_value()
+ return sanitize_hexcolor("[pick("7F", "FF")][pick("7F", "FF")][pick("7F", "FF")]")
+
+/datum/preference/color/mutant_color_secondary/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["mcolor_secondary"] = value
diff --git a/yogstation/code/modules/client/preferences/species_features/vox.dm b/yogstation/code/modules/client/preferences/species_features/vox.dm
new file mode 100644
index 000000000000..3b08c4b224ea
--- /dev/null
+++ b/yogstation/code/modules/client/preferences/species_features/vox.dm
@@ -0,0 +1,255 @@
+/proc/generate_vox_side_shots(list/sprite_accessories, accessory_color = COLOR_DARKER_BROWN)
+ var/list/values = list()
+ var/icon/vox_head = icon('icons/mob/species/vox/bodyparts.dmi', "vox_head_lime")
+ var/icon/eyes = icon('icons/mob/species/vox/eyes.dmi', "eyes")
+ var/icon/eyes_static = icon('icons/mob/species/vox/eyes.dmi', "eyes_static_green")
+ eyes.Blend(COLOR_CYAN, ICON_MULTIPLY)
+ vox_head.Blend(eyes, ICON_OVERLAY)
+ vox_head.Blend(eyes_static, ICON_OVERLAY)
+ var/icon/beak = icon('icons/mob/species/vox/bodyparts.dmi', "vox_head_static")
+ vox_head.Blend(beak, ICON_OVERLAY)
+ for(var/name in sprite_accessories)
+ var/datum/sprite_accessory/sprite_accessory = sprite_accessories[name]
+ var/icon/final_icon = icon(vox_head)
+ if(name != "None")
+ var/icon/accessory_icon = icon(sprite_accessory.icon, sprite_accessory.icon_state)
+ accessory_icon.Blend(accessory_color, sprite_accessory.color_blend_mode == COLOR_BLEND_ADD ? ICON_ADD : ICON_MULTIPLY)
+ final_icon.Blend(accessory_icon, ICON_OVERLAY)
+ final_icon.Crop(10, 19, 22, 31)
+ final_icon.Scale(32, 32)
+ values[name] = final_icon
+ return values
+
+/datum/preference/choiced/vox_skin_tone
+ savefile_key = "feature_vox_skin_tone"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ relevant_mutant_bodypart = "vox_tail"
+ main_feature_name = "Skin Tone"
+
+/datum/preference/choiced/vox_skin_tone/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["vox_skin_tone"] = value
+
+/datum/preference/choiced/vox_skin_tone/init_possible_values()
+ return GLOB.vox_skin_tones
+
+/datum/preference/choiced/vox_skin_tone/compile_constant_data()
+ var/list/data = ..()
+ var/list/capitalized_skin_tones = list()
+ for(var/skin_tone in GLOB.vox_skin_tones)
+ capitalized_skin_tones[skin_tone] = capitalize(skin_tone)
+ data[CHOICED_PREFERENCE_DISPLAY_NAMES] = capitalized_skin_tones
+ var/list/skin_tones_to_hex = list(
+ "lime" = "#617b0f",
+ "crimson" = "#a32e2e",
+ "plum" = "#564759",
+ "azure" = "#124746",
+ "emerald" = "#04572d",
+ "brown" = "#774c22",
+ "grey" = "#4a514b",
+ "nebula" = "#5a4787",
+ "mossy" = "#626d0d"
+ )
+ var/list/to_hex = list()
+ for (var/choice in get_choices())
+ var/hex_value = skin_tones_to_hex[choice]
+ var/list/hsl = rgb2num(hex_value, COLORSPACE_HSL)
+ to_hex[choice] = list(
+ "lightness" = hsl[3],
+ "value" = hex_value,
+ )
+ data["to_hex"] = to_hex
+ return data
+
+/datum/preference/choiced/vox_tail_markings
+ savefile_key = "feature_vox_tail_markings"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ relevant_mutant_bodypart = "vox_tail_markings"
+
+/datum/preference/choiced/vox_tail_markings/init_possible_values()
+ return assoc_to_keys(GLOB.vox_tail_markings_list)
+
+/datum/preference/choiced/vox_tail_markings/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["vox_tail_markings"] = value
+
+/datum/preference/choiced/vox_body_markings
+ savefile_key = "feature_vox_body_markings"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ relevant_mutant_bodypart = "vox_body_markings"
+
+/datum/preference/choiced/vox_body_markings/init_possible_values()
+ return assoc_to_keys(GLOB.vox_body_markings_list)
+
+/datum/preference/choiced/vox_body_markings/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["vox_body_markings"] = value
+
+/datum/preference/choiced/vox_quills
+ savefile_key = "feature_vox_quills"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_FEATURES
+ should_generate_icons = TRUE
+ relevant_mutant_bodypart = "vox_quills"
+ main_feature_name = "Quills"
+
+/datum/preference/choiced/vox_quills/init_possible_values()
+ return generate_vox_side_shots(GLOB.vox_quills_list)
+
+/datum/preference/choiced/vox_quills/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["vox_quills"] = value
+
+/datum/preference/choiced/vox_quills/compile_constant_data()
+ var/list/data = ..()
+ data[SUPPLEMENTAL_FEATURE_KEY] = "feature_quill_color"
+ return data
+
+/datum/preference/choiced/vox_facial_quills
+ savefile_key = "feature_vox_facial_quills"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_FEATURES
+ should_generate_icons = TRUE
+ relevant_mutant_bodypart = "vox_facial_quills"
+ main_feature_name = "Facial Quills"
+
+/datum/preference/choiced/vox_facial_quills/init_possible_values()
+ return generate_vox_side_shots(GLOB.vox_facial_quills_list)
+
+/datum/preference/choiced/vox_facial_quills/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["vox_facial_quills"] = value
+
+/datum/preference/choiced/vox_facial_quills/compile_constant_data()
+ var/list/data = ..()
+ data[SUPPLEMENTAL_FEATURE_KEY] = "feature_facial_quill_color"
+ return data
+
+/datum/preference/color/hair_color/vox
+ category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES
+ savefile_key = "feature_quill_color"
+ relevant_species_trait = null
+ relevant_mutant_bodypart = "vox_quills"
+ unique = TRUE
+
+/datum/preference/color/facial_hair_color/vox
+ category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES
+ savefile_key = "feature_facial_quill_color"
+ relevant_species_trait = null
+ relevant_mutant_bodypart = "vox_facial_quills"
+ unique = TRUE
+
+/datum/preference/color/mutant_color/vox_body_markings_color
+ savefile_key = "feature_body_markings_color"
+ relevant_mutant_bodypart = "vox_body_markings"
+ relevant_species_trait = null
+ blacklisted_species = null
+ unique = TRUE
+
+/datum/preference/color/mutant_color/vox_body_markings_color/is_valid(value)
+ return findtext(value, GLOB.is_color)
+
+/datum/preference/color/mutant_color_secondary/vox_tail_markings_color
+ savefile_key = "feature_tail_markings_color"
+ relevant_mutant_bodypart = "vox_tail_markings"
+ relevant_species_trait = null
+ blacklisted_species = null
+ unique = TRUE
+
+/datum/preference/choiced/underwear/vox
+ savefile_key = "feature_vox_underwear"
+ relevant_mutant_bodypart = "vox_tail"
+ blacklisted_species = null
+ unique = TRUE
+
+/datum/preference/choiced/underwear/vox/init_possible_values()
+ return generate_values_for_underwear('icons/mob/clothing/species/vox/underwear.dmi', GLOB.underwear_list, list("vox_chest_lime", "vox_r_leg_lime", "vox_l_leg_lime", "vox_r_leg_static", "vox_l_leg_static"), 'icons/mob/species/vox/bodyparts.dmi')
+
+/datum/preference/choiced/socks/vox
+ savefile_key = "feature_vox_socks"
+ relevant_mutant_bodypart = "vox_tail"
+ blacklisted_species = null
+ unique = TRUE
+
+/datum/preference/choiced/socks/vox/init_possible_values()
+ return generate_values_for_underwear('icons/mob/clothing/species/vox/socks.dmi', GLOB.socks_list, list("vox_r_leg_lime", "vox_l_leg_lime", "vox_r_leg_static", "vox_l_leg_static"), 'icons/mob/species/vox/bodyparts.dmi')
+
+/datum/preference/choiced/undershirt/vox
+ savefile_key = "feature_vox_undershirt"
+ should_generate_icons = TRUE
+ relevant_mutant_bodypart = "vox_tail"
+ blacklisted_species = null
+ unique = TRUE
+
+/datum/preference/choiced/undershirt/vox/init_possible_values()
+ var/bodyparts_icon = 'icons/mob/species/vox/bodyparts.dmi'
+ var/icon/body = icon(bodyparts_icon, "vox_r_leg_lime")
+ body.Blend(icon(bodyparts_icon, "vox_l_leg_lime"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_r_arm_lime"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_l_arm_lime"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_r_hand"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_l_hand"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_chest_lime"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_l_arm_static"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_r_arm_static"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_l_leg_static"), ICON_OVERLAY)
+ body.Blend(icon(bodyparts_icon, "vox_r_leg_static"), ICON_OVERLAY)
+ var/vox_undershirt_icon = 'icons/mob/clothing/species/vox/undershirt.dmi'
+ var/list/values = list()
+ var/list/undershirt_list = GLOB.undershirt_list.Copy()
+ for(var/undershirt in undershirt_list)
+ if(undershirt == "Nude")
+ continue
+ var/datum/sprite_accessory/undershirt_accessory = undershirt_list[undershirt]
+ if(!icon_exists(vox_undershirt_icon, undershirt_accessory.icon_state))
+ undershirt_list -= undershirt
+ for(var/accessory_name in undershirt_list)
+ var/icon/icon_with_undershirt = icon(body)
+ if(accessory_name != "Nude")
+ var/datum/sprite_accessory/accessory = undershirt_list[accessory_name]
+ icon_with_undershirt.Blend(icon(vox_undershirt_icon, accessory.icon_state), ICON_OVERLAY)
+ icon_with_undershirt.Crop(9, 9, 23, 23)
+ icon_with_undershirt.Scale(32, 32)
+ values[accessory_name] = icon_with_undershirt
+ return values
+
+/datum/preference/choiced/vox_tank_type
+ savefile_key = "feature_vox_tank_type"
+ relevant_mutant_bodypart = "vox_tail"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+
+/datum/preference/choiced/vox_tank_type/init_possible_values()
+ return list("Large", "Specialized")
+
+/datum/preference/choiced/vox_tank_type/create_default_value()
+ return "Specialized"
+
+/datum/preference/choiced/vox_tank_type/apply_to_human()
+ return
+
+/datum/preference/choiced/vox_mask
+ savefile_key = "feature_vox_mask"
+ relevant_mutant_bodypart = "vox_tail"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_NON_CONTEXTUAL
+
+/datum/preference/choiced/vox_mask/init_possible_values()
+ return list("Breath Mask", "Respirator")
+
+/datum/preference/choiced/vox_mask/create_default_value()
+ return "Breath Mask"
+
+/datum/preference/choiced/vox_mask/apply_to_human()
+ return
+
+/datum/preference/choiced/hair_gradient/vox
+ savefile_key = "feature_quill_gradientstyle"
+ relevant_species_trait = null
+ relevant_mutant_bodypart = "vox_quills"
+ unique = TRUE
+
+/datum/preference/color/hair_gradient/vox
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ savefile_key = "feature_quill_gradientcolor"
+ relevant_species_trait = null
+ relevant_mutant_bodypart = "vox_quills"
+ unique = TRUE
diff --git a/yogstation/code/modules/clothing/clothing.dm b/yogstation/code/modules/clothing/clothing.dm
index d3cf29df1b58..5e66750d7cc2 100644
--- a/yogstation/code/modules/clothing/clothing.dm
+++ b/yogstation/code/modules/clothing/clothing.dm
@@ -1,3 +1,32 @@
+/obj/item/clothing
+ var/list/species_restricted = null //Only these species can wear this kit.
+
+//BS12: Species-restricted clothing check.
+/obj/item/clothing/mob_can_equip(mob/living/M, mob/living/equipper, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE)
+ //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam)
+ if(!..())
+ return FALSE
+ // Skip species restriction checks on non-equipment slots
+ if(slot in list(ITEM_SLOT_BACKPACK, ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET))
+ return TRUE
+ if(species_restricted && ishuman(M))
+ var/wearable
+ var/exclusive
+ var/mob/living/carbon/human/H = M
+ if("exclude" in species_restricted)
+ exclusive = TRUE
+ if(H.dna.species)
+ if(exclusive)
+ if(!(H.dna.species.id in species_restricted))
+ wearable = TRUE
+ else
+ if(H.dna.species.id in species_restricted)
+ wearable = TRUE
+ if(!wearable)
+ to_chat(M, span_warning("Your species cannot wear [src]."))
+ return FALSE
+ return TRUE
+
/obj/item/clothing/ears/yogs
worn_icon = 'yogstation/icons/mob/clothing/ears/ears.dmi'
icon = 'yogstation/icons/obj/clothing/ears.dmi'
diff --git a/yogstation/code/modules/clothing/ears/_ears.dm b/yogstation/code/modules/clothing/ears/_ears.dm
new file mode 100644
index 000000000000..240afdcf4cdd
--- /dev/null
+++ b/yogstation/code/modules/clothing/ears/_ears.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/ears
+ sprite_sheets = list(SPECIES_VOX = VOX_EARS_FILE)
diff --git a/yogstation/code/modules/clothing/glasses/_glasses.dm b/yogstation/code/modules/clothing/glasses/_glasses.dm
index 0481844811b6..63d733d5f1bc 100644
--- a/yogstation/code/modules/clothing/glasses/_glasses.dm
+++ b/yogstation/code/modules/clothing/glasses/_glasses.dm
@@ -1,3 +1,6 @@
+/obj/item/clothing/glasses
+ sprite_sheets = list(SPECIES_VOX = VOX_EYES_FILE)
+
/obj/item/clothing/glasses/sunglasses/cheap
name = "cheap sunglasses"
desc = "Made in China."
diff --git a/yogstation/code/modules/clothing/gloves/_gloves.dm b/yogstation/code/modules/clothing/gloves/_gloves.dm
new file mode 100644
index 000000000000..5ab19a74c095
--- /dev/null
+++ b/yogstation/code/modules/clothing/gloves/_gloves.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/gloves
+ sprite_sheets = list(SPECIES_VOX = VOX_GLOVES_FILE)
diff --git a/yogstation/code/modules/clothing/gloves/miscellaneous.dm b/yogstation/code/modules/clothing/gloves/miscellaneous.dm
index f30b0e6b7063..7754204b13f8 100644
--- a/yogstation/code/modules/clothing/gloves/miscellaneous.dm
+++ b/yogstation/code/modules/clothing/gloves/miscellaneous.dm
@@ -29,3 +29,11 @@
heat_protection = HANDS
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
+
+/obj/item/clothing/gloves/color/yellow/vox
+ name = "insulated gauntlets"
+ desc = "These bizarre gauntlets seem to be fitted for...bird claws?"
+ icon_state = "gloves-vox"
+ item_state = "gloves-vox"
+ icon = 'icons/obj/clothing/species/vox/gloves.dmi'
+ species_restricted = list(SPECIES_VOX)
diff --git a/yogstation/code/modules/clothing/head/_head.dm b/yogstation/code/modules/clothing/head/_head.dm
new file mode 100644
index 000000000000..fa710799e9d6
--- /dev/null
+++ b/yogstation/code/modules/clothing/head/_head.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/head
+ sprite_sheets = list(SPECIES_VOX = VOX_HEAD_FILE)
diff --git a/yogstation/code/modules/clothing/head/helmet.dm b/yogstation/code/modules/clothing/head/helmet.dm
index 6a397a4aec93..6b550b0aed7a 100644
--- a/yogstation/code/modules/clothing/head/helmet.dm
+++ b/yogstation/code/modules/clothing/head/helmet.dm
@@ -1,4 +1,5 @@
/obj/item/clothing/head/helmet
+ sprite_sheets = list(SPECIES_VOX = VOX_HELMET_FILE)
var/initial_state
/obj/item/clothing/head/helmet/Initialize(mapload)
diff --git a/yogstation/code/modules/clothing/masks/_masks.dm b/yogstation/code/modules/clothing/masks/_masks.dm
new file mode 100644
index 000000000000..c4e18c7b3fd9
--- /dev/null
+++ b/yogstation/code/modules/clothing/masks/_masks.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/mask
+ sprite_sheets = list(SPECIES_VOX = VOX_MASK_FILE)
diff --git a/yogstation/code/modules/clothing/masks/breath.dm b/yogstation/code/modules/clothing/masks/breath.dm
new file mode 100644
index 000000000000..c54677883176
--- /dev/null
+++ b/yogstation/code/modules/clothing/masks/breath.dm
@@ -0,0 +1,20 @@
+/obj/item/clothing/mask/breath/vox
+ name = "vox breath mask"
+ desc = "A weirdly-shaped breath mask."
+ icon_state = "voxmask"
+ species_restricted = list(SPECIES_VOX)
+ flags_cover = NONE
+ visor_flags_cover = NONE
+ actions_types = list()
+
+/obj/item/clothing/mask/breath/vox/attack_self(mob/user)
+ return
+
+/obj/item/clothing/mask/breath/vox/AltClick(mob/user)
+ return
+
+/obj/item/clothing/mask/breath/vox/respirator
+ name = "vox respirator"
+ desc = "A weirdly-shaped breath mask, this one seems to be designed for a vox beak."
+ icon_state = "voxmask2"
+ item_state = "voxmask2"
diff --git a/yogstation/code/modules/clothing/shoes/_shoes.dm b/yogstation/code/modules/clothing/shoes/_shoes.dm
new file mode 100644
index 000000000000..31cb4a1d40b6
--- /dev/null
+++ b/yogstation/code/modules/clothing/shoes/_shoes.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/shoes
+ sprite_sheets = list(SPECIES_VOX = VOX_SHOES_FILE)
diff --git a/yogstation/code/modules/clothing/shoes/colour.dm b/yogstation/code/modules/clothing/shoes/colour.dm
new file mode 100644
index 000000000000..f072d8caf51e
--- /dev/null
+++ b/yogstation/code/modules/clothing/shoes/colour.dm
@@ -0,0 +1,5 @@
+/obj/item/clothing/shoes/sneakers
+ sprite_sheets = list("GAGS_sprite" = list(SPECIES_VOX))
+
+/obj/item/clothing/shoes/sneakers/orange
+ sprite_sheets = list("GAGS_sprite" = list(SPECIES_VOX))
diff --git a/yogstation/code/modules/clothing/shoes/magboots.dm b/yogstation/code/modules/clothing/shoes/magboots.dm
new file mode 100644
index 000000000000..74db4dc7fd68
--- /dev/null
+++ b/yogstation/code/modules/clothing/shoes/magboots.dm
@@ -0,0 +1,45 @@
+/obj/item/clothing/shoes/magboots/vox
+ name = "vox magclaws"
+ desc = "A pair of heavy, jagged armoured foot pieces, seemingly suitable for a velociraptor."
+ item_state = "boots-vox"
+ icon_state = "boots-vox"
+ icon = 'icons/obj/clothing/species/vox/shoes.dmi'
+ species_restricted = list(SPECIES_VOX)
+
+/obj/item/clothing/shoes/magboots/vox/attack_self(mob/user)
+ if(magpulse)
+ clothing_flags &= ~NOSLIP
+ REMOVE_TRAIT(src, TRAIT_NODROP, "vox_magclaws")
+ to_chat(user, "You relax your deathgrip on the flooring.")
+ else
+ //make sure these can only be used when equipped.
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+ if(H.shoes != src)
+ to_chat(user, span_warning("You will have to put on [src] before you can do that."))
+ return
+ clothing_flags |= NOSLIP //kinda hard to take off magclaws when you are gripping them tightly.
+ ADD_TRAIT(src, TRAIT_NODROP, "vox_magclaws")
+ to_chat(user, "You dig your claws deeply into the flooring, bracing yourself.")
+ to_chat(user, "It would be hard to take off [src] without relaxing your grip first.")
+ magpulse = !magpulse
+ user.update_inv_shoes() //so our mob-overlays update
+ user.update_gravity(user.has_gravity())
+ for(var/X in actions)
+ var/datum/action/A = X
+ A.build_all_button_icons()
+
+//In case they somehow come off while enabled.
+/obj/item/clothing/shoes/magboots/vox/dropped(mob/user)
+ ..()
+ if(magpulse)
+ user.visible_message("[src] go limp as they are removed from [usr]'s feet.", "[src] go limp as they are removed from your feet.")
+ magpulse = FALSE
+ clothing_flags &= ~NOSLIP
+ REMOVE_TRAIT(src, TRAIT_NODROP, "vox_magclaws")
+
+/obj/item/clothing/shoes/magboots/vox/examine(mob/user)
+ . = ..()
+ if(magpulse)
+ . += "It would be hard to take these off without relaxing your grip first."//theoretically this message should only be seen by the wearer when the claws are equipped.
diff --git a/yogstation/code/modules/clothing/spacesuits/alien.dm b/yogstation/code/modules/clothing/spacesuits/alien.dm
new file mode 100644
index 000000000000..fa1c19291cae
--- /dev/null
+++ b/yogstation/code/modules/clothing/spacesuits/alien.dm
@@ -0,0 +1,68 @@
+// Vox space gear (vaccuum suit, low pressure armour)
+// Can't be equipped by any other species due to bone structure and vox cybernetics.
+
+/obj/item/clothing/suit/space/vox
+ w_class = WEIGHT_CLASS_NORMAL
+ allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/transforming/energy/sword, \
+ /obj/item/restraints/handcuffs, /obj/item/tank/internals)
+ armor = list(MELEE = 40, BULLET = 40, LASER = 30, ENERGY = 15, BOMB = 30, BIO = 30, RAD = 30, FIRE = 80, ACID = 85)
+ icon = 'icons/obj/clothing/species/vox/suits.dmi'
+ species_restricted = list(SPECIES_VOX)
+ slowdown = 2
+
+/obj/item/clothing/head/helmet/space/vox
+ armor = list(MELEE = 40, BULLET = 40, LASER = 30, ENERGY = 15, BOMB = 30, BIO = 30, RAD = 30, FIRE = 80, ACID = 85)
+ clothing_flags = STOPSPRESSUREDAMAGE
+ flags_cover = HEADCOVERSEYES
+ icon = 'icons/obj/clothing/species/vox/hats.dmi'
+ species_restricted = list(SPECIES_VOX)
+ sprite_sheets = list(SPECIES_VOX = VOX_HEAD_FILE)
+
+/obj/item/clothing/head/helmet/space/vox/pressure
+ name = "alien helmet"
+ icon_state = "vox-pressure"
+ item_state = "vox-pressure"
+ desc = "Hey, wasn't this a prop in \'The Abyss\'?"
+
+/obj/item/clothing/suit/space/vox/pressure
+ name = "alien pressure suit"
+ icon_state = "vox-pressure"
+ item_state = "vox-pressure"
+ desc = "A huge, armoured, pressurized suit, designed for distinctly nonhuman proportions."
+
+/obj/item/clothing/head/helmet/space/vox/carapace
+ name = "alien visor"
+ icon_state = "vox-carapace"
+ item_state = "vox-carapace"
+ desc = "A glowing visor, perhaps stolen from a depressed Cylon."
+
+/obj/item/clothing/suit/space/vox/carapace
+ name = "alien carapace armour"
+ icon_state = "vox-carapace"
+ item_state = "vox-carapace"
+ desc = "An armoured, segmented carapace with glowing purple lights. It looks pretty run-down."
+ slowdown = 1
+
+/obj/item/clothing/head/helmet/space/vox/stealth
+ name = "alien stealth helmet"
+ icon_state = "vox-stealth"
+ item_state = "vox-stealth"
+ desc = "A smoothly contoured, matte-black alien helmet."
+
+/obj/item/clothing/suit/space/vox/stealth
+ name = "alien stealth suit"
+ icon_state = "vox-stealth"
+ item_state = "vox-stealth"
+ desc = "A sleek black suit. It seems to have a tail, and is very heavy."
+
+/obj/item/clothing/head/helmet/space/vox/medic
+ name = "alien goggled helmet"
+ icon_state = "vox-medic"
+ item_state = "vox-medic"
+ desc = "An alien helmet with enormous goggled lenses."
+
+/obj/item/clothing/suit/space/vox/medic
+ name = "alien armour"
+ icon_state = "vox-medic"
+ item_state = "vox-medic"
+ desc = "An almost organic looking nonhuman pressure suit."
diff --git a/yogstation/code/modules/clothing/suits/_suits.dm b/yogstation/code/modules/clothing/suits/_suits.dm
new file mode 100644
index 000000000000..3772a3097154
--- /dev/null
+++ b/yogstation/code/modules/clothing/suits/_suits.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/suit
+ sprite_sheets = list(SPECIES_VOX = VOX_SUIT_FILE)
diff --git a/yogstation/code/modules/clothing/suits/miscellaneous.dm b/yogstation/code/modules/clothing/suits/miscellaneous.dm
index 09dec5e8907e..f5d7016d9b9d 100644
--- a/yogstation/code/modules/clothing/suits/miscellaneous.dm
+++ b/yogstation/code/modules/clothing/suits/miscellaneous.dm
@@ -403,3 +403,32 @@
desc = "You're taking the piss outta this, aren't you?"
icon_state = "urinal"
item_state = "urinal"
+
+/obj/item/clothing/suit/hooded/vox_robes
+ name = "alien hooded robes"
+ desc = "Large, comfortable robes worn by those who need a bit more covering. The thick fabric contains a pocket suitable for those that need their hands free during their work, while the cloth serves to cover scars or other injuries to the wearer's body."
+ icon = VOX_SUIT_FILE
+ icon_state = "vox-robes"
+ item_state = "vox-robes"
+ body_parts_covered = CHEST|GROIN|LEGS|ARMS
+ hoodtype = /obj/item/clothing/head/hooded/vox_robe_hood
+ species_restricted = list(SPECIES_VOX)
+
+/obj/item/clothing/head/hooded/vox_robe_hood
+ name = "alien hood"
+ desc = "The thick fabric of this hood serves a variety of purposes to the vox wearing it - serving as a method to hide a scarred face or a way to keep warm in the coldest areas onboard the ship."
+ icon = VOX_HEAD_FILE
+ icon_state = "vox-robes-hood"
+ item_state = "vox-robes-hood"
+ flags_inv = HIDEHAIR
+ flags_cover = HEADCOVERSEYES
+ species_restricted = list(SPECIES_VOX)
+
+/obj/item/clothing/suit/armor/vox_scrap
+ name = "rusted metal armor"
+ desc = "A hodgepodge of various pieces of metal scrapped together into a rudimentary vox-shaped piece of armor."
+ allowed = list(/obj/item/gun, /obj/item/tank)
+ armor = list(MELEE = 70, BULLET = 30, LASER = 20, ENERGY = 5, BOMB = 40, BIO = 0, RAD = 0, FIRE = 50, ACID = 50, WOUND = 15) //Higher melee armor versus lower everything else.
+ icon_state = "vox-scrap"
+ body_parts_covered = CHEST|ARMS|GROIN|LEGS
+ species_restricted = list(SPECIES_VOX)
diff --git a/yogstation/code/modules/clothing/under/_under.dm b/yogstation/code/modules/clothing/under/_under.dm
new file mode 100644
index 000000000000..9b8a4beeba16
--- /dev/null
+++ b/yogstation/code/modules/clothing/under/_under.dm
@@ -0,0 +1,2 @@
+/obj/item/clothing/under
+ sprite_sheets = list(SPECIES_VOX = VOX_UNIFORM_FILE)
diff --git a/yogstation/code/modules/clothing/under/color.dm b/yogstation/code/modules/clothing/under/color.dm
new file mode 100644
index 000000000000..ece2b7d9933b
--- /dev/null
+++ b/yogstation/code/modules/clothing/under/color.dm
@@ -0,0 +1,5 @@
+/obj/item/clothing/under/color
+ sprite_sheets = list("GAGS_sprite" = list(SPECIES_VOX))
+
+/obj/item/clothing/under/skirt/color
+ sprite_sheets = list("GAGS_sprite" = list(SPECIES_VOX))
diff --git a/yogstation/code/modules/clothing/under/miscellaneous.dm b/yogstation/code/modules/clothing/under/miscellaneous.dm
index 0ae9bc0d2380..193b7b42e5cd 100644
--- a/yogstation/code/modules/clothing/under/miscellaneous.dm
+++ b/yogstation/code/modules/clothing/under/miscellaneous.dm
@@ -574,3 +574,28 @@
item_state = "hawaiian_skirt"
fitted = FEMALE_UNIFORM_TOP //no hole in the skirt
can_adjust = 0
+
+/obj/item/clothing/under/rank/prisoner
+ sprite_sheets = list("GAGS_sprite" = list(SPECIES_VOX))
+
+/obj/item/clothing/under/vox
+ icon = 'icons/obj/clothing/species/vox/uniforms.dmi'
+ species_restricted = list(SPECIES_VOX)
+
+/obj/item/clothing/under/vox/vox_casual
+ name = "alien clothing"
+ desc = "This doesn't look very comfortable."
+ icon_state = "vox-casual-1"
+ item_state = "vox-casual-1"
+
+/obj/item/clothing/under/vox/vox_robes
+ name = "alien robes"
+ desc = "Weird and flowing!"
+ icon_state = "vox-casual-2"
+ item_state = "vox-casual-2"
+
+/obj/item/clothing/under/vox/vox_casual
+ name = "alien jumpsuit"
+ desc = "These loose clothes are optimized for the labors of the lower castes onboard the arkships. Large openings in the top allow for breathability while the pants are durable yet flexible enough to not restrict movement."
+ icon_state = "vox-jumpsuit"
+ item_state = "vox-jumpsuit"
diff --git a/yogstation/code/modules/food_and_drinks/food/recipes/tablecraft/recipes_meat.dm b/yogstation/code/modules/food_and_drinks/food/recipes/tablecraft/recipes_meat.dm
new file mode 100644
index 000000000000..c1ee2c11344e
--- /dev/null
+++ b/yogstation/code/modules/food_and_drinks/food/recipes/tablecraft/recipes_meat.dm
@@ -0,0 +1,8 @@
+/datum/crafting_recipe/food/fried_vox
+ name = "Kentucky Fried Vox"
+ reqs = list(
+ /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/vox = 1,
+ /obj/item/stack/sheet/animalhide/vox = 1
+ )
+ result = /obj/item/reagent_containers/food/snacks/fried_vox
+ category = CAT_MEAT
diff --git a/yogstation/code/modules/food_and_drinks/food/snacks/meat.dm b/yogstation/code/modules/food_and_drinks/food/snacks/meat.dm
index fd94bd15add4..18cede39ff29 100644
--- a/yogstation/code/modules/food_and_drinks/food/snacks/meat.dm
+++ b/yogstation/code/modules/food_and_drinks/food/snacks/meat.dm
@@ -3,4 +3,11 @@
desc = "Damn dirty steak."
filling_color = "#FF0000"
tastes = list("meat" = 4, "fur" = 1)
- foodtype = MEAT | RAW
\ No newline at end of file
+ foodtype = MEAT | RAW
+
+/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/vox
+ icon_state = "voxmeat"
+ desc = "Surprisingly, it doesn't actually taste like chicken."
+ filling_color = COLOR_BLOOD_VOX
+ tastes = list("skrek" = 1)
+ foodtype = MEAT | RAW | TOXIC
diff --git a/yogstation/code/modules/food_and_drinks/food/snacks_meat.dm b/yogstation/code/modules/food_and_drinks/food/snacks_meat.dm
new file mode 100644
index 000000000000..edf48bd61ff9
--- /dev/null
+++ b/yogstation/code/modules/food_and_drinks/food/snacks_meat.dm
@@ -0,0 +1,7 @@
+/obj/item/reagent_containers/food/snacks/fried_vox
+ name = "Kentucky Fried Vox"
+ desc = "11 herbs and spices."
+ icon_state = "fried_vox"
+ trash = /obj/item/trash/fried_vox
+ list_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/nutriment/protein = 5)
+ tastes = list("quills" = 1, "the Shoal" = 1)
diff --git a/yogstation/code/modules/language/language_holder.dm b/yogstation/code/modules/language/language_holder.dm
new file mode 100644
index 000000000000..df4e5f7e51fb
--- /dev/null
+++ b/yogstation/code/modules/language/language_holder.dm
@@ -0,0 +1,5 @@
+/datum/language_holder/vox
+ understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/vox = list(LANGUAGE_ATOM))
+ spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+ /datum/language/vox = list(LANGUAGE_ATOM))
diff --git a/yogstation/code/modules/language/voxpidgin.dm b/yogstation/code/modules/language/voxpidgin.dm
new file mode 100644
index 000000000000..353c94f8a99b
--- /dev/null
+++ b/yogstation/code/modules/language/voxpidgin.dm
@@ -0,0 +1,11 @@
+/datum/language/vox
+ name = "Vox-pidgin"
+ desc = "The common tongue of the various Vox ships making up the Shoal. It sounds like chaotic shrieking to everyone else."
+ speech_verb = "shrieks"
+ ask_verb = "creels"
+ exclaim_verb = "loudly skrees"
+ key = "v"
+ syllables = list("ti","ti","ti","hi","hi","ki","ki","ki","ki","ya","ta","ha","ka","ya","yi","chi","cha","kah", \
+ "SKRE","AHK","EHK","RAWK","KRA","AAA","EEE","KI","II","KRI","KA")
+ default_priority = 90
+ icon_state = "voxpidgin"
diff --git a/yogstation/code/modules/mob/dead/new_player/sprite_accessories.dm b/yogstation/code/modules/mob/dead/new_player/sprite_accessories.dm
index b6c3742e18d4..8b4a7c482588 100644
--- a/yogstation/code/modules/mob/dead/new_player/sprite_accessories.dm
+++ b/yogstation/code/modules/mob/dead/new_player/sprite_accessories.dm
@@ -1,5 +1,303 @@
+/datum/sprite_accessory
+ var/color_blend_mode = COLOR_BLEND_MULTIPLY
+ ///the body slots outside of the main slot this accessory exists in, so we can draw to those spots seperately
+ var/list/body_slots = list()
+ /// the list of external organs covered
+ var/list/external_slots = list()
+ var/list/sprite_sheets = list() //For accessories common across species but need to use 'fitted' sprites (like underwear). e.g. list(SPECIES_VOX = 'icons/mob/species/vox/iconfile.dmi')
+
+/datum/sprite_accessory/undershirt
+ sprite_sheets = list(SPECIES_VOX = 'icons/mob/clothing/species/vox/undershirt.dmi')
+
/datum/sprite_accessory/undershirt/goose
name = "Shirt (Vomit Goose)"
icon_state = "vomitgooseshirt"
icon = 'yogstation/icons/mob/clothing/sprite_accessories/undershirt.dmi'
gender = NEUTER
+
+/datum/sprite_accessory/underwear
+ sprite_sheets = list(SPECIES_VOX = 'icons/mob/clothing/species/vox/underwear.dmi')
+
+/datum/sprite_accessory/socks
+ sprite_sheets = list(SPECIES_VOX = 'icons/mob/clothing/species/vox/socks.dmi')
+
+// Vox Accessories
+
+/datum/sprite_accessory/vox_quills
+ icon = 'icons/mob/species/vox/quills.dmi'
+ color_src = HAIR
+ color_blend_mode = COLOR_BLEND_ADD
+
+/datum/sprite_accessory/vox_quills/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/vox_quills/crested
+ name = "Crested"
+ icon_state = "crested"
+
+/datum/sprite_accessory/vox_quills/emperor
+ name = "Emperor"
+ icon_state = "emperor"
+
+/datum/sprite_accessory/vox_quills/keel
+ name = "Keel"
+ icon_state = "keel"
+
+/datum/sprite_accessory/vox_quills/keet
+ name = "Keet"
+ icon_state = "keet"
+
+/datum/sprite_accessory/vox_quills/short
+ name = "Short"
+ icon_state = "short"
+
+/datum/sprite_accessory/vox_quills/tiel
+ name = "Tiel"
+ icon_state = "tiel"
+
+/datum/sprite_accessory/vox_quills/kingly
+ name = "Kingly"
+ icon_state = "kingly"
+
+/datum/sprite_accessory/vox_quills/afro
+ name = "Fluffy"
+ icon_state = "afro"
+
+/datum/sprite_accessory/vox_quills/yasuhiro
+ name = "Long"
+ icon_state = "yasuhiro"
+
+/datum/sprite_accessory/vox_quills/razor
+ name = "Razorback"
+ icon_state = "razor"
+
+/datum/sprite_accessory/vox_quills/razor_clipped
+ name = "Clipped Razorback"
+ icon_state = "razor_clipped"
+
+/datum/sprite_accessory/vox_quills/long_braid
+ name = "Long Braided"
+ icon_state = "long_braid"
+
+/datum/sprite_accessory/vox_quills/short_braid
+ name = "Short Braided"
+ icon_state = "short_braid"
+
+/datum/sprite_accessory/vox_quills/mowhawk
+ name = "Mohawk"
+ icon_state = "mohawk"
+
+/datum/sprite_accessory/vox_quills/hawk
+ name = "Hawk"
+ icon_state = "hawk"
+
+/datum/sprite_accessory/vox_quills/horns
+ name = "Horns"
+ icon_state = "horns"
+
+/datum/sprite_accessory/vox_quills/mange
+ name = "Mange"
+ icon_state = "mange"
+
+/datum/sprite_accessory/vox_quills/ponytail
+ name = "Ponytail"
+ icon_state = "ponytail"
+
+/datum/sprite_accessory/vox_quills/rows
+ name = "Rows"
+ icon_state = "rows"
+
+/datum/sprite_accessory/vox_quills/surf
+ name = "Surf"
+ icon_state = "surf"
+
+/datum/sprite_accessory/vox_quills/flowing
+ name = "Flowing"
+ icon_state = "flowing"
+
+/datum/sprite_accessory/vox_quills/nights
+ name = "Nights"
+ icon_state = "nights"
+
+/datum/sprite_accessory/vox_quills/cropped
+ name = "Cropped"
+ icon_state = "cropped"
+
+/datum/sprite_accessory/vox_facial_quills
+ icon = 'icons/mob/species/vox/facial_quills.dmi'
+ color_src = FACEHAIR
+ color_blend_mode = COLOR_BLEND_ADD
+
+/datum/sprite_accessory/vox_facial_quills/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/vox_facial_quills/colonel
+ name = "Colonel"
+ icon_state = "colonel"
+
+/datum/sprite_accessory/vox_facial_quills/fu
+ name = "Fu"
+ icon_state = "fu"
+
+/datum/sprite_accessory/vox_facial_quills/neck
+ name = "Neck"
+ icon_state = "neck"
+
+/datum/sprite_accessory/vox_facial_quills/beard
+ name = "Beard"
+ icon_state = "beard"
+
+/datum/sprite_accessory/vox_facial_quills/ruff
+ name = "Ruff"
+ icon_state = "ruff"
+
+/datum/sprite_accessory/vox_tails
+ icon = 'icons/mob/species/vox/tails.dmi'
+ color_src = NONE
+
+/datum/sprite_accessory/vox_tails/lime
+ name = "lime"
+ icon_state = "lime"
+
+/datum/sprite_accessory/vox_tails/crimson
+ name = "crimson"
+ icon_state = "crimson"
+
+/datum/sprite_accessory/vox_tails/grey
+ name = "grey"
+ icon_state = "grey"
+
+/datum/sprite_accessory/vox_tails/nebula
+ name = "nebula"
+ icon_state = "nebula"
+
+/datum/sprite_accessory/vox_tails/azure
+ name = "azure"
+ icon_state = "azure"
+
+/datum/sprite_accessory/vox_tails/emerald
+ name = "emerald"
+ icon_state = "emerald"
+
+/datum/sprite_accessory/vox_tails/brown
+ name = "brown"
+ icon_state = "brown"
+
+/datum/sprite_accessory/vox_tails/plum
+ name = "plum"
+ icon_state = "plum"
+
+/datum/sprite_accessory/vox_tails/mossy
+ name = "mossy"
+ icon_state = "mossy"
+
+/datum/sprite_accessory/tails_animated/vox
+ icon = 'icons/mob/species/vox/tails.dmi'
+ color_src = NONE
+
+/datum/sprite_accessory/tails_animated/vox/lime
+ name = "lime"
+ icon_state = "lime"
+
+/datum/sprite_accessory/tails_animated/vox/crimson
+ name = "crimson"
+ icon_state = "crimson"
+
+/datum/sprite_accessory/tails_animated/vox/grey
+ name = "grey"
+ icon_state = "grey"
+
+/datum/sprite_accessory/tails_animated/vox/nebula
+ name = "nebula"
+ icon_state = "nebula"
+
+/datum/sprite_accessory/tails_animated/vox/azure
+ name = "azure"
+ icon_state = "azure"
+
+/datum/sprite_accessory/tails_animated/vox/emerald
+ name = "emerald"
+ icon_state = "emerald"
+
+/datum/sprite_accessory/tails_animated/vox/brown
+ name = "brown"
+ icon_state = "brown"
+
+/datum/sprite_accessory/tails_animated/vox/plum
+ name = "plum"
+ icon_state = "plum"
+
+/datum/sprite_accessory/tails_animated/vox/mossy
+ name = "mossy"
+ icon_state = "mossy"
+
+/datum/sprite_accessory/vox_body_markings
+ icon = 'icons/mob/species/vox/body_markings.dmi'
+ color_blend_mode = COLOR_BLEND_ADD
+
+/datum/sprite_accessory/vox_body_markings/none
+ name = "None"
+
+/datum/sprite_accessory/vox_body_markings/heart
+ name = "Heart"
+ icon_state = "heart"
+ body_slots = list(BODY_ZONE_R_ARM)
+
+/datum/sprite_accessory/vox_body_markings/hive
+ name = "Hive"
+ icon_state = "hive"
+ body_slots = list(BODY_ZONE_CHEST)
+
+/datum/sprite_accessory/vox_body_markings/nightling
+ name = "Nightling"
+ icon_state = "nightling"
+ body_slots = list(BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)
+
+/datum/sprite_accessory/vox_body_markings/tiger_body
+ name = "Tiger-stripe"
+ icon_state = "tiger"
+ body_slots = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+
+/datum/sprite_accessory/vox_tail_markings
+ icon = 'icons/mob/species/vox/tail_markings.dmi'
+ color_src = MUTCOLORS_SECONDARY
+ color_blend_mode = COLOR_BLEND_ADD
+
+/datum/sprite_accessory/vox_tail_markings/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/vox_tail_markings/bands
+ name = "Bands"
+ icon_state = "bands"
+
+/datum/sprite_accessory/vox_tail_markings/tip
+ name = "Tip"
+ icon_state = "tip"
+
+/datum/sprite_accessory/vox_tail_markings/stripe
+ name = "Stripe"
+ icon_state = "stripe"
+
+/datum/sprite_accessory/vox_tail_markings_animated
+ icon = 'icons/mob/species/vox/tail_markings.dmi'
+ color_src = MUTCOLORS_SECONDARY
+ color_blend_mode = COLOR_BLEND_ADD
+
+/datum/sprite_accessory/vox_tail_markings_animated/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/vox_tail_markings_animated/bands
+ name = "Bands"
+ icon_state = "bands"
+
+/datum/sprite_accessory/vox_tail_markings_animated/tip
+ name = "Tip"
+ icon_state = "tip"
+
+/datum/sprite_accessory/vox_tail_markings_animated/stripe
+ name = "Stripe"
+ icon_state = "stripe"
diff --git a/yogstation/code/modules/mob/living/brain/MMI.dm b/yogstation/code/modules/mob/living/brain/MMI.dm
index 6fc408eca8df..f95595d96224 100644
--- a/yogstation/code/modules/mob/living/brain/MMI.dm
+++ b/yogstation/code/modules/mob/living/brain/MMI.dm
@@ -6,7 +6,7 @@
brain = newbrain
name = "[initial(name)]: [brain.real_name]"
- to_chat(brainmob, "(If your brain was removed after your death you don't remember how you died, or who killed you. See rule 1.6.") //yogs
+ to_chat(brainmob, "(If your [brain.brain_name] was removed after your death you don't remember how you died, or who killed you. See rule 1.6.") //yogs
update_appearance(UPDATE_ICON)
return
diff --git a/yogstation/code/modules/mob/living/brain/brain_item.dm b/yogstation/code/modules/mob/living/brain/brain_item.dm
index 72746d0c20e7..f97f3554decb 100644
--- a/yogstation/code/modules/mob/living/brain/brain_item.dm
+++ b/yogstation/code/modules/mob/living/brain/brain_item.dm
@@ -1,6 +1,27 @@
/obj/item/organ/brain
var/real_name = null //the name we use for MMIs, only used by changelings
+ var/brain_name = "brain"
/obj/item/organ/brain/transfer_identity(mob/living/L)
real_name = L.real_name
- .=..()
\ No newline at end of file
+ .=..()
+
+/obj/item/organ/brain/proc/get_mmi_brain_sprite()
+ return "mmi_brain"
+
+/obj/item/organ/brain/alien/get_mmi_brain_sprite()
+ return "mmi_brain_alien"
+
+/obj/item/organ/brain/vox
+ name = "cortical stack"
+ brain_name = "cortical stack"
+ desc = "A peculiarly advanced bio-electronic device that seems to hold the memories and identity of a Vox."
+ icon_state = "cortical-stack"
+ decay_factor = 0
+
+/obj/item/organ/brain/vox/get_mmi_brain_sprite()
+ return "mmi_cortical_stack"
+
+/obj/item/organ/brain/vox/emp_act(severity)
+ to_chat(owner, span_warning("Your head hurts."))
+ owner.adjustOrganLoss(ORGAN_SLOT_BRAIN, clamp(severity*5, 5, 50))
diff --git a/yogstation/code/modules/mob/living/carbon/human/emote.dm b/yogstation/code/modules/mob/living/carbon/human/emote.dm
new file mode 100644
index 000000000000..52f0013ab290
--- /dev/null
+++ b/yogstation/code/modules/mob/living/carbon/human/emote.dm
@@ -0,0 +1,20 @@
+/datum/emote/living/carbon/human/scream
+ message_vox = "shrieks!"
+
+/datum/emote/living/carbon/human/quill
+ key = "quill"
+ key_third_person = "quills"
+ message = "rustles their quills."
+ message_param = "rustles their quills at %t."
+ emote_type = EMOTE_AUDIBLE
+ // Credit to sound-ideas (freesfx.co.uk) for the sound.
+
+/datum/emote/living/carbon/human/quill/get_sound(mob/living/user)
+ return 'sound/effects/voxrustle.ogg'
+
+/datum/emote/living/carbon/human/quill/can_run_emote(mob/user, status_check, intentional)
+ . = ..()
+ if(!.)
+ return FALSE
+ if(!isvox(user))
+ return FALSE
diff --git a/yogstation/code/modules/mob/living/carbon/human/human.dm b/yogstation/code/modules/mob/living/carbon/human/human.dm
index ea569d177b53..df1047b565ec 100644
--- a/yogstation/code/modules/mob/living/carbon/human/human.dm
+++ b/yogstation/code/modules/mob/living/carbon/human/human.dm
@@ -16,6 +16,9 @@
/mob/living/carbon/human/species/pod/ivymen //jungleland
race = /datum/species/pod/ivymen
+/mob/living/carbon/human/species/vox
+ race = /datum/species/vox
+
/mob/living/carbon/human/get_blood_state()
if(NOBLOOD in dna.species.species_traits) //Can't have blood problems if your species doesn't have any blood, innit?
return BLOOD_SAFE
diff --git a/yogstation/code/modules/mob/living/carbon/human/species.dm b/yogstation/code/modules/mob/living/carbon/human/species.dm
index ad7bc8cb7048..fb3b92355c26 100644
--- a/yogstation/code/modules/mob/living/carbon/human/species.dm
+++ b/yogstation/code/modules/mob/living/carbon/human/species.dm
@@ -4,6 +4,17 @@
/datum/species
var/yogs_draw_robot_hair = FALSE //DAMN ROBOTS STEALING OUR HAIR AND AIR
var/yogs_virus_infect_chance = 100
+ ///Should we use the same static husk sprite or create a husk sprite on-the-fly by recoloring bodyparts
+ var/generate_husk_icon = FALSE
+ var/limb_icon_file
+ ///Icon containing eye sprites, this is copied to the head upon updating the head
+ var/eyes_icon = 'icons/mob/human_face.dmi'
+ ///Which body zones contain "static" sprite parts, which are independent of the rest of the sprite
+ var/list/static_part_body_zones
+ ///Species specific suicide messages, these are added to the global list of human suicide messages
+ var/list/suicide_messages
+ ///Items to add to and remove from the survival box in backpacks
+ var/list/survival_box_replacements /*= list(items_to_delete= list(), new_items= list())*/
/datum/species/on_species_gain(mob/living/carbon/C, datum/species/old_species, pref_load)
. = ..()
@@ -18,3 +29,61 @@
/datum/species/proc/spec_AltClickOn(atom/A,mob/living/carbon/human/H)
return FALSE
+
+/datum/species/proc/return_accessory_layer(layer, datum/sprite_accessory/added_accessory, mob/living/carbon/human/host, passed_color)
+ var/list/return_list = list()
+ var/layertext = mutant_bodyparts_layertext(layer)
+ var/g = (host.gender == FEMALE) ? "f" : "m"
+ for(var/list_item in added_accessory.external_slots)
+ var/can_hidden_render = return_external_render_state(list_item, host)
+ if(!can_hidden_render)
+ continue // we failed the render check just dont bother
+ if(!host.getorganslot(list_item))
+ continue
+ var/mutable_appearance/new_overlay = mutable_appearance(added_accessory.icon, layer = -layer)
+ if(added_accessory.gender_specific)
+ new_overlay.icon_state = "[g]_[list_item]_[added_accessory.icon_state]_[layertext]"
+ else
+ new_overlay.icon_state = "m_[list_item]_[added_accessory.icon_state]_[layertext]"
+ new_overlay.color = passed_color
+ return_list += new_overlay
+
+ for(var/list_item in added_accessory.body_slots)
+ if(!host.get_bodypart(list_item))
+ continue
+ var/mutable_appearance/new_overlay = mutable_appearance(added_accessory.icon, layer = -layer)
+ if(added_accessory.gender_specific)
+ new_overlay.icon_state = "[g]_[list_item]_[added_accessory.icon_state]_[layertext]"
+ else
+ new_overlay.icon_state = "m_[list_item]_[added_accessory.icon_state]_[layertext]"
+ new_overlay.color = passed_color
+ return_list += new_overlay
+
+ return return_list
+
+/proc/return_external_render_state(external_slot, mob/living/carbon/human/human)
+ switch(external_slot)
+ if(ORGAN_SLOT_TAIL)
+ if(human.wear_suit && (human.wear_suit.flags_inv & HIDEJUMPSUIT))
+ return FALSE
+ return TRUE
+
+/datum/species/proc/get_icon_variant(mob/living/carbon/person_to_check)
+ return
+
+///Species with eyes bigger than 1px may have an independent part of the eye sprite
+/datum/species/proc/get_eyes_static(mob/living/carbon/person_to_check)
+ return
+
+///Species with static sprite parts in their bodyparts, may have variant static parts. Example is the Mossy Vox skin tone, which looks much scruffier than the others
+/datum/species/proc/get_special_statics(mob/living/carbon/person_to_check)
+ return list()
+
+///Proc to swap out contents of survival boxes for species with different contents, examples are Plasmamen and Vox
+/datum/species/proc/survival_box_replacement(mob/living/carbon/human/box_holder, obj/item/storage/box/survival_box, list/soon_deleted_items, list/soon_added_items)
+ for(var/item as anything in soon_deleted_items)
+ var/obj/item/item_to_delete = (locate(item) in survival_box)
+ if(item_to_delete)
+ qdel(item_to_delete)
+ for(var/item as anything in soon_added_items)
+ new item(survival_box)
diff --git a/yogstation/code/modules/mob/living/carbon/human/species_types/vox.dm b/yogstation/code/modules/mob/living/carbon/human/species_types/vox.dm
new file mode 100644
index 000000000000..b6cb8a8ffd49
--- /dev/null
+++ b/yogstation/code/modules/mob/living/carbon/human/species_types/vox.dm
@@ -0,0 +1,184 @@
+/datum/species/vox
+ name = "Vox"
+ plural_form = "Vox"
+ id = SPECIES_VOX
+ is_dimorphic = FALSE
+ generate_husk_icon = TRUE
+ species_traits = list(EYECOLOR, HAS_TAIL, HAS_FLESH, HAS_BONE, HAIRCOLOR, FACEHAIRCOLOR, MUTCOLORS, MUTCOLORS_SECONDARY) // Robust, but cannot be cloned easily.
+ inherent_traits = list(TRAIT_RESISTCOLD, TRAIT_NOCLONE)
+ mutant_bodyparts = list("vox_quills", "vox_body_markings", "vox_facial_quills", "vox_tail", "vox_tail_markings")
+ default_features = list("vox_quills" = "None", "vox_facial_quills" = "None", "vox_body_markings" = "None", "vox_tail" = "lime", "vox_tail_markings" = "None", "vox_skin_tone" = "lime")
+ attack_verbs = list("scratch", "claw")
+ attack_effect = ATTACK_EFFECT_CLAW
+ attack_sound = 'sound/weapons/slash.ogg'
+ miss_sound = 'sound/weapons/slashmiss.ogg'
+ screamsound = list('sound/voice/vox/shriek1.ogg'/*, 'sound/voice/vox/ashriek.ogg'*/)
+ mutantbrain = /obj/item/organ/brain/vox // Brain damage on EMP
+ mutantheart = /obj/item/organ/heart/vox
+ mutantliver = /obj/item/organ/liver/vox // Liver damage on EMP
+ mutantstomach = /obj/item/organ/stomach/vox // Disgust on EMP
+ mutanttongue = /obj/item/organ/tongue/vox
+ mutantlungs = /obj/item/organ/lungs/vox // Causes them to.. gasp.
+ mutantears = /obj/item/organ/ears/vox // Very brief deafness
+ mutanteyes = /obj/item/organ/eyes/vox // Quick hallucination
+ mutanttail = /obj/item/organ/tail/vox
+ mutantappendix = /obj/item/organ/appendix/vox
+ meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/vox
+ skinned_type = /obj/item/stack/sheet/animalhide/vox
+ changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT
+ breathid = "n2"
+ suicide_messages = list(
+ "%%SUICIDER%% is jamming %%P_THEIR%% claws into %%P_THEIR%% eye sockets!",
+ "%%SUICIDER%% is deeply inhaling oxygen!")
+ husk_color = null
+ eyes_icon = 'icons/mob/species/vox/eyes.dmi'
+ icon_husk = 'icons/mob/species/vox/bodyparts.dmi'
+ limb_icon_file = 'icons/mob/species/vox/bodyparts.dmi'
+ static_part_body_zones = list(BODY_ZONE_HEAD, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ survival_box_replacements = list(items_to_delete = list(/obj/item/clothing/mask/breath, /obj/item/tank/internals/emergency_oxygen),\
+ new_items = list(/obj/item/tank/internals/emergency_oxygen/nitrogen, /obj/item/clothing/mask/breath/vox))
+ creampie_id = "creampie_vox"
+ exotic_bloodtype = "V"
+ smells_like = "musty quills"
+ liked_food = MEAT | FRIED
+ species_language_holder = /datum/language_holder/vox
+
+/datum/species/vox/get_species_description()
+ return "The Vox are remnants of an ancient race, that originate from arkships. \
+ These bioengineered, reptilian, beaked, and quilled beings have a physiological caste system and follow 'The Inviolate' tenets. \
+ Breathing pure nitrogen, they need specialized masks and tanks for survival outside their arkships. \
+ Their insular nature limits their involvement in broader galactic affairs, maintaining a distinct, yet isolated presence away from other species."
+
+/datum/species/vox/get_species_lore()
+ return list("Vox have no colonies of their own to speak of. Most Vox originate from a location known as the Shoal, a sprawling, labyrinth-like space megalith of debris and asteroids fused together over countless orbits. It is there where their cutthroat and opportunistic behavior stems from.",\
+ "Little is known of Vox history itself, as the Vox do not keep many records beyond personal accomplishments and tales of profit and triumph. They have lived among the Shoal and the stars as long as they or anyone else can remember.",\
+ "It is possible the species went through a dark age before being introduced to the greater galactic community. Those taken by Vox raiders are rumored to be taken back to the Shoal to be sold as slaves. Other rumors speculate those not cut out to be slaves are sold by fleshpeddlers for “extraction”.",\
+ "Many Vox have grown fond of life among the stars, and typically avoid living on planetary colonies. They have a penchant for capitalism and goods peddling, deep space salvage, and sometimes interstellar crime. Many Vox now seek their fortune on the border of civilized space. Some trading with stations and worlds as (questionably legal) merchants, or applying to work for any one of the countless mega-corporations on the frontier. Others profit off crime and marauding, hijacking ships, cargo, and sometimes people to bring back to the Shoal.")
+
+/datum/species/vox/get_butt_sprite()
+ return BUTT_SPRITE_VOX
+
+/datum/species/vox/get_cough_sound(mob/living/carbon/human/vox)
+ return 'sound/voice/vox/shriekcough.ogg'
+
+/datum/species/vox/get_sneeze_sound(mob/living/carbon/human/vox)
+ return 'sound/voice/vox/shrieksneeze.ogg'
+
+/datum/species/vox/random_name(unique)
+ if(unique)
+ return random_unique_vox_name()
+ return capitalize(vox_name())
+
+/datum/species/vox/go_bald(mob/living/carbon/human/vox)
+ if(QDELETED(vox)) //may be called from a timer
+ return
+ vox.dna.features["vox_facial_quills"] = "None"
+ vox.dna.features["vox_quills"] = "None"
+ vox.dna.update_uf_block(DNA_VOX_FACIAL_QUILLS_BLOCK)
+ vox.dna.update_uf_block(DNA_VOX_QUILLS_BLOCK)
+ vox.update_hair()
+
+/datum/species/vox/survival_box_replacement(mob/living/carbon/human/box_holder, obj/item/storage/box/survival_box, list/soon_deleted_items, list/soon_added_items)
+ var/mask_to_replace = /obj/item/clothing/mask/breath/vox
+ if(mask_to_replace in soon_added_items)
+ var/list/possible_masks = list(/obj/item/clothing/mask/breath/vox, /obj/item/clothing/mask/breath/vox/respirator)
+ soon_added_items -= mask_to_replace
+ soon_added_items += pick(possible_masks)
+ ..()
+
+/datum/species/vox/after_equip_job(datum/job/J, mob/living/carbon/human/H, visualsOnly = FALSE) // Don't forget your voxygen tank
+ if(!H.can_breathe_mask())
+ var/obj/item/clothing/mask/current_mask = H.get_item_by_slot(ITEM_SLOT_MASK)
+ if(!H.equip_to_slot_if_possible(current_mask, ITEM_SLOT_BACKPACK, disable_warning = TRUE))
+ H.put_in_hands(current_mask)
+ var/obj/item/clothing/mask/vox_mask
+ var/mask_pref = H.client?.prefs?.read_preference(/datum/preference/choiced/vox_mask)
+ if(mask_pref == "Respirator")
+ vox_mask = new /obj/item/clothing/mask/breath/vox/respirator
+ else
+ vox_mask = new /obj/item/clothing/mask/breath/vox
+ H.equip_to_slot_or_del(vox_mask, ITEM_SLOT_MASK)
+ var/obj/item/tank/internals_tank
+ var/tank_pref = H.client?.prefs?.read_preference(/datum/preference/choiced/vox_tank_type)
+ if(tank_pref == "Large")
+ internals_tank = new /obj/item/tank/internals/nitrogen
+ else
+ internals_tank = new /obj/item/tank/internals/emergency_oxygen/vox
+ if(!H.equip_to_appropriate_slot(internals_tank))
+ H.put_in_hands(internals_tank)
+ to_chat(H, span_notice("You are now running on nitrogen internals from [internals_tank]. Your species finds oxygen toxic, so you must breathe pure nitrogen."))
+ H.open_internals(internals_tank)
+
+/datum/species/vox/get_icon_variant(mob/living/carbon/person_to_check)
+ return person_to_check.dna.features["vox_skin_tone"]
+
+/datum/species/vox/get_footprint_sprite()
+ return FOOTPRINT_SPRITE_CLAWS
+
+/datum/species/vox/get_eyes_static(mob/living/carbon/person_to_check)
+ var/list/blue_static_skin_tones = list("crimson", "mossy")
+ if(person_to_check.dna.features["vox_skin_tone"] in blue_static_skin_tones)
+ return "blue"
+ else
+ return "green"
+
+/datum/species/vox/handle_body(mob/living/carbon/human/H)
+ update_skin_tone(H)
+ ..()
+
+/datum/species/vox/proc/update_skin_tone(mob/living/carbon/human/vox, skin_tone)
+ if(!skin_tone)
+ skin_tone = vox.dna.features["vox_skin_tone"]
+ vox.dna.features["vox_skin_tone"] = skin_tone
+ vox.dna.update_uf_block(DNA_VOX_SKIN_TONE_BLOCK)
+ var/obj/item/organ/tail/vox/vox_tail = vox.getorganslot(ORGAN_SLOT_TAIL)
+ if(vox_tail && istype(vox_tail))
+ vox_tail.update_tail_appearance(vox)
+
+/datum/species/vox/get_special_statics(mob/living/carbon/human/vox)
+ return list("mossy")
+
+/datum/species/vox/create_pref_unique_perks()
+ var/list/to_add = list()
+
+ to_add += list(
+ list(
+ SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
+ SPECIES_PERK_ICON = "temperature-low",
+ SPECIES_PERK_NAME = "Cold Resistance",
+ SPECIES_PERK_DESC = "Vox hides provide excellent insulation against the coldness of space.",
+ ),
+ list(
+ SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK,
+ SPECIES_PERK_ICON = "heart-circle-check",
+ SPECIES_PERK_NAME = "Imperishable Organs",
+ SPECIES_PERK_DESC = "Vox organs contain advanced cybernetics that prevent them from decaying.",
+ ),
+ list(
+ SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
+ SPECIES_PERK_ICON = "bolt",
+ SPECIES_PERK_NAME = "EMP Sensitivity",
+ SPECIES_PERK_DESC = "Due to their organs being partially synthetic, they are susceptible to EMP damage.",
+ ),
+ list(
+ SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
+ SPECIES_PERK_ICON = "head-side-mask",
+ SPECIES_PERK_NAME = "Nitrogen Breathing",
+ SPECIES_PERK_DESC = "Oxygen is toxic to Vox, they must breathe pure nitrogen.",
+ ),
+ list(
+ SPECIES_PERK_TYPE = SPECIES_NEGATIVE_PERK,
+ SPECIES_PERK_ICON = "dna",
+ SPECIES_PERK_NAME = "Unclonable",
+ SPECIES_PERK_DESC = "Their peculiar physiology prevents Vox from being cloned.",
+ ),
+ )
+
+ return to_add
+
+/datum/species/vox/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
+ if(chem.type == /datum/reagent/gas/oxygen)
+ H.adjustToxLoss(1*REAGENTS_EFFECT_MULTIPLIER)
+ H.reagents.remove_reagent(chem.type, chem.metabolization_rate)
+ return FALSE
+ return ..()
diff --git a/yogstation/code/modules/reagents/chemistry/reagents/other_reagents.dm b/yogstation/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 21fd26add6ed..62336c19d1fc 100644
--- a/yogstation/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/yogstation/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -12,6 +12,11 @@
race = /datum/species/pod/ivymen
mutationtext = span_danger("The pain subsides. You feel... thorny.")
+/datum/reagent/mutationtoxin/vox
+ name = "Vox Mutation Toxin"
+ race = /datum/species/vox
+ mutationtext = span_danger("The pain subsides. You feel... beaked.")
+
/datum/reagent/cluwnification
name = "Cluwne Tears"
description = "Tears from thousands of cluwnes compressed into a dangerous cluwnification virus."
diff --git a/yogstation/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/yogstation/code/modules/reagents/chemistry/recipes/slime_extracts.dm
index 21de610a504a..6edc2bf3ca81 100644
--- a/yogstation/code/modules/reagents/chemistry/recipes/slime_extracts.dm
+++ b/yogstation/code/modules/reagents/chemistry/recipes/slime_extracts.dm
@@ -4,4 +4,12 @@
results = list(/datum/reagent/mutationtoxin/gorilla = 1)
required_reagents = list(/datum/reagent/consumable/milk = 1)
required_other = TRUE
- required_container = /obj/item/slime_extract/green
\ No newline at end of file
+ required_container = /obj/item/slime_extract/green
+
+/datum/chemical_reaction/slime/slimevox
+ name = "Vox Mutation Toxin"
+ id = "voxmuttoxin"
+ results = list(/datum/reagent/mutationtoxin/vox = 1)
+ required_reagents = list(/datum/reagent/gas/nitrogen = 1)
+ required_other = TRUE
+ required_container = /obj/item/slime_extract/green
diff --git a/yogstation/code/modules/reagents/reagent_containers/blood_pack.dm b/yogstation/code/modules/reagents/reagent_containers/blood_pack.dm
new file mode 100644
index 000000000000..30b21a33d047
--- /dev/null
+++ b/yogstation/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -0,0 +1,2 @@
+/obj/item/reagent_containers/blood/vox
+ blood_type = "V"
diff --git a/yogstation/code/modules/research/designs/limbgrower_designs.dm b/yogstation/code/modules/research/designs/limbgrower_designs.dm
new file mode 100644
index 000000000000..f3956b3dc7b0
--- /dev/null
+++ b/yogstation/code/modules/research/designs/limbgrower_designs.dm
@@ -0,0 +1,49 @@
+/datum/design/vox_tail
+ name = "Vox Tail"
+ id = "voxtail"
+ build_type = LIMBGROWER
+ reagents_list = list(/datum/reagent/medicine/synthflesh = 20)
+ build_path = /obj/item/organ/tail/vox/fake
+ category = list("vox")
+
+/datum/design/vox_tongue
+ name = "Vox Tongue"
+ id = "voxtongue"
+ build_type = LIMBGROWER
+ reagents_list = list(/datum/reagent/medicine/synthflesh = 10)
+ build_path = /obj/item/organ/tongue/vox
+ category = list("vox")
+
+/datum/design/vox_eyes
+ name = "Vox Eyes"
+ id = "voxeyes"
+ build_type = LIMBGROWER
+ reagents_list = list(/datum/reagent/medicine/synthflesh = 10)
+ build_path = /obj/item/organ/eyes/vox
+ category = list("vox")
+
+/datum/design/vox_liver
+ name = "Vox Liver"
+ id = "voxliver"
+ build_type = LIMBGROWER
+ reagents_list = list(/datum/reagent/medicine/synthflesh = 10)
+ build_path = /obj/item/organ/liver/vox
+ category = list("vox")
+
+/datum/design/vox_lungs
+ name = "Vox Lungs"
+ id = "voxlungs"
+ build_type = LIMBGROWER
+ reagents_list = list(/datum/reagent/medicine/synthflesh = 10)
+ build_path = /obj/item/organ/lungs/vox
+ category = list("vox")
+
+/obj/item/disk/design_disk/limbs/vox
+ name = "Vox Limb Design Disk"
+ limb_designs = list(/datum/design/vox_tail, /datum/design/vox_tongue, /datum/design/vox_eyes, /datum/design/vox_liver, /datum/design/vox_lungs)
+
+/datum/design/limb_disk/vox
+ name = "Vox Limb Design Disk"
+ desc = "Contains designs for vox organs for the limbgrower - Vox tail, tongue, eyes, liver, and lungs."
+ id = "limbdesign_vox"
+ build_path = /obj/item/disk/design_disk/limbs/vox
diff --git a/yogstation/code/modules/scripting/Implementations/telecomms_translator.dm b/yogstation/code/modules/scripting/Implementations/telecomms_translator.dm
index a8e16f052025..64ebda9594e7 100644
--- a/yogstation/code/modules/scripting/Implementations/telecomms_translator.dm
+++ b/yogstation/code/modules/scripting/Implementations/telecomms_translator.dm
@@ -13,6 +13,7 @@
#define MOTH (1<<9)
#define CAT (1<<10)
#define ENGLISH (1<<11)
+#define VOXPIDGIN (1<<12)
///Span classes that players are allowed to set in a radio transmission.
GLOBAL_LIST_INIT(allowed_custom_spans, list(
@@ -146,6 +147,8 @@ GLOBAL_LIST_INIT(allowed_translations, list(
"mothian" = MOTH,
"cat" = CAT,
"english" = ENGLISH,
+ "voxpidgin" = VOXPIDGIN,
+
)))
interpreter.Run() // run the thing
@@ -194,6 +197,8 @@ GLOBAL_LIST_INIT(allowed_translations, list(
oldlangbits = CAT
if(/datum/language/english)
oldlangbits = ENGLISH
+ if(/datum/language/vox)
+ oldlangbits = VOXPIDGIN
// Signal data
var/datum/n_struct/signal/script_signal = new(list(
@@ -336,6 +341,8 @@ GLOBAL_LIST_INIT(allowed_translations, list(
return /datum/language/felinid
if(ENGLISH)
return /datum/language/english
+ if(VOXPIDGIN)
+ return /datum/language/vox
/datum/n_function/default/mem
name = "mem"
@@ -516,3 +523,4 @@ GLOBAL_LIST_INIT(allowed_translations, list(
#undef MOTH
#undef CAT
#undef ENGLISH
+#undef VOXPIDGIN
diff --git a/yogstation/code/modules/surgery/bodypart.dm b/yogstation/code/modules/surgery/bodypart.dm
deleted file mode 100644
index 14ac6280c2fc..000000000000
--- a/yogstation/code/modules/surgery/bodypart.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/obj/item/bodypart
- var/yogs_draw_robot_hair = FALSE
\ No newline at end of file
diff --git a/yogstation/code/modules/surgery/bodyparts/_bodyparts.dm b/yogstation/code/modules/surgery/bodyparts/_bodyparts.dm
new file mode 100644
index 000000000000..671e14cc3dae
--- /dev/null
+++ b/yogstation/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -0,0 +1,7 @@
+/obj/item/bodypart
+ var/yogs_draw_robot_hair = FALSE
+ ///Does the bodypart have a "static" portion? Example is Vox limbs, the hand part is always the same golden-brown color, while the arm is different colors according to the Vox skin tone
+ var/has_static_sprite_part = FALSE
+ ///For species with different icon-based skin tones, like Vox
+ var/limb_icon_variant
+ var/limb_icon_file
diff --git a/yogstation/code/modules/surgery/bodyparts/head.dm b/yogstation/code/modules/surgery/bodyparts/head.dm
new file mode 100644
index 000000000000..f7bea89ccf54
--- /dev/null
+++ b/yogstation/code/modules/surgery/bodyparts/head.dm
@@ -0,0 +1,2 @@
+/obj/item/bodypart/head
+ var/eyes_static
diff --git a/yogstation/code/modules/surgery/organs/appendix.dm b/yogstation/code/modules/surgery/organs/appendix.dm
new file mode 100644
index 000000000000..04d6e36bc17a
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/appendix.dm
@@ -0,0 +1,4 @@
+/obj/item/organ/appendix/vox
+ name = "vox appendix"
+ icon_state = "appendix-vox"
+ decay_factor = 0
diff --git a/yogstation/code/modules/surgery/organs/ears.dm b/yogstation/code/modules/surgery/organs/ears.dm
new file mode 100644
index 000000000000..c34b92c79957
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/ears.dm
@@ -0,0 +1,8 @@
+/obj/item/organ/ears/vox
+ name = "vox ears"
+ icon_state = "ears-vox"
+ desc = "Researchers hypothesize that the titanium reinforced tympanic membrane takes the piercing edge off their own shrieking."
+ decay_factor = 0
+
+/obj/item/organ/ears/vox/emp_act()
+ deaf = 10
diff --git a/yogstation/code/modules/surgery/organs/eyes.dm b/yogstation/code/modules/surgery/organs/eyes.dm
new file mode 100644
index 000000000000..6c15ab93a4be
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/eyes.dm
@@ -0,0 +1,7 @@
+/obj/item/organ/eyes/vox
+ name = "vox eyes"
+ icon_state = "eyes-vox"
+ decay_factor = 0
+
+/obj/item/organ/eyes/vox/emp_act()
+ owner.adjust_hallucinations(10 SECONDS)
diff --git a/yogstation/code/modules/surgery/organs/heart.dm b/yogstation/code/modules/surgery/organs/heart.dm
index da98967b4da4..e78fb3ee7e2c 100644
--- a/yogstation/code/modules/surgery/organs/heart.dm
+++ b/yogstation/code/modules/surgery/organs/heart.dm
@@ -20,3 +20,8 @@
to_chat(owner, span_userdanger("You feel your heart collapse in on itself!"))
Remove(owner) //the heart is made of nanites so without them it just breaks down
qdel(src)
+
+/obj/item/organ/heart/vox
+ name = "vox heart"
+ icon_state = "heart-vox"
+ decay_factor = 0
diff --git a/yogstation/code/modules/surgery/organs/liver.dm b/yogstation/code/modules/surgery/organs/liver.dm
new file mode 100644
index 000000000000..ad6e152497ae
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/liver.dm
@@ -0,0 +1,4 @@
+/obj/item/organ/liver/vox
+ name = "vox liver"
+ icon_state = "liver-vox"
+ decay_factor = 0
diff --git a/yogstation/code/modules/surgery/organs/lungs.dm b/yogstation/code/modules/surgery/organs/lungs.dm
index 0260538d8ce5..1df5ddd6e405 100644
--- a/yogstation/code/modules/surgery/organs/lungs.dm
+++ b/yogstation/code/modules/surgery/organs/lungs.dm
@@ -61,3 +61,18 @@
heat_level_2_threshold = 600
heat_level_3_threshold = 1100
+/obj/item/organ/lungs/vox
+ name = "vox lungs"
+ icon_state = "lungs-vox"
+ desc = "They're filled with dust...wow."
+ decay_factor = 0
+ breathing_class = BREATH_VOX
+
+/obj/item/organ/lungs/vox/populate_gas_info()
+ ..()
+ gas_max[GAS_O2] = 0.05
+ gas_max -= BREATH_VOX
+ gas_damage[GAS_O2] = list(min = MIN_TOXIC_GAS_DAMAGE, max = MAX_TOXIC_GAS_DAMAGE, damage_type = TOX)
+
+/obj/item/organ/lungs/vox/emp_act()
+ owner.emote("gasp")
diff --git a/yogstation/code/modules/surgery/organs/stomach.dm b/yogstation/code/modules/surgery/organs/stomach.dm
new file mode 100644
index 000000000000..e52c02acff6f
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/stomach.dm
@@ -0,0 +1,8 @@
+/obj/item/organ/stomach/vox
+ name = "gizzard"
+ icon_state = "stomach-vox"
+ desc = "Mechanical digestion."
+ decay_factor = 0
+
+/obj/item/organ/stomach/vox/emp_act()
+ owner.adjust_disgust(10)
diff --git a/yogstation/code/modules/surgery/organs/tails.dm b/yogstation/code/modules/surgery/organs/tails.dm
new file mode 100644
index 000000000000..8e90291f0c8d
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/tails.dm
@@ -0,0 +1,73 @@
+/obj/item/organ/tail/vox
+ name = "vox tail"
+ desc = "A severed vox tail. Somewhere, no doubt, a vox hater is very pleased with themselves."
+ icon = 'icons/mob/species/vox/tails.dmi'
+ icon_state = "m_tail_lime_BEHIND"
+ tail_type = "lime"
+ var/icon_state_text = "m_tail_lime"
+ var/icon/constructed_tail_icon
+ var/tail_markings = "None"
+ var/tail_markings_color
+ var/original_owner
+
+/obj/item/organ/tail/vox/Initialize(mapload)
+ . = ..()
+ set_icon_state()
+
+/obj/item/organ/tail/vox/proc/set_icon_state()
+ icon_state_text = "m_tail_[tail_type]"
+ icon_state = "[icon_state_text]_BEHIND"
+ constructed_tail_icon = icon(initial(icon), icon_state)
+ var/icon/constructed_tail_icon_north = icon(constructed_tail_icon, dir = SOUTH)
+ constructed_tail_icon_north.Turn(180)
+ constructed_tail_icon.Insert(constructed_tail_icon_north, dir = NORTH)
+ if(tail_markings && tail_markings != "None")
+ var/datum/sprite_accessory/vox_tail_markings/vox_markings = GLOB.vox_tail_markings_list[tail_markings]
+ var/vox_markings_state_text = "m_vox_tail_markings_[vox_markings.icon_state]"
+ var/icon/constructed_markings = icon(vox_markings.icon, "[vox_markings_state_text]_BEHIND")
+ var/icon/constructed_markings_north = icon(constructed_markings, dir = SOUTH)
+ constructed_markings_north.Turn(180)
+ constructed_markings.Insert(constructed_markings_north, dir = NORTH)
+ constructed_markings.Blend(tail_markings_color, ICON_ADD)
+ constructed_tail_icon.Blend(constructed_markings, ICON_OVERLAY)
+ icon = constructed_tail_icon
+
+/obj/item/organ/tail/vox/Insert(mob/living/carbon/human/H, special = 0, drop_if_replaced = TRUE)
+ ..()
+ if(istype(H))
+ if(!original_owner)
+ original_owner = H
+ var/default_part = H.dna.species.mutant_bodyparts["vox_tail"]
+ if(!default_part || default_part == "None")
+ if(original_owner != H)
+ H.dna.species.mutant_bodyparts["vox_tail"] = tail_type
+ else
+ tail_type = H.dna.species.mutant_bodyparts["vox_tail"] = H.dna.features["vox_skin_tone"]
+
+ default_part = H.dna.species.mutant_bodyparts["vox_tail_markings"]
+ if(!default_part || default_part == "None")
+ if(original_owner != H)
+ H.dna.species.mutant_bodyparts["vox_tail_markings"] = tail_markings
+ else
+ tail_markings = H.dna.species.mutant_bodyparts["vox_tail_markings"] = H.dna.features["vox_tail_markings"]
+ H.update_body()
+
+/obj/item/organ/tail/vox/Remove(mob/living/carbon/human/H, special = 0)
+ ..()
+ if(istype(H))
+ H.dna.species.mutant_bodyparts -= "vox_tail"
+ H.dna.species.mutant_bodyparts -= "vox_tail_markings"
+ update_tail_appearance(H)
+ set_icon_state()
+ H.update_body()
+
+/obj/item/organ/tail/vox/proc/update_tail_appearance(mob/living/carbon/human/tail_owner)
+ if(original_owner && original_owner != tail_owner)
+ return
+ tail_type = tail_owner.dna.features["vox_skin_tone"]
+ tail_markings = tail_owner.dna.features["vox_tail_markings"]
+ tail_markings_color = tail_owner.dna.features["mcolor_secondary"]
+
+/obj/item/organ/tail/vox/fake
+ name = "fabricated vox tail"
+ desc = "A fabricated severed vox tail. This one's made of synthflesh."
diff --git a/yogstation/code/modules/surgery/organs/tongue.dm b/yogstation/code/modules/surgery/organs/tongue.dm
new file mode 100644
index 000000000000..e8c6e7a2cdf0
--- /dev/null
+++ b/yogstation/code/modules/surgery/organs/tongue.dm
@@ -0,0 +1,14 @@
+/obj/item/organ/tongue/vox
+ name = "vox tongue"
+ desc = "You almost swear you can hear it shrieking."
+ icon_state = "tongue-vox"
+ taste_sensitivity = 50 // There's not much need for taste when you're a scavenger.
+
+/obj/item/organ/tongue/vox/Initialize(mapload)
+ . = ..()
+ attack_verb += "skree'd"
+
+/obj/item/organ/tongue/vox/handle_speech(datum/source, list/speech_args)
+ ..()
+ if(prob(20))
+ playsound(owner, 'sound/voice/vox/shriek1.ogg', 25, 1, 1)
diff --git a/yogstation/code/modules/vending/gift.dm b/yogstation/code/modules/vending/gift.dm
index a08225e21103..b7edf63050ef 100644
--- a/yogstation/code/modules/vending/gift.dm
+++ b/yogstation/code/modules/vending/gift.dm
@@ -39,6 +39,37 @@
/obj/item/toy/plush/foxplushie = 2,
/obj/item/toy/plush/pkplushie = 2,
/obj/item/toy/plush/cdragon = 2,
+ /obj/item/toy/plush/voxplushie = 2,
+ /obj/item/flag/nt = 2,
+ /obj/item/flag/clown = 2,
+ /obj/item/flag/mime = 2,
+ /obj/item/flag/ian = 2,
+ /obj/item/flag/species/slime = 2,
+ /obj/item/flag/species/skrell = 2,
+ /obj/item/flag/species/vox = 2,
+ /obj/item/flag/species/machine = 2,
+ /obj/item/flag/species/diona = 2,
+ /obj/item/flag/species/human = 2,
+ /obj/item/flag/species/greys = 2,
+ /obj/item/flag/species/kidan = 2,
+ /obj/item/flag/species/taj = 2,
+ /obj/item/flag/species/lizard = 2,
+ /obj/item/flag/species/vulp = 2,
+ /obj/item/flag/species/drask = 2,
+ /obj/item/flag/species/plasma = 2,
+ /obj/item/flag/species/moth = 2,
+ /obj/item/flag/cargo = 2,
+ /obj/item/flag/med = 2,
+ /obj/item/flag/sec = 2,
+ /obj/item/flag/rnd = 2,
+ /obj/item/flag/atmos = 2,
+ /obj/item/flag/command = 2,
+ /obj/item/flag/grey = 2,
+ /obj/item/flag/syndi = 2,
+ /obj/item/flag/wiz = 2,
+ /obj/item/flag/cult = 2,
+ /obj/item/flag/ussp = 2,
+ /obj/item/flag/solgov = 2,
/obj/item/clothing/head/yogs/froghat = 3,
/obj/item/instrument/accordion = 1,
/obj/item/instrument/eguitar = 1,
diff --git a/yogstation/icons/obj/janitor.dmi b/yogstation/icons/obj/janitor.dmi
index 9dde83583ea1..46c2e2d07af9 100644
Binary files a/yogstation/icons/obj/janitor.dmi and b/yogstation/icons/obj/janitor.dmi differ
diff --git a/yogstation/icons/obj/stack_objects.dmi b/yogstation/icons/obj/stack_objects.dmi
index 972399a583d3..d1ea574ddcf4 100644
Binary files a/yogstation/icons/obj/stack_objects.dmi and b/yogstation/icons/obj/stack_objects.dmi differ
diff --git a/yogstation/icons/obj/tank.dmi b/yogstation/icons/obj/tank.dmi
index 999bdfbfb579..25b84b448345 100644
Binary files a/yogstation/icons/obj/tank.dmi and b/yogstation/icons/obj/tank.dmi differ