diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql
index d8f8edfe7d7..a801eb17898 100644
--- a/SQL/paradise_schema.sql
+++ b/SQL/paradise_schema.sql
@@ -48,6 +48,7 @@ CREATE TABLE `characters` (
`facial_style_name` varchar(45) NOT NULL,
`marking_styles` mediumtext NOT NULL,
`head_accessory_style_name` varchar(45) NOT NULL,
+ `alt_head_name` varchar(45) NOT NULL,
`eyes_red` smallint(4) NOT NULL,
`eyes_green` smallint(4) NOT NULL,
`eyes_blue` smallint(4) NOT NULL,
diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql
index a75af823a5b..e4b7e375786 100644
--- a/SQL/paradise_schema_prefixed.sql
+++ b/SQL/paradise_schema_prefixed.sql
@@ -48,6 +48,7 @@ CREATE TABLE `SS13_characters` (
`facial_style_name` varchar(45) NOT NULL,
`marking_styles` mediumtext NOT NULL,
`head_accessory_style_name` varchar(45) NOT NULL,
+ `alt_head_name` varchar(45) NOT NULL,
`eyes_red` smallint(4) NOT NULL,
`eyes_green` smallint(4) NOT NULL,
`eyes_blue` smallint(4) NOT NULL,
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 8a44c1b7cab..ea8411ef30b 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -18,6 +18,8 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, undershirt_list, undershirt_m, undershirt_f)
//socks
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, socks_list, socks_m, socks_f)
+ //alt heads
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, alt_heads_list)
init_subtypes(/datum/surgery_step, surgery_steps)
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index b6e851ec452..92eefcd54a9 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -26,6 +26,8 @@ var/global/list/undershirt_f = list() //stores only undershirt name
var/global/list/socks_list = list() //stores /datum/sprite_accessory/socks indexed by name
var/global/list/socks_m = list() //stores only socks name
var/global/list/socks_f = list() //stores only socks name
+ //Alt Heads
+var/global/list/alt_heads_list = list() //stores /datum/sprite_accessory/alt_heads indexed by name
//Backpacks
var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel", "Satchel Alt")
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 0f71fa429ea..de485d12056 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -140,7 +140,12 @@ proc/get_id_photo(var/mob/living/carbon/human/H)
var/icon/temp
temp = new /icon(icobase, "groin_[g]")
preview_icon.Blend(temp, ICON_OVERLAY)
- temp = new /icon(icobase, "head_[g]")
+ var/head = "head"
+ if(head_organ.alt_head)
+ var/datum/sprite_accessory/alt_heads/alternate_head = alt_heads_list[head_organ.alt_head]
+ if(alternate_head.icon_state)
+ head = alternate_head.icon_state
+ temp = new /icon(icobase, "[head]_[g]")
preview_icon.Blend(temp, ICON_OVERLAY)
//Tail
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 0d796197f6a..8802d937904 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -131,6 +131,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
var/r_eyes = 0 //Eye color
var/g_eyes = 0 //Eye color
var/b_eyes = 0 //Eye color
+ var/alt_head = null //Alt head style.
var/species = "Human"
var/language = "None" //Secondary language
@@ -339,6 +340,9 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
dat += "Character Records
"
dat += "
Limbs
"
+ if(species in list("Unathi")) //Species with alt heads.
+ dat += "Alternate Head: "
+ dat += "[alt_head]
"
dat += "Limbs and Parts: Adjust
"
if(species != "Slime People" && species != "Machine")
dat += "Internal Organs: Adjust
"
@@ -1372,16 +1376,46 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
if(new_head_accessory_style)
ha_style = new_head_accessory_style
+ if("alt_head")
+ if(organ_data["head"] == "cyborg")
+ return
+ if(species in list("Unathi")) //Species with head accessories.
+ var/list/valid_alt_heads = list()
+ valid_alt_heads["None"] = alt_heads_list["None"] //The only null entry should be the "None" option
+ for(var/alternate_head in alt_heads_list)
+ var/datum/sprite_accessory/alt_heads/head = alt_heads_list[alternate_head]
+ if(!(species in head.species_allowed))
+ continue
+
+ valid_alt_heads[alternate_head] = alt_heads_list[alternate_head]
+
+ var/new_alt_head = input(user, "Choose your character's alternate head style:", "Character Preference") as null|anything in valid_alt_heads
+ if(new_alt_head)
+ alt_head = new_alt_head
+ var/list/marking_styles = params2list(m_styles)
+ if(marking_styles["head"])
+ var/head_marking = marking_styles["head"]
+ var/datum/sprite_accessory/body_markings/head/head_marking_style = marking_styles_list[head_marking]
+ if(!head_marking_style.heads_allowed || !(alt_head in head_marking_style.heads_allowed))
+ marking_styles["head"] = "None"
+ m_styles = list2params(marking_styles)
+
if("m_style_head")
if(species in list("Machine", "Tajaran", "Unathi")) //Species with head markings.
var/list/valid_markings = list()
valid_markings["None"] = marking_styles_list["None"]
for(var/markingstyle in marking_styles_list)
- var/datum/sprite_accessory/M = marking_styles_list[markingstyle]
+ var/datum/sprite_accessory/body_markings/head/M = marking_styles_list[markingstyle]
if(!(species in M.species_allowed))
continue
if(M.marking_location != "head")
continue
+ if(alt_head && alt_head != "None")
+ if(!(alt_head in M.heads_allowed))
+ continue
+ else
+ if(M.heads_allowed)
+ continue
if(species == "Machine") //Species that can use prosthetic heads.
var/obj/item/organ/external/head/H = new()
@@ -1738,11 +1772,15 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
subchoice = input(user, "Which model of [choice] [limb_name] do you wish to use?") as null|anything in robolimb_models
if(subchoice)
choice = subchoice
- if(limb == "head")
- ha_style = "None"
- h_style = hair_styles_list["Bald"]
- f_style = facial_hair_styles_list["Shaved"]
- marking_styles["head"] = "None"
+ if(limb in list("head", "chest", "groin"))
+ if(species != "Machine")
+ return
+ if(limb == "head")
+ ha_style = "None"
+ alt_head = null
+ h_style = hair_styles_list["Bald"]
+ f_style = facial_hair_styles_list["Shaved"]
+ marking_styles["head"] = "None"
rlimb_data[limb] = choice
organ_data[limb] = "cyborg"
if(second_limb)
@@ -1964,6 +2002,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
H.h_style = h_style
H.f_style = f_style
+
+ H.alt_head = alt_head
//End of head-specific.
character.r_skin = r_skin
diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm
index 0dcfade53af..3a06395ba77 100644
--- a/code/modules/client/preference/preferences_mysql.dm
+++ b/code/modules/client/preference/preferences_mysql.dm
@@ -125,6 +125,7 @@
facial_style_name,
marking_styles,
head_accessory_style_name,
+ alt_head_name,
eyes_red,
eyes_green,
eyes_blue,
@@ -194,46 +195,47 @@
f_style = query.item[23]
m_styles = query.item[24]
ha_style = query.item[25]
- r_eyes = text2num(query.item[26])
- g_eyes = text2num(query.item[27])
- b_eyes = text2num(query.item[28])
- underwear = query.item[29]
- undershirt = query.item[30]
- backbag = text2num(query.item[31])
- b_type = query.item[32]
+ alt_head = query.item[26]
+ r_eyes = text2num(query.item[27])
+ g_eyes = text2num(query.item[28])
+ b_eyes = text2num(query.item[29])
+ underwear = query.item[30]
+ undershirt = query.item[31]
+ backbag = text2num(query.item[32])
+ b_type = query.item[33]
//Jobs
- alternate_option = text2num(query.item[33])
- job_support_high = text2num(query.item[34])
- job_support_med = text2num(query.item[35])
- job_support_low = text2num(query.item[36])
- job_medsci_high = text2num(query.item[37])
- job_medsci_med = text2num(query.item[38])
- job_medsci_low = text2num(query.item[39])
- job_engsec_high = text2num(query.item[40])
- job_engsec_med = text2num(query.item[41])
- job_engsec_low = text2num(query.item[42])
- job_karma_high = text2num(query.item[43])
- job_karma_med = text2num(query.item[44])
- job_karma_low = text2num(query.item[44])
+ alternate_option = text2num(query.item[34])
+ job_support_high = text2num(query.item[35])
+ job_support_med = text2num(query.item[36])
+ job_support_low = text2num(query.item[37])
+ job_medsci_high = text2num(query.item[38])
+ job_medsci_med = text2num(query.item[39])
+ job_medsci_low = text2num(query.item[40])
+ job_engsec_high = text2num(query.item[41])
+ job_engsec_med = text2num(query.item[42])
+ job_engsec_low = text2num(query.item[43])
+ job_karma_high = text2num(query.item[44])
+ job_karma_med = text2num(query.item[45])
+ job_karma_low = text2num(query.item[46])
//Miscellaneous
- flavor_text = query.item[46]
- med_record = query.item[47]
- sec_record = query.item[48]
- gen_record = query.item[49]
- disabilities = text2num(query.item[50])
- player_alt_titles = params2list(query.item[51])
- organ_data = params2list(query.item[52])
- rlimb_data = params2list(query.item[53])
- nanotrasen_relation = query.item[54]
- speciesprefs = text2num(query.item[55])
+ flavor_text = query.item[47]
+ med_record = query.item[48]
+ sec_record = query.item[49]
+ gen_record = query.item[50]
+ disabilities = text2num(query.item[51])
+ player_alt_titles = params2list(query.item[52])
+ organ_data = params2list(query.item[53])
+ rlimb_data = params2list(query.item[54])
+ nanotrasen_relation = query.item[55]
+ speciesprefs = text2num(query.item[56])
//socks
- socks = query.item[56]
- body_accessory = query.item[57]
- gear = params2list(query.item[58])
+ socks = query.item[57]
+ body_accessory = query.item[58]
+ gear = params2list(query.item[59])
//Sanitize
metadata = sanitize_text(metadata, initial(metadata))
@@ -262,6 +264,7 @@
h_style = sanitize_inlist(h_style, hair_styles_list, initial(h_style))
f_style = sanitize_inlist(f_style, facial_hair_styles_list, initial(f_style))
ha_style = sanitize_inlist(ha_style, head_accessory_styles_list, initial(ha_style))
+ alt_head = sanitize_inlist(alt_head, alt_heads_list, initial(alt_head))
r_eyes = sanitize_integer(r_eyes, 0, 255, initial(r_eyes))
g_eyes = sanitize_integer(g_eyes, 0, 255, initial(g_eyes))
b_eyes = sanitize_integer(b_eyes, 0, 255, initial(b_eyes))
@@ -341,6 +344,7 @@
facial_style_name='[sql_sanitize_text(f_style)]',
marking_styles='[m_styles]',
head_accessory_style_name='[sql_sanitize_text(ha_style)]',
+ alt_head_name='[sql_sanitize_text(alt_head)]',
eyes_red='[r_eyes]',
eyes_green='[g_eyes]',
eyes_blue='[b_eyes]',
@@ -393,7 +397,7 @@
skin_tone, skin_red, skin_green, skin_blue,
marking_colours,
head_accessory_red, head_accessory_green, head_accessory_blue,
- hair_style_name, facial_style_name, marking_styles, head_accessory_style_name,
+ hair_style_name, facial_style_name, marking_styles, head_accessory_style_name, alt_head_name,
eyes_red, eyes_green, eyes_blue,
underwear, undershirt,
backbag, b_type, alternate_option,
@@ -414,7 +418,7 @@
'[s_tone]', '[r_skin]', '[g_skin]', '[b_skin]',
'[m_colours]',
'[r_headacc]', '[g_headacc]', '[b_headacc]',
- '[sql_sanitize_text(h_style)]', '[sql_sanitize_text(f_style)]', '[m_styles]', '[sql_sanitize_text(ha_style)]',
+ '[sql_sanitize_text(h_style)]', '[sql_sanitize_text(f_style)]', '[m_styles]', '[sql_sanitize_text(ha_style)]', '[sql_sanitize_text(alt_head)]',
'[r_eyes]', '[g_eyes]', '[b_eyes]',
'[underwear]', '[undershirt]',
'[backbag]', '[b_type]', '[alternate_option]',
diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm
index 535c5676a9b..e01211a0f94 100644
--- a/code/modules/mob/living/carbon/human/appearance.dm
+++ b/code/modules/mob/living/carbon/human/appearance.dm
@@ -96,10 +96,16 @@
if(!(marking_style in marking_styles_list))
return
- var/datum/sprite_accessory/marking = marking_styles_list[marking_style]
+ var/datum/sprite_accessory/body_markings/marking = marking_styles_list[marking_style]
if(marking.name != "None" && marking.marking_location != location)
return
+ var/obj/item/organ/external/head/head_organ = get_organ("head")
+ if(location == "head" && head_organ.alt_head)
+ var/datum/sprite_accessory/body_markings/head/H
+ if(!H.heads_allowed || !(head_organ.alt_head in H.heads_allowed))
+ return
+
marking_styles[location] = marking_style
m_styles = list2params(marking_styles)
@@ -403,10 +409,18 @@
else
if(!S.tails_allowed || !(body_accessory in S.tails_allowed))
continue
- if(location == "head" && H.species.flags & ALL_RPARTS) //If the user is a species that can have a robotic head...
- var/datum/robolimb/robohead = all_robolimbs[H.model]
- if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head.
- continue
+ if(location == "head")
+ var/datum/sprite_accessory/body_markings/head/M = marking_styles_list[S.name]
+ if(H.species.flags & ALL_RPARTS)//If the user is a species that can have a robotic head...
+ var/datum/robolimb/robohead = all_robolimbs[H.model]
+ if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head.
+ continue
+ else if(H.alt_head && H.alt_head != "None") //If the user's got an alt head, validate markings for that head.
+ if(!(H.alt_head in M.heads_allowed))
+ continue
+ else
+ if(M.heads_allowed)
+ continue
valid_markings += marking
return valid_markings
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index dfe570e8a98..04e602fab0d 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -365,7 +365,7 @@ var/global/list/damage_icon_parts = list()
if(chest_organ && !chest_organ.is_stump() && !(chest_organ.status & ORGAN_DESTROYED) && marking_styles["body"])
var/body_marking = marking_styles["body"]
var/datum/sprite_accessory/body_marking_style = marking_styles_list[body_marking]
- if(body_marking_style)
+ if(body_marking_style && body_marking_style.species_allowed && (species.name in body_marking_style.species_allowed))
var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s")
if(body_marking_style.do_colouration)
marking_colours["body"] = sanitize_hexcolor(marking_colours["body"])
@@ -376,7 +376,7 @@ var/global/list/damage_icon_parts = list()
if(head_organ && !head_organ.is_stump() && !(head_organ.status & ORGAN_DESTROYED) && marking_styles["head"]) //If the head is destroyed, forget the head markings. This prevents floating optical markings on decapitated IPCs, for example.
var/head_marking = marking_styles["head"]
var/datum/sprite_accessory/head_marking_style = marking_styles_list[head_marking]
- if(head_marking_style)
+ if(head_marking_style && head_marking_style.species_allowed && (head_organ.species.name in head_marking_style.species_allowed))
var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s")
if(head_marking_style.do_colouration)
marking_colours["head"] = sanitize_hexcolor(marking_colours["head"])
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm
index b7ca2de44a1..7c6426ed93d 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/mob/new_player/preferences_setup.dm
@@ -221,7 +221,12 @@
fat="_fat"
preview_icon = new /icon(icobase, "torso_[g][fat]")
preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY)
- preview_icon.Blend(new /icon(icobase, "head_[g]"), ICON_OVERLAY)
+ var/head = "head"
+ if(alt_head)
+ var/datum/sprite_accessory/alt_heads/H = alt_heads_list[alt_head]
+ if(H.icon_state)
+ head = H.icon_state
+ preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY)
for(var/name in list("chest", "groin", "head", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "l_arm", "l_hand"))
if(organ_data[name] == "amputated") continue
diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm
index 952bf0453fc..19768d19430 100644
--- a/code/modules/mob/new_player/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories.dm
@@ -44,6 +44,7 @@
// Restrict some styles to specific species
var/list/species_allowed = list("Human", "Slime People")
var/list/models_allowed = list() //Specifies which, if any, hairstyles or markings can be accessed by which prosthetics. Should equal the manufacturing company name in robolimbs.dm.
+ var/list/heads_allowed = null //Specifies which, if any, alt heads a head marking, hairstyle or facial hair style is compatible with.
var/list/tails_allowed = null //Specifies which, if any, tails a tail marking is compatible with.
var/marking_location //Specifies which bodypart a body marking is located on.
@@ -1962,10 +1963,22 @@
species_allowed = list("Unathi")
icon_state = "markings_face_snout_round_una"
+/datum/sprite_accessory/body_markings/head/snout_round_lower_una
+ name = "Unathi Round Snout - Lower"
+ species_allowed = list("Unathi")
+ icon_state = "markings_face_snout_round_lower_una"
+
/datum/sprite_accessory/body_markings/head/snout_sharp_una
name = "Unathi Sharp Snout"
species_allowed = list("Unathi")
icon_state = "markings_face_snout_sharp_una"
+ heads_allowed = list("Unathi Sharp Snout")
+
+/datum/sprite_accessory/body_markings/head/snout_sharp_lower_una
+ name = "Unathi Sharp Snout - Lower"
+ species_allowed = list("Unathi")
+ icon_state = "markings_face_snout_sharp_lower_una"
+ heads_allowed = list("Unathi Sharp Snout")
/datum/sprite_accessory/body_markings/head/optics
name = "Humanoid Optics"
@@ -2043,4 +2056,23 @@
name = "Vulpkanin Short Tail Tip"
species_allowed = list("Vulpkanin")
tails_allowed = list("Vulpkanin Alt 4 (Short)")
- icon_state = "markings_vulptail5_tip"
\ No newline at end of file
+ icon_state = "markings_vulptail5_tip"
+
+/* ALT HEADS */
+
+/datum/sprite_accessory/alt_heads
+ name = "Alternate Head"
+ icon = null
+ icon_state = null
+ species_allowed = null
+
+/datum/sprite_accessory/alt_heads/none
+ name = "None"
+ icon = null
+ icon_state = null
+ species_allowed = null
+
+/datum/sprite_accessory/alt_heads/una_sharp_snout
+ name = "Unathi Sharp Snout"
+ species_allowed = list("Unathi")
+ icon_state = "head_sharp"
diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm
index 431c4484040..e7702426c7f 100644
--- a/code/modules/surgery/organs/organ_icon.dm
+++ b/code/modules/surgery/organs/organ_icon.dm
@@ -139,6 +139,13 @@ var/global/list/limb_icon_cache = list()
else if(status & ORGAN_ROBOT)
mob_icon = new /icon('icons/mob/human_races/robotic.dmi', "[icon_name][gender ? "_[gender]" : ""]")
else
+ if(limb_name == "head")
+ var/obj/item/organ/external/head/head_organ = src
+ if(head_organ.alt_head)
+ var/datum/sprite_accessory/alt_heads/alternate_head = alt_heads_list[head_organ.alt_head]
+ if(alternate_head.icon_state)
+ icon_name = alternate_head.icon_state
+
if(status & ORGAN_MUTATED)
mob_icon = new /icon(species.deform, "[icon_name][gender ? "_[gender]" : ""]")
else
diff --git a/code/modules/surgery/organs/subtypes/standard.dm b/code/modules/surgery/organs/subtypes/standard.dm
index 1c6240318fe..182f0875417 100644
--- a/code/modules/surgery/organs/subtypes/standard.dm
+++ b/code/modules/surgery/organs/subtypes/standard.dm
@@ -140,6 +140,7 @@
gendered_icon = 1
encased = "skull"
var/can_intake_reagents = 1
+ var/alt_head = null
//Hair colour and style
var/r_hair = 0
diff --git a/icons/mob/body_accessory.dmi b/icons/mob/body_accessory.dmi
index a53460c1d24..ee52b20206c 100644
Binary files a/icons/mob/body_accessory.dmi and b/icons/mob/body_accessory.dmi differ
diff --git a/icons/mob/human_races/r_def_lizard.dmi b/icons/mob/human_races/r_def_lizard.dmi
index 3926ca17092..e1c40712b23 100644
Binary files a/icons/mob/human_races/r_def_lizard.dmi and b/icons/mob/human_races/r_def_lizard.dmi differ
diff --git a/icons/mob/human_races/r_lizard.dmi b/icons/mob/human_races/r_lizard.dmi
index 457e50b204e..d60ae4979df 100644
Binary files a/icons/mob/human_races/r_lizard.dmi and b/icons/mob/human_races/r_lizard.dmi differ