diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm
index e43002ba50b5..9d042a26fcf9 100644
--- a/_maps/map_files/Birdshot/birdshot.dmm
+++ b/_maps/map_files/Birdshot/birdshot.dmm
@@ -57098,7 +57098,7 @@
desc = "A cold, hard place for your final rest.";
name = "Morgue Slab"
},
-/mob/living/carbon/human/species/monkey/humand_legged{
+/mob/living/carbon/human/species/monkey{
name = "Charles";
real_name = "Charles"
},
diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm
index 6a71a8d6c3c0..c7da3473fa3b 100644
--- a/code/__DEFINES/colors.dm
+++ b/code/__DEFINES/colors.dm
@@ -430,3 +430,5 @@ GLOBAL_LIST_INIT(cable_colors, list(
CABLE_COLOR_YELLOW = CABLE_HEX_COLOR_YELLOW,
CABLE_COLOR_BROWN = CABLE_HEX_COLOR_BROWN
))
+
+#define HUSK_COLOR_TONE rgb(96, 88, 80)
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index ab131cc3d728..479373bb6518 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -64,6 +64,20 @@
/// Total amount of slots
#define SLOTS_AMT 20 // Keep this up to date!
+///Inventory slots that can be blacklisted by a species from being equipped into
+DEFINE_BITFIELD(no_equip_flags, list(
+ "EXOSUIT" = ITEM_SLOT_OCLOTHING,
+ "JUMPSUIT" = ITEM_SLOT_ICLOTHING,
+ "GLOVES" = ITEM_SLOT_GLOVES,
+ "GLASSES" = ITEM_SLOT_EYES,
+ "EARPIECES" = ITEM_SLOT_EARS,
+ "MASKS" = ITEM_SLOT_MASK,
+ "HATS" = ITEM_SLOT_HEAD,
+ "SHOES" = ITEM_SLOT_FEET,
+ "BACKPACKS" = ITEM_SLOT_BACK,
+ "TIES" = ITEM_SLOT_NECK,
+))
+
//SLOT GROUP HELPERS
#define ITEM_SLOT_POCKETS (ITEM_SLOT_LPOCKET|ITEM_SLOT_RPOCKET)
/// Slots that are physically on you
@@ -135,11 +149,10 @@
#define CLOTHING_DIGITIGRADE_VARIATION (1<<1)
///The sprite works fine for digitigrade legs as-is.
#define CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON (1<<2)
-///has a sprite for monkeys
-#define CLOTHING_MONKEY_VARIATION (1<<3)
/// When worn by a mob with digitigrade, apply a filter
#define CLOTHING_DIGITIGRADE_FILTER (1<<3)
+/// Any flag which has a variation for digi
#define DIGITIGRADE_VARIATIONS (CLOTHING_DIGITIGRADE_VARIATION|CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON|CLOTHING_DIGITIGRADE_FILTER)
//flags for covering body parts
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 4696b532ed38..1e4e1ddd1c52 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -120,9 +120,7 @@
#define SPECIES_LIZARD_SILVER "silverscale"
#define SPECIES_NIGHTMARE "nightmare"
#define SPECIES_MONKEY "monkey"
-#define SPECIES_MONKEY_FREAK "monkey_freak"
-#define SPECIES_MONKEY_HOLODECK "monkey_holodeck"
-#define SPECIES_MONKEY_HUMAN_LEGGED "monkey_human_legged"
+#define SPECIES_MONKEY_LIZARD "kobold"
#define SPECIES_MOTH "moth"
#define SPECIES_MUSHROOM "mush"
#define SPECIES_PLASMAMAN "plasmaman"
@@ -130,7 +128,6 @@
#define SPECIES_SHADOW "shadow"
#define SPECIES_SKELETON "skeleton"
#define SPECIES_SNAIL "snail"
-#define SPECIES_TALLBOY "tallboy"
#define SPECIES_VAMPIRE "vampire"
#define SPECIES_ZOMBIE "zombie"
#define SPECIES_ZOMBIE_INFECTIOUS "memezombie"
@@ -668,18 +665,22 @@
// - They do not start at 0 for futureproofing
// - They skip numbers for futureproofing as well
// Otherwise they are completely arbitrary
-#define HUMAN_HEIGHT_DWARF 2
-#define HUMAN_HEIGHT_SHORTEST 4
-#define HUMAN_HEIGHT_SHORT 6
-#define HUMAN_HEIGHT_MEDIUM 8
-#define HUMAN_HEIGHT_TALL 10
-#define HUMAN_HEIGHT_TALLER 12
-#define HUMAN_HEIGHT_TALLEST 14
+#define MONKEY_HEIGHT_DWARF 2
+#define MONKEY_HEIGHT_MEDIUM 4
+#define HUMAN_HEIGHT_DWARF 6
+#define HUMAN_HEIGHT_SHORTEST 8
+#define HUMAN_HEIGHT_SHORT 10
+#define HUMAN_HEIGHT_MEDIUM 12
+#define HUMAN_HEIGHT_TALL 14
+#define HUMAN_HEIGHT_TALLER 16
+#define HUMAN_HEIGHT_TALLEST 18
/// Assoc list of all heights, cast to strings, to """"tuples"""""
/// The first """tuple""" index is the upper body offset
/// The second """tuple""" index is the lower body offset
GLOBAL_LIST_INIT(human_heights_to_offsets, list(
+ "[MONKEY_HEIGHT_DWARF]" = list(-9, -3),
+ "[MONKEY_HEIGHT_MEDIUM]" = list(-7, -4),
"[HUMAN_HEIGHT_DWARF]" = list(-5, -4),
"[HUMAN_HEIGHT_SHORTEST]" = list(-2, -1),
"[HUMAN_HEIGHT_SHORT]" = list(-1, -1),
diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm
index 12a114439c7d..24a29f7bda7c 100644
--- a/code/__DEFINES/research/anomalies.dm
+++ b/code/__DEFINES/research/anomalies.dm
@@ -20,11 +20,6 @@
GLOBAL_LIST_INIT(bioscrambler_parts_blacklist, typecacheof(list(
/obj/item/bodypart/chest/larva,
/obj/item/bodypart/head/larva,
- // Re-add the ones below this line when the bug with offset is fixed
- /obj/item/bodypart/leg/left/monkey,
- /obj/item/bodypart/leg/right/monkey,
- /obj/item/bodypart/leg/left/tallboy,
- /obj/item/bodypart/leg/right/tallboy,
)))
/// Blacklist of organs which should not appear when bioscrambled.
diff --git a/code/__DEFINES/species_clothing_paths.dm b/code/__DEFINES/species_clothing_paths.dm
index 2582d81b613b..99fe7baab1dd 100644
--- a/code/__DEFINES/species_clothing_paths.dm
+++ b/code/__DEFINES/species_clothing_paths.dm
@@ -6,10 +6,6 @@
///The dmi for humanoid oversuits
#define DEFAULT_SUIT_FILE 'icons/mob/clothing/suits/default.dmi'
-//MONKEY PATHS
-///The dmi for monkey uniforms
-#define MONKEY_UNIFORM_FILE 'icons/mob/human/species/monkey/uniform.dmi'
-
//DIGITIGRADE PATHS
///The dmi containing digitigrade uniforms
#define DIGITIGRADE_UNIFORM_FILE 'icons/mob/human/species/misc/digitigrade.dmi'
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 928a1b13c3d2..1c29bf639b3b 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -17,7 +17,7 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.body_markings_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/human, GLOB.tails_list_human, add_blank = TRUE)
init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/lizard, GLOB.tails_list_lizard, add_blank = TRUE)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/monkey, GLOB.tails_list_monkey, add_blank = TRUE)
+ init_sprite_accessory_subtypes(/datum/sprite_accessory/tails/monkey, GLOB.tails_list_monkey, add_blank = FALSE)
init_sprite_accessory_subtypes(/datum/sprite_accessory/snouts, GLOB.snouts_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/horns,GLOB.horns_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/ears, GLOB.ears_list)
@@ -40,13 +40,6 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/plumage, GLOB.avian_ears_list) //NON-MODULE CHANGE
init_sprite_accessory_subtypes(/datum/sprite_accessory/synth_head_cover, GLOB.synth_head_cover_list) //NON-MODULE CHANGE
-/// Inits GLOB.species_list. Not using GLOBAL_LIST_INIT b/c it depends on GLOB.string_lists
-/proc/init_species_list()
- for(var/species_path in subtypesof(/datum/species))
- var/datum/species/species = new species_path()
- GLOB.species_list[species.id] = species_path
- sort_list(GLOB.species_list, GLOBAL_PROC_REF(cmp_typepaths_asc))
-
/// Inits GLOB.surgeries
/proc/init_surgeries()
var/surgeries = list()
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 02f2450630d2..0d0d75d6df7e 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -227,7 +227,7 @@ world
/icon/proc/ColorTone(tone)
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
@@ -1544,7 +1544,7 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
var/list/icon_dimensions = get_icon_dimensions(source.icon)
var/width = icon_dimensions["width"]
var/height = icon_dimensions["height"]
-
+
if(width > world.icon_size)
alert_overlay.pixel_x = -(world.icon_size / 2) * ((width - world.icon_size) / world.icon_size)
if(height > world.icon_size)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index a45b6b92cc63..d17019bff426 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -40,6 +40,20 @@
else
return "#000000"
+/proc/random_hair_color()
+ var/static/list/natural_hair_colors = list(
+ "#111111", "#362925", "#3B3831", "#41250C", "#412922",
+ "#544C49", "#583322", "#593029", "#703b30", "#714721",
+ "#744729", "#74482a", "#7b746e", "#855832", "#863019",
+ "#8c4734", "#9F550E", "#A29A96", "#A4381C", "#B17B41",
+ "#C0BAB7", "#EFE5E4", "#F7F3F1", "#FFF2D6", "#a15537",
+ "#a17e61", "#b38b67", "#ba673c", "#c89f73", "#d9b380",
+ "#dbc9b8", "#e1621d", "#e17d17", "#e1af93", "#f1cc8f",
+ "#fbe7a1",
+ )
+
+ return pick(natural_hair_colors)
+
/proc/random_underwear(gender)
if(!GLOB.underwear_list.len)
init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
@@ -88,47 +102,6 @@
else
return pick(GLOB.facial_hairstyles_list)
-/proc/random_unique_name(gender, attempts_to_find_unique_name=10)
- for(var/i in 1 to attempts_to_find_unique_name)
- if(gender == FEMALE)
- . = capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names))
- else
- . = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names))
-
- if(!findname(.))
- break
-
-/proc/random_unique_lizard_name(gender, attempts_to_find_unique_name=10)
- for(var/i in 1 to attempts_to_find_unique_name)
- . = capitalize(lizard_name(gender))
-
- if(!findname(.))
- break
-
-/proc/random_unique_plasmaman_name(attempts_to_find_unique_name=10)
- for(var/i in 1 to attempts_to_find_unique_name)
- . = capitalize(plasmaman_name())
-
- if(!findname(.))
- break
-
-/proc/random_unique_ethereal_name(attempts_to_find_unique_name=10)
- for(var/i in 1 to attempts_to_find_unique_name)
- . = capitalize(ethereal_name())
-
- if(!findname(.))
- break
-
-/proc/random_unique_moth_name(attempts_to_find_unique_name=10)
- for(var/i in 1 to attempts_to_find_unique_name)
- . = capitalize(pick(GLOB.moth_first)) + " " + capitalize(pick(GLOB.moth_last))
-
- if(!findname(.))
- break
-
-/proc/random_skin_tone()
- return pick(GLOB.skin_tones)
-
GLOBAL_LIST_INIT(skin_tones, sort_list(list(
"albino",
"caucasian1",
@@ -167,9 +140,6 @@ GLOBAL_LIST_INIT(skin_tone_names, list(
"mixed4" = "Macadamia",
))
-/// An assoc list of species IDs to type paths
-GLOBAL_LIST_EMPTY(species_list)
-
/proc/age2agedescription(age)
switch(age)
if(0 to 1)
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index 33bb0348df56..95e6938330cc 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -1,20 +1,75 @@
-/proc/lizard_name(gender)
- if(gender == MALE)
- return "[pick(GLOB.lizard_names_male)]-[pick(GLOB.lizard_names_male)]"
- else
- return "[pick(GLOB.lizard_names_female)]-[pick(GLOB.lizard_names_female)]"
+/**
+ * Generate a random name based off of one of the roundstart languages
+ *
+ * * gender - What gender to pick from. Picks between male, female if not provided.
+ * * unique - If the name should be unique, IE, avoid picking names that mobs already have.
+ * * list/language_weights - A list of language weights to pick from.
+ * If not provided, it will default to a list of roundstart languages, with common being the most likely.
+ */
+/proc/generate_random_name(gender, unique, list/language_weights)
+ if(isnull(language_weights))
+ language_weights = list()
+ for(var/lang_type in GLOB.uncommon_roundstart_languages)
+ language_weights[lang_type] = 1
+ language_weights[/datum/language/common] = 20
+
+ var/datum/language/picked = GLOB.language_datum_instances[pick_weight(language_weights)]
+ if(unique)
+ return picked.get_random_unique_name(gender)
+ return picked.get_random_name(gender)
+
+/**
+ * Generate a random name based off of a species
+ * This will pick a name from the species language, and avoid picking common if there are alternatives
+ *
+ * * gender - What gender to pick from. Picks between male, female if not provided.
+ * * unique - If the name should be unique, IE, avoid picking names that mobs already have.
+ * * datum/species/species_type - The species to pick from
+ * * include_all - Makes the generated name a mix of all the languages the species can speak rather than just one of them
+ * Does this on a per-name basis, IE "Lizard first name, uncommon last name".
+ */
+/proc/generate_random_name_species_based(gender, unique, datum/species/species_type, include_all = FALSE)
+ ASSERT(ispath(species_type, /datum/species))
+ var/datum/language_holder/holder = GLOB.prototype_language_holders[species_type::species_language_holder]
-/proc/ethereal_name()
- var/tempname = "[pick(GLOB.ethereal_names)] [random_capital_letter()]"
- if(prob(65))
- tempname += random_capital_letter()
- return tempname
+ var/list/languages_to_pick_from = list()
+ for(var/language in holder.spoken_languages)
+ languages_to_pick_from[language] = 1
-/proc/plasmaman_name()
- return "[pick(GLOB.plasmaman_names)] \Roman[rand(1,99)]"
+ if(length(languages_to_pick_from) >= 2)
+ // Basically, if we have alternatives, don't pick common it's boring
+ languages_to_pick_from -= /datum/language/common
-/proc/moth_name()
- return "[pick(GLOB.moth_first)] [pick(GLOB.moth_last)]"
+ if(!include_all || length(languages_to_pick_from) <= 1)
+ return generate_random_name(gender, unique, languages_to_pick_from)
+
+ var/list/name_parts = list()
+ for(var/lang_type in shuffle(languages_to_pick_from))
+ name_parts += GLOB.language_datum_instances[lang_type].get_random_name(gender, name_count = 1, force_use_syllables = TRUE)
+ return jointext(name_parts, " ")
+
+/**
+ * Generates a random name for the mob based on their gender or species (for humans)
+ *
+ * * unique - If the name should be unique, IE, avoid picking names that mobs already have.
+ */
+/mob/proc/generate_random_mob_name(unique)
+ return generate_random_name_species_based(gender, unique, /datum/species/human)
+
+/mob/living/carbon/generate_random_mob_name(unique)
+ return generate_random_name_species_based(gender, unique, dna?.species?.type || /datum/species/human)
+
+/mob/living/silicon/generate_random_mob_name(unique)
+ return generate_random_name(gender, unique, list(/datum/language/machine = 1))
+
+/mob/living/basic/drone/generate_random_mob_name(unique)
+ return generate_random_name(gender, unique, list(/datum/language/machine = 1))
+
+/mob/living/basic/bot/generate_random_mob_name(unique)
+ return generate_random_name(gender, unique, list(/datum/language/machine = 1))
+
+/mob/living/simple_animal/bot/generate_random_mob_name(unique)
+ return generate_random_name(gender, unique, list(/datum/language/machine = 1))
GLOBAL_VAR(command_name)
/proc/command_name()
@@ -194,16 +249,11 @@ GLOBAL_DATUM(syndicate_code_response_regex, /regex)
if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc.
switch(rand(1,2))//Mainly to add more options later.
if(1)
- if(names.len && prob(70))
+ if(length(names) && prob(70))
. += pick(names)
else
- if(prob(10))
- . += pick(lizard_name(MALE),lizard_name(FEMALE))
- else
- var/new_name = pick(pick(GLOB.first_names_male,GLOB.first_names_female))
- new_name += " "
- new_name += pick(GLOB.last_names)
- . += new_name
+ . += generate_random_name()
+
if(2)
var/datum/job/job = pick(SSjob.joinable_occupations)
if(job)
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index fda5f03d38d8..8d35f2192062 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -117,6 +117,23 @@ GLOBAL_LIST_INIT(language_types_by_name, init_language_types_by_name())
lang_list[initial(lang_type.name)] = lang_type
return lang_list
+/// An assoc list of species IDs to type paths
+GLOBAL_LIST_INIT(species_list, init_species_list())
+/// List of all species prototypes to reference, assoc [type] = prototype
+GLOBAL_LIST_INIT_TYPED(species_prototypes, /datum/species, init_species_prototypes())
+
+/proc/init_species_list()
+ var/list/species_list = list()
+ for(var/datum/species/species_path as anything in subtypesof(/datum/species))
+ species_list[initial(species_path.id)] = species_path
+ return species_list
+
+/proc/init_species_prototypes()
+ var/list/species_list = list()
+ for(var/species_type in subtypesof(/datum/species))
+ species_list[species_type] = new species_type()
+ return species_list
+
GLOBAL_LIST_EMPTY(sentient_disease_instances)
GLOBAL_LIST_EMPTY(latejoin_ai_cores)
diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm
index c51fbaa9eb7a..81fe08373b31 100644
--- a/code/_globalvars/lists/names.dm
+++ b/code/_globalvars/lists/names.dm
@@ -8,12 +8,12 @@ GLOBAL_LIST_INIT(first_names, world.file2list("strings/names/first.txt"))
GLOBAL_LIST_INIT(first_names_male, world.file2list("strings/names/first_male.txt"))
GLOBAL_LIST_INIT(first_names_female, world.file2list("strings/names/first_female.txt"))
GLOBAL_LIST_INIT(last_names, world.file2list("strings/names/last.txt"))
-GLOBAL_LIST_INIT(lizard_names_male, world.file2list("strings/names/lizard_male.txt"))
-GLOBAL_LIST_INIT(lizard_names_female, world.file2list("strings/names/lizard_female.txt"))
GLOBAL_LIST_INIT(clown_names, world.file2list("strings/names/clown.txt"))
GLOBAL_LIST_INIT(mime_names, world.file2list("strings/names/mime.txt"))
GLOBAL_LIST_INIT(religion_names, world.file2list("strings/names/religion.txt"))
GLOBAL_LIST_INIT(carp_names, world.file2list("strings/names/carp.txt"))
+GLOBAL_LIST_INIT(lizard_names_male, world.file2list("strings/names/lizard_male.txt"))
+GLOBAL_LIST_INIT(lizard_names_female, world.file2list("strings/names/lizard_female.txt"))
GLOBAL_LIST_INIT(golem_names, world.file2list("strings/names/golem.txt"))
GLOBAL_LIST_INIT(moth_first, world.file2list("strings/names/moth_first.txt"))
GLOBAL_LIST_INIT(moth_last, world.file2list("strings/names/moth_last.txt"))
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index 664bf50fd666..1d17c515cd9d 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -126,8 +126,8 @@
/// Randomise friend name and appearance
/mob/camera/imaginary_friend/proc/setup_friend()
- var/gender = pick(MALE, FEMALE)
- real_name = random_unique_name(gender)
+ gender = pick(MALE, FEMALE)
+ real_name = generate_random_name_species_based(gender, FALSE, /datum/species/human)
name = real_name
human_image = get_flat_human_icon(null, pick(SSjob.joinable_occupations))
Show()
diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm
index 0cf6631f8074..1f432efc7345 100644
--- a/code/datums/components/butchering.dm
+++ b/code/datums/components/butchering.dm
@@ -107,6 +107,12 @@
var/final_effectiveness = effectiveness - target.butcher_difficulty
var/bonus_chance = max(0, (final_effectiveness - 100) + bonus_modifier) //so 125 total effectiveness = 25% extra chance
+ if(target.flags_1 & HOLOGRAM_1)
+ butcher.visible_message(span_notice("[butcher] tries to butcher [target], but it vanishes."), \
+ span_notice("You try to butcher [target], but it vanishes."))
+ qdel(target)
+ return
+
for(var/result_typepath in target.butcher_results)
var/obj/remains = result_typepath
var/amount = target.butcher_results[remains]
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index bf2b1084d7f2..77d787eb85ba 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -313,6 +313,16 @@
filter_data[name] = copied_parameters
update_filters()
+///A version of add_filter that takes a list of filters to add rather than being individual, to limit calls to update_filters().
+/datum/proc/add_filters(list/list/filters)
+ LAZYINITLIST(filter_data)
+ for(var/list/individual_filter as anything in filters)
+ var/list/params = individual_filter["params"]
+ var/list/copied_parameters = params.Copy()
+ copied_parameters["priority"] = individual_filter["priority"]
+ filter_data[individual_filter["name"]] = copied_parameters
+ update_filters()
+
/// Reapplies all the filters.
/datum/proc/update_filters()
ASSERT(isatom(src) || isimage(src))
diff --git a/code/datums/diseases/advance/symptoms/voice_change.dm b/code/datums/diseases/advance/symptoms/voice_change.dm
index 255c2a3f3a7f..9654365c49d3 100644
--- a/code/datums/diseases/advance/symptoms/voice_change.dm
+++ b/code/datums/diseases/advance/symptoms/voice_change.dm
@@ -54,7 +54,7 @@
else
if(ishuman(M))
var/mob/living/carbon/human/H = M
- H.SetSpecialVoice(H.dna.species.random_name(H.gender))
+ H.SetSpecialVoice(H.generate_random_mob_name())
if(scramble_language && !current_language) // Last part prevents rerolling language with small amounts of cure.
current_language = pick(subtypesof(/datum/language) - /datum/language/common)
H.add_blocked_language(subtypesof(/datum/language) - current_language, LANGUAGE_VOICECHANGE)
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 2d84900d7955..c275ed4b55cd 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -410,8 +410,9 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(holder && (human_mutation in mutations))
set_se(0, human_mutation)
. = human_mutation.on_losing(holder)
- qdel(human_mutation) // qdel mutations on removal
- update_instability(FALSE)
+ if(!(human_mutation in mutations))
+ qdel(human_mutation) // qdel mutations on removal
+ update_instability(FALSE)
return
/**
@@ -478,14 +479,10 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block())
if(create_mutation_blocks) //I hate this
generate_dna_blocks()
if(randomize_features)
- var/static/list/all_species_protoypes
- if(isnull(all_species_protoypes))
- all_species_protoypes = list()
- for(var/species_path in subtypesof(/datum/species))
- all_species_protoypes += new species_path()
-
- for(var/datum/species/random_species as anything in all_species_protoypes)
- features |= random_species.randomize_features()
+ for(var/species_type in GLOB.species_prototypes)
+ var/list/new_features = GLOB.species_prototypes[species_type].randomize_features()
+ for(var/feature in new_features)
+ features[feature] = new_features[feature]
features["mcolor"] = "#[random_color()]"
diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm
index 0f5a15206d8a..4913a057e011 100644
--- a/code/datums/mutations/_mutations.dm
+++ b/code/datums/mutations/_mutations.dm
@@ -49,6 +49,7 @@
* make sure to enter it both ways (so that A conflicts with B, and B with A)
*/
var/list/conflicts
+ var/remove_on_aheal = TRUE
/**
* can we take chromosomes?
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index ef028e14fb70..7686c00dde48 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -191,26 +191,35 @@
/datum/mutation/human/race
name = "Monkified"
desc = "A strange genome, believing to be what differentiates monkeys from humans."
- text_gain_indication = "You feel unusually monkey-like."
- text_lose_indication = "You feel like your old self."
+ text_gain_indication = span_green("You feel unusually primitive.")
+ text_lose_indication = span_notice("You feel like your old self.")
quality = NEGATIVE
+ remove_on_aheal = FALSE
locked = TRUE //Species specific, keep out of actual gene pool
var/datum/species/original_species = /datum/species/human
var/original_name
/datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner)
- if(..())
+ . = ..()
+ if(.)
return
- if(!ismonkey(owner))
- original_species = owner.dna.species.type
- original_name = owner.real_name
- owner.fully_replace_character_name(null, "monkey ([rand(1,999)])")
- . = owner.monkeyize()
+ if(ismonkey(owner))
+ return
+ original_species = owner.dna.species.type
+ original_name = owner.real_name
+ owner.monkeyize()
/datum/mutation/human/race/on_losing(mob/living/carbon/human/owner)
- if(!QDELETED(owner) && owner.stat != DEAD && (owner.dna.mutations.Remove(src)) && ismonkey(owner))
- owner.fully_replace_character_name(null, original_name)
- . = owner.humanize(original_species)
+ if(owner.stat == DEAD)
+ return
+ . = ..()
+ if(.)
+ return
+ if(QDELETED(owner))
+ return
+
+ owner.fully_replace_character_name(null, original_name)
+ owner.humanize(original_species)
/datum/mutation/human/glow
name = "Glowy"
diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm
index bf4da0d98ea8..5264267417ee 100644
--- a/code/datums/sprite_accessories.dm
+++ b/code/datums/sprite_accessories.dm
@@ -55,7 +55,7 @@
/// Determines if the accessory will be skipped or included in random hair generations.
var/gender = NEUTER
/// Something that can be worn by either gender, but looks different on each.
- var/gender_specific
+ var/gender_specific = FALSE
/// Determines if the accessory will be skipped by color preferences.
var/use_static
/**
@@ -75,6 +75,9 @@
var/dimension_y = 32
/// Should this sprite block emissives?
var/em_block = FALSE
+ /// Determines if this is considered "sane" for the purpose of [/proc/randomize_human_normie]
+ /// Basically this is to blacklist the extremely wacky stuff from being picked in random human generation.
+ var/natural_spawn = TRUE
/datum/sprite_accessory/blank
name = "None"
@@ -103,11 +106,13 @@
/datum/sprite_accessory/hair/afro_large
name = "Afro (Large)"
icon_state = "hair_bigafro"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/afro_huge
name = "Afro (Huge)"
icon_state = "hair_hugeafro"
y_offset = 6
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/allthefuzz
name = "All The Fuzz"
@@ -148,6 +153,7 @@
/datum/sprite_accessory/hair/bedheadfloorlength
name = "Floorlength Bedhead"
icon_state = "hair_floorlength_bedhead"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/badlycut
name = "Shorter Long Bedhead"
@@ -388,6 +394,7 @@
/datum/sprite_accessory/hair/bigflattop
name = "Flat Top (Big)"
icon_state = "hair_bigflattop"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/flow_hair
name = "Flow Hair"
@@ -448,6 +455,7 @@
/datum/sprite_accessory/hair/joestar
name = "Joestar"
icon_state = "hair_joestar"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/keanu
name = "Keanu Hair"
@@ -504,22 +512,27 @@
/datum/sprite_accessory/hair/mohawk
name = "Mohawk"
icon_state = "hair_d"
+ natural_spawn = FALSE // sorry little one
/datum/sprite_accessory/hair/nitori
name = "Nitori"
icon_state = "hair_nitori"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/reversemohawk
name = "Mohawk (Reverse)"
icon_state = "hair_reversemohawk"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/shavedmohawk
name = "Mohawk (Shaved)"
icon_state = "hair_shavedmohawk"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/unshavenmohawk
name = "Mohawk (Unshaven)"
icon_state = "hair_unshaven_mohawk"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/mulder
name = "Mulder"
@@ -528,6 +541,7 @@
/datum/sprite_accessory/hair/odango
name = "Odango"
icon_state = "hair_odango"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/ombre
name = "Ombre"
@@ -560,14 +574,17 @@
/datum/sprite_accessory/hair/kagami
name = "Pigtails"
icon_state = "hair_kagami"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/pigtail
name = "Pigtails 2"
icon_state = "hair_pigtails"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/pigtail2
name = "Pigtails 3"
icon_state = "hair_pigtails2"
+ natural_spawn = FALSE
/datum/sprite_accessory/hair/pixie
name = "Pixie Cut"
@@ -946,6 +963,7 @@
/datum/sprite_accessory/facial_hair/brokenman
name = "Beard (Broken Man)"
icon_state = "facial_brokenman"
+ natural_spawn = FALSE
/datum/sprite_accessory/facial_hair/chinstrap
name = "Beard (Chinstrap)"
@@ -990,6 +1008,7 @@
/datum/sprite_accessory/facial_hair/martialartist
name = "Beard (Martial Artist)"
icon_state = "facial_martialartist"
+ natural_spawn = FALSE
/datum/sprite_accessory/facial_hair/chinlessbeard
name = "Beard (Chinless Beard)"
@@ -1739,17 +1758,17 @@
/datum/sprite_accessory/body_markings/dtiger
name = "Dark Tiger Body"
icon_state = "dtiger"
- gender_specific = 1
+ gender_specific = TRUE
/datum/sprite_accessory/body_markings/ltiger
name = "Light Tiger Body"
icon_state = "ltiger"
- gender_specific = 1
+ gender_specific = TRUE
/datum/sprite_accessory/body_markings/lbelly
name = "Light Belly"
icon_state = "lbelly"
- gender_specific = 1
+ gender_specific = TRUE
/datum/sprite_accessory/tails
em_block = TRUE
@@ -1787,14 +1806,12 @@
icon_state = "default"
color_src = HAIR_COLOR
-/datum/sprite_accessory/tails/monkey
+/datum/sprite_accessory/tails/monkey/default
+ name = "Monkey"
icon = 'icons/mob/human/species/monkey/monkey_tail.dmi'
+ icon_state = "default"
color_src = FALSE
-/datum/sprite_accessory/tails/monkey/standard
- name = "Monkey"
- icon_state = "monkey"
-
/datum/sprite_accessory/pod_hair
icon = 'icons/mob/human/species/podperson_hair.dmi'
em_block = TRUE
diff --git a/code/datums/status_effects/_status_effect.dm b/code/datums/status_effects/_status_effect.dm
index b8d77db4ff7f..637f2c3a0767 100644
--- a/code/datums/status_effects/_status_effect.dm
+++ b/code/datums/status_effects/_status_effect.dm
@@ -13,7 +13,7 @@
/// -1 = will prevent ticks, and if duration is also unlimited (-1), stop processing wholesale.
var/tick_interval = 1 SECONDS
/// The mob affected by the status effect.
- var/mob/living/owner
+ VAR_FINAL/mob/living/owner
/// How many of the effect can be on one mob, and/or what happens when you try to add a duplicate.
var/status_type = STATUS_EFFECT_UNIQUE
/// If TRUE, we call [proc/on_remove] when owner is deleted. Otherwise, we call [proc/be_replaced].
@@ -22,7 +22,9 @@
/// Status effect "name"s and "description"s are shown to the owner here.
var/alert_type = /atom/movable/screen/alert/status_effect
/// The alert itself, created in [proc/on_creation] (if alert_type is specified).
- var/atom/movable/screen/alert/status_effect/linked_alert
+ VAR_FINAL/atom/movable/screen/alert/status_effect/linked_alert
+ /// If TRUE, and we have an alert, we will show a duration on the alert
+ var/show_duration = FALSE
/// Used to define if the status effect should be using SSfastprocess or SSprocessing
var/processing_speed = STATUS_EFFECT_FAST_PROCESS
/// Do we self-terminate when a fullheal is called?
@@ -30,7 +32,7 @@
/// If remove_on_fullheal is TRUE, what flag do we need to be removed?
var/heal_flag_necessary = HEAL_STATUS
/// A particle effect, for things like embers - Should be set on update_particles()
- var/obj/effect/abstract/particle_holder/particle_effect
+ VAR_FINAL/obj/effect/abstract/particle_holder/particle_effect
/datum/status_effect/New(list/arguments)
on_creation(arglist(arguments))
@@ -57,6 +59,7 @@
var/atom/movable/screen/alert/status_effect/new_alert = owner.throw_alert(id, alert_type)
new_alert.attached_effect = src //so the alert can reference us, if it needs to
linked_alert = new_alert //so we can reference the alert, if we need to
+ update_shown_duration()
if(duration > world.time || tick_interval > world.time) //don't process if we don't care
switch(processing_speed)
@@ -86,14 +89,24 @@
QDEL_NULL(particle_effect)
return ..()
+/// Updates the status effect alert's maptext (if possible)
+/datum/status_effect/proc/update_shown_duration()
+ PRIVATE_PROC(TRUE)
+ if(!linked_alert || !show_duration)
+ return
+
+ linked_alert.maptext = MAPTEXT_TINY_UNICODE("[round((duration - world.time)/10, 1)]s")
+
// Status effect process. Handles adjusting its duration and ticks.
// If you're adding processed effects, put them in [proc/tick]
// instead of extending / overriding the process() proc.
/datum/status_effect/process(seconds_per_tick)
SHOULD_NOT_OVERRIDE(TRUE)
+
if(QDELETED(owner))
qdel(src)
return
+
if(tick_interval != -1 && tick_interval < world.time)
var/tick_length = initial(tick_interval)
tick(tick_length / (1 SECONDS))
@@ -101,8 +114,12 @@
if(QDELING(src))
// tick deleted us, no need to continue
return
- if(duration != -1 && duration < world.time)
- qdel(src)
+
+ if(duration != -1)
+ if(duration < world.time)
+ qdel(src)
+ return
+ update_shown_duration()
/// Called whenever the effect is applied in on_created
/// Returning FALSE will cause it to delete itself during creation instead.
@@ -185,24 +202,25 @@
qdel(src)
return TRUE
+ update_shown_duration()
return FALSE
/**
* Updates the particles for the status effects
* Should be handled by subtypes!
*/
-
/datum/status_effect/proc/update_particles()
SHOULD_CALL_PARENT(FALSE)
+ return
/// Alert base type for status effect alerts
/atom/movable/screen/alert/status_effect
name = "Curse of Mundanity"
desc = "You don't feel any different..."
+ maptext_y = 2
/// The status effect we're linked to
var/datum/status_effect/attached_effect
/atom/movable/screen/alert/status_effect/Destroy()
attached_effect = null //Don't keep a ref now
return ..()
-
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 6cd180ab5970..744f32fca8f8 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -114,6 +114,7 @@
id = "fleshmend"
duration = 10 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/fleshmend
+ show_duration = TRUE
/datum/status_effect/fleshmend/on_apply()
. = ..()
@@ -376,6 +377,7 @@
duration = 1 MINUTES
status_type = STATUS_EFFECT_REPLACE
alert_type = /atom/movable/screen/alert/status_effect/regenerative_core
+ show_duration = TRUE
/datum/status_effect/regenerative_core/on_apply()
ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, STATUS_EFFECT_TRAIT)
@@ -395,6 +397,7 @@
id = "Lightning Orb"
duration = 30 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/lightningorb
+ show_duration = TRUE
/datum/status_effect/lightningorb/on_apply()
. = ..()
@@ -457,6 +460,7 @@
id = "speed_boost"
duration = 2 SECONDS
status_type = STATUS_EFFECT_REPLACE
+ show_duration = TRUE
/datum/status_effect/speed_boost/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm
index 3c751432d3b5..f4e70c637ea9 100644
--- a/code/datums/status_effects/debuffs/debuffs.dm
+++ b/code/datums/status_effects/debuffs/debuffs.dm
@@ -677,6 +677,7 @@
duration = 150
status_type = STATUS_EFFECT_REFRESH
alert_type = /atom/movable/screen/alert/status_effect/convulsing
+ show_duration = TRUE
/datum/status_effect/convulsing/on_creation(mob/living/zappy_boy)
. = ..()
diff --git a/code/datums/status_effects/food_effects.dm b/code/datums/status_effects/food_effects.dm
index e41ef67ad105..625a3fcc8762 100644
--- a/code/datums/status_effects/food_effects.dm
+++ b/code/datums/status_effects/food_effects.dm
@@ -3,6 +3,8 @@
id = "food_buff"
duration = 5 MINUTES // Same as food mood buffs
status_type = STATUS_EFFECT_REPLACE // Only one food buff allowed
+ alert_type = /atom/movable/screen/alert/status_effect/food
+ show_duration = TRUE
/// Buff power
var/strength
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 638f4e3367a5..1fe2c90a7791 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -20,7 +20,7 @@
#define GENETIC_DAMAGE_ACCURACY_MULTIPLIER 3
/// Special status indicating a scanner occupant is transforming eg. from monkey to human
-#define STATUS_TRANSFORMING 4
+#define STATUS_TRANSFORMING 5
/// Multiplier for how much genetic damage received from DNA Console functionality
#define GENETIC_DAMAGE_IRGENETIC_DAMAGE_MULTIPLIER 1
diff --git a/code/game/machinery/computer/records/security.dm b/code/game/machinery/computer/records/security.dm
index 3a929f7a298a..00dba3722944 100644
--- a/code/game/machinery/computer/records/security.dm
+++ b/code/game/machinery/computer/records/security.dm
@@ -49,10 +49,8 @@
if(prob(10/severity))
switch(rand(1,5))
if(1)
- if(prob(10))
- target.name = "[pick(lizard_name(MALE),lizard_name(FEMALE))]"
- else
- target.name = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]"
+ target.name = generate_random_name()
+
if(2)
target.gender = pick("Male", "Female", "Other")
if(3)
diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm
index 8978bd4150a1..b9094192a917 100644
--- a/code/game/machinery/medical_kiosk.dm
+++ b/code/game/machinery/medical_kiosk.dm
@@ -239,6 +239,7 @@
var/blood_percent = round((patient.blood_volume / BLOOD_VOLUME_NORMAL)*100)
var/blood_type = "[patient.get_blood_type() || "None"]" // NON-MODULE CHANGE
var/blood_warning = " "
+ var/blood_alcohol = patient.get_blood_alcohol_content()
for(var/thing in patient.diseases) //Disease Information
var/datum/disease/D = thing
@@ -354,6 +355,7 @@
data["bleed_status"] = bleed_status
data["blood_levels"] = blood_percent - (chaos_modifier * (rand(1,35)))
data["blood_status"] = blood_status
+ data["blood_alcohol"] = blood_alcohol
data["chemical_list"] = chemical_list
data["overdose_list"] = overdose_list
data["addict_list"] = addict_list
diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm
index f58ea90211d1..9f5d9e6cc9e7 100644
--- a/code/game/objects/items/cardboard_cutouts.dm
+++ b/code/game/objects/items/cardboard_cutouts.dm
@@ -296,7 +296,7 @@
outfit = /datum/outfit/ashwalker/spear
/datum/cardboard_cutout/ash_walker/get_name()
- return lizard_name(pick(MALE, FEMALE))
+ return generate_random_name_species_based(species_type = /datum/species/lizard)
/datum/cardboard_cutout/death_squad
name = "Deathsquad Officer"
diff --git a/code/game/objects/items/debug_items.dm b/code/game/objects/items/debug_items.dm
index 4f6239acbe81..44f53df2c2b2 100644
--- a/code/game/objects/items/debug_items.dm
+++ b/code/game/objects/items/debug_items.dm
@@ -21,7 +21,7 @@
/obj/item/debug/human_spawner/attack_self(mob/user)
..()
- var/choice = input("Select a species", "Human Spawner", null) in GLOB.species_list
+ var/choice = input("Select a species", "Human Spawner", null) in sortTim(GLOB.species_list, GLOBAL_PROC_REF(cmp_text_asc))
selected_species = GLOB.species_list[choice]
/obj/item/debug/omnitool
@@ -168,4 +168,3 @@
var/turf/loc_turf = get_turf(src)
for(var/spawn_atom in (choice == "No" ? typesof(path) : subtypesof(path)))
new spawn_atom(loc_turf)
-
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 3866afd6add1..19db85cec156 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -372,6 +372,14 @@
render_list += "Blood level: [blood_percent] %, [target.blood_volume] cl, type: [blood_type]\n"
// NON-MODULE CHANGE END
+ // Blood Alcohol Content
+ var/blood_alcohol_content = target.get_blood_alcohol_content()
+ if(blood_alcohol_content > 0)
+ if(blood_alcohol_content >= 0.24)
+ render_list += "Blood alcohol content: CRITICAL [blood_alcohol_content]%\n"
+ else
+ render_list += "Blood alcohol content: [blood_alcohol_content]%\n"
+
// Cybernetics
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index eab3f34d343f..ca1425fe06ac 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -311,6 +311,7 @@
young = TRUE
name = "[Mama] Jr" //Icelandic naming convention pending
normal_desc = "[src] is a little baby of [maternal_parent] and [paternal_parent]!" //original desc won't be used so the child can have moods
+ transform *= 0.75
update_desc()
Mama.mood_message = pick(Mama.parent_message)
diff --git a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
index c89fae800b9b..ee459ec6e317 100644
--- a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
+++ b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
@@ -3,6 +3,7 @@
id = "golem_status"
duration = 5 MINUTES
alert_type = /atom/movable/screen/alert/status_effect/golem_status
+ show_duration = TRUE
/// Icon state prefix for overlay to display on golem limbs
var/overlay_state_prefix
/// Name of the mineral we ate to get this
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index b48cafc85d99..5f19807e4d3d 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -32,7 +32,7 @@
victim = locate() in parts_list
if(!victim) //likely a mapspawned one
victim = new(src)
- victim.real_name = random_unique_name(prob(50))
+ victim.real_name = generate_random_name()
spear = locate(speartype) in parts_list
if(!spear)
spear = new speartype(src)
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 72f28ed82517..339f10b6b181 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -23,6 +23,7 @@
if(length(give_turf_traits))
give_turf_traits = string_list(give_turf_traits)
AddElement(/datum/element/give_turf_traits, give_turf_traits)
+ AddElement(/datum/element/footstep_override, footstep = FOOTSTEP_CATWALK)
/datum/armor/structure_lattice
melee = 50
@@ -100,10 +101,6 @@
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP
give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_IMMERSE_STOPPED, TRAIT_HYPERSPACE_STOPPED)
-/obj/structure/lattice/catwalk/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/footstep_override, footstep = FOOTSTEP_CATWALK)
-
/obj/structure/lattice/catwalk/deconstruction_hints(mob/user)
return span_notice("The supporting rods look like they could be cut.")
diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm
index 64aa0c60de77..b4cad70d7584 100644
--- a/code/modules/admin/create_mob.dm
+++ b/code/modules/admin/create_mob.dm
@@ -11,12 +11,12 @@
user << browse(create_panel_helper(create_mob_html), "window=create_mob;size=425x475")
/**
- * Randomizes everything about a human, including DNA and name
+ * Fully randomizes everything about a human, including DNA and name.
*/
/proc/randomize_human(mob/living/carbon/human/human, randomize_mutations = FALSE)
human.gender = human.dna.species.sexes ? pick(MALE, FEMALE, PLURAL, NEUTER) : PLURAL
human.physique = human.gender
- human.real_name = human.dna?.species.random_name(human.gender) || random_unique_name(human.gender)
+ human.real_name = human.generate_random_mob_name()
human.name = human.get_visible_name()
human.set_hairstyle(random_hairstyle(human.gender), update = FALSE)
human.set_facial_hairstyle(random_facial_hairstyle(human.gender), update = FALSE)
@@ -24,10 +24,39 @@
human.set_facial_haircolor(human.hair_color, update = FALSE)
human.eye_color_left = random_eye_color()
human.eye_color_right = human.eye_color_left
- human.skin_tone = random_skin_tone()
+ human.skin_tone = pick(GLOB.skin_tones)
human.dna.species.randomize_active_underwear_only(human)
// Needs to be called towards the end to update all the UIs just set above
human.dna.initialize_dna(create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
// Snowflake for Ethereals
human.updatehealth()
human.updateappearance(mutcolor_update = TRUE)
+
+/**
+ * Randomizes a human, but produces someone who looks exceedingly average (by most standards).
+ *
+ * (IE, no wacky hair styles / colors)
+ */
+/proc/randomize_human_normie(mob/living/carbon/human/human, randomize_mutations = FALSE)
+ // Sorry enbys but statistically you are not average enough
+ human.gender = human.dna.species.sexes ? pick(MALE, FEMALE) : PLURAL
+ human.physique = human.gender
+ human.real_name = human.generate_random_mob_name()
+ human.name = human.get_visible_name()
+ human.eye_color_left = random_eye_color()
+ human.eye_color_right = human.eye_color_left
+ human.skin_tone = pick(GLOB.skin_tones)
+ // No underwear generation handled here
+ var/picked_color = random_hair_color()
+ human.set_haircolor(picked_color, update = FALSE)
+ human.set_facial_haircolor(picked_color, update = FALSE)
+ var/datum/sprite_accessory/hairstyle = GLOB.hairstyles_list[random_hairstyle(human.gender)]
+ if(hairstyle && hairstyle.natural_spawn && !hairstyle.locked)
+ human.set_hairstyle(hairstyle.name, update = FALSE)
+ var/datum/sprite_accessory/facial_hair = GLOB.facial_hairstyles_list[random_facial_hairstyle(human.gender)]
+ if(facial_hair && facial_hair.natural_spawn && !facial_hair.locked)
+ human.set_facial_hairstyle(facial_hair.name, update = FALSE)
+ // Normal DNA init stuff, these can generally be wacky but we care less, they're aliens after all
+ human.dna.initialize_dna(create_mutation_blocks = randomize_mutations, randomize_features = TRUE)
+ human.updatehealth()
+ human.updateappearance(mutcolor_update = TRUE)
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index c8c3b660920e..31c34957544e 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -231,10 +231,10 @@
if(isliving(M))
if(iscarbon(M)) //Carbon stuff
- if(ismonkey(M))
- M_job = "Monkey"
- else if(ishuman(M))
+ if(ishuman(M) && M.job)
M_job = M.job
+ else if(ismonkey(M))
+ M_job = "Monkey"
else if(isalien(M)) //aliens
if(islarva(M))
M_job = "Alien larva"
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index e5cd0cf21e5e..2313c1741315 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -199,7 +199,12 @@
if(posttransformoutfit && istype(newmob))
newmob.equipOutfit(posttransformoutfit)
if("monkey")
- M.change_mob_type( /mob/living/carbon/human/species/monkey , null, null, delmob )
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ H.dna.add_mutation(/datum/mutation/human/race, MUT_NORMAL)
+ H.dna.activate_mutation(/datum/mutation/human/race)
+ else
+ M.change_mob_type( /mob/living/carbon/human/species/monkey , null, null, delmob )
if("robot")
M.change_mob_type( /mob/living/silicon/robot , null, null, delmob )
diff --git a/code/modules/admin/verbs/anonymousnames.dm b/code/modules/admin/verbs/anonymousnames.dm
index 9a71d68637a8..7f56155c9989 100644
--- a/code/modules/admin/verbs/anonymousnames.dm
+++ b/code/modules/admin/verbs/anonymousnames.dm
@@ -94,7 +94,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
return
var/mob/living/carbon/human/human_mob = player
var/original_name = player.real_name //id will not be changed if you do not do this
- randomize_human(player) //do this first so the special name can be given
+ randomize_human_normie(player) //do this first so the special name can be given
player.fully_replace_character_name(original_name, anonymous_name(player))
if(extras_enabled)
player_extras(player)
@@ -131,8 +131,7 @@ GLOBAL_DATUM(current_anonymous_theme, /datum/anonymous_theme)
/datum/anonymous_theme/proc/anonymous_name(mob/target)
var/datum/client_interface/client = GET_CLIENT(target)
var/species_type = client.prefs.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
- return species.random_name(target.gender,1)
+ return generate_random_name_species_based(target.gender, TRUE, species_type)
/**
* anonymous_ai_name: generates a random name, based off of whatever the round's anonymousnames is set to (but for sillycones).
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index f69745f1f1d6..4bc8fb042354 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -307,7 +307,7 @@ GLOBAL_VAR_INIT(say_disabled, FALSE)
else
for(var/obj/item/I in D)
qdel(I)
- randomize_human(D)
+ randomize_human_normie(D)
D.dress_up_as_job(
equipping = JB,
visual_only = TRUE,
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index f9879428ddfe..93354850b847 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -225,7 +225,7 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
if("allspecies")
if(!is_funmin)
return
- var/result = input(holder, "Please choose a new species","Species") as null|anything in GLOB.species_list
+ var/result = input(holder, "Please choose a new species","Species") as null|anything in sortTim(GLOB.species_list, GLOBAL_PROC_REF(cmp_text_asc))
if(result)
SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Species Change", "[result]"))
log_admin("[key_name(holder)] turned all humans into [result]")
diff --git a/code/modules/antagonists/heretic/magic/realignment.dm b/code/modules/antagonists/heretic/magic/realignment.dm
index 081138b7181b..d3ddc03fbbef 100644
--- a/code/modules/antagonists/heretic/magic/realignment.dm
+++ b/code/modules/antagonists/heretic/magic/realignment.dm
@@ -53,6 +53,7 @@
duration = 8 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/realignment
tick_interval = 0.2 SECONDS
+ show_duration = TRUE
/datum/status_effect/realignment/get_examine_text()
return span_notice("[owner.p_Theyre()] glowing a soft white.")
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index 607f485ff642..4a6586caa45e 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -6,6 +6,7 @@
status_type = STATUS_EFFECT_REFRESH
duration = 15 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/crucible_soul
+ show_duration = TRUE
var/turf/location
/datum/status_effect/crucible_soul/on_apply()
@@ -30,6 +31,7 @@
id = "Blessing of Dusk and Dawn"
status_type = STATUS_EFFECT_REFRESH
duration = 60 SECONDS
+ show_duration = TRUE
alert_type =/atom/movable/screen/alert/status_effect/duskndawn
/datum/status_effect/duskndawn/on_apply()
@@ -47,6 +49,7 @@
status_type = STATUS_EFFECT_REFRESH
duration = 60 SECONDS
tick_interval = 1 SECONDS
+ show_duration = TRUE
alert_type = /atom/movable/screen/alert/status_effect/marshal
/datum/status_effect/marshal/on_apply()
@@ -299,6 +302,7 @@
id = "Moon Grasp Hide Identity"
status_type = STATUS_EFFECT_REFRESH
duration = 15 SECONDS
+ show_duration = TRUE
alert_type = /atom/movable/screen/alert/status_effect/moon_grasp_hide
/datum/status_effect/moon_grasp_hide/on_apply()
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
index bda79c908c04..d2f4a5a07635 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
@@ -24,8 +24,7 @@
var/datum/action/cooldown/spell/conjure_item/clown_pockets/new_spell = new(victim)
new_spell.Grant(victim)
continue
- if (!ismonkey(victim)) // Monkeys cannot yet wear clothes
- dress_as_magic_clown(victim)
+ dress_as_magic_clown(victim)
if (prob(15))
create_vendetta(victim.mind, invoker.mind)
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
index 50054417362d..b506b1f4df48 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
@@ -83,21 +83,6 @@
. = ..()
disconnect_from_area(area_to_unregister)
-///adds a gas or list of gases to our filter_types. used so that the scrubber can check if its supposed to be processing after each change
-/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/add_filters(filter_or_filters)
- if(!islist(filter_or_filters))
- filter_or_filters = list(filter_or_filters)
-
- for(var/gas_to_filter in filter_or_filters)
- var/translated_gas = istext(gas_to_filter) ? gas_id2path(gas_to_filter) : gas_to_filter
-
- if(ispath(translated_gas, /datum/gas))
- filter_types |= translated_gas
- continue
-
- atmos_conditions_changed()
- return TRUE
-
///remove a gas or list of gases from our filter_types.used so that the scrubber can check if its supposed to be processing after each change
/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/remove_filters(filter_or_filters)
if(!islist(filter_or_filters))
diff --git a/code/modules/capture_the_flag/medieval_sim/medisim_game.dm b/code/modules/capture_the_flag/medieval_sim/medisim_game.dm
index 3546ce0881d6..9b274de740f4 100644
--- a/code/modules/capture_the_flag/medieval_sim/medisim_game.dm
+++ b/code/modules/capture_the_flag/medieval_sim/medisim_game.dm
@@ -23,7 +23,7 @@
if(!.)
return
var/mob/living/carbon/human/human_knight = .
- randomize_human(human_knight)
+ randomize_human_normie(human_knight)
human_knight.dna.add_mutation(/datum/mutation/human/medieval, MUT_OTHER)
var/oldname = human_knight.name
var/title = "error"
diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm
index 82a9d24451d2..7522e4ff3af7 100644
--- a/code/modules/client/preferences/_preference.dm
+++ b/code/modules/client/preferences/_preference.dm
@@ -18,16 +18,19 @@
/// support the "use gender" option.
#define PREFERENCE_PRIORITY_BODY_TYPE 5
+/// Used for preferences that rely on body setup being finalized.
+#define PREFERENCE_PRORITY_LATE_BODY_TYPE 6
+
/// Equpping items based on preferences.
/// Should happen after species and body type to make sure it looks right.
/// Mostly redundant, but a safety net for saving/loading.
-#define PREFERENCE_PRIORITY_LOADOUT 6
+#define PREFERENCE_PRIORITY_LOADOUT 7
/// The priority at which names are decided, needed for proper randomization.
-#define PREFERENCE_PRIORITY_NAMES 7
+#define PREFERENCE_PRIORITY_NAMES 8
/// Preferences that aren't names, but change the name changes set by PREFERENCE_PRIORITY_NAMES.
-#define PREFERENCE_PRIORITY_NAME_MODIFICATIONS 8
+#define PREFERENCE_PRIORITY_NAME_MODIFICATIONS 9
/// The maximum preference priority, keep this updated, but don't use it for `priority`.
#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_NAME_MODIFICATIONS
@@ -336,7 +339,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
)
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
+ var/datum/species/species = GLOB.species_prototypes[species_type]
if (!(savefile_key in species.get_features()))
return FALSE
diff --git a/code/modules/client/preferences/age.dm b/code/modules/client/preferences/age.dm
index cad9786ce1fe..07b4644bc6a9 100644
--- a/code/modules/client/preferences/age.dm
+++ b/code/modules/client/preferences/age.dm
@@ -8,3 +8,6 @@
/datum/preference/numeric/age/apply_to_human(mob/living/carbon/human/target, value)
target.age = value
+
+/datum/preference/numeric/age/create_informed_default_value(datum/preferences/preferences)
+ return rand(max(minimum, 21), min(maximum, 50))
diff --git a/code/modules/client/preferences/body_type.dm b/code/modules/client/preferences/body_type.dm
index 469b564e46f7..6b27e0f0f0da 100644
--- a/code/modules/client/preferences/body_type.dm
+++ b/code/modules/client/preferences/body_type.dm
@@ -5,6 +5,7 @@
priority = PREFERENCE_PRIORITY_BODY_TYPE
savefile_key = "body_type"
savefile_identifier = PREFERENCE_CHARACTER
+ can_randomize = FALSE
/datum/preference/choiced/body_type/init_possible_values()
return list(USE_GENDER, MALE, FEMALE)
diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm
index cf200ad1ffd6..b81291c7cbdd 100644
--- a/code/modules/client/preferences/clothing.dm
+++ b/code/modules/client/preferences/clothing.dm
@@ -33,6 +33,9 @@
DMESSENGER,
)
+/datum/preference/choiced/backpack/create_default_value()
+ return GBACKPACK
+
/datum/preference/choiced/backpack/icon_for(value)
switch (value)
if (GBACKPACK)
@@ -66,6 +69,7 @@
/datum/preference/choiced/jumpsuit
savefile_key = "jumpsuit_style"
savefile_identifier = PREFERENCE_CHARACTER
+ priority = PREFERENCE_PRIORITY_BODY_TYPE
main_feature_name = "Jumpsuit"
category = PREFERENCE_CATEGORY_CLOTHING
should_generate_icons = TRUE
@@ -86,6 +90,15 @@
/datum/preference/choiced/jumpsuit/apply_to_human(mob/living/carbon/human/target, value)
target.jumpsuit_style = value
+/datum/preference/choiced/jumpsuit/create_informed_default_value(datum/preferences/preferences)
+ switch(preferences.read_preference(/datum/preference/choiced/gender))
+ if(MALE)
+ return PREF_SUIT
+ if(FEMALE)
+ return PREF_SKIRT
+
+ return ..()
+
/// Socks preference
/datum/preference/choiced/socks
savefile_key = "socks"
@@ -93,10 +106,14 @@
main_feature_name = "Socks"
category = PREFERENCE_CATEGORY_CLOTHING
should_generate_icons = TRUE
+ can_randomize = FALSE
/datum/preference/choiced/socks/init_possible_values()
return assoc_to_keys_features(GLOB.socks_list)
+/datum/preference/choiced/socks/create_default_value()
+ return /datum/sprite_accessory/socks/nude::name
+
/datum/preference/choiced/socks/icon_for(value)
var/static/icon/lower_half
@@ -114,13 +131,27 @@
/datum/preference/choiced/undershirt
savefile_key = "undershirt"
savefile_identifier = PREFERENCE_CHARACTER
+ priority = PREFERENCE_PRIORITY_BODY_TYPE
main_feature_name = "Undershirt"
category = PREFERENCE_CATEGORY_CLOTHING
should_generate_icons = TRUE
+ can_randomize = FALSE
/datum/preference/choiced/undershirt/init_possible_values()
return assoc_to_keys_features(GLOB.undershirt_list)
+/datum/preference/choiced/undershirt/create_default_value()
+ return /datum/sprite_accessory/undershirt/nude::name
+
+/datum/preference/choiced/undershirt/create_informed_default_value(datum/preferences/preferences)
+ switch(preferences.read_preference(/datum/preference/choiced/gender))
+ if(MALE)
+ return /datum/sprite_accessory/undershirt/nude::name
+ if(FEMALE)
+ return /datum/sprite_accessory/undershirt/sports_bra::name
+
+ return ..()
+
/datum/preference/choiced/undershirt/icon_for(value)
var/static/icon/body
if (isnull(body))
@@ -152,10 +183,14 @@
main_feature_name = "Underwear"
category = PREFERENCE_CATEGORY_CLOTHING
should_generate_icons = TRUE
+ can_randomize = FALSE
/datum/preference/choiced/underwear/init_possible_values()
return assoc_to_keys_features(GLOB.underwear_list)
+/datum/preference/choiced/underwear/create_default_value()
+ return /datum/sprite_accessory/underwear/male_hearts::name
+
/datum/preference/choiced/underwear/icon_for(value)
var/static/icon/lower_half
@@ -175,7 +210,7 @@
return FALSE
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
+ var/datum/species/species = GLOB.species_prototypes[species_type]
return !(TRAIT_NO_UNDERWEAR in species.inherent_traits)
/datum/preference/choiced/underwear/compile_constant_data()
diff --git a/code/modules/client/preferences/gender.dm b/code/modules/client/preferences/gender.dm
index bea6674d7b08..a95874f16068 100644
--- a/code/modules/client/preferences/gender.dm
+++ b/code/modules/client/preferences/gender.dm
@@ -11,3 +11,8 @@
if(!target.dna.species.sexes)
value = PLURAL //disregard gender preferences on this species
target.gender = value
+
+/datum/preference/choiced/gender/create_informed_default_value(datum/preferences/preferences)
+ // The only reason I'm limiting this to male or female
+ // is that hairstyle randomization handles enbies poorly
+ return pick(MALE, FEMALE)
diff --git a/code/modules/client/preferences/glasses.dm b/code/modules/client/preferences/glasses.dm
index 03c975abce78..d8ec141dbe8d 100644
--- a/code/modules/client/preferences/glasses.dm
+++ b/code/modules/client/preferences/glasses.dm
@@ -4,6 +4,9 @@
savefile_identifier = PREFERENCE_CHARACTER
should_generate_icons = TRUE
+/datum/preference/choiced/glasses/create_default_value()
+ return "Random"
+
/datum/preference/choiced/glasses/init_possible_values()
return assoc_to_keys(GLOB.nearsighted_glasses) + "Random"
diff --git a/code/modules/client/preferences/language.dm b/code/modules/client/preferences/language.dm
index f602d6b3a66c..637c4542da27 100644
--- a/code/modules/client/preferences/language.dm
+++ b/code/modules/client/preferences/language.dm
@@ -3,6 +3,9 @@
savefile_key = "language"
savefile_identifier = PREFERENCE_CHARACTER
+/datum/preference/choiced/language/create_default_value()
+ return "Random"
+
/datum/preference/choiced/language/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
diff --git a/code/modules/client/preferences/names.dm b/code/modules/client/preferences/names.dm
index 476fc7381a28..9afc8da18c1a 100644
--- a/code/modules/client/preferences/names.dm
+++ b/code/modules/client/preferences/names.dm
@@ -45,12 +45,11 @@
target.log_mob_tag("TAG: [target.tag] RENAMED: [key_name(target)]")
/datum/preference/name/real_name/create_informed_default_value(datum/preferences/preferences)
- var/species_type = preferences.read_preference(/datum/preference/choiced/species)
- var/gender = preferences.read_preference(/datum/preference/choiced/gender)
-
- var/datum/species/species = new species_type
-
- return species.random_name(gender, unique = TRUE)
+ return generate_random_name_species_based(
+ preferences.read_preference(/datum/preference/choiced/gender),
+ TRUE,
+ preferences.read_preference(/datum/preference/choiced/species),
+ )
/datum/preference/name/real_name/deserialize(input, datum/preferences/preferences)
input = ..(input)
@@ -73,9 +72,7 @@
savefile_key = "human_name"
/datum/preference/name/backup_human/create_informed_default_value(datum/preferences/preferences)
- var/gender = preferences.read_preference(/datum/preference/choiced/gender)
-
- return random_unique_name(gender)
+ return generate_random_name(preferences.read_preference(/datum/preference/choiced/gender))
/datum/preference/name/clown
savefile_key = "clown_name"
diff --git a/code/modules/client/preferences/species.dm b/code/modules/client/preferences/species.dm
index 9e4923d2b11d..1c74d7981b65 100644
--- a/code/modules/client/preferences/species.dm
+++ b/code/modules/client/preferences/species.dm
@@ -34,7 +34,7 @@
for (var/species_id in get_selectable_species())
var/species_type = GLOB.species_list[species_id]
- var/datum/species/species = new species_type()
+ var/datum/species/species = GLOB.species_prototypes[species_type]
data[species_id] = list()
data[species_id]["name"] = species.name
@@ -47,6 +47,4 @@
data[species_id]["perks"] = species.get_species_perks()
data[species_id]["diet"] = species.get_species_diet()
- qdel(species)
-
return data
diff --git a/code/modules/client/preferences/species_features/basic.dm b/code/modules/client/preferences/species_features/basic.dm
index abf4ea0e44e2..68fac62f43dd 100644
--- a/code/modules/client/preferences/species_features/basic.dm
+++ b/code/modules/client/preferences/species_features/basic.dm
@@ -7,7 +7,7 @@
var/icon/final_icon = new(head_icon)
if (!isnull(sprite_accessory))
ASSERT(istype(sprite_accessory))
-
+
var/icon/head_accessory_icon = icon(sprite_accessory.icon, sprite_accessory.icon_state)
if(y_offset)
head_accessory_icon.Shift(NORTH, y_offset)
@@ -52,7 +52,7 @@
return random_eye_color()
/datum/preference/choiced/facial_hairstyle
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRORITY_LATE_BODY_TYPE
savefile_key = "facial_style_name"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -69,15 +69,32 @@
/datum/preference/choiced/facial_hairstyle/apply_to_human(mob/living/carbon/human/target, value)
target.set_facial_hairstyle(value, update = FALSE)
+/datum/preference/choiced/facial_hairstyle/create_default_value()
+ return /datum/sprite_accessory/facial_hair/shaved::name
+
+/datum/preference/choiced/facial_hairstyle/create_informed_default_value(datum/preferences/preferences)
+ var/gender = preferences.read_preference(/datum/preference/choiced/gender)
+ var/species_type = preferences.read_preference(/datum/preference/choiced/species)
+ var/datum/species/species_real = GLOB.species_prototypes[species_type]
+ if(!gender || !species_real || !species_real.sexes)
+ return ..()
+
+ var/picked_beard = random_facial_hairstyle(gender)
+ var/datum/sprite_accessory/beard_style = GLOB.facial_hairstyles_list[picked_beard]
+ if(!beard_style || !beard_style.natural_spawn || beard_style.locked) // Invalid, go with god(bald)
+ return ..()
+
+ return picked_beard
+
/datum/preference/choiced/facial_hairstyle/compile_constant_data()
var/list/data = ..()
- data[SUPPLEMENTAL_FEATURE_KEY] = "facial_hair_color"
+ data[SUPPLEMENTAL_FEATURE_KEY] = /datum/preference/color/facial_hair_color::savefile_key
return data
/datum/preference/color/facial_hair_color
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRORITY_LATE_BODY_TYPE // Need to happen after hair oclor is set so we can match by default
savefile_key = "facial_hair_color"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES
@@ -86,12 +103,16 @@
/datum/preference/color/facial_hair_color/apply_to_human(mob/living/carbon/human/target, value)
target.set_facial_haircolor(value, update = FALSE)
+/datum/preference/color/facial_hair_color/create_informed_default_value(datum/preferences/preferences)
+ return preferences.read_preference(/datum/preference/color/hair_color) || random_hair_color()
+
/datum/preference/choiced/facial_hair_gradient
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRORITY_LATE_BODY_TYPE
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "facial_hair_gradient"
relevant_head_flag = HEAD_FACIAL_HAIR
+ can_randomize = FALSE
/datum/preference/choiced/facial_hair_gradient/init_possible_values()
return assoc_to_keys_features(GLOB.facial_hair_gradients_list)
@@ -100,10 +121,10 @@
target.set_facial_hair_gradient_style(new_style = value, update = FALSE)
/datum/preference/choiced/facial_hair_gradient/create_default_value()
- return "None"
+ return /datum/sprite_accessory/gradient/none::name
/datum/preference/color/facial_hair_gradient
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRORITY_LATE_BODY_TYPE
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "facial_hair_gradient_color"
@@ -115,10 +136,10 @@
/datum/preference/color/facial_hair_gradient/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
- return preferences.read_preference(/datum/preference/choiced/facial_hair_gradient) != "None"
+ return preferences.read_preference(/datum/preference/choiced/facial_hair_gradient) != /datum/sprite_accessory/gradient/none::name
/datum/preference/color/hair_color
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRIORITY_BODY_TYPE
savefile_key = "hair_color"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SUPPLEMENTAL_FEATURES
@@ -127,8 +148,11 @@
/datum/preference/color/hair_color/apply_to_human(mob/living/carbon/human/target, value)
target.set_haircolor(value, update = FALSE)
+/datum/preference/color/hair_color/create_informed_default_value(datum/preferences/preferences)
+ return random_hair_color()
+
/datum/preference/choiced/hairstyle
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRIORITY_BODY_TYPE // Happens after gender so we can picka hairstyle based on that
savefile_key = "hairstyle_name"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_FEATURES
@@ -146,19 +170,37 @@
/datum/preference/choiced/hairstyle/apply_to_human(mob/living/carbon/human/target, value)
target.set_hairstyle(value, update = FALSE)
+/datum/preference/choiced/hairstyle/create_default_value()
+ return /datum/sprite_accessory/hair/bald::name
+
+/datum/preference/choiced/hairstyle/create_informed_default_value(datum/preferences/preferences)
+ var/gender = preferences.read_preference(/datum/preference/choiced/gender)
+ var/species_type = preferences.read_preference(/datum/preference/choiced/species)
+ var/datum/species/species_real = GLOB.species_prototypes[species_type]
+ if(!gender || !species_real || !species_real.sexes)
+ return ..()
+
+ var/picked_hair = random_hairstyle(gender)
+ var/datum/sprite_accessory/hair_style = GLOB.hairstyles_list[picked_hair]
+ if(!hair_style || !hair_style.natural_spawn || hair_style.locked) // Invalid, go with god(bald)
+ return ..()
+
+ return picked_hair
+
/datum/preference/choiced/hairstyle/compile_constant_data()
var/list/data = ..()
- data[SUPPLEMENTAL_FEATURE_KEY] = "hair_color"
+ data[SUPPLEMENTAL_FEATURE_KEY] = /datum/preference/color/hair_color::savefile_key
return data
/datum/preference/choiced/hair_gradient
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRIORITY_BODY_TYPE
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "hair_gradient"
relevant_head_flag = HEAD_HAIR
+ can_randomize = FALSE
/datum/preference/choiced/hair_gradient/init_possible_values()
return assoc_to_keys_features(GLOB.hair_gradients_list)
@@ -167,10 +209,10 @@
target.set_hair_gradient_style(new_style = value, update = FALSE)
/datum/preference/choiced/hair_gradient/create_default_value()
- return "None"
+ return /datum/sprite_accessory/gradient/none::name
/datum/preference/color/hair_gradient
- priority = PREFERENCE_PRIORITY_BODYPARTS
+ priority = PREFERENCE_PRIORITY_BODY_TYPE
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
savefile_identifier = PREFERENCE_CHARACTER
savefile_key = "hair_gradient_color"
@@ -182,4 +224,4 @@
/datum/preference/color/hair_gradient/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE
- return preferences.read_preference(/datum/preference/choiced/hair_gradient) != "None"
+ return preferences.read_preference(/datum/preference/choiced/hair_gradient) != /datum/sprite_accessory/gradient/none::name
diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm
index b9f3c7bfa337..de794d3b770a 100644
--- a/code/modules/client/preferences/species_features/felinid.dm
+++ b/code/modules/client/preferences/species_features/felinid.dm
@@ -29,5 +29,4 @@
target.dna.features["ears"] = value
/datum/preference/choiced/ears/create_default_value()
- var/datum/sprite_accessory/ears/cat/ears = /datum/sprite_accessory/ears/cat
- return initial(ears.name)
+ return /datum/sprite_accessory/ears/cat::name
diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm
index 8c53d0ba45c6..c070b195f498 100644
--- a/code/modules/client/preferences/species_features/lizard.dm
+++ b/code/modules/client/preferences/species_features/lizard.dm
@@ -142,5 +142,4 @@
target.dna.features["tail_lizard"] = value
/datum/preference/choiced/lizard_tail/create_default_value()
- var/datum/sprite_accessory/tails/lizard/smooth/tail = /datum/sprite_accessory/tails/lizard/smooth
- return initial(tail.name)
+ return /datum/sprite_accessory/tails/lizard/smooth::name
diff --git a/code/modules/client/preferences/species_features/monkey.dm b/code/modules/client/preferences/species_features/monkey.dm
new file mode 100644
index 000000000000..7ad357e21afd
--- /dev/null
+++ b/code/modules/client/preferences/species_features/monkey.dm
@@ -0,0 +1,15 @@
+/datum/preference/choiced/monkey_tail
+ savefile_key = "feature_monkey_tail"
+ savefile_identifier = PREFERENCE_CHARACTER
+ category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
+ relevant_external_organ = /obj/item/organ/external/tail/monkey
+ can_randomize = FALSE
+
+/datum/preference/choiced/monkey_tail/init_possible_values()
+ return assoc_to_keys_features(GLOB.tails_list_monkey)
+
+/datum/preference/choiced/monkey_tail/apply_to_human(mob/living/carbon/human/target, value)
+ target.dna.features["tail_monkey"] = value
+
+/datum/preference/choiced/monkey_tail/create_default_value()
+ return /datum/sprite_accessory/tails/monkey/default::name
diff --git a/code/modules/client/preferences/species_features/mutants.dm b/code/modules/client/preferences/species_features/mutants.dm
index 7ecf25d9abce..1d18c78ee1ad 100644
--- a/code/modules/client/preferences/species_features/mutants.dm
+++ b/code/modules/client/preferences/species_features/mutants.dm
@@ -9,7 +9,7 @@
return FALSE
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
+ var/datum/species/species = GLOB.species_prototypes[species_type]
return !(TRAIT_FIXED_MUTANT_COLORS in species.inherent_traits)
/datum/preference/color/mutant_color/create_default_value()
diff --git a/code/modules/client/preferences/underwear_color.dm b/code/modules/client/preferences/underwear_color.dm
index 6e64b4423e50..1304bdaf2da8 100644
--- a/code/modules/client/preferences/underwear_color.dm
+++ b/code/modules/client/preferences/underwear_color.dm
@@ -8,7 +8,7 @@
return FALSE
var/species_type = preferences.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
+ var/datum/species/species = GLOB.species_prototypes[species_type]
return !(TRAIT_NO_UNDERWEAR in species.inherent_traits)
/datum/preference/color/underwear_color/apply_to_human(mob/living/carbon/human/target, value)
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 15ba900b756a..6451d180e46a 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -291,13 +291,13 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return FALSE
// Read everything into cache
- for (var/preference_type in GLOB.preference_entries)
- var/datum/preference/preference = GLOB.preference_entries[preference_type]
+ // Uses priority order as some values may rely on others for creating default values
+ for (var/datum/preference/preference as anything in get_preferences_in_priority_order())
if (preference.savefile_identifier != PREFERENCE_CHARACTER)
continue
- value_cache -= preference_type
- read_preference(preference_type)
+ value_cache -= preference.type
+ read_preference(preference.type)
//Character
randomise = save_data?["randomise"]
diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm
index 9a3d94e1dbf4..ca82b687d8b4 100644
--- a/code/modules/clothing/suits/labcoat.dm
+++ b/code/modules/clothing/suits/labcoat.dm
@@ -69,6 +69,10 @@
greyscale_config_worn = /datum/greyscale_config/labcoat/worn
greyscale_colors = "#EEEEEE#4A77A1#4A77A1#7095C2"
+/obj/item/clothing/suit/toggle/labcoat/genetics/Initialize(mapload)
+ . = ..()
+ allowed += /obj/item/sequence_scanner
+
/obj/item/clothing/suit/toggle/labcoat/chemist
name = "chemist labcoat"
desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder."
@@ -106,6 +110,9 @@
allowed += list(
/obj/item/autopsy_scanner,
/obj/item/scythe,
+ /obj/item/shovel,
+ /obj/item/shovel/serrated,
+ /obj/item/trench_tool,
)
/obj/item/clothing/suit/toggle/labcoat/science
diff --git a/code/modules/clothing/suits/wintercoats.dm b/code/modules/clothing/suits/wintercoats.dm
index 7ade6df6f7c3..45f17146617c 100644
--- a/code/modules/clothing/suits/wintercoats.dm
+++ b/code/modules/clothing/suits/wintercoats.dm
@@ -388,6 +388,9 @@
allowed += list(
/obj/item/autopsy_scanner,
/obj/item/scythe,
+ /obj/item/shovel,
+ /obj/item/shovel/serrated,
+ /obj/item/trench_tool,
)
/obj/item/clothing/head/hooded/winterhood/medical/coroner
@@ -503,6 +506,10 @@
inhand_icon_state = null
hoodtype = /obj/item/clothing/head/hooded/winterhood/science/genetics
+/obj/item/clothing/suit/hooded/wintercoat/science/genetics/Initialize(mapload)
+ . = ..()
+ allowed += /obj/item/sequence_scanner
+
/obj/item/clothing/head/hooded/winterhood/science/genetics
desc = "A white winter coat hood. It's warm."
icon_state = "hood_genetics"
@@ -630,6 +637,8 @@
/obj/item/storage/bag/ore,
/obj/item/t_scanner/adv_mining_scanner,
/obj/item/tank/internals,
+ /obj/item/shovel,
+ /obj/item/trench_tool,
)
armor_type = /datum/armor/wintercoat_miner
hoodtype = /obj/item/clothing/head/hooded/winterhood/miner
diff --git a/code/modules/clothing/under/suits.dm b/code/modules/clothing/under/suits.dm
index 02f047c2f489..0dbf1880d7d2 100644
--- a/code/modules/clothing/under/suits.dm
+++ b/code/modules/clothing/under/suits.dm
@@ -84,7 +84,6 @@
desc = "It's a very smart uniform with a special pocket for tip."
icon_state = "waiter"
inhand_icon_state = "waiter"
- supports_variations_flags = CLOTHING_MONKEY_VARIATION
/obj/item/clothing/under/suit/black_really
name = "executive suit"
diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm
index 20a5cda05920..e6f7c9f26d00 100644
--- a/code/modules/clothing/under/syndicate.dm
+++ b/code/modules/clothing/under/syndicate.dm
@@ -8,7 +8,6 @@
alt_covers_chest = TRUE
icon = 'icons/obj/clothing/under/syndicate.dmi'
worn_icon = 'icons/mob/clothing/under/syndicate.dmi'
- supports_variations_flags = CLOTHING_MONKEY_VARIATION
/datum/armor/clothing_under/syndicate
melee = 10
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index 57c3248f8cdc..a33a8d75f177 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -147,6 +147,12 @@
if(!occupant)
audible_message(span_hear("You hear a loud metallic grinding sound."))
return
+ if(occupant.flags_1 & HOLOGRAM_1)
+ audible_message(span_hear("You hear a very short metallic grinding sound."))
+ playsound(loc, 'sound/machines/hiss.ogg', 20, TRUE)
+ qdel(occupant)
+ set_occupant(null)
+ return
use_power(active_power_usage)
audible_message(span_hear("You hear a loud squelchy grinding sound."))
diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm
index 1bbedefb2e0e..afd4c2270388 100644
--- a/code/modules/holodeck/holo_effect.dm
+++ b/code/modules/holodeck/holo_effect.dm
@@ -62,11 +62,9 @@
mobtype = pick(mobtype)
our_mob = new mobtype(loc)
our_mob.flags_1 |= HOLOGRAM_1
- ADD_TRAIT(our_mob, TRAIT_PERMANENTLY_MORTAL, INNATE_TRAIT)
// these vars are not really standardized but all would theoretically create stuff on death
- for(var/v in list("butcher_results","corpse","weapon1","weapon2","blood_volume") & our_mob.vars)
- our_mob.vars[v] = null
+ our_mob.add_traits(list(TRAIT_PERMANENTLY_MORTAL, TRAIT_NO_BLOOD_OVERLAY, TRAIT_NOBLOOD, TRAIT_NOHUNGER), INNATE_TRAIT)
RegisterSignal(our_mob, COMSIG_QDELETING, PROC_REF(handle_mob_delete))
return our_mob
@@ -103,7 +101,7 @@
mobtype = /mob/living/basic/bee/toxin
/obj/effect/holodeck_effect/mobspawner/monkey
- mobtype = /mob/living/carbon/human/species/monkey/holodeck
+ mobtype = /mob/living/carbon/human/species/monkey
/obj/effect/holodeck_effect/mobspawner/monkey/activate(obj/machinery/computer/holodeck/computer)
var/mob/living/carbon/human/monkey = ..()
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 06cb8cbc55d9..65b5d9a4100d 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -117,6 +117,9 @@
///RPG job names, for the memes
var/rpg_title
+ /// Alternate titles to register as pointing to this job.
+ var/list/alternate_titles
+
/// Does this job ignore human authority?
var/ignore_human_authority = FALSE
@@ -545,11 +548,11 @@
dna.species.roundstart_changed = TRUE
apply_pref_name(/datum/preference/name/backup_human, player_client)
if(CONFIG_GET(flag/force_random_names))
- var/species_type = player_client.prefs.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
-
- var/gender = player_client.prefs.read_preference(/datum/preference/choiced/gender)
- real_name = species.random_name(gender, TRUE)
+ real_name = generate_random_name_species_based(
+ player_client.prefs.read_preference(/datum/preference/choiced/gender),
+ TRUE,
+ player_client.prefs.read_preference(/datum/preference/choiced/species),
+ )
dna.update_dna_identity()
@@ -571,9 +574,11 @@
if(!player_client)
return // Disconnected while checking the appearance ban.
- var/species_type = player_client.prefs.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
- organic_name = species.random_name(player_client.prefs.read_preference(/datum/preference/choiced/gender), TRUE)
+ organic_name = generate_random_name_species_based(
+ player_client.prefs.read_preference(/datum/preference/choiced/gender),
+ TRUE,
+ player_client.prefs.read_preference(/datum/preference/choiced/species),
+ )
else
if(!player_client)
return // Disconnected while checking the appearance ban.
diff --git a/code/modules/language/_language.dm b/code/modules/language/_language.dm
new file mode 100644
index 000000000000..3876720cbd44
--- /dev/null
+++ b/code/modules/language/_language.dm
@@ -0,0 +1,164 @@
+/// maximum of 50 specific scrambled lines per language
+#define SCRAMBLE_CACHE_LEN 50
+
+/// Datum based languages. Easily editable and modular.
+/datum/language
+ /// Fluff name of language if any.
+ var/name = "an unknown language"
+ /// Short description for 'Check Languages'.
+ var/desc = "A language."
+ /// Character used to speak in language
+ /// If key is null, then the language isn't real or learnable.
+ var/key
+ /// Various language flags.
+ var/flags = NONE
+ /// Used when scrambling text for a non-speaker.
+ var/list/syllables
+ /// List of characters that will randomly be inserted between syllables.
+ var/list/special_characters
+ /// Likelihood of making a new sentence after each syllable.
+ var/sentence_chance = 5
+ /// Likelihood of getting a space in the random scramble string
+ var/space_chance = 55
+ /// Spans to apply from this language
+ var/list/spans
+ /// Cache of recently scrambled text
+ /// This allows commonly reused words to not require a full re-scramble every time.
+ var/list/scramble_cache = list()
+ /// The language that an atom knows with the highest "default_priority" is selected by default.
+ var/default_priority = 0
+ /// If TRUE, when generating names, we will always use the default human namelist, even if we have syllables set.
+ /// This is to be used for languages with very outlandish syllable lists (like pirates).
+ var/always_use_default_namelist = FALSE
+ /// Icon displayed in the chat window when speaking this language.
+ /// if you are seeing someone speak popcorn language, then something is wrong.
+ var/icon = 'icons/misc/language.dmi'
+ /// Icon state displayed in the chat window when speaking this language.
+ var/icon_state = "popcorn"
+
+ /// By default, random names picks this many names
+ var/default_name_count = 2
+ /// By default, random names picks this many syllables (min)
+ var/default_name_syllable_min = 2
+ /// By default, random names picks this many syllables (max)
+ var/default_name_syllable_max = 4
+ /// What char to place in between randomly generated names
+ var/random_name_spacer = " "
+
+/// Checks whether we should display the language icon to the passed hearer.
+/datum/language/proc/display_icon(atom/movable/hearer)
+ var/understands = hearer.has_language(src.type)
+ if((flags & LANGUAGE_HIDE_ICON_IF_UNDERSTOOD) && understands)
+ return FALSE
+ if((flags & LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD) && !understands)
+ return FALSE
+ return TRUE
+
+/// Returns the icon to display in the chat window when speaking this language.
+/datum/language/proc/get_icon()
+ var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
+ return sheet.icon_tag("language-[icon_state]")
+
+/// Simple helper for getting a default firstname lastname
+/datum/language/proc/default_name(gender = NEUTER)
+ if(gender != MALE)
+ gender = pick(MALE, FEMALE)
+ if(gender == FEMALE)
+ return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names))
+ return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names))
+
+
+/**
+ * Generates a random name this language would use.
+ *
+ * * gender: What gender to generate from, if neuter / plural coin flips between male and female
+ * * name_count: How many names to generate in, by default 2, for firstname lastname
+ * * syllable_count: How many syllables to generate in each name, min
+ * * syllable_max: How many syllables to generate in each name, max
+ * * force_use_syllables: If the name should be generated from the syllables list.
+ * Only used for subtypes which implement custom name lists. Also requires the language has syllables set.
+ */
+/datum/language/proc/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(gender != MALE)
+ gender = pick(MALE, FEMALE)
+ if(!length(syllables) || always_use_default_namelist)
+ return default_name(gender)
+
+ var/list/full_name = list()
+ for(var/i in 1 to name_count)
+ var/new_name = ""
+ for(var/j in 1 to rand(default_name_syllable_min, default_name_syllable_max))
+ new_name += pick_weight_recursive(syllables)
+ full_name += capitalize(lowertext(new_name))
+
+ return jointext(full_name, random_name_spacer)
+
+/// Generates a random name, and attempts to ensure it is unique (IE, no other mob in the world has it)
+/datum/language/proc/get_random_unique_name(...)
+ var/result = get_random_name(arglist(args))
+ for(var/i in 1 to 10)
+ if(!findname(result))
+ break
+ result = get_random_name(arglist(args))
+
+ return result
+
+/datum/language/proc/check_cache(input)
+ var/lookup = scramble_cache[input]
+ if(lookup)
+ scramble_cache -= input
+ scramble_cache[input] = lookup
+ . = lookup
+
+/datum/language/proc/add_to_cache(input, scrambled_text)
+ // Add it to cache, cutting old entries if the list is too long
+ scramble_cache[input] = scrambled_text
+ if(scramble_cache.len > SCRAMBLE_CACHE_LEN)
+ scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1)
+
+/datum/language/proc/scramble(input)
+
+ if(!length(syllables))
+ return stars(input)
+
+ // If the input is cached already, move it to the end of the cache and return it
+ var/lookup = check_cache(input)
+ if(lookup)
+ return lookup
+
+ var/input_size = length_char(input)
+ var/scrambled_text = ""
+ var/capitalize = TRUE
+
+ while(length_char(scrambled_text) < input_size)
+ var/next = (length(scrambled_text) && length(special_characters) && prob(1)) ? pick(special_characters) : pick_weight_recursive(syllables)
+ if(capitalize)
+ next = capitalize(next)
+ capitalize = FALSE
+ scrambled_text += next
+ var/chance = rand(100)
+ if(chance <= sentence_chance)
+ scrambled_text += ". "
+ capitalize = TRUE
+ else if(chance > sentence_chance && chance <= space_chance)
+ scrambled_text += " "
+
+ scrambled_text = trim(scrambled_text)
+ var/ending = copytext_char(scrambled_text, -1)
+ if(ending == ".")
+ scrambled_text = copytext_char(scrambled_text, 1, -2)
+ var/input_ending = copytext_char(input, -1)
+ if(input_ending in list("!","?","."))
+ scrambled_text += input_ending
+
+ add_to_cache(input, scrambled_text)
+
+ return scrambled_text
+
+#undef SCRAMBLE_CACHE_LEN
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/_language_holder.dm
similarity index 100%
rename from code/modules/language/language_holder.dm
rename to code/modules/language/_language_holder.dm
diff --git a/code/modules/language/language_manuals.dm b/code/modules/language/_language_manuals.dm
similarity index 100%
rename from code/modules/language/language_manuals.dm
rename to code/modules/language/_language_manuals.dm
diff --git a/code/modules/language/language_menu.dm b/code/modules/language/_language_menu.dm
similarity index 100%
rename from code/modules/language/language_menu.dm
rename to code/modules/language/_language_menu.dm
diff --git a/code/modules/language/aphasia.dm b/code/modules/language/aphasia.dm
index 9d4e317c4d88..2d82b79892ee 100644
--- a/code/modules/language/aphasia.dm
+++ b/code/modules/language/aphasia.dm
@@ -7,3 +7,4 @@
space_chance = 20
default_priority = 10
icon_state = "aphasia"
+ always_use_default_namelist = TRUE // Shouldn't generate names for this anyways
diff --git a/code/modules/language/beachbum.dm b/code/modules/language/beachbum.dm
index d78be9788f35..bd319e717ffd 100644
--- a/code/modules/language/beachbum.dm
+++ b/code/modules/language/beachbum.dm
@@ -17,5 +17,5 @@
"heavy", "stellar", "excellent", "triumphant", "babe", "four",
"tail", "trim", "tube", "wobble", "roll", "gnarly", "epic",
)
-
icon_state = "beach"
+ always_use_default_namelist = TRUE
diff --git a/code/modules/language/buzzwords.dm b/code/modules/language/buzzwords.dm
index c46088c0ad5b..2ed033bca345 100644
--- a/code/modules/language/buzzwords.dm
+++ b/code/modules/language/buzzwords.dm
@@ -8,3 +8,4 @@
)
icon_state = "buzz"
default_priority = 90
+ always_use_default_namelist = TRUE // Otherwise we get Bzzbzbz Zzzbzbz.
diff --git a/code/modules/language/calcic.dm b/code/modules/language/calcic.dm
index f4882e1105b9..477e442203bc 100644
--- a/code/modules/language/calcic.dm
+++ b/code/modules/language/calcic.dm
@@ -13,4 +13,16 @@
icon_state = "calcic"
default_priority = 90
+/datum/language/calcic/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(force_use_syllables)
+ return ..()
+
+ return "[pick(GLOB.plasmaman_names)] \Roman[rand(1, 99)]"
+
// Yeah, this goes to skeletons too, since it's basically just skeleton clacking.
diff --git a/code/modules/language/codespeak.dm b/code/modules/language/codespeak.dm
index 09db7ef511b4..242095b3bb7f 100644
--- a/code/modules/language/codespeak.dm
+++ b/code/modules/language/codespeak.dm
@@ -5,6 +5,7 @@
default_priority = 0
flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
icon_state = "codespeak"
+ always_use_default_namelist = TRUE // No syllables anyways
/datum/language/codespeak/scramble(input)
var/lookup = check_cache(input)
diff --git a/code/modules/language/common.dm b/code/modules/language/common.dm
index 2dc7294983c0..6bad808fef26 100644
--- a/code/modules/language/common.dm
+++ b/code/modules/language/common.dm
@@ -7,50 +7,51 @@
default_priority = 100
icon_state = "galcom"
-
-//Syllable Lists
-/*
- This list really long, mainly because I can't make up my mind about which mandarin syllables should be removed,
- and the english syllables had to be duplicated so that there is roughly a 50-50 weighting.
-
- Sources:
- http://www.sttmedia.com/syllablefrequency-english
- http://www.chinahighlights.com/travelguide/learning-chinese/pinyin-syllables.htm
-*/
-/datum/language/common/syllables = list(
- // each sublist has an equal chance of being picked, so each syllable has an equal chance of being english or chinese
- list(
- "a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao",
- "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "cei", "cen", "ceng", "cha", "chai",
- "chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chua", "chuai", "chuan", "chuang", "chui", "chun",
- "chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei",
- "den", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e",
- "ei", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang",
- "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha",
- "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hm", "hng", "hong", "hou", "hu", "hua", "huai", "huan",
- "huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan",
- "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "kei", "ken", "keng", "kong", "kou", "ku", "kua", "kuai",
- "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian",
- "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "luan", "lun", "luo", "ma", "mai", "man", "mang",
- "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na",
- "nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ng", "ni", "nian", "niang", "niao", "nie", "nin", "ning",
- "niu", "nong", "nou", "nu", "nuan", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng",
- "pi", "pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin",
- "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou",
- "ru", "rua", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sei", "sen", "seng", "sha",
- "shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui",
- "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te",
- "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan",
- "wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu",
- "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yong", "you", "yu", "yuan",
- "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao",
- "zhe", "zhei", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi",
- "zong", "zou", "zuan", "zui", "zun", "zuo", "zu",
- ),
- list(
- "al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it",
- "le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to",
- "ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin",
- "his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi",
- ),
-)
+ // Default namelist is the human namelist, and common is the human language, so might as well.
+ // Feel free to remove this at some point because common can generate some pretty cool names.
+ always_use_default_namelist = TRUE
+ /**
+ * This list really long, mainly because I can't make up my mind about which mandarin syllables should be removed,
+ * and the english syllables had to be duplicated so that there is roughly a 50-50 weighting.
+ *
+ * Sources:
+ * http://www.sttmedia.com/syllablefrequency-english
+ * http://www.chinahighlights.com/travelguide/learning-chinese/pinyin-syllables.htm
+ */
+ syllables = list(
+ // each sublist has an equal chance of being picked, so each syllable has an equal chance of being english or chinese
+ list(
+ "a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao",
+ "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "cei", "cen", "ceng", "cha", "chai",
+ "chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chua", "chuai", "chuan", "chuang", "chui", "chun",
+ "chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "dei",
+ "den", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e",
+ "ei", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang",
+ "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha",
+ "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hm", "hng", "hong", "hou", "hu", "hua", "huai", "huan",
+ "huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan",
+ "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "kei", "ken", "keng", "kong", "kou", "ku", "kua", "kuai",
+ "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian",
+ "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "luan", "lun", "luo", "ma", "mai", "man", "mang",
+ "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na",
+ "nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ng", "ni", "nian", "niang", "niao", "nie", "nin", "ning",
+ "niu", "nong", "nou", "nu", "nuan", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng",
+ "pi", "pian", "piao", "pie", "pin", "ping", "po", "pou", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin",
+ "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou",
+ "ru", "rua", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sei", "sen", "seng", "sha",
+ "shai", "shan", "shang", "shao", "she", "shei", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui",
+ "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te",
+ "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan",
+ "wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu",
+ "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yong", "you", "yu", "yuan",
+ "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao",
+ "zhe", "zhei", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi",
+ "zong", "zou", "zuan", "zui", "zun", "zuo", "zu",
+ ),
+ list(
+ "al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it",
+ "le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to",
+ "ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin",
+ "his", "ing", "ion", "ith", "not", "ome", "oul", "our", "sho", "ted", "ter", "tha", "the", "thi",
+ ),
+ )
diff --git a/code/modules/language/draconic.dm b/code/modules/language/draconic.dm
index f812c8dc1311..55ebd1ec2026 100644
--- a/code/modules/language/draconic.dm
+++ b/code/modules/language/draconic.dm
@@ -13,5 +13,25 @@
"ra", "ar", "re", "er", "ri", "ir", "ro", "or", "ru", "ur", "rs", "sr",
"a", "a", "e", "e", "i", "i", "o", "o", "u", "u", "s", "s"
)
+ special_characters = list("-")
icon_state = "lizard"
default_priority = 90
+ default_name_syllable_min = 3
+ default_name_syllable_max = 5
+ random_name_spacer = "-"
+
+/datum/language/draconic/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(force_use_syllables)
+ return ..()
+ if(gender != MALE)
+ gender = pick(MALE, FEMALE)
+
+ if(gender == MALE)
+ return "[pick(GLOB.lizard_names_male)][random_name_spacer][pick(GLOB.lizard_names_male)]"
+ return "[pick(GLOB.lizard_names_female)][random_name_spacer][pick(GLOB.lizard_names_female)]"
diff --git a/code/modules/language/drone.dm b/code/modules/language/drone.dm
index 5b47533d45e3..09fb6546e4a1 100644
--- a/code/modules/language/drone.dm
+++ b/code/modules/language/drone.dm
@@ -11,3 +11,4 @@
default_priority = 20
icon_state = "drone"
+ always_use_default_namelist = TRUE // Nonsense language
diff --git a/code/modules/language/language.dm b/code/modules/language/language.dm
deleted file mode 100644
index a47d097dd140..000000000000
--- a/code/modules/language/language.dm
+++ /dev/null
@@ -1,107 +0,0 @@
-#define SCRAMBLE_CACHE_LEN 50 //maximum of 50 specific scrambled lines per language
-
-/*
- Datum based languages. Easily editable and modular.
-*/
-
-/datum/language
- var/name = "an unknown language" // Fluff name of language if any.
- var/desc = "A language." // Short description for 'Check Languages'.
- var/key // Character used to speak in language
- // If key is null, then the language isn't real or learnable.
- var/flags // Various language flags.
- var/list/syllables // Used when scrambling text for a non-speaker.
- var/sentence_chance = 5 // Likelihood of making a new sentence after each syllable.
- var/space_chance = 55 // Likelihood of getting a space in the random scramble string
- var/list/spans = list()
- var/list/scramble_cache = list()
- var/default_priority = 0 // the language that an atom knows with the highest "default_priority" is selected by default.
-
- // if you are seeing someone speak popcorn language, then something is wrong.
- var/icon = 'icons/misc/language.dmi'
- var/icon_state = "popcorn"
-
-/datum/language/proc/display_icon(atom/movable/hearer)
- var/understands = hearer.has_language(src.type)
- if(flags & LANGUAGE_HIDE_ICON_IF_UNDERSTOOD && understands)
- return FALSE
- if(flags & LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD && !understands)
- return FALSE
- return TRUE
-
-/datum/language/proc/get_icon()
- var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
- return sheet.icon_tag("language-[icon_state]")
-
-/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4, syllable_divisor=2)
- if(!syllables || !syllables.len)
- if(gender == FEMALE)
- return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names))
- else
- return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names))
-
- var/full_name = ""
- var/new_name = ""
-
- for(var/i in 0 to name_count)
- new_name = ""
- var/Y = rand(FLOOR(syllable_count/syllable_divisor, 1), syllable_count)
- for(var/x in Y to 0)
- new_name += pick_weight_recursive(syllables)
- full_name += " [capitalize(lowertext(new_name))]"
-
- return "[trim(full_name)]"
-
-/datum/language/proc/check_cache(input)
- var/lookup = scramble_cache[input]
- if(lookup)
- scramble_cache -= input
- scramble_cache[input] = lookup
- . = lookup
-
-/datum/language/proc/add_to_cache(input, scrambled_text)
- // Add it to cache, cutting old entries if the list is too long
- scramble_cache[input] = scrambled_text
- if(scramble_cache.len > SCRAMBLE_CACHE_LEN)
- scramble_cache.Cut(1, scramble_cache.len-SCRAMBLE_CACHE_LEN-1)
-
-/datum/language/proc/scramble(input)
-
- if(!syllables || !syllables.len)
- return stars(input)
-
- // If the input is cached already, move it to the end of the cache and return it
- var/lookup = check_cache(input)
- if(lookup)
- return lookup
-
- var/input_size = length_char(input)
- var/scrambled_text = ""
- var/capitalize = TRUE
-
- while(length_char(scrambled_text) < input_size)
- var/next = pick_weight_recursive(syllables)
- if(capitalize)
- next = capitalize(next)
- capitalize = FALSE
- scrambled_text += next
- var/chance = rand(100)
- if(chance <= sentence_chance)
- scrambled_text += ". "
- capitalize = TRUE
- else if(chance > sentence_chance && chance <= space_chance)
- scrambled_text += " "
-
- scrambled_text = trim(scrambled_text)
- var/ending = copytext_char(scrambled_text, -1)
- if(ending == ".")
- scrambled_text = copytext_char(scrambled_text, 1, -2)
- var/input_ending = copytext_char(input, -1)
- if(input_ending in list("!","?","."))
- scrambled_text += input_ending
-
- add_to_cache(input, scrambled_text)
-
- return scrambled_text
-
-#undef SCRAMBLE_CACHE_LEN
diff --git a/code/modules/language/machine.dm b/code/modules/language/machine.dm
index 36962a712a1b..4be282a5e281 100644
--- a/code/modules/language/machine.dm
+++ b/code/modules/language/machine.dm
@@ -14,7 +14,16 @@
icon_state = "eal"
-/datum/language/machine/get_random_name()
+/datum/language/machine/get_random_name(
+ gender = NEUTER,
+ name_count = 2,
+ syllable_min = 2,
+ syllable_max = 4,
+ unique = FALSE,
+ force_use_syllables = FALSE,
+)
+ if(force_use_syllables)
+ return ..()
if(prob(70))
return "[pick(GLOB.posibrain_names)]-[rand(100, 999)]"
return pick(GLOB.ai_names)
diff --git a/code/modules/language/moffic.dm b/code/modules/language/moffic.dm
index 1d0aea96697f..fb8dea63dcc8 100644
--- a/code/modules/language/moffic.dm
+++ b/code/modules/language/moffic.dm
@@ -13,4 +13,20 @@
icon_state = "moth"
default_priority = 90
+ default_name_syllable_min = 5
+ default_name_syllable_max = 10
+
+/datum/language/moffic/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(force_use_syllables)
+ return ..()
+
+ return "[pick(GLOB.moth_first)] [pick(GLOB.moth_last)]"
+
+
// Fuck guest accounts, and fuck language testing.
diff --git a/code/modules/language/monkey.dm b/code/modules/language/monkey.dm
index e44f6a6268e2..423e94f22bd8 100644
--- a/code/modules/language/monkey.dm
+++ b/code/modules/language/monkey.dm
@@ -7,3 +7,12 @@
default_priority = 80
icon_state = "animal"
+
+/datum/language/monkey/get_random_name(
+ gender = NEUTER,
+ name_count = 2,
+ syllable_min = 2,
+ syllable_max = 4,
+ force_use_syllables = FALSE,
+)
+ return "monkey ([rand(1, 999)])"
diff --git a/code/modules/language/mushroom.dm b/code/modules/language/mushroom.dm
index 08d494cc04d6..910489fd6dd9 100644
--- a/code/modules/language/mushroom.dm
+++ b/code/modules/language/mushroom.dm
@@ -5,3 +5,5 @@
sentence_chance = 0
default_priority = 80
syllables = list("poof", "pff", "pFfF", "piff", "puff", "pooof", "pfffff", "piffpiff", "puffpuff", "poofpoof", "pifpafpofpuf")
+ default_name_syllable_min = 1
+ default_name_syllable_max = 2
diff --git a/code/modules/language/nekomimetic.dm b/code/modules/language/nekomimetic.dm
index 82edc2afcb57..4be943f84417 100644
--- a/code/modules/language/nekomimetic.dm
+++ b/code/modules/language/nekomimetic.dm
@@ -12,3 +12,16 @@
)
icon_state = "neko"
default_priority = 90
+ default_name_syllable_min = 2
+ default_name_syllable_max = 2
+
+/datum/language/nekomimetic/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(prob(33))
+ return default_name(gender)
+ return ..()
diff --git a/code/modules/language/piratespeak.dm b/code/modules/language/piratespeak.dm
index 5f6cb4897715..a2faddb544f7 100644
--- a/code/modules/language/piratespeak.dm
+++ b/code/modules/language/piratespeak.dm
@@ -10,3 +10,4 @@
"shiver", "timbers", "matey", "swashbuckler"
)
icon_state = "pirate"
+ always_use_default_namelist = TRUE
diff --git a/code/modules/language/shadowtongue.dm b/code/modules/language/shadowtongue.dm
index 9c0adb5eea3f..351589393856 100644
--- a/code/modules/language/shadowtongue.dm
+++ b/code/modules/language/shadowtongue.dm
@@ -16,3 +16,5 @@
)
icon_state = "shadow"
default_priority = 90
+ default_name_syllable_min = 2
+ default_name_syllable_max = 3
diff --git a/code/modules/language/slime.dm b/code/modules/language/slime.dm
index fcb471774118..15960898673d 100644
--- a/code/modules/language/slime.dm
+++ b/code/modules/language/slime.dm
@@ -2,7 +2,8 @@
name = "Slime"
desc = "A melodic and complex language spoken by slimes. Some of the notes are inaudible to humans."
key = "k"
- syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix","*","!")
+ syllables = list("qr","qrr","xuq","qil","quum","xuqm","vol","xrim","zaoo","qu-uu","qix","qoo","zix")
+ special_characters = list("!","*")
default_priority = 70
icon_state = "slime"
diff --git a/code/modules/language/sylvan.dm b/code/modules/language/sylvan.dm
index 68cb73f9d525..4f66fb5931c1 100644
--- a/code/modules/language/sylvan.dm
+++ b/code/modules/language/sylvan.dm
@@ -13,3 +13,5 @@
)
icon_state = "plant"
default_priority = 90
+ default_name_syllable_min = 2
+ default_name_syllable_max = 3
diff --git a/code/modules/language/terrum.dm b/code/modules/language/terrum.dm
index 361106ed16c9..63b527202f4c 100644
--- a/code/modules/language/terrum.dm
+++ b/code/modules/language/terrum.dm
@@ -7,8 +7,25 @@
"sha", "vu", "nah", "ha", "yom", "ma", "cha", "ar", "et", "mol", "lua",
"ch", "na", "sh", "ni", "yah", "bes", "ol", "hish", "ev", "la", "ot", "la",
"khe", "tza", "chak", "hak", "hin", "hok", "lir", "tov", "yef", "yfe",
- "cho", "ar", "kas", "kal", "ra", "lom", "im", "'", "'", "'", "'", "bok",
+ "cho", "ar", "kas", "kal", "ra", "lom", "im", "bok",
"erev", "shlo", "lo", "ta", "im", "yom"
)
+ special_characters = list("'")
icon_state = "golem"
default_priority = 90
+
+/datum/language/terrum/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(force_use_syllables)
+ return ..()
+
+ var/name = pick(GLOB.golem_names)
+ // 3% chance to be given a human surname for "lore reasons"
+ if (prob(3))
+ name += " [pick(GLOB.last_names)]"
+ return name
diff --git a/code/modules/language/voltaic.dm b/code/modules/language/voltaic.dm
index 40fa9dcb1e82..90ab90dbe48e 100644
--- a/code/modules/language/voltaic.dm
+++ b/code/modules/language/voltaic.dm
@@ -12,3 +12,21 @@
)
icon_state = "volt"
default_priority = 90
+ default_name_syllable_min = 2
+ default_name_syllable_max = 3
+
+
+/datum/language/voltaic/get_random_name(
+ gender = NEUTER,
+ name_count = default_name_count,
+ syllable_min = default_name_syllable_min,
+ syllable_max = default_name_syllable_max,
+ force_use_syllables = FALSE,
+)
+ if(force_use_syllables)
+ return ..()
+
+ var/picked = "[pick(GLOB.ethereal_names)] [random_capital_letter()]"
+ if(prob(65))
+ picked += random_capital_letter()
+ return picked
diff --git a/code/modules/language/xenocommon.dm b/code/modules/language/xenocommon.dm
index c5e6366715d8..f4949b7d73cb 100644
--- a/code/modules/language/xenocommon.dm
+++ b/code/modules/language/xenocommon.dm
@@ -6,3 +6,4 @@
default_priority = 50
icon_state = "xeno"
+ always_use_default_namelist = TRUE // Sssss Ssss?
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index ed69e8af884b..3824bc9eefff 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -921,8 +921,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
var/datum/species/new_human_species = GLOB.species_list[species_to_pick]
if(new_human_species)
new_human.set_species(new_human_species)
- new_human_species = new_human.dna.species
- new_human.fully_replace_character_name(new_human.real_name, new_human_species.random_name(new_human.gender, TRUE, TRUE))
+ new_human.fully_replace_character_name(new_human.real_name, new_human.generate_random_mob_name())
else
stack_trace("failed to spawn cadaver with species ID [species_to_pick]") //if it's invalid they'll just be a human, so no need to worry too much aside from yelling at the server owner lol.
else
diff --git a/code/modules/mining/equipment/monster_organs/rush_gland.dm b/code/modules/mining/equipment/monster_organs/rush_gland.dm
index b3932afdaab7..7a97248df0bc 100644
--- a/code/modules/mining/equipment/monster_organs/rush_gland.dm
+++ b/code/modules/mining/equipment/monster_organs/rush_gland.dm
@@ -41,6 +41,7 @@
id = "lobster_rush"
duration = 3 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/lobster_rush
+ show_duration = TRUE
var/spawned_last_move = FALSE
/atom/movable/screen/alert/status_effect/lobster_rush
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 525f6c636df9..0c9a5b942ab2 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -89,15 +89,10 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
gender = body.gender
if(body.mind && body.mind.name)
- if(body.mind.ghostname)
- name = body.mind.ghostname
- else
- name = body.mind.name
+ name = body.mind.ghostname || body.mind.name
else
- if(body.real_name)
- name = body.real_name
- else
- name = random_unique_name(gender)
+ name = body.real_name || generate_random_mob_name(gender)
+
mind = body.mind //we don't transfer the mind but we keep a reference to it.
@@ -125,8 +120,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
abstract_move(T)
- if(!name) //To prevent nameless ghosts
- name = random_unique_name(gender)
+ //To prevent nameless ghosts
+ name ||= generate_random_mob_name(FALSE)
real_name = name
if(!fun_verbs)
@@ -860,7 +855,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
client.prefs.apply_character_randomization_prefs()
var/species_type = client.prefs.read_preference(/datum/preference/choiced/species)
- var/datum/species/species = new species_type
+ var/datum/species/species = GLOB.species_prototypes[species_type]
if(species.check_head_flags(HEAD_HAIR))
hairstyle = client.prefs.read_preference(/datum/preference/choiced/hairstyle)
hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color/hair_color))
@@ -869,8 +864,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
facial_hairstyle = client.prefs.read_preference(/datum/preference/choiced/facial_hairstyle)
facial_hair_color = brighten_color(client.prefs.read_preference(/datum/preference/color/facial_hair_color))
- qdel(species)
-
update_appearance()
/mob/dead/observer/can_perform_action(atom/movable/target, action_bitflags)
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 219d8e357e7c..b5957a70b852 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -467,7 +467,12 @@
if(M.active_storage?.attempt_insert(src, M))
return TRUE
- var/list/obj/item/possible = list(M.get_inactive_held_item(), M.get_item_by_slot(ITEM_SLOT_BELT), M.get_item_by_slot(ITEM_SLOT_DEX_STORAGE), M.get_item_by_slot(ITEM_SLOT_BACK))
+ var/list/obj/item/possible = list(
+ M.get_inactive_held_item(),
+ M.get_item_by_slot(ITEM_SLOT_BELT),
+ M.get_item_by_slot(ITEM_SLOT_DEX_STORAGE),
+ M.get_item_by_slot(ITEM_SLOT_BACK),
+ )
for(var/i in possible)
if(!i)
continue
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
index 5a901fa2c79c..c906ce17c319 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
@@ -102,7 +102,7 @@
RegisterSignal(src, COMSIG_LIVING_BANED, PROC_REF(on_baned))
RegisterSignal(src, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_move))
RegisterSignal(src, COMSIG_LIVING_LIFE, PROC_REF(on_life))
- set_random_revenant_name()
+ name = generate_random_mob_name()
GLOB.revenant_relay_mobs |= src
@@ -345,13 +345,13 @@
returnable_list += span_bold("Be sure to read the wiki page to learn more.")
return returnable_list
-/mob/living/basic/revenant/proc/set_random_revenant_name()
+/mob/living/basic/revenant/generate_random_mob_name()
var/list/built_name_strings = list()
built_name_strings += pick(strings(REVENANT_NAME_FILE, "spirit_type"))
built_name_strings += " of "
built_name_strings += pick(strings(REVENANT_NAME_FILE, "adverb"))
built_name_strings += pick(strings(REVENANT_NAME_FILE, "theme"))
- name = built_name_strings.Join("")
+ return built_name_strings.Join("")
/mob/living/basic/revenant/proc/on_baned(obj/item/weapon, mob/living/user)
SIGNAL_HANDLER
diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
index 70b3506527a1..bba6e0eb460c 100644
--- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
+++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
@@ -39,6 +39,7 @@
id = "wumbo_inflated"
duration = 10 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/inflated
+ show_duration = TRUE
/atom/movable/screen/alert/status_effect/inflated
name = "WUMBO"
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 58df9bb19bf3..702c0725735c 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -1,4 +1,6 @@
#define BLOOD_DRIP_RATE_MOD 90 //Greater number means creating blood drips more often while bleeding
+// Conversion between internal drunk power and common blood alcohol content
+#define DRUNK_POWER_TO_BLOOD_ALCOHOL 0.003
/****************************************************
BLOOD SYSTEM
@@ -310,4 +312,13 @@
// NON-MODULE CHANGE END
+/mob/living/proc/get_blood_alcohol_content()
+ var/blood_alcohol_content = 0
+ var/datum/status_effect/inebriated/inebriation = has_status_effect(/datum/status_effect/inebriated)
+ if(!isnull(inebriation))
+ blood_alcohol_content = round(inebriation.drunk_value * DRUNK_POWER_TO_BLOOD_ALCOHOL, 0.01)
+
+ return blood_alcohol_content
+
#undef BLOOD_DRIP_RATE_MOD
+#undef DRUNK_POWER_TO_BLOOD_ALCOHOL
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index e1f2fc05c99b..f1516906b387 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1370,7 +1370,6 @@
else
set_lying_angle(new_lying_angle)
-
/mob/living/carbon/vv_edit_var(var_name, var_value)
switch(var_name)
if(NAMEOF(src, disgust))
@@ -1386,17 +1385,18 @@
return ..()
-
/mob/living/carbon/get_attack_type()
if(has_active_hand())
var/obj/item/bodypart/arm/active_arm = get_active_hand()
return active_arm.attack_type
return ..()
-
/mob/living/carbon/proc/attach_rot()
- if(mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))
- AddComponent(/datum/component/rot, 6 MINUTES, 10 MINUTES, 1)
+ if(flags_1 & HOLOGRAM_1)
+ return
+ if(!(mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)))
+ return
+ AddComponent(/datum/component/rot, 6 MINUTES, 10 MINUTES, 1)
/**
* This proc is used to determine whether or not the mob can handle touching an acid affected object.
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 5071bdcbc1ea..3cc47547c479 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -483,7 +483,7 @@
var/old_key = icon_render_keys?[limb.body_zone] //Checks the mob's icon render key list for the bodypart
icon_render_keys[limb.body_zone] = (limb.is_husked) ? limb.generate_husk_key().Join() : limb.generate_icon_key().Join() //Generates a key for the current bodypart
- if(icon_render_keys[limb.body_zone] != old_key || get_top_offset() != last_top_offset) //If the keys match, that means the limb doesn't need to be redrawn
+ if(icon_render_keys[limb.body_zone] != old_key) //If the keys match, that means the limb doesn't need to be redrawn
needs_update += limb
var/list/missing_bodyparts = get_missing_limbs()
@@ -500,15 +500,10 @@
for(var/obj/item/bodypart/limb as anything in bodyparts)
if(limb in needs_update)
var/bodypart_icon = limb.get_limb_icon()
- if(!istype(limb, /obj/item/bodypart/leg))
- var/top_offset = get_top_offset()
- for(var/image/image as anything in bodypart_icon)
- image.pixel_y += top_offset
new_limbs += bodypart_icon
limb_icon_cache[icon_render_keys[limb.body_zone]] = bodypart_icon //Caches the icon with the bodypart key, as it is new
else
new_limbs += limb_icon_cache[icon_render_keys[limb.body_zone]] //Pulls existing sprites from the cache
- last_top_offset = get_top_offset()
remove_overlay(BODYPARTS_LAYER)
@@ -518,19 +513,6 @@
apply_overlay(BODYPARTS_LAYER)
-/// This looks at the chest and legs of the mob and decides how much our chest, arms, and head should be adjusted. This is useful for limbs that are larger or smaller than the scope of normal human height while keeping the feet anchored to the bottom of the tile
-/mob/living/carbon/proc/get_top_offset()
- var/from_chest
- var/from_leg
- for(var/obj/item/bodypart/leg/leg_checked in bodyparts)
- if(leg_checked.top_offset > from_leg || isnull(from_leg)) // We find the tallest leg available
- from_leg = leg_checked.top_offset
- if(isnull(from_leg))
- from_leg = 0 // If we have no legs, we set this to zero to avoid any math issues that might stem from it being NULL
- for(var/obj/item/bodypart/chest/chest_checked in bodyparts) // Take the height from the chest
- from_chest = chest_checked.top_offset
- return (from_chest + from_leg) // The total hight of the chest and legs together
-
/////////////////////////
// Limb Icon Cache 2.0 //
/////////////////////////
@@ -568,6 +550,7 @@
/obj/item/bodypart/proc/generate_husk_key()
RETURN_TYPE(/list)
. = list()
+ . += "[limb_id]-"
. += "[husk_type]"
. += "-husk"
. += "-[body_zone]"
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index b31169401098..83be5e52acef 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -190,6 +190,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
///A list containing outfits that will be overridden in the species_equip_outfit proc. [Key = Typepath passed in] [Value = Typepath of outfit you want to equip for this specific species instead].
var/list/outfit_override_registry = list()
+ /// What species is our monkey form
+ var/datum/species/monkey_type = /datum/species/monkey
+
///////////
// PROCS //
///////////
@@ -211,6 +214,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
GLOB.roundstart_races = generate_selectable_species_and_languages()
return GLOB.roundstart_races
+
/**
* Generates species available to choose in character setup at roundstart
*
@@ -221,14 +225,12 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/list/selectable_species = list()
for(var/species_type in subtypesof(/datum/species))
- var/datum/species/species = new species_type
+ var/datum/species/species = GLOB.species_prototypes[species_type]
if(species.check_roundstart_eligible())
selectable_species += species.id
- var/datum/language_holder/temp_holder = new species.species_language_holder
+ var/datum/language_holder/temp_holder = GLOB.prototype_language_holders[species.species_language_holder]
for(var/datum/language/spoken_language as anything in temp_holder.understood_languages)
GLOB.uncommon_roundstart_languages |= spoken_language
- qdel(temp_holder)
- qdel(species)
GLOB.uncommon_roundstart_languages -= /datum/language/common
if(!selectable_species.len)
@@ -247,32 +249,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
return TRUE
return FALSE
-/**
- * Generates a random name for a carbon.
- *
- * This generates a random unique name based on a human's species and gender.
- * Arguments:
- * * gender - The gender that the name should adhere to. Use MALE for male names, use anything else for female names.
- * * unique - If true, ensures that this new name is not a duplicate of anyone else's name currently on the station.
- * * last_name - Do we use a given last name or pick a random new one?
- */
-/datum/species/proc/random_name(gender, unique, last_name)
- if(unique)
- return random_unique_name(gender)
-
- var/randname
- if(gender == MALE)
- randname = pick(GLOB.first_names_male)
- else
- randname = pick(GLOB.first_names_female)
-
- if(last_name)
- randname += " [last_name]"
- else
- randname += " [pick(GLOB.last_names)]"
-
- return randname
-
/**
* Copies some vars and properties over that should be kept when creating a copy of this species.
*
@@ -613,7 +589,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
*/
/datum/species/proc/handle_body(mob/living/carbon/human/species_human)
species_human.remove_overlay(BODY_LAYER)
- var/height_offset = species_human.get_top_offset() // From high changed by varying limb height
if(HAS_TRAIT(species_human, TRAIT_INVISIBLE_MAN))
return handle_mutant_bodyparts(species_human)
var/list/standing = list()
@@ -625,9 +600,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/obj/item/organ/internal/eyes/eye_organ = species_human.get_organ_slot(ORGAN_SLOT_EYES)
if(eye_organ)
eye_organ.refresh(call_update = FALSE)
- for(var/mutable_appearance/eye_overlay in eye_organ.generate_body_overlay(species_human))
- eye_overlay.pixel_y += height_offset
- standing += eye_overlay
+ standing += eye_organ.generate_body_overlay(species_human)
// organic body markings (oh my god this is terrible please rework this to be done on the limbs themselves i beg you)
if(HAS_TRAIT(species_human, TRAIT_HAS_MARKINGS))
@@ -638,29 +611,23 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/obj/item/bodypart/leg/left/left_leg = species_human.get_bodypart(BODY_ZONE_L_LEG)
var/datum/sprite_accessory/markings = GLOB.moth_markings_list[species_human.dna.features["moth_markings"]]
var/mutable_appearance/marking = mutable_appearance(layer = -BODY_LAYER, appearance_flags = KEEP_TOGETHER)
- if(noggin && (IS_ORGANIC_LIMB(noggin)))
- var/mutable_appearance/markings_head_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_head")
- marking.overlays += markings_head_overlay
+ if(noggin && IS_ORGANIC_LIMB(noggin))
+ marking.overlays += mutable_appearance(markings.icon, "[markings.icon_state]_head")
- if(chest && (IS_ORGANIC_LIMB(chest)))
- var/mutable_appearance/markings_chest_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_chest")
- marking.overlays += markings_chest_overlay
+ if(chest && IS_ORGANIC_LIMB(chest))
+ marking.overlays += mutable_appearance(markings.icon, "[markings.icon_state]_chest")
- if(right_arm && (IS_ORGANIC_LIMB(right_arm)))
- var/mutable_appearance/markings_r_arm_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_r_arm")
- marking.overlays += markings_r_arm_overlay
+ if(right_arm && IS_ORGANIC_LIMB(right_arm))
+ marking.overlays += mutable_appearance(markings.icon, "[markings.icon_state]_r_arm")
- if(left_arm && (IS_ORGANIC_LIMB(left_arm)))
- var/mutable_appearance/markings_l_arm_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_l_arm")
- marking.overlays += markings_l_arm_overlay
+ if(left_arm && IS_ORGANIC_LIMB(left_arm))
+ marking.overlays += mutable_appearance(markings.icon, "[markings.icon_state]_l_arm")
- if(right_leg && (IS_ORGANIC_LIMB(right_leg)))
- var/mutable_appearance/markings_r_leg_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_r_leg")
- marking.overlays += markings_r_leg_overlay
+ if(right_leg && IS_ORGANIC_LIMB(right_leg))
+ marking.overlays += mutable_appearance(markings.icon, "[markings.icon_state]_r_leg")
- if(left_leg && (IS_ORGANIC_LIMB(left_leg)))
- var/mutable_appearance/markings_l_leg_overlay = mutable_appearance(markings.icon, "[markings.icon_state]_l_leg")
- marking.overlays += markings_l_leg_overlay
+ if(left_leg && IS_ORGANIC_LIMB(left_leg))
+ marking.overlays += mutable_appearance(markings.icon, "[markings.icon_state]_l_leg")
standing += marking
@@ -676,7 +643,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
underwear_overlay = mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
if(!underwear.use_static)
underwear_overlay.color = species_human.underwear_color
- underwear_overlay.pixel_y += height_offset
standing += underwear_overlay
if(species_human.undershirt)
@@ -687,7 +653,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
working_shirt = wear_female_version(undershirt.icon_state, undershirt.icon, BODY_LAYER)
else
working_shirt = mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
- working_shirt.pixel_y += height_offset
standing += working_shirt
if(species_human.socks && species_human.num_legs >= 2 && !(species_human.bodytype & BODYTYPE_DIGITIGRADE))
@@ -929,12 +894,6 @@ GLOBAL_LIST_EMPTY(features_by_species)
return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_ICLOTHING)
- var/obj/item/bodypart/chest = H.get_bodypart(BODY_ZONE_CHEST)
- if(chest && (chest.bodytype & BODYTYPE_MONKEY))
- if(!(I.supports_variations_flags & CLOTHING_MONKEY_VARIATION))
- if(!disable_warning)
- to_chat(H, span_warning("[I] doesn't fit your [chest.name]!"))
- return FALSE
return equip_delay_self_check(I, H, bypass_equip_delay_self)
if(ITEM_SLOT_ID)
var/obj/item/bodypart/O = H.get_bodypart(BODY_ZONE_CHEST)
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 7d31dd5ff90d..6d15f696bed5 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -6,6 +6,8 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift)
new /obj/effect/temp_visual/dust_animation(loc, dna.species.dust_anim)
/mob/living/carbon/human/spawn_gibs(drop_bitflags=NONE)
+ if(flags_1 & HOLOGRAM_1)
+ return
if(drop_bitflags & DROP_BODYPARTS)
new /obj/effect/gibspawner/human(drop_location(), src, get_static_viruses())
else
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index 305bf132f721..c172288da651 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -76,7 +76,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
cut_overlays(TRUE)
/mob/living/carbon/human/dummy/setup_human_dna()
- randomize_human(src, randomize_mutations = FALSE)
+ randomize_human_normie(src, randomize_mutations = FALSE)
/mob/living/carbon/human/dummy/log_mob_tag(text)
return
@@ -102,6 +102,7 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
target.dna.features["spines"] = get_consistent_feature_entry(GLOB.spines_list)
target.dna.features["tail_cat"] = get_consistent_feature_entry(GLOB.tails_list_human) // it's a lie
target.dna.features["tail_lizard"] = get_consistent_feature_entry(GLOB.tails_list_lizard)
+ target.dna.features["tail_monkey"] = get_consistent_feature_entry(GLOB.tails_list_monkey)
target.dna.features["pod_hair"] = get_consistent_feature_entry(GLOB.pod_hair_list)
target.dna.features["head_tentacles"] = get_consistent_feature_entry(GLOB.head_tentacles_list) // NON-MODULE CHANGE
target.dna.features["arm_wings"] = get_consistent_feature_entry(GLOB.arm_wings_list) // NON-MODULE CHANGE
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 3a086be09583..bbe8c103ba25 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -202,7 +202,7 @@
emote_type = EMOTE_AUDIBLE
/datum/emote/living/carbon/human/hiss/get_sound(mob/user)
- if(islizard(user))
+ if(islizard(user) || ismonkey(user))
return pick(user.get_speech_sounds())
return null
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index e025308b8c4b..d91915f932a3 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -56,7 +56,7 @@
ADD_TRAIT(src, TRAIT_AGEUSIA, NO_TONGUE_TRAIT)
/mob/living/carbon/human/proc/setup_human_dna()
- randomize_human(src, randomize_mutations = TRUE)
+ randomize_human_normie(src, randomize_mutations = TRUE)
/mob/living/carbon/human/Destroy()
QDEL_NULL(physiology)
@@ -691,7 +691,7 @@
/mob/living/carbon/human/fully_heal(heal_flags = HEAL_ALL)
if(heal_flags & HEAL_NEGATIVE_MUTATIONS)
for(var/datum/mutation/human/existing_mutation in dna.mutations)
- if(existing_mutation.quality != POSITIVE)
+ if(existing_mutation.quality != POSITIVE && existing_mutation.remove_on_aheal)
dna.remove_mutation(existing_mutation)
if(heal_flags & HEAL_TEMP)
@@ -718,6 +718,10 @@
/mob/living/carbon/human/vv_edit_var(var_name, var_value)
if(var_name == NAMEOF(src, mob_height))
+ var/static/list/monkey_heights = list(
+ MONKEY_HEIGHT_DWARF,
+ MONKEY_HEIGHT_MEDIUM,
+ )
var/static/list/heights = list(
HUMAN_HEIGHT_SHORTEST,
HUMAN_HEIGHT_SHORT,
@@ -726,7 +730,10 @@
HUMAN_HEIGHT_TALLER,
HUMAN_HEIGHT_TALLEST
)
- if(!(var_value in heights))
+ if(ismonkey(src))
+ if(!(var_value in monkey_heights))
+ return
+ else if(!(var_value in heights))
return
. = set_mob_height(var_value)
@@ -803,7 +810,7 @@
if(href_list[VV_HK_SET_SPECIES])
if(!check_rights(R_SPAWN))
return
- var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list
+ var/result = input(usr, "Please choose a new species","Species") as null|anything in sortTim(GLOB.species_list, GLOBAL_PROC_REF(cmp_text_asc))
if(result)
var/newtype = GLOB.species_list[result]
admin_ticket_log("[key_name_admin(usr)] has modified the bodyparts of [src] to [result]")
@@ -995,7 +1002,7 @@
/mob/living/carbon/human/species/set_species(datum/species/mrace, icon_update, pref_load)
. = ..()
if(use_random_name)
- fully_replace_character_name(real_name, dna.species.random_name())
+ fully_replace_character_name(real_name, generate_random_mob_name())
/mob/living/carbon/human/species/abductor
race = /datum/species/abductor
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index ef44318676e7..79ef66af7d6c 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -259,7 +259,7 @@
if (preference.is_randomizable())
preference.apply_to_human(src, preference.create_random_value(preferences))
- fully_replace_character_name(real_name, dna.species.random_name())
+ fully_replace_character_name(real_name, generate_random_mob_name())
/**
* Setter for mob height
@@ -271,8 +271,10 @@
/mob/living/carbon/human/proc/set_mob_height(new_height)
if(mob_height == new_height)
return FALSE
- if(new_height == HUMAN_HEIGHT_DWARF)
- CRASH("Don't set height to dwarf height directly, use dwarf trait")
+ if(new_height == HUMAN_HEIGHT_DWARF || new_height == MONKEY_HEIGHT_DWARF)
+ CRASH("Don't set height to dwarf height directly, use dwarf trait instead.")
+ if(new_height == MONKEY_HEIGHT_MEDIUM)
+ CRASH("Don't set height to monkey height directly, use monkified gene/species instead.")
mob_height = new_height
regenerate_icons()
@@ -287,7 +289,10 @@
*/
/mob/living/carbon/human/proc/get_mob_height()
if(HAS_TRAIT(src, TRAIT_DWARF))
- return HUMAN_HEIGHT_DWARF
+ return ismonkey(src) ? MONKEY_HEIGHT_DWARF : HUMAN_HEIGHT_DWARF
+
+ if(ismonkey(src))
+ return MONKEY_HEIGHT_MEDIUM
return mob_height
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 82676f62eca0..99d08f9c716d 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -102,9 +102,7 @@ There are several things that need to be remembered:
var/icon_file
var/woman
//BEGIN SPECIES HANDLING
- if((bodytype & BODYTYPE_MONKEY) && (uniform.supports_variations_flags & CLOTHING_MONKEY_VARIATION))
- icon_file = MONKEY_UNIFORM_FILE
- else if((bodytype & BODYTYPE_DIGITIGRADE) && (uniform.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION))
+ if((bodytype & BODYTYPE_DIGITIGRADE) && (uniform.supports_variations_flags & CLOTHING_DIGITIGRADE_VARIATION))
// NON-MODULE CHANGE kapu why
if(uniform.greyscale_config_worn && uniform.digitigrade_greyscale_config_worn && uniform.greyscale_colors)
icon_file = SSgreyscale.GetColoredIconByType(uniform.digitigrade_greyscale_config_worn, uniform.greyscale_colors)
@@ -709,9 +707,6 @@ generate/load female uniform sprites matching all previously decided variables
.[2] = offsets["y"]
else
.[2] = worn_y_offset
- if(ishuman(loc) && slot_flags != ITEM_SLOT_FEET) /// we adjust the human body for high given by body parts, execpt shoes, because they are always on the bottom
- var/mob/living/carbon/human/human_holder = loc
- .[2] += human_holder.get_top_offset()
//Can't think of a better way to do this, sadly
/mob/proc/get_item_offsets_for_index(i)
@@ -816,26 +811,97 @@ generate/load female uniform sprites matching all previously decided variables
"Lenghten_Torso",
"Gnome_Cut_Torso",
"Gnome_Cut_Legs",
+ "Monkey_Torso",
+ "Monkey_Legs",
+ "Monkey_Gnome_Cut_Torso",
+ "Monkey_Gnome_Cut_Legs",
))
switch(get_mob_height())
// Don't set this one directly, use TRAIT_DWARF
+ if(MONKEY_HEIGHT_DWARF)
+ appearance.add_filters(list(
+ list(
+ "name" = "Monkey_Gnome_Cut_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 3),
+ ),
+ list(
+ "name" = "Monkey_Gnome_Cut_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 4),
+ ),
+ ))
+ if(MONKEY_HEIGHT_MEDIUM)
+ appearance.add_filters(list(
+ list(
+ "name" = "Monkey_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 2),
+ ),
+ list(
+ "name" = "Monkey_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 4),
+ ),
+ ))
+ // Don't set this one directly, use TRAIT_DWARF
if(HUMAN_HEIGHT_DWARF)
- appearance.add_filter("Gnome_Cut_Torso", 1, displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 2))
- appearance.add_filter("Gnome_Cut_Legs", 1, displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 3))
+ appearance.add_filters(list(
+ list(
+ "name" = "Gnome_Cut_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 2),
+ ),
+ list(
+ "name" = "Gnome_Cut_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 3),
+ ),
+ ))
if(HUMAN_HEIGHT_SHORTEST)
- appearance.add_filter("Cut_Torso", 1, displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 1))
- appearance.add_filter("Cut_Legs", 1, displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 1))
+ appearance.add_filters(list(
+ list(
+ "name" = "Cut_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_torso_mask, x = 0, y = 0, size = 1),
+ ),
+ list(
+ "name" = "Cut_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 1),
+ ),
+ ))
if(HUMAN_HEIGHT_SHORT)
appearance.add_filter("Cut_Legs", 1, displacement_map_filter(cut_legs_mask, x = 0, y = 0, size = 1))
if(HUMAN_HEIGHT_TALL)
appearance.add_filter("Lenghten_Legs", 1, displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 1))
if(HUMAN_HEIGHT_TALLER)
- appearance.add_filter("Lenghten_Torso", 1, displacement_map_filter(lenghten_torso_mask, x = 0, y = 0, size = 1))
- appearance.add_filter("Lenghten_Legs", 1, displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 1))
+ appearance.add_filters(list(
+ list(
+ "name" = "Lenghten_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(lenghten_torso_mask, x = 0, y = 0, size = 1),
+ ),
+ list(
+ "name" = "Lenghten_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 1),
+ ),
+ ))
if(HUMAN_HEIGHT_TALLEST)
- appearance.add_filter("Lenghten_Torso", 1, displacement_map_filter(lenghten_torso_mask, x = 0, y = 0, size = 1))
- appearance.add_filter("Lenghten_Legs", 1, displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 2))
+ appearance.add_filters(list(
+ list(
+ "name" = "Lenghten_Torso",
+ "priority" = 1,
+ "params" = displacement_map_filter(lenghten_torso_mask, x = 0, y = 0, size = 1),
+ ),
+ list(
+ "name" = "Lenghten_Legs",
+ "priority" = 1,
+ "params" = displacement_map_filter(lenghten_legs_mask, x = 0, y = 0, size = 2),
+ ),
+ ))
// Kinda gross but because many humans overlays do not use KEEP_TOGETHER we need to manually propogate the filter
// Otherwise overlays, such as worn overlays on icons, won't have the filter "applied", and the effect kinda breaks
diff --git a/code/modules/mob/living/carbon/human/monkey.dm b/code/modules/mob/living/carbon/human/monkey.dm
index 88d46855a5e3..d11e4f5208bc 100644
--- a/code/modules/mob/living/carbon/human/monkey.dm
+++ b/code/modules/mob/living/carbon/human/monkey.dm
@@ -32,12 +32,6 @@
equip_to_slot_or_del(helmet, ITEM_SLOT_HEAD)
helmet.attack_self(src) // todo encapsulate toggle
-/mob/living/carbon/human/species/monkey/holodeck
- race = /datum/species/monkey/holodeck
-
-/mob/living/carbon/human/species/monkey/holodeck/spawn_gibs() // no blood and no gibs
- return
-
GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/punpun)
/mob/living/carbon/human/species/monkey/punpun
diff --git a/code/modules/mob/living/carbon/human/species_types/abominations.dm b/code/modules/mob/living/carbon/human/species_types/abominations.dm
deleted file mode 100644
index 43ca71311c22..000000000000
--- a/code/modules/mob/living/carbon/human/species_types/abominations.dm
+++ /dev/null
@@ -1,51 +0,0 @@
-/// These won't appear normally in games, they are meant to for debuging the adjustment of limbs based on the height of a humans bodyparts.
-/datum/species/human/tallboy
- name = "\improper Tall Boy"
- id = SPECIES_TALLBOY
- examine_limb_id = SPECIES_HUMAN
- changesource_flags = MIRROR_BADMIN | WABBAJACK
- bodypart_overrides = list(
- BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left,
- BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right,
- BODY_ZONE_HEAD = /obj/item/bodypart/head,
- BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/tallboy,
- BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/tallboy,
- BODY_ZONE_CHEST = /obj/item/bodypart/chest,
- )
-
-/datum/species/monkey/human_legged
- name = "human-legged monkey"
- id = SPECIES_MONKEY_HUMAN_LEGGED
- examine_limb_id = SPECIES_MONKEY
- changesource_flags = MIRROR_BADMIN | WABBAJACK
- bodypart_overrides = list(
- BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/monkey,
- BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/monkey,
- BODY_ZONE_HEAD = /obj/item/bodypart/head/monkey,
- BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left,
- BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right,
- BODY_ZONE_CHEST = /obj/item/bodypart/chest/monkey,
- )
-
-/datum/species/monkey/monkey_freak
- name = "human-armed monkey"
- id = SPECIES_MONKEY_FREAK
- examine_limb_id = SPECIES_MONKEY
- changesource_flags = MIRROR_BADMIN | WABBAJACK
- bodypart_overrides = list(
- BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left,
- BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right,
- BODY_ZONE_HEAD = /obj/item/bodypart/head/monkey,
- BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/monkey,
- BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/monkey,
- BODY_ZONE_CHEST = /obj/item/bodypart/chest,
- )
-
-/mob/living/carbon/human/species/monkey/humand_legged
- race = /datum/species/monkey/human_legged
-
-/mob/living/carbon/human/species/monkey/monkey_freak
- race = /datum/species/monkey/monkey_freak
-
-/mob/living/carbon/human/species/tallboy
- race = /datum/species/human/tallboy
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index 9fc68a635095..e5d53d9d58bd 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -89,22 +89,6 @@
QDEL_NULL(ethereal_light)
return ..()
-// NON-MODULE CHANGE
-// /datum/species/ethereal/update_quirk_mail_goodies(mob/living/carbon/human/recipient, datum/quirk/quirk, list/mail_goodies = list())
-// if(istype(quirk, /datum/quirk/blooddeficiency))
-// mail_goodies += list(
-// /obj/item/reagent_containers/blood/ethereal
-// )
-// return ..()
-
-/datum/species/ethereal/random_name(gender,unique,lastname)
- if(unique)
- return random_unique_ethereal_name()
-
- var/randname = ethereal_name()
-
- return randname
-
/datum/species/ethereal/randomize_features()
var/list/features = ..()
features["ethcolor"] = GLOB.color_list_ethereal[pick(GLOB.color_list_ethereal)]
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index 0a9232476cc3..56de52b77108 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -50,15 +50,6 @@
BODY_ZONE_CHEST = /obj/item/bodypart/chest/golem,
)
- /// Chance that we will generate a human surname, for lore reasons
- var/human_surname_chance = 3
-
-/datum/species/golem/random_name(gender,unique,lastname)
- var/name = pick(GLOB.golem_names)
- if (prob(human_surname_chance))
- name += " [pick(GLOB.last_names)]"
- return name
-
/datum/species/golem/get_physical_attributes()
return "Golems are hardy creatures made out of stone, which are thus naturally resistant to many dangers, including asphyxiation, fire, radiation, electricity, and viruses.\
They gain special abilities depending on the type of material consumed, but they need to consume material to keep their body animated."
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 9907d28ef933..cde32fc6726a 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -45,36 +45,12 @@
BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/lizard,
)
-// NON-MODULE CHANGE
-// /datum/species/lizard/on_species_gain(mob/living/carbon/new_lizard, datum/species/old_species, pref_load)
-// . = ..()
-// if(ishuman(new_lizard))
-// update_mail_goodies(new_lizard)
-
-// /datum/species/lizard/update_quirk_mail_goodies(mob/living/carbon/human/recipient, datum/quirk/quirk, list/mail_goodies = list())
-// if(istype(quirk, /datum/quirk/blooddeficiency))
-// mail_goodies += list(
-// /obj/item/reagent_containers/blood/lizard
-// )
-// return ..()
-// NON-MODULE CHANGE end
+ monkey_type = /datum/species/monkey/lizard
/// Lizards are cold blooded and do not stabilize body temperature naturally
/datum/species/lizard/body_temperature_core(mob/living/carbon/human/humi, seconds_per_tick, times_fired)
return
-/datum/species/lizard/random_name(gender,unique,lastname)
- if(unique)
- return random_unique_lizard_name(gender)
-
- var/randname = lizard_name(gender)
-
- if(lastname)
- randname += " [lastname]"
-
- return randname
-
-
/datum/species/lizard/randomize_features()
var/list/features = ..()
features["body_markings"] = pick(GLOB.body_markings_list)
@@ -182,9 +158,10 @@ Lizard subspecies: ASHWALKERS
mutantlungs = /obj/item/organ/internal/lungs/lavaland
mutantbrain = /obj/item/organ/internal/brain/primitive
inherent_traits = list(
+ TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
TRAIT_MUTANT_COLORS,
+ TRAIT_TACKLING_TAILED_DEFENDER,
TRAIT_VIRUSIMMUNE,
- TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION,
)
species_language_holder = /datum/language_holder/lizard/ash
digitigrade_customization = DIGITIGRADE_FORCED
@@ -215,6 +192,7 @@ Lizard subspecies: SILVER SCALED
TRAIT_PIERCEIMMUNE,
TRAIT_RESISTHIGHPRESSURE,
TRAIT_RESISTLOWPRESSURE,
+ TRAIT_TACKLING_TAILED_DEFENDER,
TRAIT_VIRUSIMMUNE,
TRAIT_WINE_TASTER,
)
diff --git a/code/modules/mob/living/carbon/human/species_types/monkeys.dm b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
index fea8b71cb677..0de795f74ef6 100644
--- a/code/modules/mob/living/carbon/human/species_types/monkeys.dm
+++ b/code/modules/mob/living/carbon/human/species_types/monkeys.dm
@@ -1,10 +1,10 @@
#define MONKEY_SPEC_ATTACK_BITE_MISS_CHANCE 25
/datum/species/monkey
- name = "Monkey"
+ name = "\improper Monkey"
id = SPECIES_MONKEY
external_organs = list(
- /obj/item/organ/external/tail/monkey = "Monkey"
+ /obj/item/organ/external/tail/monkey = "Monkey",
)
mutanttongue = /obj/item/organ/internal/tongue/monkey
mutantbrain = /obj/item/organ/internal/brain/primate
@@ -22,6 +22,7 @@
)
no_equip_flags = ITEM_SLOT_OCLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_FEET | ITEM_SLOT_SUITSTORE
changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | MIRROR_MAGIC | ERT_SPAWN | SLIME_EXTRACT
+ species_cookie = /obj/item/food/grown/banana
sexes = FALSE
species_language_holder = /datum/language_holder/monkey
@@ -39,31 +40,21 @@
payday_modifier = 1.5
ai_controlled_species = TRUE
+ monkey_type = null
-/datum/species/monkey/random_name(gender,unique,lastname)
- var/randname = "monkey ([rand(1,999)])"
-
- return randname
-
-/datum/species/monkey/on_species_gain(mob/living/carbon/human/H, datum/species/old_species)
+/datum/species/monkey/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load)
. = ..()
- passtable_on(H, SPECIES_TRAIT)
- H.dna.add_mutation(/datum/mutation/human/race, MUT_NORMAL)
- H.dna.activate_mutation(/datum/mutation/human/race)
- H.AddElement(/datum/element/human_biter)
+ passtable_on(human_who_gained_species, SPECIES_TRAIT)
+ human_who_gained_species.dna.add_mutation(/datum/mutation/human/race, MUT_NORMAL)
+ human_who_gained_species.dna.activate_mutation(/datum/mutation/human/race)
+ human_who_gained_species.AddElement(/datum/element/human_biter)
-/datum/species/monkey/on_species_loss(mob/living/carbon/C)
+/datum/species/monkey/on_species_loss(mob/living/carbon/human/C)
. = ..()
passtable_off(C, SPECIES_TRAIT)
C.dna.remove_mutation(/datum/mutation/human/race)
C.RemoveElement(/datum/element/human_biter)
-/datum/species/monkey/check_roundstart_eligible()
- // STOP ADDING MONKEY SUBTYPES YOU HEATHEN
- if(check_holidays(MONKEYDAY) && id == SPECIES_MONKEY)
- return TRUE
- return ..()
-
/datum/species/monkey/get_scream_sound(mob/living/carbon/human/monkey)
return get_sfx(SFX_SCREECH)
@@ -158,7 +149,6 @@
to_chat(monkey_brain.owner, span_notice("You will now stumble while while colliding with people who are in combat mode."))
build_all_button_icons()
-
/obj/item/organ/internal/brain/primate/on_mob_insert(mob/living/carbon/primate)
. = ..()
RegisterSignal(primate, COMSIG_MOVABLE_CROSS, PROC_REF(on_crossed), TRUE)
@@ -183,23 +173,99 @@
/obj/item/organ/internal/brain/primate/get_attacking_limb(mob/living/carbon/human/target)
return owner.get_bodypart(BODY_ZONE_HEAD)
-/// Virtual monkeys that crave virtual bananas. Everything about them is ephemeral (except that bite).
-/datum/species/monkey/holodeck
- id = SPECIES_MONKEY_HOLODECK
- knife_butcher_results = list()
- meat = null
- skinned_type = null
+#undef MONKEY_SPEC_ATTACK_BITE_MISS_CHANCE
+
+/datum/species/monkey/lizard
+ name = "\improper Kobold"
+ id = SPECIES_MONKEY_LIZARD
+ examine_limb_id = SPECIES_LIZARD
inherent_traits = list(
- TRAIT_GENELESS,
+ // monke
TRAIT_GUN_NATURAL,
TRAIT_NO_AUGMENTS,
TRAIT_NO_BLOOD_OVERLAY,
TRAIT_NO_DNA_COPY,
TRAIT_NO_UNDERWEAR,
- TRAIT_NO_ZOMBIFY,
- TRAIT_NOBLOOD,
- TRAIT_NOHUNGER,
TRAIT_VENTCRAWLER_NUDE,
+ TRAIT_WEAK_SOUL,
+ // unique
+ TRAIT_MUTANT_COLORS,
+ TRAIT_TACKLING_TAILED_DEFENDER,
)
+ inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_REPTILE
+ digitigrade_customization = DIGITIGRADE_FORCED
+ mutant_bodyparts = list("legs" = DIGITIGRADE_LEGS)
+ external_organs = list(
+ /obj/item/organ/external/horns = "None",
+ /obj/item/organ/external/frills = "None",
+ /obj/item/organ/external/snout = "Round",
+ /obj/item/organ/external/spines = "None",
+ /obj/item/organ/external/tail/lizard = "Smooth",
+ )
+ mutanttongue = /datum/species/lizard::mutanttongue
+ species_cookie = /datum/species/lizard::species_cookie
+ meat = /datum/species/lizard::meat
+ skinned_type = /datum/species/lizard::skinned_type
+ knife_butcher_results = list(/datum/species/lizard::meat = 5, /datum/species/lizard::skinned_type = 1)
+ species_language_holder = /datum/language_holder/lizard/ash/primative
-#undef MONKEY_SPEC_ATTACK_BITE_MISS_CHANCE
+ bodytemp_heat_damage_limit = /datum/species/lizard::bodytemp_heat_damage_limit
+ bodytemp_cold_damage_limit = /datum/species/lizard::bodytemp_cold_damage_limit
+
+ ass_image = /datum/species/lizard::ass_image
+
+ bodypart_overrides = list(
+ BODY_ZONE_HEAD = /obj/item/bodypart/head/lizard,
+ BODY_ZONE_CHEST = /obj/item/bodypart/chest/lizard/lizmonkey,
+ BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/lizard/lizmonkey,
+ BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/lizard/lizmonkey,
+ BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/digitigrade,
+ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/digitigrade,
+ )
+
+/datum/species/monkey/lizard/body_temperature_core(mob/living/carbon/human/humi, seconds_per_tick, times_fired)
+ return
+
+/datum/species/monkey/lizard/get_scream_sound(mob/living/carbon/human/lizard)
+ return pick(
+ 'sound/voice/lizard/lizard_scream_1.ogg',
+ 'sound/voice/lizard/lizard_scream_2.ogg',
+ 'sound/voice/lizard/lizard_scream_3.ogg',
+ )
+
+/datum/species/monkey/lizard/get_laugh_sound(mob/living/carbon/human/lizard)
+ return 'sound/voice/lizard/lizard_laugh1.ogg'
+
+/obj/item/bodypart/arm/left/lizard/lizmonkey
+ wound_resistance = -10
+ unarmed_damage_low = 1
+ unarmed_damage_high = 2
+ unarmed_effectiveness = 0
+
+/obj/item/bodypart/arm/left/lizard/lizmonkey/Initialize(mapload)
+ . = ..()
+ name = "kobold [plaintext_zone]"
+
+/obj/item/bodypart/arm/right/lizard/lizmonkey
+ wound_resistance = -10
+ unarmed_damage_low = 1
+ unarmed_damage_high = 2
+ unarmed_effectiveness = 0
+
+/obj/item/bodypart/arm/right/lizard/lizmonkey/Initialize(mapload)
+ . = ..()
+ name = "kobold [plaintext_zone]"
+
+/obj/item/bodypart/chest/lizard/lizmonkey
+ wound_resistance = -10
+
+/obj/item/bodypart/chest/lizard/lizmonkey/Initialize(mapload)
+ . = ..()
+ name = "kobold [plaintext_zone]"
+
+/obj/item/bodypart/head/lizard/lizmonkey
+ wound_resistance = -10
+
+/obj/item/bodypart/head/lizard/lizmonkey/Initialize(mapload)
+ . = ..()
+ name = "kobold [plaintext_zone]"
diff --git a/code/modules/mob/living/carbon/human/species_types/mothmen.dm b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
index d3b98fe67424..85c61cc26d85 100644
--- a/code/modules/mob/living/carbon/human/species_types/mothmen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/mothmen.dm
@@ -34,17 +34,6 @@
var/mob/living/carbon/human/H = C
handle_mutant_bodyparts(H)
-/datum/species/moth/random_name(gender,unique,lastname)
- if(unique)
- return random_unique_moth_name()
-
- var/randname = moth_name()
-
- if(lastname)
- randname += " [lastname]"
-
- return randname
-
/datum/species/moth/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load)
. = ..()
RegisterSignal(human_who_gained_species, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(damage_weakness))
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 1e0e3ad564b8..aeb0f2e0bf4a 100644
--- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
+++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm
@@ -124,17 +124,6 @@
else
give_important_for_life(equipping)
-/datum/species/plasmaman/random_name(gender,unique,lastname)
- if(unique)
- return random_unique_plasmaman_name()
-
- var/randname = plasmaman_name()
-
- if(lastname)
- randname += " [lastname]"
-
- return randname
-
/datum/species/plasmaman/get_scream_sound(mob/living/carbon/human/plasmaman)
return pick(
'sound/voice/plasmaman/plasmeme_scream_1.ogg',
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 4da845c11df4..0c228775662d 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -37,6 +37,8 @@
* * DROP_BODYPARTS - Gibs will spawn with bodypart limbs present
**/
/mob/living/proc/spawn_gibs(drop_bitflags=NONE)
+ if(flags_1 & HOLOGRAM_1)
+ return
new /obj/effect/gibspawner/generic(drop_location(), src, get_static_viruses())
/**
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 1dbe5626e912..7b3e04263876 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -658,13 +658,15 @@
return copytext(sanitize(input("Choose an emote to display.") as text|null), 1, MAX_MESSAGE_LEN)
/datum/emote/living/custom/proc/get_custom_emote_type_from_user()
- var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable")
+ var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable", "Both")
switch(type)
if("Visible")
return EMOTE_VISIBLE
if("Hearable")
return EMOTE_AUDIBLE
+ if("Both")
+ return EMOTE_VISIBLE | EMOTE_AUDIBLE
else
tgui_alert(usr,"Unable to use this emote, must be either hearable or visible.")
return FALSE
diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm
index 7770179bcf5e..267deaed7b39 100644
--- a/code/modules/mob/living/living_say.dm
+++ b/code/modules/mob/living/living_say.dm
@@ -198,9 +198,9 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list(
spans |= speech_span
- if(language)
- var/datum/language/L = GLOB.language_datum_instances[language]
- spans |= L.spans
+ var/datum/language/spoken_lang = GLOB.language_datum_instances[language]
+ if(LAZYLEN(spoken_lang?.spans))
+ spans |= spoken_lang.spans
if(message_mods[MODE_SING])
var/randomnote = pick("\u2669", "\u266A", "\u266B")
diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm
index da8bd502fb1e..8c6bb0d917c5 100644
--- a/code/modules/mob/mob_say.dm
+++ b/code/modules/mob/mob_say.dm
@@ -42,6 +42,7 @@
/mob/verb/me_verb(message as text)
set name = "Me"
set category = "IC"
+ set desc = "Perform a custom emote. Leave blank to pick between an audible or a visible emote (Defaults to visible)."
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, span_danger("Speech is currently admin-disabled."))
@@ -49,7 +50,7 @@
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
- QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", 1, message, TRUE), SSspeech_controller)
+ QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", EMOTE_VISIBLE|EMOTE_AUDIBLE, message, TRUE), SSspeech_controller)
/mob/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE)
SHOULD_CALL_PARENT(TRUE)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index ca82e13e803e..537da8eae2da 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -8,7 +8,7 @@
if (transformation_timer || HAS_TRAIT(src, TRAIT_NO_TRANSFORM))
return
- if(ismonkey(src))
+ if(!dna?.species?.monkey_type)
return
if(instant)
@@ -17,7 +17,7 @@
//Make mob invisible and spawn animation
ADD_TRAIT(src, TRAIT_NO_TRANSFORM, TEMPORARY_TRANSFORMATION_TRAIT)
- Paralyze(TRANSFORMATION_DURATION, ignore_canstun = TRUE)
+ Stun(TRANSFORMATION_DURATION, ignore_canstun = TRUE)
icon = null
cut_overlays()
@@ -29,12 +29,13 @@
/mob/living/carbon/proc/finish_monkeyize()
transformation_timer = null
- to_chat(src, span_boldnotice("You are now a monkey."))
REMOVE_TRAIT(src, TRAIT_NO_TRANSFORM, TEMPORARY_TRANSFORMATION_TRAIT)
icon = initial(icon)
RemoveInvisibility(type)
- set_species(/datum/species/monkey)
- name = "monkey"
+ set_species(dna.species.monkey_type)
+ to_chat(src, span_boldnotice("You are now \a [dna.species.name]."))
+ name = lowertext(dna.species.name)
+ regenerate_icons()
set_name()
SEND_SIGNAL(src, COMSIG_HUMAN_MONKEYIZE)
uncuff()
@@ -56,7 +57,7 @@
//Make mob invisible and spawn animation
ADD_TRAIT(src, TRAIT_NO_TRANSFORM, TEMPORARY_TRANSFORMATION_TRAIT)
- Paralyze(TRANSFORMATION_DURATION, ignore_canstun = TRUE)
+ Stun(TRANSFORMATION_DURATION, ignore_canstun = TRUE)
icon = null
cut_overlays()
@@ -66,16 +67,23 @@
transformation_timer = addtimer(CALLBACK(src, PROC_REF(finish_humanize), species), TRANSFORMATION_DURATION, TIMER_UNIQUE)
+
/mob/living/carbon/proc/finish_humanize(species = /datum/species/human)
transformation_timer = null
- to_chat(src, span_boldnotice("You are now a human."))
REMOVE_TRAIT(src, TRAIT_NO_TRANSFORM, TEMPORARY_TRANSFORMATION_TRAIT)
icon = initial(icon)
RemoveInvisibility(type)
set_species(species)
+ to_chat(src, span_boldnotice("You are now \a [dna.species.name]."))
SEND_SIGNAL(src, COMSIG_MONKEY_HUMANIZE)
return src
+/mob/living/carbon/human/finish_humanize(species = /datum/species/human)
+ underwear = "Nude"
+ undershirt = "Nude"
+ socks = "Nude"
+ return ..()
+
/mob/proc/AIize(client/preference_source, move = TRUE)
var/list/turf/landmark_loc = list()
diff --git a/code/modules/mob_spawn/ghost_roles/golem_roles.dm b/code/modules/mob_spawn/ghost_roles/golem_roles.dm
index b3475e9207f8..5fc643bffa62 100644
--- a/code/modules/mob_spawn/ghost_roles/golem_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/golem_roles.dm
@@ -36,8 +36,7 @@
if(forced_name || !iscarbon(spawned_mob))
return ..()
- var/datum/species/golem/golem_species = new()
- forced_name = golem_species.random_name()
+ forced_name = generate_random_name_species_based(spawned_mob.gender, TRUE, species_type = /datum/species/golem)
return ..()
/obj/effect/mob_spawn/ghost_role/human/golem/special(mob/living/new_spawn, mob/mob_possessor)
diff --git a/code/modules/mob_spawn/ghost_roles/mining_roles.dm b/code/modules/mob_spawn/ghost_roles/mining_roles.dm
index 208a74a3edbf..53fa00109703 100644
--- a/code/modules/mob_spawn/ghost_roles/mining_roles.dm
+++ b/code/modules/mob_spawn/ghost_roles/mining_roles.dm
@@ -194,9 +194,9 @@
/obj/structure/ash_walker_eggshell/Destroy()
if(!egg)
return ..()
- var/mob/living/carbon/human/yolk = new /mob/living/carbon/human/(get_turf(src))
- yolk.fully_replace_character_name(null,random_unique_lizard_name(gender))
+ var/mob/living/carbon/human/yolk = new(get_turf(src))
yolk.set_species(/datum/species/lizard/ashwalker)
+ yolk.fully_replace_character_name(null, yolk.generate_random_mob_name(TRUE))
yolk.underwear = "Nude"
yolk.equipOutfit(/datum/outfit/ashwalker)//this is an authentic mess we're making
yolk.update_body()
@@ -235,7 +235,7 @@
/obj/effect/mob_spawn/ghost_role/human/ash_walker/special(mob/living/carbon/human/spawned_human)
. = ..()
- spawned_human.fully_replace_character_name(null,random_unique_lizard_name(gender))
+ spawned_human.fully_replace_character_name(null, spawned_human.generate_random_mob_name(TRUE))
to_chat(spawned_human, "Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Invade the strange structure of the outsiders if you must. Do not cause unnecessary destruction, as littering the wastes with ugly wreckage is certain to not gain you favor. Glory to the Necropolis!")
spawned_human.mind.add_antag_datum(/datum/antagonist/ashwalker, team)
diff --git a/code/modules/mob_spawn/mob_spawn.dm b/code/modules/mob_spawn/mob_spawn.dm
index 086254aae388..b0c8bdadac9f 100644
--- a/code/modules/mob_spawn/mob_spawn.dm
+++ b/code/modules/mob_spawn/mob_spawn.dm
@@ -59,26 +59,15 @@
spawned_human.underwear = "Nude"
spawned_human.undershirt = "Nude"
spawned_human.socks = "Nude"
+ randomize_human_normie(spawned_human)
if(hairstyle)
- spawned_human.hairstyle = hairstyle
- else
- spawned_human.hairstyle = random_hairstyle(spawned_human.gender)
+ spawned_human.set_hairstyle(hairstyle, update = FALSE)
if(facial_hairstyle)
- spawned_human.facial_hairstyle = facial_hairstyle
- else
- spawned_human.facial_hairstyle = random_facial_hairstyle(spawned_human.gender)
+ spawned_human.set_facial_hairstyle(facial_hairstyle, update = FALSE)
if(haircolor)
- spawned_human.hair_color = haircolor
- else
- spawned_human.hair_color = "#[random_color()]"
+ spawned_human.set_haircolor(haircolor, update = FALSE)
if(facial_haircolor)
- spawned_human.facial_hair_color = facial_haircolor
- else
- spawned_human.facial_hair_color = "#[random_color()]"
- if(skin_tone)
- spawned_human.skin_tone = skin_tone
- else
- spawned_human.skin_tone = random_skin_tone()
+ spawned_human.set_facial_haircolor(facial_haircolor, update = FALSE)
spawned_human.update_body(is_creating = TRUE)
/obj/effect/mob_spawn/proc/name_mob(mob/living/spawned_mob, forced_name)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 0340d5b860dd..6df2e99c098f 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -912,7 +912,7 @@
return
to_chat(affected_mob, span_warning("You grit your teeth in pain as your body rapidly mutates!"))
affected_mob.visible_message("[affected_mob] suddenly transforms!")
- randomize_human(affected_mob)
+ randomize_human_normie(affected_mob)
/datum/reagent/aslimetoxin
name = "Advanced Mutation Toxin"
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 29ccd0ccd872..4458793027f4 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -188,10 +188,10 @@
/obj/item/reagent_containers/hypospray/medipen/methamphetamine
name = "methamphetamine medipen"
- volume = 24
- amount_per_transfer_from_this = 24
+ volume = 25
+ amount_per_transfer_from_this = 25
desc = "Contains a relatively safe quantity of methamphetamine, along with mannitol to ensure that brain damage is kept at a minimum."
- list_reagents = list(/datum/reagent/drug/methamphetamine = 10, /datum/reagent/medicine/mannitol = 14)
+ list_reagents = list(/datum/reagent/drug/methamphetamine = 10, /datum/reagent/medicine/mannitol = 15)
// NON-MODULE CHANGE replacing this
/*
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index ac144938ea7f..393d165da9a4 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -144,7 +144,7 @@
name = "mannitol pill"
desc = "Used to treat brain damage."
icon_state = "pill17"
- list_reagents = list(/datum/reagent/medicine/mannitol = 14)
+ list_reagents = list(/datum/reagent/medicine/mannitol = 15)
rename_with_volume = TRUE
/obj/item/reagent_containers/pill/sansufentanyl
diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
index a1d4eff50a6e..ca4b71f48c7e 100644
--- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
@@ -7,6 +7,7 @@
id = "rainbow_protection"
duration = 100
alert_type = /atom/movable/screen/alert/status_effect/rainbow_protection
+ show_duration = TRUE
var/originalcolor
/datum/status_effect/rainbow_protection/on_apply()
@@ -37,6 +38,7 @@
id = "slimeskin"
duration = 300
alert_type = /atom/movable/screen/alert/status_effect/slimeskin
+ show_duration = TRUE
var/originalcolor
/datum/status_effect/slimeskin/on_apply()
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index e6b701d015b0..1f14aeb07f32 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -282,13 +282,12 @@
obj_flags |= EMAGGED
SSshuttle.emergency.movement_force = list("KNOCKDOWN" = 60, "THROW" = 20)//YOUR PUNY SEATBELTS can SAVE YOU NOW, MORTAL
- var/datum/species/S = new
for(var/i in 1 to 10)
// the shuttle system doesn't know who these people are, but they
// must be important, surely
var/obj/item/card/id/ID = new(src)
var/datum/job/J = pick(SSjob.joinable_occupations)
- ID.registered_name = S.random_name(pick(MALE, FEMALE))
+ ID.registered_name = generate_random_name_species_based(species_type = /datum/species/human)
ID.assignment = J.title
authorized += ID
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 50ac0e5021ac..d4309bd48831 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -17,6 +17,8 @@
VAR_PROTECTED/icon_invisible = 'icons/mob/human/bodyparts.dmi'
///The type of husk for building an iconstate
var/husk_type = "humanoid"
+ ///The color to multiply the greyscaled husk sprites by. Can be null. Old husk sprite chest color is #A6A6A6
+ var/husk_color = "#A6A6A6"
layer = BELOW_MOB_LAYER //so it isn't hidden behind objects when on the floor
grind_results = list(/datum/reagent/bone_dust = 10, /datum/reagent/consumable/liquidgibs = 5) // robotic bodyparts and chests/heads cannot be ground
/// The mob that "owns" this limb
@@ -178,8 +180,6 @@
var/unarmed_damage_high = 1
///Determines the accuracy bonus, armor penetration and knockdown probability.
var/unarmed_effectiveness = 10
- /// How many pixels this bodypart will offset the top half of the mob, used for abnormally sized torsos and legs
- var/top_offset = 0
/// Traits that are given to the holder of the part. If you want an effect that changes this, don't add directly to this. Use the add_bodypart_trait() proc
var/list/bodypart_traits = list()
@@ -1041,16 +1041,6 @@
var/image/limb = image(layer = -BODYPARTS_LAYER, dir = image_dir)
var/image/aux
- // Handles making bodyparts look husked
- if(is_husked)
- limb.icon = icon_husk
- limb.icon_state = "[husk_type]_husk_[body_zone]"
- icon_exists(limb.icon, limb.icon_state, scream = TRUE) //Prints a stack trace on the first failure of a given iconstate.
- . += limb
- if(aux_zone) //Hand shit
- aux = image(limb.icon, "[husk_type]_husk_[aux_zone]", -aux_layer, image_dir)
- . += aux
-
// Handles invisibility (not alpha or actual invisibility but invisibility)
if(is_invisible)
limb.icon = icon_invisible
@@ -1059,38 +1049,42 @@
return .
// Normal non-husk handling
- if(!is_husked)
// This is the MEAT of limb icon code
- limb.icon = icon_greyscale
- if(!should_draw_greyscale || !icon_greyscale)
- limb.icon = icon_static
+ limb.icon = icon_greyscale
+ if(!should_draw_greyscale || !icon_greyscale)
+ limb.icon = icon_static
- if(is_dimorphic) //Does this type of limb have sexual dimorphism?
- limb.icon_state = "[limb_id]_[body_zone]_[limb_gender]"
- else
- limb.icon_state = "[limb_id]_[body_zone]"
-
- icon_exists(limb.icon, limb.icon_state, TRUE) //Prints a stack trace on the first failure of a given iconstate.
+ if(is_dimorphic) //Does this type of limb have sexual dimorphism?
+ limb.icon_state = "[limb_id]_[body_zone]_[limb_gender]"
+ else
+ limb.icon_state = "[limb_id]_[body_zone]"
- . += limb
+ icon_exists(limb.icon, limb.icon_state, TRUE) //Prints a stack trace on the first failure of a given iconstate.
- if(aux_zone) //Hand shit
- aux = image(limb.icon, "[limb_id]_[aux_zone]", -aux_layer, image_dir)
- . += aux
+ . += limb
- draw_color = variable_color
- if(should_draw_greyscale) //Should the limb be colored outside of a forced color?
- draw_color ||= (species_color) || (skin_tone && skintone2hex(skin_tone))
+ if(aux_zone) //Hand shit
+ aux = image(limb.icon, "[limb_id]_[aux_zone]", -aux_layer, image_dir)
+ . += aux
+ draw_color = variable_color
+ if(should_draw_greyscale) //Should the limb be colored outside of a forced color?
+ draw_color ||= (species_color) || (skin_tone && skintone2hex(skin_tone))
- if(draw_color)
- limb.color = "[draw_color]"
- if(aux_zone)
- aux.color = "[draw_color]"
+ if(is_husked)
+ huskify_image(thing_to_husk = limb)
+ if(aux)
+ huskify_image(thing_to_husk = aux)
+ draw_color = husk_color
+ if(draw_color)
+ limb.color = "[draw_color]"
+ if(aux_zone)
+ aux.color = "[draw_color]"
//EMISSIVE CODE START
// For some reason this was applied as an overlay on the aux image and limb image before.
// I am very sure that this is unnecessary, and i need to treat it as part of the return list
// to be able to mask it proper in case this limb is a leg.
+ if(!is_husked)
if(blocks_emissive != EMISSIVE_BLOCK_NONE)
var/atom/location = loc || owner || src
var/mutable_appearance/limb_em_block = emissive_blocker(limb.icon, limb.icon_state, location, layer = limb.layer, alpha = limb.alpha)
@@ -1126,6 +1120,17 @@
return .
+/obj/item/bodypart/proc/huskify_image(image/thing_to_husk, draw_blood = TRUE)
+ var/icon/husk_icon = new(thing_to_husk.icon)
+ husk_icon.ColorTone(HUSK_COLOR_TONE)
+ thing_to_husk.icon = husk_icon
+ if(draw_blood)
+ var/mutable_appearance/husk_blood = mutable_appearance(icon_husk, "[husk_type]_husk_[body_zone]")
+ husk_blood.blend_mode = BLEND_INSET_OVERLAY
+ husk_blood.appearance_flags |= RESET_COLOR
+ husk_blood.dir = thing_to_husk.dir
+ thing_to_husk.add_overlay(husk_blood)
+
///Add a bodypart overlay and call the appropriate update procs
/obj/item/bodypart/proc/add_bodypart_overlay(datum/bodypart_overlay/overlay)
bodypart_overlays += overlay
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index fcf3f8f19ecc..eee42c49afb0 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -72,7 +72,6 @@
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_husk = 'icons/mob/human/species/monkey/bodyparts.dmi'
husk_type = "monkey"
- top_offset = -5
icon_state = "default_monkey_chest"
limb_id = SPECIES_MONKEY
should_draw_greyscale = FALSE
@@ -453,7 +452,6 @@
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_husk = 'icons/mob/human/species/monkey/bodyparts.dmi'
husk_type = "monkey"
- top_offset = -3
icon_state = "default_monkey_l_leg"
limb_id = SPECIES_MONKEY
should_draw_greyscale = FALSE
@@ -542,7 +540,6 @@
icon_static = 'icons/mob/human/species/monkey/bodyparts.dmi'
icon_husk = 'icons/mob/human/species/monkey/bodyparts.dmi'
husk_type = "monkey"
- top_offset = -3
icon_state = "default_monkey_r_leg"
limb_id = SPECIES_MONKEY
should_draw_greyscale = FALSE
@@ -566,15 +563,3 @@
can_be_disabled = FALSE
max_damage = LIMB_MAX_HP_ALIEN_LIMBS
should_draw_greyscale = FALSE
-
-/obj/item/bodypart/leg/right/tallboy
- limb_id = SPECIES_TALLBOY
- top_offset = 23
- unarmed_damage_low = 30
- unarmed_damage_low = 50
-
-/obj/item/bodypart/leg/left/tallboy
- limb_id = SPECIES_TALLBOY
- top_offset = 23
- unarmed_damage_low = 30
- unarmed_damage_low = 50
diff --git a/code/modules/surgery/lipoplasty.dm b/code/modules/surgery/lipoplasty.dm
index bd60e84d2427..dd2f66000be2 100644
--- a/code/modules/surgery/lipoplasty.dm
+++ b/code/modules/surgery/lipoplasty.dm
@@ -81,14 +81,17 @@
var/mob/living/carbon/human/human = target
var/typeofmeat = /obj/item/food/meat/slab/human
- if(human.dna && human.dna.species)
+ if(target.flags_1 & HOLOGRAM_1)
+ typeofmeat = null
+ else if(human.dna && human.dna.species)
typeofmeat = human.dna.species.meat
- var/obj/item/food/meat/slab/human/newmeat = new typeofmeat
- newmeat.name = "fatty meat"
- newmeat.desc = "Extremely fatty tissue taken from a patient."
- newmeat.subjectname = human.real_name
- newmeat.subjectjob = human.job
- newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, (removednutriment / 15)) //To balance with nutriment_factor of nutriment
- newmeat.forceMove(target.loc)
+ if(typeofmeat)
+ var/obj/item/food/meat/slab/human/newmeat = new typeofmeat
+ newmeat.name = "fatty meat"
+ newmeat.desc = "Extremely fatty tissue taken from a patient."
+ newmeat.subjectname = human.real_name
+ newmeat.subjectjob = human.job
+ newmeat.reagents.add_reagent (/datum/reagent/consumable/nutriment, (removednutriment / 15)) //To balance with nutriment_factor of nutriment
+ newmeat.forceMove(target.loc)
return ..()
diff --git a/code/modules/surgery/organs/external/tails.dm b/code/modules/surgery/organs/external/tails.dm
index 9fda5a87f1b8..2643dba07528 100644
--- a/code/modules/surgery/organs/external/tails.dm
+++ b/code/modules/surgery/organs/external/tails.dm
@@ -141,13 +141,16 @@
var/wagging = FALSE
/datum/bodypart_overlay/mutant/tail/get_base_icon_state()
- return (wagging ? "wagging_" : "") + sprite_datum.icon_state //add the wagging tag if we be wagging
+ return "[wagging ? "wagging_" : ""][sprite_datum.icon_state]" //add the wagging tag if we be wagging
/datum/bodypart_overlay/mutant/tail/can_draw_on_bodypart(mob/living/carbon/human/human)
if(human.wear_suit && (human.wear_suit.flags_inv & HIDEJUMPSUIT))
return FALSE
return TRUE
+/datum/bodypart_overlay/mutant/tail/get_global_feature_list()
+ return GLOB.tails_list_human
+
/obj/item/organ/external/tail/cat
name = "tail"
preference = "feature_human_tail"
@@ -161,12 +164,14 @@
feature_key = "tail_cat"
color_source = ORGAN_COLOR_HAIR
-/datum/bodypart_overlay/mutant/tail/cat/get_global_feature_list()
- return GLOB.tails_list_human
-
/obj/item/organ/external/tail/monkey
+ name = "monkey tail"
+ preference = "feature_monkey_tail"
+
bodypart_overlay = /datum/bodypart_overlay/mutant/tail/monkey
+ dna_block = null
+
///Monkey tail bodypart overlay
/datum/bodypart_overlay/mutant/tail/monkey
color_source = NONE
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index b77238400bee..aa709771d4db 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -47,6 +47,7 @@
var/safe_plasma_min = 0
///How much breath partial pressure is a safe amount of plasma. 0 means that we are immune to plasma.
var/safe_plasma_max = 0.05
+ var/n2o_detect_min = 0.08 //Minimum n2o for effects
var/n2o_para_min = 1 //Sleeping agent
var/n2o_sleep_min = 5 //Sleeping agent
var/BZ_trip_balls_min = 1 //BZ gas
@@ -503,12 +504,12 @@
/obj/item/organ/internal/lungs/proc/too_much_n2o(mob/living/carbon/breather, datum/gas_mixture/breath, n2o_pp, old_n2o_pp)
if(n2o_pp < n2o_para_min)
// Small amount of N2O, small side-effects.
- if(n2o_pp <= 0.01)
- if(old_n2o_pp > 0.01)
+ if(n2o_pp <= n2o_detect_min)
+ if(old_n2o_pp > n2o_detect_min)
return BREATH_LOST
return
// No alert for small amounts, but the mob randomly feels euphoric.
- if(old_n2o_pp >= n2o_para_min || old_n2o_pp <= 0.01)
+ if(old_n2o_pp >= n2o_para_min || old_n2o_pp <= n2o_detect_min)
breather.clear_alert(ALERT_TOO_MUCH_N2O)
if(prob(20))
diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm
index d8d22c881610..b8ecb875fbe7 100644
--- a/code/modules/surgery/plastic_surgery.dm
+++ b/code/modules/surgery/plastic_surgery.dm
@@ -95,11 +95,11 @@
else
user.visible_message(span_warning("You have no picture to base the appearance on, reverting to random appearances."))
for(var/i in 1 to 10)
- names += target.dna.species.random_name(target.gender, TRUE)
+ names += target.generate_random_mob_name(TRUE)
else
- for(var/_i in 1 to 9)
+ for(var/j in 1 to 9)
names += "Subject [target.gender == MALE ? "i" : "o"]-[pick("a", "b", "c", "d", "e")]-[rand(10000, 99999)]"
- names += target.dna.species.random_name(target.gender, TRUE) //give one normal name in case they want to do regular plastic surgery
+ names += target.generate_random_mob_name(TRUE) //give one normal name in case they want to do regular plastic surgery
var/chosen_name = tgui_input_list(user, "New name to assign", "Plastic Surgery", names)
if(isnull(chosen_name))
return
diff --git a/code/modules/transport/tram/tram_doors.dm b/code/modules/transport/tram/tram_doors.dm
index d836685acf11..6008c383ec93 100644
--- a/code/modules/transport/tram/tram_doors.dm
+++ b/code/modules/transport/tram/tram_doors.dm
@@ -136,7 +136,7 @@
for(var/mob/living/future_pancake in checked_turf)
future_pancake.visible_message(span_warning("[src] beeps angrily and closes on [future_pancake]!"), span_userdanger("[src] beeps angrily and closes on you!"))
SEND_SIGNAL(future_pancake, COMSIG_LIVING_DOORCRUSHED, src)
- if(ishuman(future_pancake) || ismonkey(future_pancake))
+ if(ishuman(future_pancake))
future_pancake.emote("scream")
future_pancake.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 2)
future_pancake.Paralyze(2 SECONDS)
diff --git a/code/modules/unit_tests/limbsanity.dm b/code/modules/unit_tests/limbsanity.dm
index 9988c7471e29..d7acfeedf4a6 100644
--- a/code/modules/unit_tests/limbsanity.dm
+++ b/code/modules/unit_tests/limbsanity.dm
@@ -10,29 +10,3 @@
TEST_FAIL("[path] does not have a valid icon for female variants")
else if(!icon_exists(UNLINT(part.should_draw_greyscale ? part.icon_greyscale : part.icon_static), "[part.limb_id]_[part.body_zone]"))
TEST_FAIL("[path] does not have a valid icon")
-
-/// Tests the height adjustment system which dynamically changes how much the chest, head, and arms of a carbon are adjusted upwards or downwards based on the length of their legs and chest.
-/datum/unit_test/limb_height_adjustment
-
-/datum/unit_test/limb_height_adjustment/Run()
- var/mob/living/carbon/human/john_doe = allocate(/mob/living/carbon/human/consistent)
- var/mob/living/carbon/human/species/monkey/monkey = allocate(/mob/living/carbon/human/species/monkey)
- var/mob/living/carbon/human/tallboy = allocate(/mob/living/carbon/human/consistent)
-
- tallboy.set_species(/datum/species/human/tallboy)
- TEST_ASSERT_EQUAL(john_doe.get_top_offset(), 0, "John Doe found to have a top offset other than zero.")
- TEST_ASSERT_EQUAL(monkey.get_top_offset(), -8, "Monkey found to have a top offset other than -8.")
- TEST_ASSERT_EQUAL(tallboy.get_top_offset(), 23, "Tallboy human varient found to have a top offset other than 23.")
-
-
- var/obj/item/bodypart/leg/left/monkey/left_monky_leg = allocate(/obj/item/bodypart/leg/left/monkey)
- var/obj/item/bodypart/leg/right/monkey/right_monky_leg = allocate(/obj/item/bodypart/leg/right/monkey)
-
- left_monky_leg.replace_limb(john_doe, TRUE)
-
- TEST_ASSERT_EQUAL(john_doe.get_top_offset(), 0, "John Doe has a top offset other than 0 with one human leg and one monkey leg.")
-
- right_monky_leg.replace_limb(john_doe, TRUE)
-
- TEST_ASSERT_EQUAL(john_doe.get_top_offset(), -3, "John Doe has a top offset other than -3 with two monkey legs.")
-
diff --git a/code/modules/unit_tests/preference_species.dm b/code/modules/unit_tests/preference_species.dm
index 8e49f49cdd6a..8d913cc8fb64 100644
--- a/code/modules/unit_tests/preference_species.dm
+++ b/code/modules/unit_tests/preference_species.dm
@@ -12,7 +12,7 @@
for(var/species_id in get_selectable_species())
var/species_type = GLOB.species_list[species_id]
- var/datum/species/species = new species_type()
+ var/datum/species/species = GLOB.species_prototypes[species_type]
// Check the species decription.
// If it's not overridden, a stack trace will be thrown (and fail the test).
@@ -29,5 +29,3 @@
TEST_FAIL("Species [species] ([species_type]) is selectable, but did not properly implement get_species_lore().")
else if(!islist(species_lore))
TEST_FAIL("Species [species] ([species_type]) is selectable, but did not properly implement get_species_lore() (Did not return a list).")
-
- qdel(species)
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_tallboy.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_tallboy.png
deleted file mode 100644
index 262353dc0a95..000000000000
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_tallboy.png and /dev/null differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey.png
index d1bac73e30e9..5fd259a0ed4f 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_holodeck.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_holodeck.png
deleted file mode 100644
index d1bac73e30e9..000000000000
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_holodeck.png and /dev/null differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_human_legged.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_human_legged.png
deleted file mode 100644
index 21cb8c508530..000000000000
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_human_legged.png and /dev/null differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_lizard.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_lizard.png
new file mode 100644
index 000000000000..581daeed9c69
Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_lizard.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_monkey_freak.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_monkey_freak.png
deleted file mode 100644
index dc13acaeffcc..000000000000
Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_monkey_monkey_freak.png and /dev/null differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_husk_body.png b/code/modules/unit_tests/screenshots/screenshot_husk_body.png
index 2911277fcd97..d113b4738467 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_husk_body.png and b/code/modules/unit_tests/screenshots/screenshot_husk_body.png differ
diff --git a/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png b/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png
index 6c526af2ebb2..1a1db7dfd87f 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png and b/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png differ
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index fbe6871c92e2..62f24a9d9033 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/mob/clothing/belt_mirror.dmi b/icons/mob/clothing/belt_mirror.dmi
index eb06288d800b..a5c2fcef7784 100644
Binary files a/icons/mob/clothing/belt_mirror.dmi and b/icons/mob/clothing/belt_mirror.dmi differ
diff --git a/icons/mob/human/bodyparts.dmi b/icons/mob/human/bodyparts.dmi
index 2150b3c2c810..d6e4472973a3 100644
Binary files a/icons/mob/human/bodyparts.dmi and b/icons/mob/human/bodyparts.dmi differ
diff --git a/icons/mob/human/species/monkey/bodyparts.dmi b/icons/mob/human/species/monkey/bodyparts.dmi
index 689ef5e63d7e..95e8ae05deff 100644
Binary files a/icons/mob/human/species/monkey/bodyparts.dmi and b/icons/mob/human/species/monkey/bodyparts.dmi differ
diff --git a/icons/mob/human/species/monkey/monkey_tail.dmi b/icons/mob/human/species/monkey/monkey_tail.dmi
index 2a06af25b797..ffebf714f9aa 100644
Binary files a/icons/mob/human/species/monkey/monkey_tail.dmi and b/icons/mob/human/species/monkey/monkey_tail.dmi differ
diff --git a/icons/mob/human/species/monkey/uniform.dmi b/icons/mob/human/species/monkey/uniform.dmi
deleted file mode 100644
index a835629528ae..000000000000
Binary files a/icons/mob/human/species/monkey/uniform.dmi and /dev/null differ
diff --git a/maplestation.dme b/maplestation.dme
index 24ded6fe9583..f47914369b91 100644
--- a/maplestation.dme
+++ b/maplestation.dme
@@ -4234,6 +4234,10 @@
#include "code\modules\keybindings\bindings_client.dm"
#include "code\modules\keybindings\focus.dm"
#include "code\modules\keybindings\setup.dm"
+#include "code\modules\language\_language.dm"
+#include "code\modules\language\_language_holder.dm"
+#include "code\modules\language\_language_manuals.dm"
+#include "code\modules\language\_language_menu.dm"
#include "code\modules\language\aphasia.dm"
#include "code\modules\language\beachbum.dm"
#include "code\modules\language\buzzwords.dm"
@@ -4242,10 +4246,6 @@
#include "code\modules\language\common.dm"
#include "code\modules\language\draconic.dm"
#include "code\modules\language\drone.dm"
-#include "code\modules\language\language.dm"
-#include "code\modules\language\language_holder.dm"
-#include "code\modules\language\language_manuals.dm"
-#include "code\modules\language\language_menu.dm"
#include "code\modules\language\machine.dm"
#include "code\modules\language\moffic.dm"
#include "code\modules\language\monkey.dm"
@@ -4853,7 +4853,6 @@
#include "code\modules\mob\living\carbon\human\status_procs.dm"
#include "code\modules\mob\living\carbon\human\suicides.dm"
#include "code\modules\mob\living\carbon\human\species_types\abductors.dm"
-#include "code\modules\mob\living\carbon\human\species_types\abominations.dm"
#include "code\modules\mob\living\carbon\human\species_types\android.dm"
#include "code\modules\mob\living\carbon\human\species_types\dullahan.dm"
#include "code\modules\mob\living\carbon\human\species_types\ethereal.dm"
diff --git a/maplestation_modules/code/controllers/subsystem/economy.dm b/maplestation_modules/code/controllers/subsystem/economy.dm
index e732e0c52fb5..8454df13759b 100644
--- a/maplestation_modules/code/controllers/subsystem/economy.dm
+++ b/maplestation_modules/code/controllers/subsystem/economy.dm
@@ -101,11 +101,11 @@
all_tracked_data += "subject_two"
if(findtext(paper_contents, "victim"))
var/list/possible_names = list(
- "human" = random_unique_name(),
- "lizard" = random_unique_lizard_name(),
- "plasmaman" = random_unique_plasmaman_name(),
- "ethereal" = random_unique_ethereal_name(),
- "moth" = random_unique_moth_name(),
+ "human" = generate_random_name_species_based(species_type = /datum/species/human),
+ "lizard" = generate_random_name_species_based(species_type = /datum/species/lizard),
+ "plasmaman" = generate_random_name_species_based(species_type = /datum/species/plasmaman),
+ "ethereal" = generate_random_name_species_based(species_type = /datum/species/ethereal),
+ "moth" = generate_random_name_species_based(species_type = /datum/species/moth),
)
paper_victim_species = pick(possible_names)
paper_victim = possible_names[paper_victim_species]
diff --git a/maplestation_modules/code/modules/language/highdraconic.dm b/maplestation_modules/code/modules/language/highdraconic.dm
index 6006bb2f8ed6..b671914981b3 100644
--- a/maplestation_modules/code/modules/language/highdraconic.dm
+++ b/maplestation_modules/code/modules/language/highdraconic.dm
@@ -4,10 +4,6 @@
/datum/language/impdraconic
name = "Tiziran Draconic"
desc = "A distinct dialect of Draconic common to lizards born and raised on Tizira."
- //speech_verb = "hisses"
- //ask_verb = "hisses"
- //exclaim_verb = "roars"
- //sing_verb = "sings"
key = "l"
flags = TONGUELESS_SPEECH
space_chance = 25
@@ -49,3 +45,12 @@
selected_language = /datum/language/uncommon
#endif
+
+/datum/language_holder/lizard/ash/primative
+ selected_language = /datum/language/impdraconic
+ understood_languages = list(
+ /datum/language/impdraconic = list(LANGUAGE_ATOM),
+ )
+ spoken_languages = list(
+ /datum/language/impdraconic = list(LANGUAGE_ATOM),
+ )
diff --git a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index f7ce076b139d..7556ed04f675 100644
--- a/maplestation_modules/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/maplestation_modules/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -26,3 +26,12 @@
horns?.bodypart_overlay.set_appearance_from_name("Simple")
human.update_body(is_creating = TRUE)
+
+// Same for the small ones
+/datum/species/monkey/lizard/get_species_speech_sounds(sound_type)
+ // These sounds have been ported from Goonstation.
+ return string_assoc_list(list(
+ 'maplestation_modules/sound/voice/lizard_1.ogg' = 80,
+ 'maplestation_modules/sound/voice/lizard_2.ogg' = 80,
+ 'maplestation_modules/sound/voice/lizard_3.ogg' = 80,
+ ))
diff --git a/tgstation.dme b/tgstation.dme
index 927ad6508a7c..5fa34ee2aa8f 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3631,6 +3631,7 @@
#include "code\modules\client\preferences\species_features\ethereal.dm"
#include "code\modules\client\preferences\species_features\felinid.dm"
#include "code\modules\client\preferences\species_features\lizard.dm"
+#include "code\modules\client\preferences\species_features\monkey.dm"
#include "code\modules\client\preferences\species_features\moth.dm"
#include "code\modules\client\preferences\species_features\mushperson.dm"
#include "code\modules\client\preferences\species_features\mutants.dm"
@@ -4234,6 +4235,10 @@
#include "code\modules\keybindings\bindings_client.dm"
#include "code\modules\keybindings\focus.dm"
#include "code\modules\keybindings\setup.dm"
+#include "code\modules\language\_language.dm"
+#include "code\modules\language\_language_holder.dm"
+#include "code\modules\language\_language_manuals.dm"
+#include "code\modules\language\_language_menu.dm"
#include "code\modules\language\aphasia.dm"
#include "code\modules\language\beachbum.dm"
#include "code\modules\language\buzzwords.dm"
@@ -4242,10 +4247,6 @@
#include "code\modules\language\common.dm"
#include "code\modules\language\draconic.dm"
#include "code\modules\language\drone.dm"
-#include "code\modules\language\language.dm"
-#include "code\modules\language\language_holder.dm"
-#include "code\modules\language\language_manuals.dm"
-#include "code\modules\language\language_menu.dm"
#include "code\modules\language\machine.dm"
#include "code\modules\language\moffic.dm"
#include "code\modules\language\monkey.dm"
@@ -4853,7 +4854,6 @@
#include "code\modules\mob\living\carbon\human\status_procs.dm"
#include "code\modules\mob\living\carbon\human\suicides.dm"
#include "code\modules\mob\living\carbon\human\species_types\abductors.dm"
-#include "code\modules\mob\living\carbon\human\species_types\abominations.dm"
#include "code\modules\mob\living\carbon\human\species_types\android.dm"
#include "code\modules\mob\living\carbon\human\species_types\dullahan.dm"
#include "code\modules\mob\living\carbon\human\species_types\ethereal.dm"
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.jsx b/tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.jsx
index 22fff93dfdda..8d6e53dbba63 100644
--- a/tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.jsx
+++ b/tgui/packages/tgui/interfaces/DnaConsole/DnaScanner.jsx
@@ -10,6 +10,7 @@ import {
import {
SUBJECT_CONCIOUS,
SUBJECT_DEAD,
+ SUBJECT_HARD_CRIT,
SUBJECT_SOFT_CRIT,
SUBJECT_TRANSFORMING,
SUBJECT_UNCONSCIOUS,
@@ -81,7 +82,7 @@ const SubjectStatus = (props) => {
);
}
- if (status === SUBJECT_UNCONSCIOUS) {
+ if (status === SUBJECT_UNCONSCIOUS || status === SUBJECT_HARD_CRIT) {
return (
Unconscious
diff --git a/tgui/packages/tgui/interfaces/DnaConsole/constants.ts b/tgui/packages/tgui/interfaces/DnaConsole/constants.ts
index c588c765e765..e3d11a5dcd50 100644
--- a/tgui/packages/tgui/interfaces/DnaConsole/constants.ts
+++ b/tgui/packages/tgui/interfaces/DnaConsole/constants.ts
@@ -31,8 +31,9 @@ export const STORAGE_MODE_ADVINJ = 'injector';
export const SUBJECT_CONCIOUS = 0;
export const SUBJECT_SOFT_CRIT = 1;
export const SUBJECT_UNCONSCIOUS = 2;
-export const SUBJECT_DEAD = 3;
-export const SUBJECT_TRANSFORMING = 4;
+export const SUBJECT_HARD_CRIT = 3;
+export const SUBJECT_DEAD = 4;
+export const SUBJECT_TRANSFORMING = 5;
export const PULSE_STRENGTH_MAX = 15;
export const PULSE_DURATION_MAX = 30;
diff --git a/tgui/packages/tgui/interfaces/MedicalKiosk.jsx b/tgui/packages/tgui/interfaces/MedicalKiosk.jsx
index 5d8b081e0ef4..98d71c043314 100644
--- a/tgui/packages/tgui/interfaces/MedicalKiosk.jsx
+++ b/tgui/packages/tgui/interfaces/MedicalKiosk.jsx
@@ -248,6 +248,7 @@ const MedicalKioskScanResults4 = (props) => {
overdose_list = [],
addict_list = [],
hallucinating_status,
+ blood_alcohol,
} = data;
return (
@@ -281,6 +282,19 @@ const MedicalKioskScanResults4 = (props) => {
{hallucinating_status}
+
+
+
+
+
);
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 457e67fd0a61..b7d0d69959f0 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/species_features.tsx
@@ -68,6 +68,15 @@ export const feature_human_tail: FeatureChoiced = {
},
};
+export const feature_monkey_tail: FeatureChoiced = {
+ name: 'Tail',
+ component: (
+ props: FeatureValueProps,
+ ) => {
+ return ;
+ },
+};
+
export const feature_lizard_legs: FeatureChoiced = {
name: 'Legs',
component: (
diff --git a/tgui/packages/tgui/interfaces/Vending.tsx b/tgui/packages/tgui/interfaces/Vending.tsx
index 2f8cd42dade9..d3e9071a8f26 100644
--- a/tgui/packages/tgui/interfaces/Vending.tsx
+++ b/tgui/packages/tgui/interfaces/Vending.tsx
@@ -12,7 +12,10 @@ import {
Stack,
Table,
} from 'tgui/components';
-import { Window } from 'tgui/layouts';
+
+import { createSearch } from '../../common/string';
+import { Input } from '../components';
+import { Window } from '../layouts';
type VendingData = {
onstation: boolean;
@@ -24,7 +27,7 @@ type VendingData = {
coin_records: CoinRecord[];
hidden_records: HiddenRecord[];
user: UserData;
- stock: StockItem[];
+ stock: Record[];
extended_inventory: boolean;
access: boolean;
vending_machine_input: CustomInput[];
@@ -81,11 +84,17 @@ export const Vending = (props) => {
product_records = [],
coin_records = [],
hidden_records = [],
- stock,
+ categories,
} = data;
const [selectedCategory, setSelectedCategory] = useState(
- Object.keys(data.categories)[0],
+ Object.keys(categories)[0],
+ );
+
+ const [stockSearch, setStockSearch] = useState('');
+ const stockSearchFn = createSearch(
+ stockSearch,
+ (item: ProductRecord | CustomInput) => item.name,
);
let inventory: (ProductRecord | CustomInput)[];
@@ -100,12 +109,15 @@ export const Vending = (props) => {
}
}
- inventory = inventory
- // Just in case we still have undefined values in the list
- .filter((item) => !!item);
+ // Just in case we still have undefined values in the list
+ inventory = inventory.filter((item) => !!item);
+
+ if (stockSearch.length >= 2) {
+ inventory = inventory.filter(stockSearchFn);
+ }
const filteredCategories = Object.fromEntries(
- Object.entries(data.categories).filter(([categoryName]) => {
+ Object.entries(categories).filter(([categoryName]) => {
return inventory.find((product) => {
if ('category' in product) {
return product.category === categoryName;
@@ -114,7 +126,7 @@ export const Vending = (props) => {
}
});
}),
- );
+ ) as { [k: string]: Category };
return (
@@ -129,19 +141,23 @@ export const Vending = (props) => {
- {Object.keys(filteredCategories).length > 1 && (
-
-
-
- )}
+ {stockSearch.length < 2 &&
+ Object.keys(filteredCategories).length > 1 && (
+
+
+
+ )}
@@ -155,7 +171,9 @@ export const UserDetails = (props) => {
if (!user) {
return (
- No ID detected! Contact the Head of Personnel.
+
+ No ID detected! Contact the Head of Personnel.
+
);
} else {
return (
@@ -181,11 +199,21 @@ export const UserDetails = (props) => {
/** Displays products in a section, with user balance at top */
const ProductDisplay = (props: {
custom: boolean;
- selectedCategory: string | null;
inventory: (ProductRecord | CustomInput)[];
+ stockSearch: string;
+ setStockSearch: (search: string) => void;
+ selectedCategory: string | null;
+ setSelectedCategory: (category: string) => void;
}) => {
const { data } = useBackend();
- const { custom, inventory, selectedCategory } = props;
+ const {
+ custom,
+ inventory,
+ stockSearch,
+ setStockSearch,
+ selectedCategory,
+ setSelectedCategory,
+ } = props;
const {
stock,
onstation,
@@ -200,20 +228,28 @@ const ProductDisplay = (props: {
scrollable
title="Products"
buttons={
- !!onstation &&
- user && (
-
- {(user && user.cash) || 0}
- {displayed_currency_name}{' '}
-
-
- )
+
+ {!!onstation && user && (
+
+ {(user && user.cash) || 0}
+ {displayed_currency_name}{' '}
+
+
+ )}
+
+ setStockSearch(value)}
+ placeholder="Search..."
+ value={stockSearch}
+ />
+
+
}
>
{inventory
.filter((product) => {
- if ('category' in product) {
+ if (!stockSearch && 'category' in product) {
return product.category === selectedCategory;
} else {
return true;