diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index 2996b1af708..9ddae527eec 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -65,6 +65,8 @@
#define VIRUSIMMUNE 4096
#define PIERCEIMMUNE 8192
+#define MUTCOLORS_PARTSONLY 16384 //Used if we want the mutant colour to be only used by mutant bodyparts. Don't combine this with MUTCOLORS, or it will be useless.
+
/*
These defines are used specifically with the atom/movable/languages bitmask.
They are used in atom/movable/Hear() and atom/movable/say() to determine whether hearers can understand a message.
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index a80a9bb9ebd..a35db68de44 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -15,10 +15,13 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, socks_list, socks_m, socks_f)
//lizard bodyparts (blizzard intensifies)
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, body_markings_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails, tails_list)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated, animated_tails_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, tails_list_lizard)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/lizard, animated_tails_list_lizard)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, tails_list_human)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails_animated/human, animated_tails_list_human)
init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, snouts_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, horns_list)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, ears_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, frills_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/spines, spines_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/spines_animated, animated_spines_list)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 01f8d00be82..46a58da0fb6 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -37,12 +37,16 @@
else return pick(socks_list)
/proc/random_features()
- if(!tails_list.len)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails, tails_list)
+ if(!tails_list_human.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, tails_list_human)
+ if(!tails_list_lizard.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, tails_list_lizard)
if(!snouts_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, snouts_list)
if(!horns_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/horns, horns_list)
+ if(!ears_list.len)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, horns_list)
if(!frills_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/frills, frills_list)
if(!spines_list.len)
@@ -50,7 +54,8 @@
if(!body_markings_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, body_markings_list)
- return(list("mcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), "tail" = pick(tails_list), "snout" = pick(snouts_list), "horns" = pick(horns_list), "frills" = pick(frills_list), "spines" = pick(spines_list), "body_markings" = pick(body_markings_list)))
+ //For now we will always return none for tail_human and ears.
+ return(list("mcolor" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"), "tail_lizard" = pick(tails_list_lizard), "tail_human" = "None", "snout" = pick(snouts_list), "horns" = pick(horns_list), "ears" = "None", "frills" = pick(frills_list), "spines" = pick(spines_list), "body_markings" = pick(body_markings_list)))
/proc/random_hair_style(gender)
switch(gender)
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index c3bc1e7c1f8..752dc718e94 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -20,10 +20,13 @@ var/global/list/socks_m = list() //stores only socks name
var/global/list/socks_f = list() //stores only socks name
//Lizard Bits (all datum lists indexed by name)
var/global/list/body_markings_list = list()
-var/global/list/tails_list = list()
-var/global/list/animated_tails_list = list()
+var/global/list/tails_list_lizard = list()
+var/global/list/tails_list_human = list()
+var/global/list/animated_tails_list_lizard = list()
+var/global/list/animated_tails_list_human = list()
var/global/list/snouts_list = list()
var/global/list/horns_list = list()
+var/global/list/ears_list = list()
var/global/list/frills_list = list()
var/global/list/spines_list = list()
var/global/list/animated_spines_list = list()
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 4f25bb4a6ab..c8670e39a3d 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -106,6 +106,7 @@
var/shuttle_refuel_delay = 12000
var/show_game_type_odds = 0 //if set this allows players to see the odds of each roundtype on the get revision screen
var/mutant_races = 0 //players can choose their mutant race before joining the game
+ var/mutant_humans = 0 //players can pick mutant bodyparts for humans before joining the game
var/no_summon_guns //No
var/no_summon_magic //Fun
@@ -480,6 +481,8 @@
config.silicon_max_law_amount = text2num(value)
if("join_with_mutant_race")
config.mutant_races = 1
+ if("join_with_mutant_humans")
+ config.mutant_humans = 1
if("assistant_cap")
config.assistant_cap = text2num(value)
if("starlight")
diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm
index 6153cee7eb7..5f85d3f162c 100644
--- a/code/controllers/subsystem/jobs.dm
+++ b/code/controllers/subsystem/jobs.dm
@@ -86,7 +86,7 @@ var/datum/subsystem/job/SSjob
if(player.mind && job.title in player.mind.restricted_roles)
Debug("FOC incompatible with antagonist role, Player: [player]")
continue
- if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
+ if(config.enforce_human_authority && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
Debug("FOC non-human failed, Player: [player]")
continue
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
@@ -118,7 +118,7 @@ var/datum/subsystem/job/SSjob
Debug("GRJ incompatible with antagonist role, Player: [player], Job: [job.title]")
continue
- if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
+ if(config.enforce_human_authority && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
Debug("GRJ non-human failed, Player: [player]")
continue
@@ -291,7 +291,7 @@ var/datum/subsystem/job/SSjob
Debug("DO incompatible with antagonist role, Player: [player], Job:[job.title]")
continue
- if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
+ if(config.enforce_human_authority && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
Debug("DO non-human failed, Player: [player], Job:[job.title]")
continue
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
index cca3970fb00..5d6dbfdac5c 100644
--- a/code/datums/datumvars.dm
+++ b/code/datums/datumvars.dm
@@ -255,6 +255,7 @@
if(ishuman(D))
body += ""
body += ""
+ body += ""
body += ""
body += ""
body += ""
@@ -802,6 +803,34 @@ body
hardset_dna(H, null, null, null, null, newtype)
H.regenerate_icons()
+ else if(href_list["purrbation"])
+ if(!check_rights(R_SPAWN)) return
+
+ var/mob/living/carbon/human/H = locate(href_list["purrbation"])
+ if(!istype(H))
+ usr << "This can only be done to instances of type /mob/living/carbon/human"
+ return
+
+ if(!H)
+ usr << "Mob doesn't exist anymore"
+ return
+
+ if(H.dna && H.dna.species.id == "human")
+ if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None")
+ usr << "Put [H] on purrbation."
+ H << "You suddenly feel valid."
+ H.dna.features["tail_human"] = "Cat"
+ H.dna.features["ears"] = "Cat"
+ else
+ usr << "Removed [H] from purrbation."
+ H << "You suddenly don't feel valid anymore."
+ H.dna.features["tail_human"] = "None"
+ H.dna.features["ears"] = "None"
+ H.regenerate_icons()
+ return
+
+ usr << "You can only put humans on purrbation."
+
else if(href_list["adjustDamage"] && href_list["mobToDamage"])
if(!check_rights(0)) return
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 1b737182ae9..011fe1a6015 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -61,7 +61,7 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set
var/skin_tone = "caucasian1" //Skin color
var/eye_color = "000" //Eye color
var/datum/species/pref_species = new /datum/species/human() //Mutant race
- var/list/features = list("mcolor" = "FFF", "tail" = "Smooth", "snout" = "Round", "horns" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None")
+ var/list/features = list("mcolor" = "FFF", "tail_lizard" = "Smooth", "tail_human" = "None", "snout" = "Round", "horns" = "None", "ears" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None")
var/list/custom_names = list("clown", "mime", "ai", "cyborg", "religion", "deity")
@@ -238,69 +238,91 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set
dat += ""
- if(MUTCOLORS in pref_species.specflags)
+ if(config.mutant_races) //We don't allow mutant bodyparts for humans either unless this is true.
- dat += "
"
+ if((MUTCOLORS in pref_species.specflags) || (MUTCOLORS_PARTSONLY in pref_species.specflags))
- dat += "Alien Color"
+ dat += " | "
- dat += " Change "
+ dat += "Alien/Mutant Color"
- dat += " | "
+ dat += " Change
"
- if("tail" in pref_species.mutant_bodyparts)
- dat += ""
+ dat += " | "
- dat += "Tail
"
+ if("tail_lizard" in pref_species.mutant_bodyparts)
+ dat += ""
- dat += "[features["tail"]] "
+ dat += "Tail"
- dat += " | "
+ dat += "[features["tail_lizard"]]
"
- if("snout" in pref_species.mutant_bodyparts)
- dat += ""
+ dat += " | "
- dat += "Snout
"
+ if("snout" in pref_species.mutant_bodyparts)
+ dat += ""
- dat += "[features["snout"]] "
+ dat += "Snout"
- dat += " | "
+ dat += "[features["snout"]]
"
- if("horns" in pref_species.mutant_bodyparts)
- dat += ""
+ dat += " | "
- dat += "Horns
"
+ if("horns" in pref_species.mutant_bodyparts)
+ dat += ""
- dat += "[features["horns"]] "
+ dat += "Horns"
- dat += " | "
+ dat += "[features["horns"]]
"
- if("frills" in pref_species.mutant_bodyparts)
- dat += ""
+ dat += " | "
- dat += "Frills
"
+ if("frills" in pref_species.mutant_bodyparts)
+ dat += ""
- dat += "[features["frills"]] "
+ dat += "Frills"
- dat += " | "
+ dat += "[features["frills"]]
"
- if("spines" in pref_species.mutant_bodyparts)
- dat += ""
+ dat += " | "
- dat += "Spines
"
+ if("spines" in pref_species.mutant_bodyparts)
+ dat += ""
- dat += "[features["spines"]] "
+ dat += "Spines"
- dat += " | "
+ dat += "[features["spines"]]
"
- if("body_markings" in pref_species.mutant_bodyparts)
- dat += ""
+ dat += " | "
- dat += "Body Markings
"
+ if("body_markings" in pref_species.mutant_bodyparts)
+ dat += ""
- dat += "[features["body_markings"]] "
+ dat += "Body Markings"
- dat += " | "
+ dat += "[features["body_markings"]]
"
+
+ dat += ""
+
+ if(config.mutant_humans)
+
+ if("tail_human" in pref_species.mutant_bodyparts)
+ dat += ""
+
+ dat += "Tail"
+
+ dat += "[features["tail_human"]] "
+
+ dat += " | "
+
+ if("ears" in pref_species.mutant_bodyparts)
+ dat += ""
+
+ dat += "Ears"
+
+ dat += "[features["ears"]] "
+
+ dat += " | "
dat += ""
@@ -423,8 +445,11 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set
if((job_civilian_low & ASSISTANT) && (rank != "Assistant") && !jobban_isbanned(user, "Assistant"))
HTML += "[rank] | "
continue
- if(config.enforce_human_authority && (rank in command_positions) && user.client.prefs.pref_species.id != "human")
- HTML += "[rank] \[NON-HUMAN\] | "
+ if(config.enforce_human_authority && !user.client.prefs.pref_species.qualifies_for_rank(rank, user.client.prefs.features))
+ if(user.client.prefs.pref_species.id == "human")
+ HTML += "[rank] \[MUTANT\] | "
+ else
+ HTML += "[rank] \[NON-HUMAN\] | "
continue
if((rank in command_positions) || (rank == "AI"))//Bold head jobs
HTML += "[rank]"
@@ -791,24 +816,32 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set
if(result)
var/newtype = roundstart_species[result]
pref_species = new newtype()
- if(features["mcolor"] == "#000")
+ //Now that we changed our species, we must verify that the mutant colour is still allowed.
+ var/temp_hsv = RGBtoHSV(features["mcolor"])
+ if(features["mcolor"] == "#000" || (!(MUTCOLORS_PARTSONLY in pref_species.specflags) && ReadHSV(temp_hsv)[3] < ReadHSV("#7F7F7F")[3]))
features["mcolor"] = pref_species.default_color
-
if("mutant_color")
- var/new_mutantcolor = input(user, "Choose your character's alien skin color:", "Character Preference") as color|null
+ var/new_mutantcolor = input(user, "Choose your character's alien/mutant color:", "Character Preference") as color|null
if(new_mutantcolor)
var/temp_hsv = RGBtoHSV(new_mutantcolor)
if(new_mutantcolor == "#000000")
features["mcolor"] = pref_species.default_color
- else if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright
+ else if(MUTCOLORS_PARTSONLY in pref_species.specflags || ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright, but only if they affect the skin
features["mcolor"] = sanitize_hexcolor(new_mutantcolor)
else
user << "Invalid color. Your color is not bright enough."
- if("tail")
+
+ if("tail_lizard")
var/new_tail
- new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list
+ new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list_lizard
if(new_tail)
- features["tail"] = new_tail
+ features["tail_lizard"] = new_tail
+
+ if("tail_human")
+ var/new_tail
+ new_tail = input(user, "Choose your character's tail:", "Character Preference") as null|anything in tails_list_human
+ if(new_tail)
+ features["tail_human"] = new_tail
if("snout")
var/new_snout
@@ -822,6 +855,12 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set
if(new_horns)
features["horns"] = new_horns
+ if("ears")
+ var/new_ears
+ new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in ears_list
+ if(new_ears)
+ features["ears"] = new_ears
+
if("frills")
var/new_frills
new_frills = input(user, "Choose your character's frills:", "Character Preference") as null|anything in frills_list
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index a239561ede0..28dffc5bddc 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -187,9 +187,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["socks"] >> socks
S["backbag"] >> backbag
S["feature_mcolor"] >> features["mcolor"]
- S["feature_lizard_tail"] >> features["tail"]
+ S["feature_lizard_tail"] >> features["tail_lizard"]
+ S["feature_human_tail"] >> features["tail_human"]
S["feature_lizard_snout"] >> features["snout"]
S["feature_lizard_horns"] >> features["horns"]
+ S["feature_human_ears"] >> features["ears"]
S["feature_lizard_frills"] >> features["frills"]
S["feature_lizard_spines"] >> features["spines"]
S["feature_lizard_body_markings"] >> features["body_markings"]
@@ -245,9 +247,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
skin_tone = sanitize_inlist(skin_tone, skin_tones)
backbag = sanitize_integer(backbag, 1, backbaglist.len, initial(backbag))
features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0)
- features["tail"] = sanitize_inlist(features["tail"], tails_list)
+ features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], tails_list_lizard)
+ features["tail_human"] = sanitize_inlist(features["tail_human"], tails_list_human)
features["snout"] = sanitize_inlist(features["snout"], snouts_list)
features["horns"] = sanitize_inlist(features["horns"], horns_list)
+ features["ears"] = sanitize_inlist(features["ears"], ears_list)
features["frills"] = sanitize_inlist(features["frills"], frills_list)
features["spines"] = sanitize_inlist(features["spines"], spines_list)
features["body_markings"] = sanitize_inlist(features["body_markings"], body_markings_list)
@@ -292,9 +296,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["backbag"] << backbag
S["species"] << pref_species.name
S["feature_mcolor"] << features["mcolor"]
- S["feature_lizard_tail"] << features["tail"]
+ S["feature_lizard_tail"] << features["tail_lizard"]
+// S["feature_human_tail"] << features["tail_human"]
S["feature_lizard_snout"] << features["snout"]
S["feature_lizard_horns"] << features["horns"]
+// S["feature_human_ears"] << features["ears"]
S["feature_lizard_frills"] << features["frills"]
S["feature_lizard_spines"] << features["spines"]
S["feature_lizard_body_markings"] << features["body_markings"]
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 9a718fb01bc..0c274d56ebf 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -309,12 +309,12 @@
m_type = 2
if("wag")
- if(dna && dna.species && ("tail" in dna.species.mutant_bodyparts))
+ if(dna && dna.species && (("tail_lizard" in dna.species.mutant_bodyparts) || ("tail_human" in dna.species.mutant_bodyparts)))
message = "[src] wags \his tail."
startTailWag()
if("stopwag")
- if(dna && dna.species && ("waggingtail" in dna.species.mutant_bodyparts))
+ if(dna && dna.species && (("waggingtail_lizard" in dna.species.mutant_bodyparts) || ("waggingtail_human" in dna.species.mutant_bodyparts)))
message = "[src] stops wagging \his tail."
endTailWag()
@@ -353,21 +353,26 @@
/mob/living/carbon/human/proc/startTailWag()
if(!dna || !dna.species)
return
- if("tail" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "tail"
+ if("tail_lizard" in dna.species.mutant_bodyparts)
+ dna.species.mutant_bodyparts -= "tail_lizard"
dna.species.mutant_bodyparts -= "spines"
- dna.species.mutant_bodyparts |= "waggingtail"
+ dna.species.mutant_bodyparts |= "waggingtail_lizard"
dna.species.mutant_bodyparts |= "waggingspines"
+ if("tail_human" in dna.species.mutant_bodyparts)
+ dna.species.mutant_bodyparts -= "tail_human"
+ dna.species.mutant_bodyparts |= "waggingtail_human"
update_body()
/mob/living/carbon/human/proc/endTailWag()
if(!dna || !dna.species)
return
- if("waggingtail" in dna.species.mutant_bodyparts)
- dna.species.mutant_bodyparts -= "waggingtail"
+ if("waggingtail_lizard" in dna.species.mutant_bodyparts)
+ dna.species.mutant_bodyparts -= "waggingtail_lizard"
dna.species.mutant_bodyparts -= "waggingspines"
- dna.species.mutant_bodyparts |= "tail"
+ dna.species.mutant_bodyparts |= "tail_lizard"
dna.species.mutant_bodyparts |= "spines"
-
+ if("waggingtail_human" in dna.species.mutant_bodyparts)
+ dna.species.mutant_bodyparts -= "waggingtail_human"
+ dna.species.mutant_bodyparts |= "tail_human"
update_body()
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index df7579f5190..88148d5d8ce 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -39,7 +39,7 @@
var/nojumpsuit = 0 // this is sorta... weird. it basically lets you equip stuff that usually needs jumpsuits without one, like belts and pockets and ids
var/dangerous_existence = null //A flag for transformation spells that tells them "hey if you turn a person into one of these without preperation, they'll probably die!"
var/say_mod = "says" // affects the speech message
-
+ var/list/default_features = list() // Default mutant bodyparts for this species. Don't forget to set one for every mutant bodypart you allow this species to have.
var/list/mutant_bodyparts = list() // Parts of the body that are diferent enough from the standard human model that they cause clipping with some equipment
var/speedmod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster
@@ -84,6 +84,10 @@
// PROCS //
///////////
+//Please override this locally if you want to define when what species qualifies for what rank if human authority is enforced.
+/datum/species/proc/qualifies_for_rank(var/rank, var/list/features)
+ return 1
+
/datum/species/proc/update_base_icon_state(var/mob/living/carbon/human/H)
if(H.disabilities & HUSK)
H.remove_overlay(SPECIES_LAYER) // races lose their color
@@ -236,16 +240,26 @@
if(!mutant_bodyparts)
return
- if("tail" in mutant_bodyparts)
+ if("tail_lizard" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
- bodyparts_to_add -= "tail"
+ bodyparts_to_add -= "tail_lizard"
+
+ if("waggingtail_lizard" in mutant_bodyparts)
+ if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
+ bodyparts_to_add -= "waggingtail_lizard"
+ else if ("tail_lizard" in mutant_bodyparts)
+ bodyparts_to_add -= "waggingtail_lizard"
+
+ if("tail_human" in mutant_bodyparts)
+ if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
+ bodyparts_to_add -= "tail_human"
- if("waggingtail" in mutant_bodyparts)
+ if("waggingtail_human" in mutant_bodyparts)
if(H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
- bodyparts_to_add -= "waggingtail"
- else if ("tail" in mutant_bodyparts)
- bodyparts_to_add -= "waggingtail"
+ bodyparts_to_add -= "waggingtail_human"
+ else if ("tail_human" in mutant_bodyparts)
+ bodyparts_to_add -= "waggingtail_human"
if("spines" in mutant_bodyparts)
if(!H.dna.features["spines"] || H.dna.features["spines"] == "None" || H.wear_suit && (H.wear_suit.flags_inv & HIDEJUMPSUIT))
@@ -269,6 +283,10 @@
if(!H.dna.features["horns"] || H.dna.features["horns"] == "None" || H.head && (H.head.flags & BLOCKHAIR) || (H.wear_mask && (H.wear_mask.flags & BLOCKHAIR)))
bodyparts_to_add -= "horns"
+ if("ears" in mutant_bodyparts)
+ if(!H.dna.features["ears"] || H.dna.features["ears"] == "None" || H.head && (H.head.flags & BLOCKHAIR) || (H.wear_mask && (H.wear_mask.flags & BLOCKHAIR)))
+ bodyparts_to_add -= "ears"
+
if(!bodyparts_to_add)
return
@@ -280,10 +298,14 @@
for(var/bodypart in bodyparts_to_add)
var/datum/sprite_accessory/S
switch(bodypart)
- if("tail")
- S = tails_list[H.dna.features["tail"]]
- if("waggingtail")
- S.= animated_tails_list[H.dna.features["tail"]]
+ if("tail_lizard")
+ S = tails_list_lizard[H.dna.features["tail_lizard"]]
+ if("waggingtail_lizard")
+ S.= animated_tails_list_lizard[H.dna.features["tail_lizard"]]
+ if("tail_human")
+ S = tails_list_human[H.dna.features["tail_human"]]
+ if("waggingtail_human")
+ S.= animated_tails_list_human[H.dna.features["tail_human"]]
if("spines")
S = spines_list[H.dna.features["spines"]]
if("waggingspines")
@@ -294,12 +316,23 @@
S = frills_list[H.dna.features["frills"]]
if("horns")
S = horns_list[H.dna.features["horns"]]
+ if("ears")
+ S = ears_list[H.dna.features["ears"]]
if("body_markings")
S = body_markings_list[H.dna.features["body_markings"]]
if(!S || S.icon_state == "none")
continue
+
+ //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"
+ else if(bodypart == "waggingtail_lizard" || bodypart == "waggingtail_human")
+ bodypart = "waggingtail"
+
+
var/icon_string
+
if(S.gender_specific)
icon_string = "[id]_[g]_[bodypart]_[S.icon_state]_[layer]"
else
@@ -308,8 +341,30 @@
I = image("icon" = 'icons/mob/mutant_bodyparts.dmi', "icon_state" = icon_string, "layer" =- layer)
if(!(H.disabilities & HUSK))
- I.color = "#[H.dna.features["mcolor"]]"
+ switch(S.color_src)
+ if(MUTCOLORS)
+ I.color = "#[H.dna.features["mcolor"]]"
+ if(HAIR)
+ if(hair_color == "mutcolor")
+ I.color = "#[H.dna.features["mcolor"]]"
+ else
+ I.color = "#[H.hair_color]"
+ if(FACEHAIR)
+ I.color = "#[H.facial_hair_color]"
+ if(EYECOLOR)
+ I.color = "#[H.eye_color]"
standing += I
+
+ if(S.hasinner)
+ if(S.gender_specific)
+ icon_string = "[id]_[g]_[bodypart]inner_[S.icon_state]_[layer]"
+ else
+ icon_string = "[id]_m_[bodypart]inner_[S.icon_state]_[layer]"
+
+ I = image("icon" = 'icons/mob/mutant_bodyparts.dmi', "icon_state" = icon_string, "layer" =- layer)
+
+ standing += I
+
H.overlays_standing[layer] = standing.Copy()
standing = list()
diff --git a/code/modules/mob/living/carbon/human/species_types.dm b/code/modules/mob/living/carbon/human/species_types.dm
index 79f84797b28..2fbd53dd373 100644
--- a/code/modules/mob/living/carbon/human/species_types.dm
+++ b/code/modules/mob/living/carbon/human/species_types.dm
@@ -5,10 +5,33 @@
/datum/species/human
name = "Human"
id = "human"
+ default_color = "FFFFFF"
roundstart = 1
specflags = list(EYECOLOR,HAIR,FACEHAIR,LIPS)
+ mutant_bodyparts = list("tail_human", "ears")
+ default_features = list("mcolor" = "FFF", "tail_human" = "None", "ears" = "None")
use_skintones = 1
+/datum/species/human/qualifies_for_rank(var/rank, var/list/features)
+ if(features["tail_human"] == "None" && features["ears"] == "None")
+ return 1 //Pure humans are always allowed in all roles.
+
+ //Mutants are not allowed in most roles.
+ if(rank in command_positions)
+ return 0
+ if(rank in security_positions) //This list does not include lawyers.
+ return 0
+ if(rank in science_positions)
+ return 0
+ if(rank in medical_positions)
+ return 0
+ if(rank in engineering_positions)
+ return 0
+ if(rank == "Quartermaster") //QM is not contained in command_positions but we still want to bar mutants from it.
+ return 0
+ return 1
+
+
/datum/species/human/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H)
if(chem.id == "mutationtoxin")
H << "Your flesh rapidly mutates!"
@@ -18,6 +41,11 @@
H.faction |= "slime"
return 1
+//Curiosity killed the cat's wagging tail.
+datum/species/human/spec_death(var/gibbed, var/mob/living/carbon/human/H)
+ if(H)
+ H.endTailWag()
+
/*
LIZARDPEOPLE
*/
@@ -30,12 +58,18 @@
default_color = "00FF00"
roundstart = 1
specflags = list(MUTCOLORS,EYECOLOR,LIPS)
- mutant_bodyparts = list("tail", "snout", "spines", "horns", "frills", "body_markings")
+ mutant_bodyparts = list("tail_lizard", "snout", "spines", "horns", "frills", "body_markings")
+ default_features = list("mcolor" = "0F0", "tail" = "Smooth", "snout" = "Round", "horns" = "None", "frills" = "None", "spines" = "None", "body_markings" = "None")
attack_verb = "slash"
attack_sound = 'sound/weapons/slash.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/human/mutant/lizard
+/datum/species/lizard/qualifies_for_rank(var/rank, var/list/features)
+ if(rank in command_positions)
+ return 0
+ return 1
+
/datum/species/lizard/handle_speech(message)
// jesus christ why
if(copytext(message, 1, 2) != "*")
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 66d67d45b70..05b6fdb0b8a 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -262,7 +262,7 @@
return 0
if(!job.player_old_enough(src.client))
return 0
- if(config.enforce_human_authority && (rank in command_positions) && client.prefs.pref_species.id != "human")
+ if(config.enforce_human_authority && !client.prefs.pref_species.qualifies_for_rank(rank, client.prefs.features))
return 0
return 1
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm
index e28dcc3adab..168ce5ce499 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/mob/new_player/preferences_setup.dm
@@ -78,8 +78,10 @@
for(var/layer in relevent_layers)
for(var/bodypart in pref_species.mutant_bodyparts)
switch(bodypart)
- if("tail")
- S = tails_list[features["tail"]]
+ if("tail_lizard")
+ S = tails_list_lizard[features["tail_lizard"]]
+ if("tail_human")
+ S = tails_list_human[features["tail_human"]]
if("spines")
S = spines_list[features["spines"]]
if("snout")
@@ -88,19 +90,42 @@
S = frills_list[features["frills"]]
if("horns")
S = horns_list[features["horns"]]
+ if("ears")
+ S = ears_list[features["ears"]]
if("body_markings")
S = body_markings_list[features["body_markings"]]
if(!S || S.icon_state == "none")
continue
+
+ //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"
+
+ if(S.hasinner)
+ preview_icon.Blend(new/icon("icon" = 'icons/mob/mutant_bodyparts.dmi', "icon_state" = "[pref_species.id]_m_[bodypart]inner_[S.icon_state]_[layer]"), ICON_OVERLAY)
+
var/icon_string
+
if(S.gender_specific)
icon_string = "[pref_species.id]_[g]_[bodypart]_[S.icon_state]_[layer]"
else
icon_string = "[pref_species.id]_m_[bodypart]_[S.icon_state]_[layer]"
var/icon/part = new/icon("icon" = 'icons/mob/mutant_bodyparts.dmi', "icon_state" = icon_string)
- part.Blend("#[features["mcolor"]]", ICON_MULTIPLY)
+ switch(S.color_src)
+ if(MUTCOLORS)
+ part.Blend("#[features["mcolor"]]", ICON_MULTIPLY)
+ if(HAIR)
+ if(hair_color == "mutcolor")
+ part.Blend("#[features["mcolor"]]", ICON_MULTIPLY)
+ else
+ part.Blend("#[hair_color]", ICON_MULTIPLY)
+ if(FACEHAIR)
+ part.Blend("#[facial_hair_color]", ICON_MULTIPLY)
+ if(EYECOLOR)
+ part.Blend("#[eye_color]", ICON_MULTIPLY)
+
preview_icon.Blend(part, ICON_OVERLAY)
if(underwear)
diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm
index c28d4fd705c..31735f73fbd 100644
--- a/code/modules/mob/new_player/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories.dm
@@ -42,6 +42,8 @@
var/name //the preview name of the accessory
var/gender = NEUTER //Determines if the accessory will be skipped or included in random hair generations
var/gender_specific //Something that can be worn by either gender, but looks different on each
+ var/color_src = MUTCOLORS //Currently only used by mutantparts so don't worry about hair and stuff. This is the source that this accessory will get its color from. Default is MUTCOLOR, but can also be HAIR, FACEHAIR, EYECOLOR and 0 if none.
+ var/hasinner //Decides if this sprite has an "inner" part, such as the fleshy parts on ears.
//////////////////////
// Hair Definitions //
@@ -1094,70 +1096,88 @@
/datum/sprite_accessory/tails_animated
icon = 'icons/mob/mutant_bodyparts.dmi'
-/datum/sprite_accessory/tails/smooth
+/datum/sprite_accessory/tails/lizard/smooth
name = "Smooth"
icon_state = "smooth"
-/datum/sprite_accessory/tails_animated/smooth
+/datum/sprite_accessory/tails_animated/lizard/smooth
name = "Smooth"
icon_state = "smooth"
-/datum/sprite_accessory/tails/light
+/datum/sprite_accessory/tails/lizard/light
name = "Light"
icon_state = "light"
-/datum/sprite_accessory/tails_animated/light
+/datum/sprite_accessory/tails_animated/lizard/light
name = "Light"
icon_state = "light"
-/datum/sprite_accessory/tails/dstripe
+/datum/sprite_accessory/tails/lizard/dstripe
name = "Dark Stripe"
icon_state = "dstripe"
-/datum/sprite_accessory/tails_animated/dstripe
+/datum/sprite_accessory/tails_animated/lizard/dstripe
name = "Dark Stripe"
icon_state = "dstripe"
-/datum/sprite_accessory/tails/lstripe
+/datum/sprite_accessory/tails/lizard/lstripe
name = "Light Stripe"
icon_state = "lstripe"
-/datum/sprite_accessory/tails_animated/lstripe
+/datum/sprite_accessory/tails_animated/lizard/lstripe
name = "Light Stripe"
icon_state = "lstripe"
-/datum/sprite_accessory/tails/dtiger
+/datum/sprite_accessory/tails/lizard/dtiger
name = "Dark Tiger"
icon_state = "dtiger"
-/datum/sprite_accessory/tails_animated/dtiger
+/datum/sprite_accessory/tails_animated/lizard/dtiger
name = "Dark Tiger"
icon_state = "dtiger"
-/datum/sprite_accessory/tails/ltiger
+/datum/sprite_accessory/tails/lizard/ltiger
name = "Light Tiger"
icon_state = "ltiger"
-/datum/sprite_accessory/tails_animated/ltiger
+/datum/sprite_accessory/tails_animated/lizard/ltiger
name = "Light Tiger"
icon_state = "ltiger"
-/datum/sprite_accessory/tails/club
+/datum/sprite_accessory/tails/lizard/club
name = "Club"
icon_state = "club"
-/datum/sprite_accessory/tails_animated/club
+/datum/sprite_accessory/tails_animated/lizard/club
name = "Club"
icon_state = "club"
-/datum/sprite_accessory/tails/aqua
+/datum/sprite_accessory/tails/lizard/aqua
name = "Aquatic"
icon_state = "aqua"
-/datum/sprite_accessory/tails_animated/aqua
+/datum/sprite_accessory/tails_animated/lizard/aqua
name = "Aquatic"
icon_state = "aqua"
+/datum/sprite_accessory/tails/human/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/tails_animated/human/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/tails/human/cat
+ name = "Cat"
+ icon_state = "cat"
+ color_src = HAIR
+
+/datum/sprite_accessory/tails_animated/human/cat
+ name = "Cat"
+ icon_state = "cat"
+ color_src = HAIR
+
/datum/sprite_accessory/snouts
icon = 'icons/mob/mutant_bodyparts.dmi'
@@ -1204,6 +1224,16 @@
name = "Angeler"
icon_state = "angler"
+/datum/sprite_accessory/ears/none
+ name = "None"
+ icon_state = "none"
+
+/datum/sprite_accessory/ears/cat
+ name = "Cat"
+ icon_state = "cat"
+ hasinner = 1
+ color_src = HAIR
+
/datum/sprite_accessory/frills
icon = 'icons/mob/mutant_bodyparts.dmi'
diff --git a/config/game_options.txt b/config/game_options.txt
index f95e7c44918..aa4f48f5dd3 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -249,9 +249,12 @@ DEFAULT_LAWS 1
## Attempting to upload laws past this point will fail unless the AI is reset
SILICON_MAX_LAW_AMOUNT 12
-## Uncoment to give players the choice of their species before they join the game
+## Uncomment to give players the choice of their species before they join the game
#JOIN_WITH_MUTANT_RACE
+## Uncomment to give players the choice of joining as a human with mutant bodyparts before they join the game
+#JOIN_WITH_MUTANT_HUMANS
+
## Assistant slot cap. Set to -1 for unlimited.
ASSISTANT_CAP -1
diff --git a/html/changelogs/ricotez-human_mutantparts.yml b/html/changelogs/ricotez-human_mutantparts.yml
new file mode 100644
index 00000000000..df56e2dd6a4
--- /dev/null
+++ b/html/changelogs/ricotez-human_mutantparts.yml
@@ -0,0 +1,10 @@
+ author: Ricotez
+
+ delete-after: True
+
+ changes:
+ - rscadd: "Added two categories of mutant bodyparts for humans with the same system that lizards use: ears and tails. Right now only admins can edit these."
+ - rscadd: "Updated the system that checks for which jobs your character qualifies to be species-dependant. At this moment you won't notice any differences, but in the future coders can use this to be more specific about which jobs a species is allowed in under which circumstances."
+ - imageadd: "Added one option to both categories, taken from the kitty ears: a cat tail and cat ears."
+ - rscadd: "Added a new button to the VV menu drop-down list for admins: Toggle Purrbation. Putting a human on purrbation will give them cat ears and a cat tail, and send them the message 'You suddenly feel valid.' Using the button a second time removes the effects."
+ - wip: "Adding categories for tattoos and scars is in the works. (Read: I need sprites!)"
\ No newline at end of file
diff --git a/icons/mob/mutant_bodyparts.dmi b/icons/mob/mutant_bodyparts.dmi
index 94acba1cbc5..5e293d3fa11 100644
Binary files a/icons/mob/mutant_bodyparts.dmi and b/icons/mob/mutant_bodyparts.dmi differ