diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 09c713f9201..a2c004a7e4d 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -367,6 +367,10 @@
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login"
///sent from borg mobs to itself, for tools to catch an upcoming destroy() due to safe decon (rather than detonation)
#define COMSIG_BORG_SAFE_DECONSTRUCT "borg_safe_decon"
+///sent from living mobs every tick of fire
+#define COMSIG_LIVING_FIRE_TICK "living_fire_tick"
+//sent from living mobs when they are ahealed
+#define COMSIG_LIVING_AHEAL "living_aheal"
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
@@ -585,6 +589,11 @@
#define COMSIG_JOB_RECEIVED "job_received"
// called after DNA is updated
#define COMSIG_HUMAN_UPDATE_DNA "human_update_dna"
+/// From mob/living/carbon/human/change_body_accessory(): (mob/living/carbon/human/H, body_accessory_style)
+#define COMSIG_HUMAN_CHANGE_BODY_ACCESSORY "human_change_body_accessory"
+ #define COMSIG_HUMAN_NO_CHANGE_APPEARANCE (1<<0)
+/// From mob/living/carbon/human/change_head_accessory(): (mob/living/carbon/human/H, head_accessory_style)
+#define COMSIG_HUMAN_CHANGE_HEAD_ACCESSORY "human_change_head_accessory"
// /datum/species signals
diff --git a/code/__DEFINES/dna.dm b/code/__DEFINES/dna.dm
index 10da1be145e..c24dbc17786 100644
--- a/code/__DEFINES/dna.dm
+++ b/code/__DEFINES/dna.dm
@@ -47,12 +47,12 @@
#define DNA_UI_GENDER 32
#define DNA_UI_BEARD_STYLE 33
#define DNA_UI_HAIR_STYLE 34
-/*#define DNA_UI_BACC_STYLE 23*/
#define DNA_UI_HACC_STYLE 35
-#define DNA_UI_HEAD_MARK_STYLE 36
-#define DNA_UI_BODY_MARK_STYLE 37
-#define DNA_UI_TAIL_MARK_STYLE 38
-#define DNA_UI_LENGTH 38 // Update this when you add something, or you WILL break shit.
+#define DNA_UI_BACC_STYLE 36
+#define DNA_UI_HEAD_MARK_STYLE 37
+#define DNA_UI_BODY_MARK_STYLE 38
+#define DNA_UI_TAIL_MARK_STYLE 39
+#define DNA_UI_LENGTH 40 // Update this when you add something, or you WILL break shit.
#define DNA_SE_LENGTH 55 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow.
diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm
index ea1cb9449ee..51a7ef474a5 100644
--- a/code/__DEFINES/flags.dm
+++ b/code/__DEFINES/flags.dm
@@ -80,11 +80,17 @@
#define HAS_HEAD_MARKINGS 64
#define HAS_BODY_MARKINGS 128
#define HAS_TAIL_MARKINGS 256
-#define HAS_MARKINGS HAS_HEAD_MARKINGS|HAS_BODY_MARKINGS|HAS_TAIL_MARKINGS
#define TAIL_WAGGING 512
#define NO_EYES 1024
#define HAS_ALT_HEADS 2048
-#define ALL_RPARTS 4096
+#define HAS_WING 4096
+#define HAS_BODYACC_COLOR 8192
+#define BALD 16384
+#define ALL_RPARTS 32768
+
+//Pre-baked combinations of the above body flags
+#define HAS_BODY_ACCESSORY HAS_TAIL|HAS_WING
+#define HAS_MARKINGS HAS_HEAD_MARKINGS|HAS_BODY_MARKINGS|HAS_TAIL_MARKINGS
//Species Diet Flags
#define DIET_CARN 1
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 3105b68630a..b16a04cb143 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -162,6 +162,8 @@
#define MFOAM_IRON 2
//Human Overlays Indexes/////////
+#define WING_LAYER 41
+#define WING_UNDERLIMBS_LAYER 40
#define BODY_LAYER 39
#define MUTANTRACE_LAYER 38
#define TAIL_UNDERLIMBS_LAYER 37 //Tail split-rendering.
@@ -201,7 +203,7 @@
#define FIRE_LAYER 3 //If you're on fire
#define MISC_LAYER 2
#define FROZEN_LAYER 1
-#define TOTAL_LAYERS 39
+#define TOTAL_LAYERS 41
///Access Region Codes///
#define REGION_ALL 0
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index a6665768980..21edc27d6db 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -235,6 +235,7 @@
#define ismachineperson(A) (is_species(A, /datum/species/machine))
#define isdrask(A) (is_species(A, /datum/species/drask))
#define iswryn(A) (is_species(A, /datum/species/wryn))
+#define ismoth(A) (is_species(A, /datum/species/moth))
#define isanimal(A) (istype((A), /mob/living/simple_animal))
#define isdog(A) (istype((A), /mob/living/simple_animal/pet/dog))
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index 235d016d0a3..76a7fecbc8e 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -80,6 +80,12 @@
//#define STATUS_EFFECT_ICHORIAL_STAIN /datum/status_effect/ichorial_stain //Prevents a servant from being revived by vitality matrices for one minute.
+/// Whether a moth's wings are burnt
+#define STATUS_EFFECT_BURNT_WINGS /datum/status_effect/burnt_wings
+
+/// If a moth is in a cocoon
+#define STATUS_EFFECT_COCOONED /datum/status_effect/cocooned
+
/////////////
// NEUTRAL //
/////////////
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 8996c196b85..53f690ef31e 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -29,6 +29,8 @@
__init_body_accessory(/datum/body_accessory/body)
// Different tails
__init_body_accessory(/datum/body_accessory/tail)
+ // Different wings
+ __init_body_accessory(/datum/body_accessory/wing)
// Setup species:accessory relations
initialize_body_accessory_by_species()
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index b3f80f85d50..98ebc1b8d60 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -158,21 +158,23 @@
return m_style
-/proc/random_body_accessory(species = "Vulpkanin")
- var/body_accessory = null
+/**
+ * Returns a random body accessory for a given species name. Can be null based on is_optional argument.
+ *
+ * Arguments:
+ * * species - The name of the species to filter valid body accessories.
+ * * is_optional - Whether *no* body accessory (null) is an option.
+ */
+/proc/random_body_accessory(species = "Vulpkanin", is_optional = TRUE)
var/list/valid_body_accessories = list()
- for(var/B in GLOB.body_accessory_by_name)
- var/datum/body_accessory/A = GLOB.body_accessory_by_name[B]
- if(!istype(A))
- valid_body_accessories += "None" //The only null entry should be the "None" option.
- continue
- if(species in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list.
- valid_body_accessories += B
+ if(is_optional)
+ valid_body_accessories += null
- if(valid_body_accessories.len)
- body_accessory = pick(valid_body_accessories)
+ if(GLOB.body_accessory_by_species[species])
+ for(var/name in GLOB.body_accessory_by_species[species])
+ valid_body_accessories += name
- return body_accessory
+ return length(valid_body_accessories) ? pick(valid_body_accessories) : null
/proc/random_name(gender, species = "Human")
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 42913514406..0d13981f658 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -2,6 +2,7 @@
#define AB_CHECK_STUNNED 2
#define AB_CHECK_LYING 4
#define AB_CHECK_CONSCIOUS 8
+#define AB_CHECK_TURF 16
/datum/action
@@ -83,6 +84,9 @@
if(check_flags & AB_CHECK_CONSCIOUS)
if(owner.stat)
return FALSE
+ if(check_flags & AB_CHECK_TURF)
+ if(!isturf(owner.loc))
+ return FALSE
return TRUE
/datum/action/proc/UpdateButtonIcon()
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 4bedba8aa66..115fcc5c1c7 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -245,12 +245,15 @@ GLOBAL_VAR_INIT(record_id_num, 1001)
preview_icon.Blend(temp, ICON_OVERLAY)
//Tail
- if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/tail))
+ if(H.body_accessory && (istype(H.body_accessory, /datum/body_accessory/tail) || istype(H.body_accessory, /datum/body_accessory/wing)))
temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state)
preview_icon.Blend(temp, ICON_OVERLAY)
else if(H.tail && H.dna.species.bodyflags & HAS_TAIL)
temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.tail]_s")
preview_icon.Blend(temp, ICON_OVERLAY)
+ else if(H.wing && H.dna.species.bodyflags & HAS_WING)
+ temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.wing]_s")
+ preview_icon.Blend(temp, ICON_OVERLAY)
for(var/obj/item/organ/external/E in H.bodyparts)
preview_icon.Blend(E.get_icon(), ICON_OVERLAY)
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 447442a9ac0..4779ffdc491 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -511,6 +511,9 @@
if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP))
return
+ if(M.dna.species.spec_thunk(M)) //Species level thunk overrides
+ return
+
if(M.buckled) //Cam't fall down if you are buckled
return
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index 2dfe4d660c9..2a0f8ef06a7 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -56,10 +56,14 @@ GLOBAL_LIST_EMPTY(bad_blocks)
new_dna.blood_type = blood_type
new_dna.real_name = real_name
new_dna.species = new species.type
- for(var/b=1;b<=DNA_SE_LENGTH;b++)
+
+ for(var/b = 1; b <= DNA_SE_LENGTH; b++)
new_dna.SE[b]=SE[b]
- if(b<=DNA_UI_LENGTH)
- new_dna.UI[b]=UI[b]
+ if(b <= DNA_UI_LENGTH)
+ if(b <= length(UI)) //We check index against the length of UI provided, because it may be shorter and thus be out of bounds
+ new_dna.UI[b]=UI[b]
+ continue
+ new_dna.UI[b] = 0
new_dna.UpdateUI()
new_dna.UpdateSE()
return new_dna
@@ -100,7 +104,7 @@ GLOBAL_LIST_EMPTY(bad_blocks)
var/body_marks = GLOB.marking_styles_list.Find(character.m_styles["body"])
var/tail_marks = GLOB.marking_styles_list.Find(character.m_styles["tail"])
- head_traits_to_dna(H)
+ head_traits_to_dna(character, H)
eye_color_to_dna(eyes_organ)
SetUIValueRange(DNA_UI_SKIN_R, color2R(character.skin_colour), 255, 1)
@@ -121,11 +125,13 @@ GLOBAL_LIST_EMPTY(bad_blocks)
SetUIValueRange(DNA_UI_SKIN_TONE, 35-character.s_tone, 220, 1) // Value can be negative.
- /*SetUIValueRange(DNA_UI_BACC_STYLE, bodyacc, GLOB.facial_hair_styles_list.len, 1)*/
SetUIValueRange(DNA_UI_HEAD_MARK_STYLE, head_marks, GLOB.marking_styles_list.len, 1)
SetUIValueRange(DNA_UI_BODY_MARK_STYLE, body_marks, GLOB.marking_styles_list.len, 1)
SetUIValueRange(DNA_UI_TAIL_MARK_STYLE, tail_marks, GLOB.marking_styles_list.len, 1)
+ var/list/bodyacc = GLOB.body_accessory_by_name.Find(character.body_accessory?.name || "None")
+ SetUIValueRange(DNA_UI_BACC_STYLE, bodyacc, length(GLOB.body_accessory_by_name), 1)
+
//Set the Gender
switch(character.gender)
if(FEMALE)
@@ -153,6 +159,8 @@ GLOBAL_LIST_EMPTY(bad_blocks)
/datum/dna/proc/GetUIValue(block)
if(block <= 0)
return FALSE
+ if(block >= length(UI))
+ return FALSE
return UI[block]
// Set a DNA UI block's value, given a value and a max possible value.
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 9ecad776e3e..5d6616d196c 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -171,6 +171,10 @@
if((tail_marks > 0) && (tail_marks <= GLOB.marking_styles_list.len))
H.m_styles["tail"] = GLOB.marking_styles_list[tail_marks]
+ var/bodyacc = dna.GetUIValueRange(DNA_UI_BACC_STYLE, length(GLOB.body_accessory_by_name))
+ if(bodyacc > 0 && bodyacc <= length(GLOB.body_accessory_by_name))
+ H.body_accessory = GLOB.body_accessory_by_name[GLOB.body_accessory_by_name[bodyacc]]
+
H.regenerate_icons()
return TRUE
@@ -205,9 +209,17 @@
head_organ.sec_facial_colour = rgb(head_organ.dna.GetUIValueRange(DNA_UI_BEARD2_R, 255), head_organ.dna.GetUIValueRange(DNA_UI_BEARD2_G, 255), head_organ.dna.GetUIValueRange(DNA_UI_BEARD2_B, 255))
//Head Accessories
- var/headacc = GetUIValueRange(DNA_UI_HACC_STYLE,GLOB.head_accessory_styles_list.len)
- if((headacc > 0) && (headacc <= GLOB.head_accessory_styles_list.len))
- head_organ.ha_style = GLOB.head_accessory_styles_list[headacc]
+ var/list/available = list()
+ for(var/head_accessory in GLOB.head_accessory_styles_list)
+ var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory]
+ if(!(head_organ.dna.species.name in S.species_allowed)) //If the user's head is not of a species the head accessory style allows, skip it. Otherwise, add it to the list.
+ continue
+ available += head_accessory
+ var/list/sorted = sortTim(available, /proc/cmp_text_asc)
+
+ var/headacc = GetUIValueRange(DNA_UI_HACC_STYLE, length(sorted))
+ if(headacc > 0 && headacc <= length(sorted))
+ head_organ.ha_style = sorted[headacc]
head_organ.headacc_colour = rgb(head_organ.dna.GetUIValueRange(DNA_UI_HACC_R, 255), head_organ.dna.GetUIValueRange(DNA_UI_HACC_G, 255), head_organ.dna.GetUIValueRange(DNA_UI_HACC_B, 255))
@@ -227,7 +239,7 @@
SetUIValueRange(DNA_UI_EYES_G, color2G(eyes_organ.eye_color), 255, 1)
SetUIValueRange(DNA_UI_EYES_B, color2B(eyes_organ.eye_color), 255, 1)
-/datum/dna/proc/head_traits_to_dna(obj/item/organ/external/head/head_organ)
+/datum/dna/proc/head_traits_to_dna(mob/living/carbon/human/character, obj/item/organ/external/head/head_organ)
if(!head_organ)
log_runtime(EXCEPTION("Attempting to reset DNA from a missing head!"), src)
return
@@ -243,7 +255,6 @@
// Head Accessory
if(!head_organ.ha_style)
head_organ.ha_style = "None"
- var/headacc = GLOB.head_accessory_styles_list.Find(head_organ.ha_style)
SetUIValueRange(DNA_UI_HAIR_R, color2R(head_organ.hair_colour), 255, 1)
SetUIValueRange(DNA_UI_HAIR_G, color2G(head_organ.hair_colour), 255, 1)
@@ -267,4 +278,6 @@
SetUIValueRange(DNA_UI_HAIR_STYLE, hair, GLOB.hair_styles_full_list.len, 1)
SetUIValueRange(DNA_UI_BEARD_STYLE, beard, GLOB.facial_hair_styles_list.len, 1)
- SetUIValueRange(DNA_UI_HACC_STYLE, headacc, GLOB.head_accessory_styles_list.len, 1)
+
+ var/list/available = character.generate_valid_head_accessories()
+ SetUIValueRange(DNA_UI_HACC_STYLE, available.Find(head_organ.ha_style), max(length(available), 1), 1)
diff --git a/code/game/dna/mutations/powers.dm b/code/game/dna/mutations/powers.dm
index ebda9032236..c73985ec230 100644
--- a/code/game/dna/mutations/powers.dm
+++ b/code/game/dna/mutations/powers.dm
@@ -821,7 +821,7 @@
M.change_head_accessory_color(new_head_accessory_colour)
//Body accessory.
- if(M.dna.species.tail && M.dna.species.bodyflags & HAS_TAIL)
+ if((M.dna.species.tail && M.dna.species.bodyflags & (HAS_TAIL)) || (M.dna.species.wing && M.dna.species.bodyflags & (HAS_WING)))
var/list/valid_body_accessories = M.generate_valid_body_accessories()
if(valid_body_accessories.len > 1) //By default valid_body_accessories will always have at the very least a 'none' entry populating the list, even if the user's species is not present in any of the list items.
var/new_body_accessory = input("Please select body accessory style", "Character Generation", M.body_accessory) as null|anything in valid_body_accessories
diff --git a/code/modules/client/preference/character.dm b/code/modules/client/preference/character.dm
index cd050378c37..3db46cb3112 100644
--- a/code/modules/client/preference/character.dm
+++ b/code/modules/client/preference/character.dm
@@ -18,7 +18,8 @@
var/list/m_styles = list(
"head" = "None",
"body" = "None",
- "tail" = "None"
+ "tail" = "None",
+ "wing" = "None"
) //Marking styles.
var/list/m_colours = list(
"head" = "#000000",
@@ -39,6 +40,7 @@
var/language = "None" //Secondary language
var/autohiss_mode = AUTOHISS_OFF //Species autohiss level. OFF, BASIC, FULL.
+ /// The body accessory name of the mob (e.g. wings, tail).
var/body_accessory = null
var/speciesprefs = 0 //I hate having to do this, I really do (Using this for oldvox code, making names universal I guess
@@ -578,10 +580,8 @@
underwear = random_underwear(gender, species)
undershirt = random_undershirt(gender, species)
socks = random_socks(gender, species)
- if(GLOB.body_accessory_by_species[species])
- body_accessory = random_body_accessory(species)
- if(body_accessory == "None") //Required to prevent a bug where the information/icons in the character preferences screen wouldn't update despite the data being changed.
- body_accessory = null
+ if(length(GLOB.body_accessory_by_species[species]))
+ body_accessory = random_body_accessory(species, S.optional_body_accessory)
if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE))
s_tone = random_skin_tone(species)
h_style = random_hair_style(gender, species, robohead)
@@ -768,6 +768,12 @@
clothes_s.Blend(new /icon('icons/mob/clothing/back.dmi', "satchel"), ICON_OVERLAY)
return clothes_s
+#define ICON_SHIFT_XY(I, X, Y)\
+ if(X)\
+ I.Shift(EAST, X);\
+ if(Y)\
+ I.Shift(NORTH, Y);\
+
/datum/character_save/proc/update_preview_icon(for_observer=0) //seriously. This is horrendous.
qdel(preview_icon_front)
qdel(preview_icon_side)
@@ -830,49 +836,52 @@
else
preview_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT)
- //Tail
- if(current_species && (current_species.bodyflags & HAS_TAIL))
- var/tail_icon
- var/tail_icon_state
- var/tail_shift_x
- var/tail_shift_y
+ // Body accessory
+ if(current_species && (current_species.bodyflags & HAS_BODY_ACCESSORY))
+ var/icon
+ var/icon_state
+ var/offset_x = 0
+ var/offset_y = 0
var/blend_mode = ICON_ADD
+ var/icon/underlay = null
if(body_accessory)
- var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[body_accessory]
- tail_icon = accessory.icon
- tail_icon_state = accessory.icon_state
- if(accessory.blend_mode)
- blend_mode = accessory.blend_mode
- if(accessory.pixel_x_offset)
- tail_shift_x = accessory.pixel_x_offset
- if(accessory.pixel_y_offset)
- tail_shift_y = accessory.pixel_y_offset
- else
- tail_icon = "icons/effects/species.dmi"
+ var/datum/body_accessory/BA = GLOB.body_accessory_by_name[body_accessory]
+ if(BA)
+ icon = BA.icon
+ icon_state = BA.icon_state
+ blend_mode = BA.blend_mode || blend_mode
+ offset_x = BA.pixel_x_offset
+ offset_y = BA.pixel_y_offset
+ // If the body accessory has an underlay, account for it.
+ if(BA.has_behind)
+ underlay = new(icon, "[icon_state]_BEHIND")
+ else if(current_species.bodyflags & HAS_TAIL)
+ icon = "icons/effects/species.dmi"
if(coloured_tail)
- tail_icon_state = "[coloured_tail]_s"
+ icon_state = "[coloured_tail]_s"
else
- tail_icon_state = "[current_species.tail]_s"
+ icon_state = "[current_species.tail]_s"
+
+ if(icon)
+ var/icon/temp = new(icon, icon_state)
+ if(current_species.bodyflags & HAS_SKIN_COLOR)
+ temp.Blend(s_colour, blend_mode)
+ if(current_species.bodyflags & HAS_TAIL_MARKINGS)
+ var/tail_marking = m_styles["tail"]
+ var/datum/sprite_accessory/body_markings/BM = GLOB.marking_styles_list[tail_marking]
+ if(BM)
+ var/icon/t_marking_s = new(BM.icon, "[BM.icon_state]_s")
+ t_marking_s.Blend(m_colours["tail"], ICON_ADD)
+ temp.Blend(t_marking_s, ICON_OVERLAY)
- var/icon/temp = new/icon("icon" = tail_icon, "icon_state" = tail_icon_state)
- if(tail_shift_x)
- temp.Shift(EAST, tail_shift_x)
- if(tail_shift_y)
- temp.Shift(NORTH, tail_shift_y)
+ // Body accessory has an underlay, add it too.
+ if(underlay)
+ ICON_SHIFT_XY(underlay, offset_x, offset_y)
+ preview_icon.Blend(underlay, ICON_UNDERLAY)
- if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR))
- temp.Blend(s_colour, blend_mode)
-
- if(current_species && (current_species.bodyflags & HAS_TAIL_MARKINGS))
- var/tail_marking = m_styles["tail"]
- var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking]
- if(tail_marking_style && tail_marking_style.species_allowed)
- var/icon/t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s")
- t_marking_s.Blend(m_colours["tail"], ICON_ADD)
- temp.Blend(t_marking_s, ICON_OVERLAY)
-
- preview_icon.Blend(temp, ICON_OVERLAY)
+ ICON_SHIFT_XY(temp, offset_x, offset_y)
+ preview_icon.Blend(temp, ICON_OVERLAY)
//Markings
if(current_species && ((current_species.bodyflags & HAS_HEAD_MARKINGS) || (current_species.bodyflags & HAS_BODY_MARKINGS)))
@@ -1490,7 +1499,7 @@
qdel(socks_s)
qdel(clothes_s)
-
+#undef ICON_SHIFT_XY
/datum/character_save/proc/get_gear_metadata(datum/gear/G) // NYI
. = loadout_gear[G.type]
diff --git a/code/modules/client/preference/link_processing.dm b/code/modules/client/preference/link_processing.dm
index fe227081368..0ceb146f6bd 100644
--- a/code/modules/client/preference/link_processing.dm
+++ b/code/modules/client/preference/link_processing.dm
@@ -216,7 +216,7 @@
if(new_age)
active_character.age = max(min(round(text2num(new_age)), AGE_MAX),AGE_MIN)
if("species")
- var/list/new_species = list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin")
+ var/list/new_species = list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin", "Nian")
var/prev_species = active_character.species
for(var/_species in GLOB.whitelisted_species)
@@ -290,8 +290,7 @@
active_character.alt_head = "None" //No alt heads on species that don't have them.
active_character.speciesprefs = 0 //My Vox tank shouldn't change how my future Grey talks.
-
- active_character.body_accessory = null //no vulptail on humans damnit
+ active_character.body_accessory = random_body_accessory(NS.name, NS.optional_body_accessory)
//Reset prosthetics.
active_character.organ_data = list()
@@ -544,11 +543,14 @@
else
for(var/B in GLOB.body_accessory_by_name)
var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B]
- if(!istype(accessory))
- possible_body_accessories += "None" //the only null entry should be the "None" option
+ if(isnull(accessory)) // None
continue
if(active_character.species in accessory.allowed_species)
possible_body_accessories += B
+ if(S.optional_body_accessory)
+ possible_body_accessories += "None" //the only null entry should be the "None" option
+ else
+ possible_body_accessories -= "None" // in case an admin is viewing it
sortTim(possible_body_accessories, /proc/cmp_text_asc)
var/new_body_accessory = input(user, "Choose your body accessory:", "Character Preference") as null|anything in possible_body_accessories
if(new_body_accessory)
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index 0d0ff2348cd..f5bacbf6ccd 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -227,37 +227,37 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts
dat += "Tail Markings: "
dat += "[active_character.m_styles["tail"]]"
dat += "Color [color_square(active_character.m_colours["tail"])]
"
+ if(!(S.bodyflags & BALD))
+ dat += "Hair: "
+ dat += "[active_character.h_style]"
+ dat += "Color [color_square(active_character.h_colour)]"
+ var/datum/sprite_accessory/temp_hair_style = GLOB.hair_styles_public_list[active_character.h_style]
+ if(temp_hair_style && temp_hair_style.secondary_theme && !temp_hair_style.no_sec_colour)
+ dat += " Color #2 [color_square(active_character.h_sec_colour)]"
+ // Hair gradient
+ dat += "
"
+ dat += "- Gradient:"
+ dat += " [active_character.h_grad_style]"
+ dat += " Color [color_square(active_character.h_grad_colour)]"
+ dat += " [active_character.h_grad_alpha]"
+ dat += "
"
+ dat += "- Gradient Offset: [active_character.h_grad_offset_x],[active_character.h_grad_offset_y]"
+ dat += "
"
- dat += "Hair: "
- dat += "[active_character.h_style]"
- dat += "Color [color_square(active_character.h_colour)]"
- var/datum/sprite_accessory/temp_hair_style = GLOB.hair_styles_public_list[active_character.h_style]
- if(temp_hair_style && temp_hair_style.secondary_theme && !temp_hair_style.no_sec_colour)
- dat += " Color #2 [color_square(active_character.h_sec_colour)]"
- // Hair gradient
- dat += "
"
- dat += "- Gradient:"
- dat += " [active_character.h_grad_style]"
- dat += " Color [color_square(active_character.h_grad_colour)]"
- dat += " [active_character.h_grad_alpha]"
- dat += "
"
- dat += "- Gradient Offset: [active_character.h_grad_offset_x],[active_character.h_grad_offset_y]"
- dat += "
"
-
- dat += "Facial Hair: "
- dat += "[active_character.f_style ? "[active_character.f_style]" : "Shaved"]"
- dat += "Color [color_square(active_character.f_colour)]"
- var/datum/sprite_accessory/temp_facial_hair_style = GLOB.facial_hair_styles_list[active_character.f_style]
- if(temp_facial_hair_style && temp_facial_hair_style.secondary_theme && !temp_facial_hair_style.no_sec_colour)
- dat += " Color #2 [color_square(active_character.f_sec_colour)]"
- dat += "
"
+ dat += "Facial Hair: "
+ dat += "[active_character.f_style ? "[active_character.f_style]" : "Shaved"]"
+ dat += "Color [color_square(active_character.f_colour)]"
+ var/datum/sprite_accessory/temp_facial_hair_style = GLOB.facial_hair_styles_list[active_character.f_style]
+ if(temp_facial_hair_style && temp_facial_hair_style.secondary_theme && !temp_facial_hair_style.no_sec_colour)
+ dat += " Color #2 [color_square(active_character.f_sec_colour)]"
+ dat += "
"
if(!(S.bodyflags & ALL_RPARTS))
dat += "Eyes: "
dat += "Color [color_square(active_character.e_colour)]
"
- if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[active_character.species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins
+ if((S.bodyflags & HAS_SKIN_COLOR) || ((S.bodyflags & HAS_BODYACC_COLOR) && GLOB.body_accessory_by_species[active_character.species]) || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins
dat += "Body Color: "
dat += "Color [color_square(active_character.s_colour)]
"
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index 6f4e3a15694..f99d3e8e604 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -20,6 +20,8 @@
var/follow = 0 // Applies to HIVEMIND languages - should a follow link be included for dead mobs?
var/english_names = 0 // Do we want English names by default, no matter what?
var/list/scramble_cache = list()
+ /// Do we want to override the word-join character for scrambled text? If null, defaults to " " or ". "
+ var/join_override
/datum/language/proc/get_random_name(gender, name_count=2, syllable_count=4)
if(!syllables || !syllables.len || english_names)
@@ -62,7 +64,9 @@
capitalize = 0
scrambled_text += next
var/chance = rand(100)
- if(chance <= 5)
+ if(join_override)
+ scrambled_text += join_override
+ else if(chance <= 5)
scrambled_text += ". "
capitalize = 1
else if(chance > 5 && chance <= space_chance)
@@ -70,7 +74,7 @@
scrambled_text = trim(scrambled_text)
var/ending = copytext(scrambled_text, length(scrambled_text))
- if(ending == ".")
+ if(ending == "." || ending == "-")
scrambled_text = copytext(scrambled_text,1,length(scrambled_text)-1)
var/input_ending = copytext(input, input_size)
if(input_ending in list("!","?","."))
@@ -353,6 +357,28 @@
new_name += "-[pick(list("Hoorm","Viisk","Saar","Mnoo","Oumn","Fmong","Gnii","Vrrm","Oorm","Dromnn","Ssooumn","Ovv", "Hoorb","Vaar","Gaar","Goom","Ruum","Rumum"))]"
return new_name
+/datum/language/moth
+ name = "Tkachi"
+ desc = "The language of the Nianae mothpeople borders on complete unintelligibility."
+ speech_verb = "buzzes"
+ ask_verb = "flaps"
+ exclaim_verbs = list("chatters")
+ colour = "moth"
+ key = "#"
+ flags = RESTRICTED | WHITELISTED
+ join_override = "-"
+ syllables = list("år", "i", "går", "sek", "mo", "ff", "ok", "gj", "ø", "gå", "la", "le",
+ "lit", "ygg", "van", "dår", "næ", "møt", "idd", "hvo", "ja", "på", "han",
+ "så", "ån", "det", "att", "nå", "gö", "bra", "int", "tyc", "om", "när", "två",
+ "må", "dag", "sjä", "vii", "vuo", "eil", "tun", "käyt", "teh", "vä", "hei",
+ "huo", "suo", "ää", "ten", "ja", "heu", "stu", "uhr", "kön", "we", "hön")
+
+/datum/language/moth/get_random_name()
+ var/new_name = "[pick(list("Abbot","Archer","Arkwright","Baker","Bard","Biologist","Broker","Caller","Chamberlain","Clerk","Cooper","Culinarian","Dean","Director","Duke","Energizer","Excavator","Explorer","Fletcher","Gatekeeper","Guardian","Guide","Healer","Horner","Keeper","Knight","Laidler","Mapper","Marshall","Mechanic","Miller","Navigator","Pilot","Prior","Seeker","Seer","Smith","Stargazer","Teacher","Tech Whisperer","Tender","Thatcher","Voidcrafter","Voidhunter","Voidwalker","Ward","Watcher","Weaver","Webster","Wright"))]"
+ new_name += "[pick(list(" of"," for"," in Service of",", Servant of"," for the Good of",", Student of"," to"))]"
+ new_name += " [pick(list("Alkaid","Andromeda","Antlia","Apus","Auriga","Caelum","Camelopardalis","Canes Venatici","Carinae","Cassiopeia","Centauri","Circinus","Cygnus","Dorado","Draco","Eridanus","Errakis","Fornax","Gliese","Grus","Horologium","Hydri","Lacerta","Leo Minor","Lupus","Lynx","Maffei","Megrez","Messier","Microscopium","Monocerotis","Muscae","Ophiuchi","Orion","Pegasi","Persei","Perseus","Polaris","Pyxis","Sculptor","Syrma","Telescopium","Tianyi","Triangulum","Trifid","Tucana","Tycho","Vir","Volans","Zavyava"))]"
+ return new_name
+
/datum/language/common
name = "Galactic Common"
desc = "The common galactic tongue."
diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm
index 88f8dc87e24..a8f4f97fa88 100644
--- a/code/modules/mob/living/carbon/human/appearance.dm
+++ b/code/modules/mob/living/carbon/human/appearance.dm
@@ -52,6 +52,8 @@
var/obj/item/organ/external/head/H = get_organ("head")
if(!head_accessory_style || !H || H.ha_style == head_accessory_style || !(head_accessory_style in GLOB.head_accessory_styles_list))
return
+ if(SEND_SIGNAL(src, COMSIG_HUMAN_CHANGE_HEAD_ACCESSORY, head_accessory_style) & COMSIG_HUMAN_NO_CHANGE_APPEARANCE)
+ return FALSE
H.ha_style = head_accessory_style
@@ -103,6 +105,8 @@
var/found
if(!body_accessory_style || (body_accessory && body_accessory.name == body_accessory_style))
return
+ if(SEND_SIGNAL(src, COMSIG_HUMAN_CHANGE_BODY_ACCESSORY, body_accessory_style) & COMSIG_HUMAN_NO_CHANGE_APPEARANCE)
+ return FALSE
for(var/B in GLOB.body_accessory_by_name)
if(B == body_accessory_style)
@@ -114,7 +118,8 @@
m_styles["tail"] = "None"
update_tail_layer()
- return 1
+ update_wing_layer()
+ return TRUE
/mob/living/carbon/human/proc/change_alt_head(alternate_head)
var/obj/item/organ/external/head/H = get_organ("head")
@@ -468,17 +473,18 @@
return sortTim(valid_markings, /proc/cmp_text_asc)
/mob/living/carbon/human/proc/generate_valid_body_accessories()
- var/list/valid_body_accessories = new()
+ var/list/valid_body_accessories = list()
for(var/B in GLOB.body_accessory_by_name)
var/datum/body_accessory/A = GLOB.body_accessory_by_name[B]
- if(check_rights(R_ADMIN, 0, src))
+ if(isnull(A))
+ continue
+ else if(check_rights(R_ADMIN, FALSE, src))
valid_body_accessories = GLOB.body_accessory_by_name.Copy()
- else
- if(!istype(A))
- valid_body_accessories["None"] = "None" //The only null entry should be the "None" option.
- continue
- if(dna.species.name in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list.
- valid_body_accessories += B
+ break
+ else if(dna.species.name in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list.
+ valid_body_accessories += B
+ if(dna.species.optional_body_accessory)
+ valid_body_accessories += "None"
return sortTim(valid_body_accessories, /proc/cmp_text_asc)
diff --git a/code/modules/mob/living/carbon/human/body_accessories.dm b/code/modules/mob/living/carbon/human/body_accessories.dm
index 3759109019f..a00af925e0d 100644
--- a/code/modules/mob/living/carbon/human/body_accessories.dm
+++ b/code/modules/mob/living/carbon/human/body_accessories.dm
@@ -1,6 +1,6 @@
GLOBAL_LIST_INIT(body_accessory_by_name, list("None" = null))
-GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null))
+GLOBAL_LIST_INIT(body_accessory_by_species, list())
/proc/initialize_body_accessory_by_species()
for(var/B in GLOB.body_accessory_by_name)
@@ -8,8 +8,9 @@ GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null))
if(!istype(accessory)) continue
for(var/species in accessory.allowed_species)
- if(!GLOB.body_accessory_by_species["[species]"]) GLOB.body_accessory_by_species["[species]"] = list()
- GLOB.body_accessory_by_species["[species]"] += accessory
+ if(!GLOB.body_accessory_by_species["[species]"])
+ GLOB.body_accessory_by_species["[species]"] = list()
+ GLOB.body_accessory_by_species["[species]"]["[accessory.name]"] = accessory
if(GLOB.body_accessory_by_species.len)
return TRUE
@@ -45,8 +46,11 @@ GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null))
var/list/allowed_species = list()
+ /// If true, adds an underlay (in addition to the regular overlay!) to the character sprite, with the state "[icon_state]_BEHIND".
+ var/has_behind = FALSE
+
/datum/body_accessory/proc/try_restrictions(mob/living/carbon/human/H)
- return TRUE
+ return (H.dna.species.name in allowed_species)
/datum/body_accessory/proc/get_animated_icon() //return animated if it has it, return static if it does not.
if(animated_icon)
@@ -74,9 +78,9 @@ GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null))
animated_icon_state = "null"
/datum/body_accessory/tail/try_restrictions(mob/living/carbon/human/H)
- if(!H.wear_suit || !(H.wear_suit.flags_inv & HIDETAIL))
- return TRUE
- return FALSE
+ if(H.wear_suit && (H.wear_suit.flags_inv & HIDETAIL))
+ return FALSE
+ return ..()
//Tajaran
/datum/body_accessory/tail/wingler_tail // Jay wingler fluff tail
@@ -121,4 +125,86 @@ GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null))
name = "Bee Tail"
icon_state = "wryntail"
allowed_species = list("Wryn")
-
+
+//Moth wings
+/datum/body_accessory/wing
+ icon = 'icons/mob/sprite_accessories/moth/moth_wings.dmi'
+ animated_icon = null
+ name = "Plain Wings"
+ icon_state = "plain"
+ allowed_species = list("Nian")
+ has_behind = TRUE
+
+/datum/body_accessory/wing/plain
+
+/datum/body_accessory/wing/monarch
+ name = "Monarch Wings"
+ icon_state = "monarch"
+
+/datum/body_accessory/wing/luna
+ name = "Luna Wings"
+ icon_state = "luna"
+
+/datum/body_accessory/wing/atlas
+ name = "Atlas Wings"
+ icon_state = "atlas"
+
+/datum/body_accessory/wing/reddish
+ name = "Reddish Wings"
+ icon_state = "redish"
+
+/datum/body_accessory/wing/royal
+ name = "Royal Wings"
+ icon_state = "royal"
+
+/datum/body_accessory/wing/gothic
+ name = "Gothic Wings"
+ icon_state = "gothic"
+
+/datum/body_accessory/wing/lovers
+ name = "Lovers Wings"
+ icon_state = "lovers"
+
+/datum/body_accessory/wing/whitefly
+ name = "White Fly Wings"
+ icon_state = "whitefly"
+
+/datum/body_accessory/wing/burnt_off
+ name = "Burnt Off Wings"
+ icon_state = "burnt_off"
+
+/datum/body_accessory/wing/firewatch
+ name = "Firewatch Wings"
+ icon_state = "firewatch"
+
+/datum/body_accessory/wing/deathhead
+ name = "Deathshead Wings"
+ icon_state = "deathhead"
+
+/datum/body_accessory/wing/poison
+ name = "Poison Wings"
+ icon_state = "poison"
+
+/datum/body_accessory/wing/ragged
+ name = "Ragged Wings"
+ icon_state = "ragged"
+
+/datum/body_accessory/wing/moonfly
+ name = "Moon Fly Wings"
+ icon_state = "moonfly"
+
+/datum/body_accessory/wing/snow
+ name = "Snow Wings"
+ icon_state = "snow"
+
+/datum/body_accessory/wing/oakworm
+ name = "Oak Worm Wings"
+ icon_state = "oakworm"
+
+/datum/body_accessory/wing/jungle
+ name = "Jungle Wings"
+ icon_state = "jungle"
+
+/datum/body_accessory/wing/witchwing
+ name = "Witch Wing Wings"
+ icon_state = "witchwing"
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index d98e00b1e3f..68f70bc3cc2 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -124,6 +124,11 @@
else //Everyone else fails, skip the emote attempt
return
+ if("flap", "flaps", "aflap", "aflaps","flutter", "flutters")
+ if(!ismoth(src))
+ return
+ on_CD = handle_emote_CD()
+
if("scream", "screams")
on_CD = handle_emote_CD(50) //longer cooldown
if("fart", "farts", "flip", "flips", "snap", "snaps")
@@ -283,11 +288,10 @@
m_type = 1
if("wag", "wags")
- if(body_accessory)
+ if(istype(body_accessory, /datum/body_accessory/tail))
if(body_accessory.try_restrictions(src))
message = "[src] starts wagging [p_their()] tail."
start_tail_wagging()
-
else if(dna.species.bodyflags & TAIL_WAGGING)
if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL))
message = "[src] starts wagging [p_their()] tail."
@@ -299,7 +303,7 @@
m_type = 1
if("swag", "swags")
- if(dna.species.bodyflags & TAIL_WAGGING || body_accessory)
+ if((dna.species.bodyflags & TAIL_WAGGING) || istype(body_accessory, /datum/body_accessory/tail))
message = "[src] stops wagging [p_their()] tail."
stop_tail_wagging()
else
@@ -392,11 +396,16 @@
to_chat(usr, "You need your hands working in order to clap.")
if("flap", "flaps")
- if(!restrained())
- message = "[src] flaps [p_their()] wings."
- m_type = 2
- if(miming)
- m_type = 1
+ message = "[src] flaps [p_their()] wings."
+ m_type = 2
+ if(miming)
+ m_type = 1
+
+ if("flutter", "flutters")
+ message = "[src] flutters [p_their()] wings."
+ m_type = 2
+ if(miming)
+ m_type = 1
if("flip", "flips")
m_type = 1
@@ -456,11 +465,10 @@
spin(20, 1)
if("aflap", "aflaps")
- if(!restrained())
- message = "[src] flaps [p_their()] wings ANGRILY!"
- m_type = 2
- if(miming)
- m_type = 1
+ message = "[src] flaps [p_their()] wings ANGRILY!"
+ m_type = 2
+ if(miming)
+ m_type = 1
if("drool", "drools")
message = "[src] drools."
@@ -959,13 +967,13 @@
return
if("help")
- var/emotelist = "aflap(s), airguitar, blink(s), blink(s)_r, blush(es), bow(s)-none/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s), cry, cries, custom, dance, dap(s)-none/mob," \
- + " deathgasp(s), drool(s), eyebrow, fart(s), faint(s), flap(s), flip(s), frown(s), gasp(s), giggle(s), glare(s)-none/mob, grin(s), groan(s), grumble(s), grin(s)," \
+ var/emotelist = "airguitar, blink(s), blink(s)_r, blush(es), bow(s)-none/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s), cry, cries, custom, dance, dap(s)-none/mob," \
+ + " deathgasp(s), drool(s), eyebrow, fart(s), faint(s), flip(s), frown(s), gasp(s), giggle(s), glare(s)-none/mob, grin(s), groan(s), grumble(s), grin(s)," \
+ " handshake-mob, hug(s)-none/mob, hem, highfive, johnny, jump, kiss(es), laugh(s), look(s)-none/mob, moan(s), mumble(s), nod(s), pale(s), point(s)-atom, quiver(s), raise(s), salute(s)-none/mob, scream(s), shake(s)," \
+ " shiver(s), shrug(s), sigh(s), signal(s)-#1-10, slap(s), smile(s),snap(s), sneeze(s), sniff(s), snore(s), spin(s) stare(s)-none/mob, tremble(s), twitch(es), twitch(es)_s," \
+ " wave(s), whimper(s), wink(s), yawn(s)"
- switch(dna.species.name)
+ switch(dna.species.name) //dear future coders, do not use strings like this
if("Diona")
emotelist += "\nDiona specific emotes :- creak(s)"
if("Drask")
@@ -986,6 +994,8 @@
emotelist += "\nPlasmaman specific emotes :- rattle(s)-none/mob"
if("Skeleton")
emotelist += "\nSkeleton specific emotes :- rattle(s)-none/mob"
+ if("Nian")
+ emotelist += "\nNian specific emotes :- aflap(s), flap(s), flutter(s)"
if(ismachineperson(src))
emotelist += "\nMachine specific emotes :- beep(s)-none/mob, buzz(es)-none/mob, no-none/mob, ping(s)-none/mob, yes-none/mob, buzz2-none/mob"
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index eafecdd4d2f..c48124568a2 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -170,6 +170,11 @@
/mob/living/carbon/human/stok/Initialize(mapload)
. = ..(mapload, /datum/species/monkey/unathi)
+/mob/living/carbon/human/moth/Initialize(mapload)
+ . = ..(mapload, /datum/species/moth)
+ if(!body_accessory)
+ change_body_accessory("Plain Wings")
+
/mob/living/carbon/human/Stat()
..()
statpanel("Status")
@@ -1256,6 +1261,8 @@
tail = dna.species.tail
+ wing = dna.species.wing
+
maxHealth = dna.species.total_health
if(dna.species.language)
@@ -1387,7 +1394,10 @@
m_styles = DEFAULT_MARKING_STYLES //Wipes out markings, setting them all to "None".
m_colours = DEFAULT_MARKING_COLOURS //Defaults colour to #00000 for all markings.
- body_accessory = null
+ if(dna.species.bodyflags & HAS_BODY_ACCESSORY)
+ body_accessory = GLOB.body_accessory_by_name[dna.species.default_bodyacc]
+ else
+ body_accessory = null
dna.real_name = real_name
@@ -1947,6 +1957,11 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
/mob/living/carbon/human/proc/get_perceived_trauma()
return min(health, maxHealth - getStaminaLoss())
+/mob/living/carbon/human/WakeUp(updating = TRUE)
+ if(dna.species.spec_WakeUp(src))
+ return
+ ..()
+
/**
* Helper to get the mobs runechat colour span
*
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index a6bc7b0d363..1eaa3e02308 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -68,7 +68,10 @@
var/fire_sprite = "Standing"
var/datum/body_accessory/body_accessory = null
- var/tail // Name of tail image in species effects icon file.
+ /// Name of tail image in species effects icon file.
+ var/tail
+ /// Same as tail but wing
+ var/wing
var/list/splinted_limbs = list() //limbs we know are splinted
var/original_eye_color = "#000000"
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 6c05cfcd0a2..333ded583b0 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -7,7 +7,7 @@
/mob/living/carbon/human/Process_Spacemove(movement_dir = 0)
if(..())
- return 1
+ return TRUE
//Do we have a working jetpack?
var/obj/item/tank/jetpack/thrust
@@ -18,8 +18,10 @@
thrust = C.jetpack
if(thrust)
if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src))
- return 1
- return 0
+ return TRUE
+ if(dna.species.spec_Process_Spacemove(src))
+ return TRUE
+ return FALSE
/mob/living/carbon/human/mob_has_gravity()
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index af8ffeca7be..46e9c930955 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -14,7 +14,10 @@
var/datum/species/primitive_form = null // Lesser form, if any (ie. monkey for humans)
var/datum/species/greater_form = null // Greater form, if any, ie. human for monkeys.
- var/tail // Name of tail image in species effects icon file.
+ /// Name of tail image in species effects icon file.
+ var/tail
+ /// like tail but wings
+ var/wing
var/datum/unarmed_attack/unarmed //For empty hand harm-intent attack
var/unarmed_type = /datum/unarmed_attack
@@ -138,6 +141,9 @@
var/default_headacc //Default head accessory style for newly created humans unless otherwise set.
var/default_headacc_colour
+ /// Name of default body accessory if any.
+ var/default_bodyacc
+
//Defining lists of icon skin tones for species that have them.
var/list/icon_skin_tones = list()
@@ -171,6 +177,9 @@
// Species specific boxes
var/speciesbox
+ /// Whether the presence of a body accessory on this species is optional or not.
+ var/optional_body_accessory = TRUE
+
/datum/species/New()
unarmed = new unarmed_type()
@@ -266,7 +275,8 @@
. += (health_deficiency / 75)
else
. += (health_deficiency / 25)
- . += 2 * H.stance_damage //damaged/missing feet or legs is slow
+ if(H.dna.species.spec_movement_delay()) //Species overrides for slowdown due to feet/legs
+ . += 2 * H.stance_damage //damaged/missing feet or legs is slow
if((hungry >= 70) && !flight)
. += hungry/50
@@ -952,6 +962,18 @@ It'll return null if the organ doesn't correspond, so include null checks when u
if(istype(ears) && !HAS_TRAIT(H, TRAIT_DEAF))
. = TRUE
+/datum/species/proc/spec_Process_Spacemove(mob/living/carbon/human/H)
+ return FALSE
+
+/datum/species/proc/spec_thunk(mob/living/carbon/human/H)
+ return FALSE
+
+/datum/species/proc/spec_movement_delay()
+ return TRUE
+
+/datum/species/proc/spec_WakeUp(mob/living/carbon/human/H)
+ return FALSE
+
/**
* Species-specific runechat colour handler
*
diff --git a/code/modules/mob/living/carbon/human/species/grey.dm b/code/modules/mob/living/carbon/human/species/grey.dm
index 75f4787e7b1..46ba8bd8e34 100644
--- a/code/modules/mob/living/carbon/human/species/grey.dm
+++ b/code/modules/mob/living/carbon/human/species/grey.dm
@@ -20,7 +20,7 @@
species_traits = list(LIPS, IS_WHITELISTED, CAN_WINGDINGS, NO_HAIR)
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_BODY_MARKINGS
+ bodyflags = HAS_BODY_MARKINGS | HAS_BODYACC_COLOR
dietflags = DIET_HERB
has_gender = FALSE
reagent_tag = PROCESS_ORG
diff --git a/code/modules/mob/living/carbon/human/species/kidan.dm b/code/modules/mob/living/carbon/human/species/kidan.dm
index df364f6122b..6ef01df5196 100644
--- a/code/modules/mob/living/carbon/human/species/kidan.dm
+++ b/code/modules/mob/living/carbon/human/species/kidan.dm
@@ -12,7 +12,7 @@
species_traits = list(IS_WHITELISTED, NO_HAIR)
inherent_biotypes = MOB_ORGANIC | MOB_HUMANOID | MOB_BUG
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_HEAD_ACCESSORY | HAS_HEAD_MARKINGS | HAS_BODY_MARKINGS
+ bodyflags = HAS_HEAD_ACCESSORY | HAS_HEAD_MARKINGS | HAS_BODY_MARKINGS | HAS_BODYACC_COLOR
eyes = "kidan_eyes_s"
dietflags = DIET_HERB
flesh_color = "#ba7814"
diff --git a/code/modules/mob/living/carbon/human/species/monkey.dm b/code/modules/mob/living/carbon/human/species/monkey.dm
index 9820f964a2a..6b1ab68353c 100644
--- a/code/modules/mob/living/carbon/human/species/monkey.dm
+++ b/code/modules/mob/living/carbon/human/species/monkey.dm
@@ -27,7 +27,7 @@
female_scream_sound = 'sound/goonstation/voice/monkey_scream.ogg'
tail = "chimptail"
- bodyflags = HAS_TAIL
+ bodyflags = HAS_TAIL | HAS_BODYACC_COLOR
reagent_tag = PROCESS_ORG
//Has standard darksight of 2.
@@ -144,7 +144,7 @@
base_color = "#000000"
reagent_tag = PROCESS_ORG
- bodyflags = HAS_TAIL
+ bodyflags = HAS_TAIL | HAS_BODYACC_COLOR
has_organ = list(
"heart" = /obj/item/organ/internal/heart/unathi,
diff --git a/code/modules/mob/living/carbon/human/species/moth.dm b/code/modules/mob/living/carbon/human/species/moth.dm
new file mode 100644
index 00000000000..74c0a81ef4b
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/moth.dm
@@ -0,0 +1,218 @@
+#define COCOON_WEAVE_DELAY 5 SECONDS
+#define COCOON_EMERGE_DELAY 15 SECONDS
+#define COCOON_HARM_AMOUNT 50
+#define COCOON_NUTRITION_AMOUNT -200
+#define FLYSWATTER_DAMAGE_MULTIPLIER 9
+
+/datum/species/moth
+ name = "Nian"
+ name_plural = "Nianae"
+ language = "Tkachi"
+ icobase = 'icons/mob/human_races/r_moth.dmi'
+ inherent_factions = list("nian")
+ species_traits = list(NO_HAIR)
+ inherent_biotypes = MOB_ORGANIC | MOB_HUMANOID | MOB_BUG
+ clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT
+ bodyflags = HAS_HEAD_ACCESSORY | HAS_HEAD_MARKINGS | HAS_BODY_MARKINGS | HAS_WING | BALD
+ reagent_tag = PROCESS_ORG
+ dietflags = DIET_HERB
+ tox_mod = 1.5
+ blood_color = "#b9ae9c"
+ unarmed_type = /datum/unarmed_attack/claws
+ scream_verb = "buzzes"
+ male_scream_sound = 'sound/voice/scream_moth.ogg'
+ female_scream_sound = 'sound/voice/scream_moth.ogg'
+ default_headacc = "Plain Antennae"
+ default_bodyacc = "Plain Wings"
+ wing = "plain"
+ eyes = "moth_eyes_s"
+ butt_sprite = "nian"
+ siemens_coeff = 1.5
+
+ has_organ = list(
+ "heart" = /obj/item/organ/internal/heart/nian,
+ "lungs" = /obj/item/organ/internal/lungs/nian,
+ "liver" = /obj/item/organ/internal/liver/nian,
+ "kidneys" = /obj/item/organ/internal/kidneys/nian,
+ "brain" = /obj/item/organ/internal/brain/nian,
+ "eyes" = /obj/item/organ/internal/eyes/nian
+ )
+
+ optional_body_accessory = FALSE
+
+ var/datum/action/innate/cocoon/cocoon
+
+ suicide_messages = list(
+ "is attempting to nibble their antenna off!",
+ "is twisting their own abdomen!",
+ "is cracking their exoskeleton!",
+ "is ripping their wings off!",
+ "is holding their breath!"
+ )
+
+
+/datum/species/moth/on_species_gain(mob/living/carbon/human/H)
+ ..()
+ cocoon = new()
+ cocoon.Grant(H)
+ RegisterSignal(H, COMSIG_LIVING_FIRE_TICK, .proc/check_burn_wings)
+ RegisterSignal(H, COMSIG_LIVING_AHEAL, .proc/on_aheal)
+ RegisterSignal(H, COMSIG_HUMAN_CHANGE_BODY_ACCESSORY, .proc/on_change_body_accessory)
+ RegisterSignal(H, COMSIG_HUMAN_CHANGE_HEAD_ACCESSORY, .proc/on_change_head_accessory)
+
+/datum/species/moth/on_species_loss(mob/living/carbon/human/H)
+ ..()
+ cocoon.Remove(H)
+ UnregisterSignal(H, COMSIG_LIVING_FIRE_TICK)
+ UnregisterSignal(H, COMSIG_LIVING_AHEAL)
+ UnregisterSignal(H, COMSIG_HUMAN_CHANGE_BODY_ACCESSORY)
+ UnregisterSignal(H, COMSIG_HUMAN_CHANGE_HEAD_ACCESSORY)
+ H.remove_status_effect(STATUS_EFFECT_BURNT_WINGS)
+
+/datum/species/moth/handle_reagents(mob/living/carbon/human/H, datum/reagent/R)
+ if(R.id == "pestkiller")
+ H.adjustToxLoss(3)
+ H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM)
+ return TRUE
+
+ return ..()
+
+/datum/species/moth/get_species_runechat_color(mob/living/carbon/human/H)
+ return H.m_colours["body"]
+
+/datum/species/moth/spec_attacked_by(obj/item/I, mob/living/user, obj/item/organ/external/affecting, intent, mob/living/carbon/human/H)
+ if(istype(I, /obj/item/melee/flyswatter) && I.force)
+ apply_damage(I.force * FLYSWATTER_DAMAGE_MULTIPLIER, I.damtype, affecting, FALSE, H) //making flyswatters do 10x damage to moff
+
+/datum/species/moth/spec_Process_Spacemove(mob/living/carbon/human/H)
+ var/turf/A = get_turf(H)
+ if(isspaceturf(A))
+ return FALSE
+ if(H.has_status_effect(STATUS_EFFECT_BURNT_WINGS))
+ return FALSE
+ var/datum/gas_mixture/current = A.return_air()
+ if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible
+ return TRUE
+
+/datum/species/moth/spec_thunk(mob/living/carbon/human/H)
+ if(!H.has_status_effect(STATUS_EFFECT_BURNT_WINGS))
+ return TRUE
+
+/datum/species/moth/spec_movement_delay()
+ return FALSE
+
+/datum/species/moth/spec_WakeUp(mob/living/carbon/human/H)
+ if(H.has_status_effect(STATUS_EFFECT_COCOONED))
+ return TRUE //Cocooned mobs dont get to wake up
+
+/datum/species/moth/proc/check_burn_wings(mob/living/carbon/human/H) //do not go into the extremely hot light. you will not survive
+ SIGNAL_HANDLER
+ if(H.on_fire && !H.has_status_effect(STATUS_EFFECT_BURNT_WINGS) && H.bodytemperature >= 400 && H.fire_stacks > 0)
+ to_chat(H, "Your precious wings burn to a crisp!")
+ H.apply_status_effect(STATUS_EFFECT_BURNT_WINGS)
+
+/datum/species/moth/proc/on_aheal(mob/living/carbon/human/H)
+ SIGNAL_HANDLER
+ H.remove_status_effect(STATUS_EFFECT_BURNT_WINGS)
+
+/datum/species/moth/proc/on_change_body_accessory(mob/living/carbon/human/H)
+ SIGNAL_HANDLER
+ if(H.has_status_effect(STATUS_EFFECT_BURNT_WINGS))
+ return COMSIG_HUMAN_NO_CHANGE_APPEARANCE
+
+/datum/species/moth/proc/on_change_head_accessory(mob/living/carbon/human/H)
+ SIGNAL_HANDLER
+ if(H.has_status_effect(STATUS_EFFECT_BURNT_WINGS))
+ return COMSIG_HUMAN_NO_CHANGE_APPEARANCE
+
+/datum/action/innate/cocoon
+ name = "Cocoon"
+ desc = "Restore your wings and antennae, and heal some damage. If your cocoon is broken externally you will take heavy damage!"
+ check_flags = AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_CONSCIOUS|AB_CHECK_TURF
+ icon_icon = 'icons/effects/effects.dmi'
+ button_icon_state = "cocoon1"
+
+/datum/action/innate/cocoon/Activate()
+ var/mob/living/carbon/human/moth/H = owner
+ if(H.nutrition < COCOON_NUTRITION_AMOUNT)
+ to_chat(H, "You are too hungry to cocoon!")
+ return
+ H.visible_message("[H] begins to hold still and concentrate on weaving a cocoon...", "You begin to focus on weaving a cocoon... (This will take [COCOON_WEAVE_DELAY / 10] seconds, and you must hold still.)")
+ if(do_after(H, COCOON_WEAVE_DELAY, FALSE, H))
+ if(H.incapacitated(ignore_lying = TRUE))
+ to_chat(H, "You cannot weave a cocoon in your current state.")
+ return
+ H.visible_message("[H] finishes weaving a cocoon!", "You finish weaving your cocoon.")
+ var/obj/structure/moth/cocoon/C = new(get_turf(H))
+ H.forceMove(C)
+ C.preparing_to_emerge = TRUE
+ H.apply_status_effect(STATUS_EFFECT_COCOONED)
+ H.KnockOut()
+ addtimer(CALLBACK(src, .proc/emerge, C), COCOON_EMERGE_DELAY, TIMER_UNIQUE)
+ else
+ to_chat(H, "You need to hold still in order to weave a cocoon!")
+
+/**
+ * Removes moth from cocoon, restores burnt wings
+ */
+/datum/action/innate/cocoon/proc/emerge(obj/structure/moth/cocoon/C)
+ for(var/mob/living/carbon/human/H in C.contents)
+ H.remove_status_effect(STATUS_EFFECT_COCOONED)
+ H.remove_status_effect(STATUS_EFFECT_BURNT_WINGS)
+ C.preparing_to_emerge = FALSE
+ qdel(C)
+
+/obj/structure/moth/cocoon
+ name = "\improper Nian cocoon"
+ desc = "Someone wrapped in a Nian cocoon."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "cocoon1"
+ color = COLOR_PALE_YELLOW //So tiders (hopefully) don't decide to immediately bust them open
+ max_integrity = 60
+ var/preparing_to_emerge
+
+/obj/structure/moth/cocoon/Initialize(mapload)
+ . = ..()
+ icon_state = pick("cocoon1", "cocoon2", "cocoon3")
+
+/obj/structure/moth/cocoon/Destroy()
+ if(!preparing_to_emerge)
+ visible_message("[src] splits open from within!")
+ else
+ visible_message("[src] is smashed open, harming the Nian within!")
+ for(var/mob/living/carbon/human/H in contents)
+ H.adjustBruteLoss(COCOON_HARM_AMOUNT)
+ H.adjustFireLoss(COCOON_HARM_AMOUNT)
+ H.AdjustWeakened(5)
+
+ for(var/mob/living/carbon/human/H in contents)
+ H.remove_status_effect(STATUS_EFFECT_COCOONED)
+ H.adjust_nutrition(COCOON_NUTRITION_AMOUNT)
+ H.WakeUp()
+ H.forceMove(loc)
+ return ..()
+
+/datum/status_effect/burnt_wings
+ id = "burnt_wings"
+ alert_type = null
+
+/datum/status_effect/burnt_wings/on_creation(mob/living/new_owner, ...)
+ var/mob/living/carbon/human/H = new_owner
+ if(istype(H))
+ H.change_body_accessory("Burnt Off Wings")
+ H.change_head_accessory("Burnt Off Antennae")
+ return ..()
+
+/datum/status_effect/burnt_wings/on_remove()
+ owner.UpdateAppearance()
+ return ..()
+
+/datum/status_effect/cocooned
+ id = "cocooned"
+ alert_type = null
+
+#undef COCOON_WEAVE_DELAY
+#undef COCOON_EMERGE_DELAY
+#undef COCOON_HARM_AMOUNT
+#undef COCOON_NUTRITION_AMOUNT
+#undef FLYSWATTER_DAMAGE_MULTIPLIER
diff --git a/code/modules/mob/living/carbon/human/species/vox.dm b/code/modules/mob/living/carbon/human/species/vox.dm
index a52ae7bb06d..771f685f9d1 100644
--- a/code/modules/mob/living/carbon/human/species/vox.dm
+++ b/code/modules/mob/living/carbon/human/species/vox.dm
@@ -26,7 +26,7 @@
inherent_traits = list(TRAIT_NOGERMS, TRAIT_NODECAY)
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS //Species-fitted 'em all.
dietflags = DIET_OMNI
- bodyflags = HAS_ICON_SKIN_TONE | HAS_TAIL | TAIL_WAGGING | TAIL_OVERLAPPED | HAS_BODY_MARKINGS | HAS_TAIL_MARKINGS
+ bodyflags = HAS_ICON_SKIN_TONE | HAS_TAIL | TAIL_WAGGING | TAIL_OVERLAPPED | HAS_BODY_MARKINGS | HAS_TAIL_MARKINGS | HAS_BODYACC_COLOR
blood_color = "#2299FC"
flesh_color = "#808D11"
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 40dec488f86..7f9eae2c26f 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -277,6 +277,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
apply_overlay(BODY_LAYER)
//tail
update_tail_layer()
+ update_wing_layer()
update_int_organs()
//head accessory
update_head_accessory()
@@ -527,6 +528,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
UpdateDamageIcon()
force_update_limbs()
update_tail_layer()
+ update_wing_layer()
update_halo_layer()
overlays.Cut() // Force all overlays to regenerate
update_fire()
@@ -898,6 +900,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
apply_overlay(SUIT_LAYER)
update_tail_layer()
+ update_wing_layer()
update_collar()
/mob/living/carbon/human/update_inv_pockets()
@@ -1069,6 +1072,31 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
I.screen_loc = ui_back
client.screen += I
+/mob/living/carbon/human/proc/update_wing_layer()
+ remove_overlay(WING_UNDERLIMBS_LAYER)
+ remove_overlay(WING_LAYER)
+ if(!istype(body_accessory, /datum/body_accessory/wing))
+ if(dna.species.optional_body_accessory)
+ return
+ else
+ body_accessory = GLOB.body_accessory_by_name[dna.species.default_bodyacc]
+
+ if(!body_accessory.try_restrictions(src))
+ return
+
+ var/mutable_appearance/wings = mutable_appearance(body_accessory.icon, body_accessory.icon_state, layer = -WING_LAYER)
+ wings.pixel_x = body_accessory.pixel_x_offset
+ wings.pixel_y = body_accessory.pixel_y_offset
+ overlays_standing[WING_LAYER] = wings
+
+ if(body_accessory.has_behind)
+ var/mutable_appearance/under_wing = mutable_appearance(body_accessory.icon, "[body_accessory.icon_state]_BEHIND", layer = -WING_UNDERLIMBS_LAYER)
+ under_wing.pixel_x = body_accessory.pixel_x_offset
+ under_wing.pixel_y = body_accessory.pixel_y_offset
+ overlays_standing[WING_UNDERLIMBS_LAYER] = under_wing
+
+ apply_overlay(WING_UNDERLIMBS_LAYER)
+ apply_overlay(WING_LAYER)
/mob/living/carbon/human/proc/update_tail_layer()
remove_overlay(TAIL_UNDERLIMBS_LAYER) // SEW direction icons, overlayed by LIMBS_LAYER.
@@ -1142,6 +1170,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
overlays_standing[TAIL_LAYER] = mutable_appearance(over, layer = -TAIL_LAYER)
else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation.
overlays_standing[TAIL_LAYER] = mutable_appearance(tail_s, layer = -TAIL_LAYER)
+
apply_overlay(TAIL_LAYER)
apply_overlay(TAIL_UNDERLIMBS_LAYER)
@@ -1242,6 +1271,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts)
/mob/living/carbon/human/handle_transform_change()
..()
update_tail_layer()
+ update_wing_layer()
//Adds a collar overlay above the helmet layer if the suit has one
// Suit needs an identically named sprite in icons/mob/clothing/collar.dmi
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 5ce7ece322a..a241da11619 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -494,6 +494,7 @@
human_mob.decaylevel = 0
human_mob.remove_all_embedded_objects()
+ SEND_SIGNAL(src, COMSIG_LIVING_AHEAL)
restore_all_organs()
surgeries.Cut() //End all surgeries.
if(stat == DEAD)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 73f3da863ee..6edcec8c385 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -207,6 +207,7 @@
return FALSE
var/turf/location = get_turf(src)
location.hotspot_expose(700, 50, 1)
+ SEND_SIGNAL(src, COMSIG_LIVING_FIRE_TICK)
return TRUE
/mob/living/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 6ebc95cc177..529063879f2 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -198,6 +198,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
add_language("Chittin", 1)
add_language("Bubblish", 1)
add_language("Clownish", 1)
+ add_language("Tkachi", 1)
if(!safety)//Only used by AIize() to successfully spawn an AI.
if(!B)//If there is no player/brain inside.
diff --git a/code/modules/mob/living/silicon/pai/software/pai_toggles.dm b/code/modules/mob/living/silicon/pai/software/pai_toggles.dm
index 138fb06d710..b962cacf236 100644
--- a/code/modules/mob/living/silicon/pai/software/pai_toggles.dm
+++ b/code/modules/mob/living/silicon/pai/software/pai_toggles.dm
@@ -55,6 +55,7 @@
user.add_language("Orluum")
user.add_language("Clownish")
user.add_language("Neo-Russkiya")
+ user.add_language("Tkachi")
else
user.remove_language("Sinta'unathi")
user.remove_language("Siik'tajr")
@@ -67,6 +68,7 @@
user.remove_language("Orluum")
user.remove_language("Clownish")
user.remove_language("Neo-Russkiya")
+ user.remove_language("Tkachi")
/datum/pai_software/translator/is_active(mob/living/silicon/pai/user)
return user.translator_on
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 3c202a35029..66baf0cbbc6 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -261,6 +261,7 @@
R.add_language("Bubblish", 0)
R.add_language("Orluum", 0)
R.add_language("Clownish", 0)
+ R.add_language("Tkachi", 0)
/// Adds anything in `subsystems` to the robot's verbs, and grants any actions that are in `module_actions`.
/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R)
@@ -527,6 +528,7 @@
R.add_language("Bubblish", 1)
R.add_language("Clownish",1)
R.add_language("Neo-Russkiya", 1)
+ R.add_language("Tkachi", 1)
/obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed)
var/obj/item/storage/bag/tray/cyborg/T = locate(/obj/item/storage/bag/tray/cyborg) in modules
diff --git a/code/modules/mob/new_player/sprite_accessories/moth/moth_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/moth/moth_body_markings.dm
new file mode 100644
index 00000000000..2ab54261755
--- /dev/null
+++ b/code/modules/mob/new_player/sprite_accessories/moth/moth_body_markings.dm
@@ -0,0 +1,59 @@
+/datum/sprite_accessory/body_markings/moth
+ icon = 'icons/mob/sprite_accessories/moth/moth_body_markings.dmi'
+ species_allowed = list("Nian")
+
+/datum/sprite_accessory/body_markings/moth/reddish
+ name = "Reddish Markings"
+ icon_state = "reddish"
+
+/datum/sprite_accessory/body_markings/moth/royal
+ name = "Royal Markings"
+ icon_state = "royal"
+
+/datum/sprite_accessory/body_markings/moth/gothic
+ name = "Gothic Markings"
+ icon_state = "gothic"
+
+/datum/sprite_accessory/body_markings/moth/whitefly
+ name = "White Fly Markings"
+ icon_state = "whitefly"
+
+/datum/sprite_accessory/body_markings/moth/lovers
+ name = "Lovers Markings"
+ icon_state = "lovers"
+
+/datum/sprite_accessory/body_markings/moth/burnt_off
+ name = "Burnt Off Markings"
+ icon_state = "burnt_off"
+
+/datum/sprite_accessory/body_markings/moth/firewatch
+ name = "Firewatch Markings"
+ icon_state = "firewatch"
+
+/datum/sprite_accessory/body_markings/moth/deathhead
+ name = "Deathshead Markings"
+ icon_state = "deathhead"
+
+/datum/sprite_accessory/body_markings/moth/poison
+ name = "Poison Markings"
+ icon_state = "poison"
+
+/datum/sprite_accessory/body_markings/moth/ragged
+ name = "Ragged Markings"
+ icon_state = "ragged"
+
+/datum/sprite_accessory/body_markings/moth/moonfly
+ name = "Moon Fly Markings"
+ icon_state = "moonfly"
+
+/datum/sprite_accessory/body_markings/moth/oakworm
+ name = "Oak Worm Markings"
+ icon_state = "oakworm"
+
+/datum/sprite_accessory/body_markings/moth/jungle
+ name = "Jungle Markings"
+ icon_state = "jungle"
+
+/datum/sprite_accessory/body_markings/moth/witchwing
+ name = "Witch Wing Markings"
+ icon_state = "witchwing"
diff --git a/code/modules/mob/new_player/sprite_accessories/moth/moth_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/moth/moth_head_accessories.dm
new file mode 100644
index 00000000000..fd7878c2fbb
--- /dev/null
+++ b/code/modules/mob/new_player/sprite_accessories/moth/moth_head_accessories.dm
@@ -0,0 +1,73 @@
+/datum/sprite_accessory/head_accessory/moth
+ icon = 'icons/mob/sprite_accessories/moth/moth_head_accessories.dmi'
+ species_allowed = list("Nian")
+ over_hair = TRUE
+ do_colouration = FALSE
+
+/datum/sprite_accessory/head_accessory/moth/plain
+ name = "Plain Antennae"
+ icon_state = "plain"
+
+/datum/sprite_accessory/head_accessory/moth/reddish
+ name = "Reddish Antennae"
+ icon_state = "reddish"
+
+/datum/sprite_accessory/head_accessory/moth/royal
+ name = "Royal Antennae"
+ icon_state = "royal"
+
+/datum/sprite_accessory/head_accessory/moth/gothic
+ name = "Gothic Antennae"
+ icon_state = "gothic"
+
+/datum/sprite_accessory/head_accessory/moth/whitefly
+ name = "White Fly Antennae"
+ icon_state = "whitefly"
+
+/datum/sprite_accessory/head_accessory/moth/lovers
+ name = "Lovers Antennae"
+ icon_state = "lovers"
+
+/datum/sprite_accessory/head_accessory/moth/burnt_off
+ name = "Burnt Off Antennae"
+ icon_state = "burnt_off"
+
+/datum/sprite_accessory/head_accessory/moth/firewatch
+ name = "Firewatch Antennae"
+ icon_state = "firewatch"
+
+/datum/sprite_accessory/head_accessory/moth/deathhead
+ name = "Deathshead Antennae"
+ icon_state = "deathhead"
+
+/datum/sprite_accessory/head_accessory/moth/poison
+ name = "Poison Antennae"
+ icon_state = "poison"
+
+/datum/sprite_accessory/head_accessory/moth/moonfly
+ name = "Moon Fly Antennae"
+ icon_state = "moonfly"
+
+/datum/sprite_accessory/head_accessory/moth/oakworm
+ name = "Oak Worm Antennae"
+ icon_state = "oakworm"
+
+/datum/sprite_accessory/head_accessory/moth/jungle
+ name = "Jungle Antennae"
+ icon_state = "jungle"
+
+/datum/sprite_accessory/head_accessory/moth/witchwing
+ name = "Witch Wing Antennae"
+ icon_state = "witchwing"
+
+/datum/sprite_accessory/head_accessory/moth/regal
+ name = "Regal Antennae"
+ icon_state = "regal"
+
+/datum/sprite_accessory/head_accessory/moth/mothra
+ name = "Mothra Antennae"
+ icon_state = "mothra"
+
+/datum/sprite_accessory/head_accessory/moth/mothra
+ name = "Snow Antennae"
+ icon_state = "snow"
diff --git a/code/modules/mob/new_player/sprite_accessories/moth/moth_head_markings.dm b/code/modules/mob/new_player/sprite_accessories/moth/moth_head_markings.dm
new file mode 100644
index 00000000000..3ce6396d9e7
--- /dev/null
+++ b/code/modules/mob/new_player/sprite_accessories/moth/moth_head_markings.dm
@@ -0,0 +1,59 @@
+/datum/sprite_accessory/body_markings/head/moth
+ icon = 'icons/mob/sprite_accessories/moth/moth_head_markings.dmi'
+ species_allowed = list("Nian")
+
+/datum/sprite_accessory/body_markings/head/moth/reddish
+ name = "Reddish Head Markings"
+ icon_state = "reddish"
+
+/datum/sprite_accessory/body_markings/head/moth/royal
+ name = "Royal Head Markings"
+ icon_state = "royal"
+
+/datum/sprite_accessory/body_markings/head/moth/gothic
+ name = "Gothic Head Markings"
+ icon_state = "gothic"
+
+/datum/sprite_accessory/body_markings/head/moth/whitefly
+ name = "White Fly Head Markings"
+ icon_state = "whitefly"
+
+/datum/sprite_accessory/body_markings/head/moth/lovers
+ name = "Lovers Head Markings"
+ icon_state = "lovers"
+
+/datum/sprite_accessory/body_markings/head/moth/burnt_off
+ name = "Burnt Off Head Markings"
+ icon_state = "burnt_off"
+
+/datum/sprite_accessory/body_markings/head/moth/firewatch
+ name = "Firewatch Head Markings"
+ icon_state = "firewatch"
+
+/datum/sprite_accessory/body_markings/head/moth/deathhead
+ name = "Deathshead Head Markings"
+ icon_state = "deathhead"
+
+/datum/sprite_accessory/body_markings/head/moth/poison
+ name = "Poison Head Markings"
+ icon_state = "poison"
+
+/datum/sprite_accessory/body_markings/head/moth/ragged
+ name = "Ragged Head Markings"
+ icon_state = "ragged"
+
+/datum/sprite_accessory/body_markings/head/moth/moonfly
+ name = "Moon Fly Head Markings"
+ icon_state = "moonfly"
+
+/datum/sprite_accessory/body_markings/head/moth/oakworm
+ name = "Oak Worm Head Markings"
+ icon_state = "oakworm"
+
+/datum/sprite_accessory/body_markings/head/moth/jungle
+ name = "Jungle Head Markings"
+ icon_state = "jungle"
+
+/datum/sprite_accessory/body_markings/head/moth/witchwing
+ name = "Witch Wing Head Markings"
+ icon_state = "witchwing"
diff --git a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
index a3ffe70eb39..c6fdfd91b2e 100644
--- a/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories/sprite_accessories.dm
@@ -70,7 +70,7 @@
icon = 'icons/mob/human_face.dmi' // Keep bald hair here, as for some reason, putting it elsewhere lead to it being colourable - Also it make sense as it is shared by everyone.
name = "Bald"
icon_state = "bald"
- species_allowed = list("Human", "Unathi", "Vox", "Diona", "Kidan", "Grey", "Plasmaman", "Skeleton", "Vulpkanin", "Tajaran")
+ species_allowed = list("Human", "Unathi", "Vox", "Diona", "Kidan", "Grey", "Plasmaman", "Skeleton", "Vulpkanin", "Tajaran", "Nian")
glasses_over = 1
/datum/sprite_accessory/facial_hair
@@ -98,7 +98,7 @@
/datum/sprite_accessory/body_markings
icon = 'icons/mob/sprite_accessories/human/human_body_markings.dmi'
- species_allowed = list("Unathi", "Tajaran", "Vulpkanin", "Machine", "Vox", "Kidan")
+ species_allowed = list("Unathi", "Tajaran", "Vulpkanin", "Machine", "Vox", "Kidan", "Nian")
icon_state = "accessory_none"
marking_location = "body"
@@ -168,7 +168,7 @@
///////////////////////////
/datum/sprite_accessory/underwear
icon = 'icons/mob/clothing/underwear.dmi'
- species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox")
+ species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
sprite_sheets = list(
"Vox" = 'icons/mob/clothing/species/vox/underwear.dmi',
"Grey" = 'icons/mob/clothing/species/grey/underwear.dmi'
@@ -178,7 +178,7 @@
/datum/sprite_accessory/underwear/nude
name = "Nude"
icon_state = null
- species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox")
+ species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
/datum/sprite_accessory/underwear/male
gender = MALE
@@ -298,7 +298,7 @@
////////////////////////////
/datum/sprite_accessory/undershirt
icon = 'icons/mob/clothing/underwear.dmi'
- species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox")
+ species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
sprite_sheets = list(
"Vox" = 'icons/mob/clothing/species/vox/underwear.dmi',
"Grey" = 'icons/mob/clothing/species/grey/underwear.dmi')
@@ -307,7 +307,7 @@
/datum/sprite_accessory/undershirt/nude
name = "Nude"
icon_state = null
- species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox")
+ species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
//plain color shirts
/datum/sprite_accessory/undershirt/shirt_white
@@ -537,14 +537,14 @@
///////////////////////
/datum/sprite_accessory/socks
icon = 'icons/mob/clothing/underwear.dmi'
- species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox")
+ species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
sprite_sheets = list("Vox" = 'icons/mob/clothing/species/vox/underwear.dmi')
gender = NEUTER
/datum/sprite_accessory/socks/nude
name = "Nude"
icon_state = null
- species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox")
+ species_allowed = list("Human", "Unathi", "Diona", "Vulpkanin", "Tajaran", "Kidan", "Grey", "Plasmaman", "Machine", "Skrell", "Slime People", "Skeleton", "Drask", "Vox", "Nian")
/datum/sprite_accessory/socks/white_norm
name = "Normal White"
diff --git a/code/modules/surgery/organs/subtypes/moth.dm b/code/modules/surgery/organs/subtypes/moth.dm
new file mode 100644
index 00000000000..fd9730af532
--- /dev/null
+++ b/code/modules/surgery/organs/subtypes/moth.dm
@@ -0,0 +1,26 @@
+/obj/item/organ/internal/eyes/nian
+ name = "nian eyeballs"
+ see_in_dark = 5
+ flash_protect = FLASH_PROTECTION_SENSITIVE //BURN THE MOTH EYES
+
+/obj/item/organ/internal/liver/nian
+ name = "nian liver"
+ icon = 'icons/obj/species_organs/nian.dmi'
+
+/obj/item/organ/internal/heart/nian
+ name = "nian heart"
+ icon = 'icons/obj/species_organs/nian.dmi'
+
+/obj/item/organ/internal/brain/nian
+ icon = 'icons/obj/species_organs/nian.dmi'
+ icon_state = "brain2"
+ mmi_icon = 'icons/obj/species_organs/nian.dmi'
+ mmi_icon_state = "mmi_full"
+
+/obj/item/organ/internal/lungs/nian
+ name = "nian lungs"
+ icon = 'icons/obj/species_organs/nian.dmi'
+
+/obj/item/organ/internal/kidneys/nian
+ name = "nian kidneys"
+ icon = 'icons/obj/species_organs/nian.dmi'
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
index f9f51cafc83..729b728c350 100644
--- a/code/modules/tgui/modules/appearance_changer.dm
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -338,7 +338,7 @@
return owner && (flags & APPEARANCE_MARKINGS) && (body_flags & marking_flag)
/datum/ui_module/appearance_changer/proc/can_change_body_accessory()
- return owner && (flags & APPEARANCE_BODY_ACCESSORY) && (owner.dna.species.bodyflags & HAS_TAIL)
+ return owner && (flags & APPEARANCE_BODY_ACCESSORY) && (owner.dna.species.bodyflags & HAS_BODY_ACCESSORY)
/datum/ui_module/appearance_changer/proc/can_change_alt_head()
if(!head_organ)
diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css
index bbee5d2625d..ee110da7704 100644
--- a/goon/browserassets/css/browserOutput-dark.css
+++ b/goon/browserassets/css/browserOutput-dark.css
@@ -346,11 +346,12 @@ h1.alert, h2.alert {color: #FFF;}
.kidan {color: #C64C05;}
.slime {color: #0077AA;}
.drask {color: #a3d4eb; font-family: "Arial Black";}
+.moth {color: #869b29; font-family: "Copperplate";}
.clown {color: #ff0000;}
.shadowling {color: #Cb2799;}
.vulpkanin {color: #B97A57;}
.abductor {color: #800080; font-style: italic;}
-.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; font-style: italic;}
+.mind_control {color: #A00D6F; font-size: 3; font-weight: bold; font-style: italic;}
.rough {font-family: 'Trebuchet MS', cursive, sans-serif;}
.say_quote {font-family: Georgia, Verdana, sans-serif;}
.cult {color: #800080; font-weight: bold; font-style: italic;}
diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css
index cc6ed7fdcb8..3411927c4aa 100644
--- a/goon/browserassets/css/browserOutput.css
+++ b/goon/browserassets/css/browserOutput.css
@@ -343,6 +343,7 @@ h1.alert, h2.alert {color: #000000;}
.kidan {color: #664205;}
.slime {color: #0077AA;}
.drask {color: #a3d4eb; font-family: "Arial Black";}
+.moth {color: #869b29; font-family: "Copperplate";}
.clown {color: #ff0000;}
.shadowling {color: #3b2769;}
.vulpkanin {color: #B97A57;}
diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi
index 9b6d9468b9d..876057298bf 100644
Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ
diff --git a/icons/mob/human_races/r_moth.dmi b/icons/mob/human_races/r_moth.dmi
new file mode 100644
index 00000000000..16a1a99f2c2
Binary files /dev/null and b/icons/mob/human_races/r_moth.dmi differ
diff --git a/icons/mob/sprite_accessories/moth/moth_body_markings.dmi b/icons/mob/sprite_accessories/moth/moth_body_markings.dmi
new file mode 100644
index 00000000000..4dd5227366b
Binary files /dev/null and b/icons/mob/sprite_accessories/moth/moth_body_markings.dmi differ
diff --git a/icons/mob/sprite_accessories/moth/moth_head_accessories.dmi b/icons/mob/sprite_accessories/moth/moth_head_accessories.dmi
new file mode 100644
index 00000000000..d5f1d4ce474
Binary files /dev/null and b/icons/mob/sprite_accessories/moth/moth_head_accessories.dmi differ
diff --git a/icons/mob/sprite_accessories/moth/moth_head_markings.dmi b/icons/mob/sprite_accessories/moth/moth_head_markings.dmi
new file mode 100644
index 00000000000..65674417733
Binary files /dev/null and b/icons/mob/sprite_accessories/moth/moth_head_markings.dmi differ
diff --git a/icons/mob/sprite_accessories/moth/moth_wings.dmi b/icons/mob/sprite_accessories/moth/moth_wings.dmi
new file mode 100644
index 00000000000..0bbcd1aeee7
Binary files /dev/null and b/icons/mob/sprite_accessories/moth/moth_wings.dmi differ
diff --git a/icons/obj/assemblies.dmi b/icons/obj/assemblies.dmi
index b6e458c0e24..92fc44efb9d 100644
Binary files a/icons/obj/assemblies.dmi and b/icons/obj/assemblies.dmi differ
diff --git a/icons/obj/butts.dmi b/icons/obj/butts.dmi
index 104906120d4..1fbdf5cdfad 100644
Binary files a/icons/obj/butts.dmi and b/icons/obj/butts.dmi differ
diff --git a/icons/obj/species_organs/nian.dmi b/icons/obj/species_organs/nian.dmi
new file mode 100644
index 00000000000..81ac3c7f142
Binary files /dev/null and b/icons/obj/species_organs/nian.dmi differ
diff --git a/paradise.dme b/paradise.dme
index f785533761a..737a8b888a8 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1950,6 +1950,7 @@
#include "code\modules\mob\living\carbon\human\species\kidan.dm"
#include "code\modules\mob\living\carbon\human\species\machine.dm"
#include "code\modules\mob\living\carbon\human\species\monkey.dm"
+#include "code\modules\mob\living\carbon\human\species\moth.dm"
#include "code\modules\mob\living\carbon\human\species\nucleation.dm"
#include "code\modules\mob\living\carbon\human\species\plasmaman.dm"
#include "code\modules\mob\living\carbon\human\species\shadow.dm"
@@ -2159,6 +2160,9 @@
#include "code\modules\mob\new_player\sprite_accessories\kidan\kidan_hair.dm"
#include "code\modules\mob\new_player\sprite_accessories\kidan\kidan_head_accessories.dm"
#include "code\modules\mob\new_player\sprite_accessories\kidan\kidan_head_markings.dm"
+#include "code\modules\mob\new_player\sprite_accessories\moth\moth_body_markings.dm"
+#include "code\modules\mob\new_player\sprite_accessories\moth\moth_head_accessories.dm"
+#include "code\modules\mob\new_player\sprite_accessories\moth\moth_head_markings.dm"
#include "code\modules\mob\new_player\sprite_accessories\nucleation\nucleation_face.dm"
#include "code\modules\mob\new_player\sprite_accessories\shared\shared_tail_markings.dm"
#include "code\modules\mob\new_player\sprite_accessories\skrell\skrell_face.dm"
@@ -2497,6 +2501,7 @@
#include "code\modules\surgery\organs\subtypes\grey.dm"
#include "code\modules\surgery\organs\subtypes\kidan.dm"
#include "code\modules\surgery\organs\subtypes\machine.dm"
+#include "code\modules\surgery\organs\subtypes\moth.dm"
#include "code\modules\surgery\organs\subtypes\nucleation.dm"
#include "code\modules\surgery\organs\subtypes\plasmaman.dm"
#include "code\modules\surgery\organs\subtypes\skrell.dm"
diff --git a/sound/voice/scream_moth.ogg b/sound/voice/scream_moth.ogg
new file mode 100644
index 00000000000..482086fb630
Binary files /dev/null and b/sound/voice/scream_moth.ogg differ