code:
/datum/species
	///Replaces bodypart icons
	var/limbs_icon
	///What accessories can a species have aswell as their default accessory of such type e.g. "frills" = "Aquatic". Default accessory colors is dictated by the accessory properties and mutcolors of the specie
	var/list/default_mutant_bodyparts = list()
	var/list/list/mutant_bodyparts = list()
	///A list of actual body markings on the owner of the species. Associative lists with keys named by limbs defines, pointing to a list with names and colors for the marking to be rendered. This is also stored in the DNA.
	var/list/list/body_markings = list()
	///Override for the alpha of bodyparts and mutant parts.
	var/bodypart_alpha = 255
	///Override for alpha value of markings, should be much lower than the above value.
	var/markings_alpha = 255
	///Whether a species can use augmentations in preferences
	var/can_augmentation = TRUE
	///If a species can always be picked in prefs for the purposes of customizing it for ghost roles or events
	var/always_customizable = FALSE

/datum/species/can_wag_tail(mob/living/carbon/human/H)
	if(!H) //Somewhere in the core code we're getting those procs with H being null
		return FALSE
	var/list/tails = H.getorganslotlist(ORGAN_SLOT_TAIL)
	if(!length(tails))
		return FALSE
	for(var/thing in tails)
		var/obj/item/organ/tail/tail = thing
		if(tail.can_wag)
			return TRUE
	return FALSE

/datum/species/is_wagging_tail(mob/living/carbon/human/H)
	if(!H) //Somewhere in the core code we're getting those procs with H being null
		return FALSE
	var/list/tails = H.getorganslotlist(ORGAN_SLOT_TAIL)
	if(!length(tails))
		return FALSE
	for(var/thing in tails)
		var/obj/item/organ/tail/tail = thing
		if(tail.wagging)
			return TRUE
	return FALSE

/datum/species/start_wagging_tail(mob/living/carbon/human/H)
	if(!H) //Somewhere in the core code we're getting those procs with H being null
		return FALSE
	var/list/tails = H.getorganslotlist(ORGAN_SLOT_TAIL)
	if(!length(tails))
		return FALSE
	for(var/thing in tails)
		var/obj/item/organ/tail/tail = thing
		if(tail.can_wag)
			tail.wagging = TRUE
	return FALSE

/datum/species/stop_wagging_tail(mob/living/carbon/human/H)
	if(!H) //Somewhere in the core code we're getting those procs with H being null
		return FALSE
	var/list/tails = H.getorganslotlist(ORGAN_SLOT_TAIL)
	if(!length(tails))
		return FALSE
	for(var/thing in tails)
		var/obj/item/organ/tail/tail = thing
		tail.wagging = FALSE
	H.update_body()

/datum/species/spec_death(gibbed, mob/living/carbon/human/H)
	. = ..()
	if(is_wagging_tail(H))
		stop_wagging_tail(H)

/datum/species/spec_stun(mob/living/carbon/human/H,amount)
	. = ..()
	if(is_wagging_tail(H))
		stop_wagging_tail(H)

/datum/species/handle_hair(mob/living/carbon/human/H, forced_colour)
	H.remove_overlay(HAIR_LAYER)
	var/obj/item/bodypart/head/HD = H.get_bodypart_nostump(BODY_ZONE_HEAD)
	if(!HD) //Decapitated
		return

	if(HAS_TRAIT(H, TRAIT_HUSK))
		return
	var/datum/sprite_accessory/S
	var/list/standing = list()

	var/hair_hidden = FALSE //ignored if the matching dynamic_X_suffix is non-empty
	var/facialhair_hidden = FALSE // ^

	var/dynamic_hair_suffix = "" //if this is non-null, and hair+suffix matches an iconstate, then we render that hair instead
	var/dynamic_fhair_suffix = ""

	//for augmented heads
	if(HD.status == BODYPART_ROBOTIC)
		return

	//we check if our hat or helmet hides our facial hair.
	if(H.head)
		var/obj/item/I = H.head
		if(isclothing(I))
			var/obj/item/clothing/C = I
			dynamic_fhair_suffix = C.dynamic_fhair_suffix
		if(I.flags_inv & HIDEFACIALHAIR)
			facialhair_hidden = TRUE

	if(H.wear_mask)
		var/obj/item/I = H.wear_mask
		if(isclothing(I))
			var/obj/item/clothing/C = I
			dynamic_fhair_suffix = C.dynamic_fhair_suffix //mask > head in terms of facial hair
		if(I.flags_inv & HIDEFACIALHAIR)
			facialhair_hidden = TRUE

	if(H.facial_hairstyle && (FACEHAIR in species_traits) && (!facialhair_hidden || dynamic_fhair_suffix))
		S = GLOB.facial_hairstyles_list[H.facial_hairstyle]
		if(S)

			//List of all valid dynamic_fhair_suffixes
			var/static/list/fextensions
			if(!fextensions)
				var/icon/fhair_extensions = icon('icons/mob/facialhair_extensions.dmi')
				fextensions = list()
				for(var/s in fhair_extensions.IconStates(1))
					fextensions[s] = TRUE
				qdel(fhair_extensions)

			//Is hair+dynamic_fhair_suffix a valid iconstate?
			var/fhair_state = S.icon_state
			var/fhair_file = S.icon
			if(fextensions[fhair_state+dynamic_fhair_suffix])
				fhair_state += dynamic_fhair_suffix
				fhair_file = 'icons/mob/facialhair_extensions.dmi'

			var/mutable_appearance/facial_overlay = mutable_appearance(fhair_file, fhair_state, -HAIR_LAYER)

			if(!forced_colour)
				if(hair_color)
					if(hair_color == "mutcolor")
						facial_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
					else if(hair_color == "fixedmutcolor")
						facial_overlay.color = sanitize_hexcolor(fixed_mut_color, 6, TRUE)
					else
						facial_overlay.color = sanitize_hexcolor(hair_color, 6, TRUE)
				else
					facial_overlay.color = sanitize_hexcolor(H.facial_hair_color, 6, TRUE)
			else
				facial_overlay.color = sanitize_hexcolor(forced_colour, 6, TRUE)

			facial_overlay.alpha = hair_alpha

			standing += facial_overlay

	if(H.head)
		var/obj/item/I = H.head
		if(isclothing(I))
			var/obj/item/clothing/C = I
			dynamic_hair_suffix = C.dynamic_hair_suffix
		if(I.flags_inv & HIDEHAIR)
			hair_hidden = TRUE

	if(H.wear_mask)
		var/obj/item/I = H.wear_mask
		if(!dynamic_hair_suffix && isclothing(I)) //head > mask in terms of head hair
			var/obj/item/clothing/C = I
			dynamic_hair_suffix = C.dynamic_hair_suffix
		if(I.flags_inv & HIDEHAIR)
			hair_hidden = TRUE

	if(!hair_hidden || dynamic_hair_suffix)
		var/mutable_appearance/hair_overlay = mutable_appearance(layer = -HAIR_LAYER)
		if(H.hairstyle && (HAIR in species_traits))
			S = GLOB.hairstyles_list[H.hairstyle]
			if(S)

				//List of all valid dynamic_hair_suffixes
				var/static/list/extensions
				if(!extensions)
					var/icon/hair_extensions = icon('icons/mob/hair_extensions.dmi') //hehe
					extensions = list()
					for(var/s in hair_extensions.IconStates(1))
						extensions[s] = TRUE
					qdel(hair_extensions)

				//Is hair+dynamic_hair_suffix a valid iconstate?
				var/hair_state = S.icon_state
				var/hair_file = S.icon
				if(extensions[hair_state+dynamic_hair_suffix])
					hair_state += dynamic_hair_suffix
					hair_file = 'icons/mob/hair_extensions.dmi'

				hair_overlay.icon = hair_file
				hair_overlay.icon_state = hair_state

				if(!forced_colour)
					if(hair_color)
						if(hair_color == "mutcolor")
							hair_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
						else if(hair_color == "fixedmutcolor")
							hair_overlay.color = sanitize_hexcolor(fixed_mut_color, 6, TRUE)
						else
							hair_overlay.color = sanitize_hexcolor(hair_color, 6, TRUE)
					else
						hair_overlay.color = sanitize_hexcolor(H.hair_color, 6, TRUE)
				else
					hair_overlay.color = sanitize_hexcolor(forced_colour, 6, TRUE)
				hair_overlay.alpha = hair_alpha
				if(OFFSET_FACE in H.dna.species.offset_features)
					hair_overlay.pixel_x += H.dna.species.offset_features[OFFSET_FACE][1]
					hair_overlay.pixel_y += H.dna.species.offset_features[OFFSET_FACE][2]
		if(hair_overlay.icon)
			standing += hair_overlay

	if(standing.len)
		H.overlays_standing[HAIR_LAYER] = standing

	H.apply_overlay(HAIR_LAYER)

/datum/species/handle_body(mob/living/carbon/human/species_human)
	species_human.remove_overlay(BODY_LAYER)

	var/list/standing = list()

	var/obj/item/bodypart/head/head = species_human.get_bodypart_nostump(BODY_ZONE_HEAD)
	if(head && !(HAS_TRAIT(species_human, TRAIT_HUSK)))
		var/obj/item/bodypart/face/face = species_human.get_bodypart_nostump(BODY_ZONE_PRECISE_FACE)
		var/obj/item/bodypart/mouth/jaw = species_human.get_bodypart_nostump(BODY_ZONE_PRECISE_MOUTH)
		// face
		if(!face)
			var/mutable_appearance/face_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', "face_missing", -BODY_LAYER)
			if(OFFSET_FACE in offset_features)
				face_overlay.pixel_x += offset_features[OFFSET_FACE][1]
				face_overlay.pixel_y += offset_features[OFFSET_FACE][2]
			standing += face_overlay
		// jaw
		if(jaw)
			// lipstick
			if(species_human.lip_style && (LIPS in species_traits))
				var/mutable_appearance/lip_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', "lips_[species_human.lip_style]", -BODY_LAYER)
				lip_overlay.color = sanitize_hexcolor(species_human.lip_color, 6, TRUE)
				if(OFFSET_FACE in species_human.dna.species.offset_features)
					lip_overlay.pixel_x += species_human.dna.species.offset_features[OFFSET_FACE][1]
					lip_overlay.pixel_y += species_human.dna.species.offset_features[OFFSET_FACE][2]
				standing += lip_overlay
		else
			var/mutable_appearance/lip_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', "lips_missing", -BODY_LAYER)
			if(OFFSET_FACE in species_human.dna.species.offset_features)
				lip_overlay.pixel_x += species_human.dna.species.offset_features[OFFSET_FACE][1]
				lip_overlay.pixel_y += species_human.dna.species.offset_features[OFFSET_FACE][2]
			standing += lip_overlay
		// eyes
		if(!(NOEYESPRITES in species_traits))
			var/obj/item/bodypart/left_eyesocket = LAZYACCESS(species_human.eye_bodyparts, 1)
			var/obj/item/bodypart/right_eyesocket = LAZYACCESS(species_human.eye_bodyparts, 2)
			var/obj/item/organ/eyes/LE
			var/obj/item/organ/eyes/RE
			for(var/obj/item/organ/eyes/eye in left_eyesocket?.get_organs())
				LE = eye
				break
			for(var/obj/item/organ/eyes/eye in right_eyesocket?.get_organs())
				RE = eye
				break
			var/obscured = species_human.check_obscured_slots(TRUE) //eyes that shine in the dark shouldn't show when you have glasses
			//cut any possible vis overlays
			if(length(body_vis_overlays))
				SSvis_overlays.remove_vis_overlay(species_human, body_vis_overlays)
			var/mutable_appearance/right_overlay
			var/mutable_appearance/right_emissive
			if(RE)
				right_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', RE.eye_icon_state, -BODY_LAYER)
				if(EYECOLOR in species_traits)
					right_overlay.color = sanitize_hexcolor(species_human.right_eye_color, 6, TRUE)
				if(RE.overlay_ignore_lighting && !(obscured & ITEM_SLOT_EYES))
					right_emissive = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', RE.eye_icon_state)
					right_emissive.plane = EMISSIVE_PLANE
			else
				right_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', "eye-right-missing", -BODY_LAYER)
			var/mutable_appearance/left_overlay
			var/mutable_appearance/left_emissive
			if(LE)
				left_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', LE.eye_icon_state, -BODY_LAYER)
				if(EYECOLOR in species_traits)
					left_overlay.color = sanitize_hexcolor(species_human.left_eye_color, 6, TRUE)
				if(LE.overlay_ignore_lighting && !(obscured & ITEM_SLOT_EYES))
					left_emissive = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', LE.eye_icon_state)
					left_emissive.plane = EMISSIVE_PLANE
			else
				left_overlay = mutable_appearance('modular_septic/icons/mob/human/sprite_accessory/human_face.dmi', "eye-left-missing", -BODY_LAYER)
			if(OFFSET_FACE in offset_features)
				if(right_overlay)
					right_overlay.pixel_x += offset_features[OFFSET_FACE][1]
					right_overlay.pixel_y += offset_features[OFFSET_FACE][2]
				if(right_emissive)
					right_emissive.pixel_x += offset_features[OFFSET_FACE][1]
					right_emissive.pixel_y += offset_features[OFFSET_FACE][2]
				if(left_overlay)
					left_overlay.pixel_x += offset_features[OFFSET_FACE][1]
					left_overlay.pixel_y += offset_features[OFFSET_FACE][2]
				if(left_emissive)
					left_emissive.pixel_x += offset_features[OFFSET_FACE][1]
					left_emissive.pixel_y += offset_features[OFFSET_FACE][2]
			if(right_overlay)
				standing += right_overlay
			if(right_emissive)
				standing += right_emissive
			if(left_overlay)
				standing += left_overlay
			if(left_emissive)
				standing += left_emissive

	//Underwear, Undershirts & Socks
	if(!(NO_UNDERWEAR in species_traits))
		if(species_human.underwear && !(species_human.underwear_visibility & UNDERWEAR_HIDE_UNDIES))
			var/datum/sprite_accessory/underwear/underwear = GLOB.underwear_list[species_human.underwear]
			var/mutable_appearance/underwear_overlay
			if(underwear)
				if(species_human.dna.species.sexes && species_human.body_type == FEMALE && (underwear.gender == MALE))
					underwear_overlay = wear_female_version(underwear.icon_state, underwear.icon, -BODY_LAYER, FEMALE_UNIFORM_FULL)
				else
					underwear_overlay = mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
				if(!underwear.use_static)
					underwear_overlay.color = sanitize_hexcolor(species_human.left_eye_color, 6, TRUE)
				standing += underwear_overlay

		if(species_human.undershirt && !(species_human.underwear_visibility & UNDERWEAR_HIDE_SHIRT))
			var/datum/sprite_accessory/undershirt/undershirt = GLOB.undershirt_list[species_human.undershirt]
			if(undershirt)
				if(species_human.dna.species.sexes && species_human.body_type == FEMALE)
					standing += wear_female_version(undershirt.icon_state, undershirt.icon, -BODY_LAYER)
				else
					standing += mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)

		if(species_human.socks && species_human.num_legs >= species_human.default_num_legs && !(DIGITIGRADE in species_traits) && !(species_human.underwear_visibility & UNDERWEAR_HIDE_SOCKS))
			var/datum/sprite_accessory/socks/socks = GLOB.socks_list[species_human.socks]
			if(socks)
				standing += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)

	if(length(standing))
		species_human.overlays_standing[BODY_LAYER] = standing

	species_human.apply_overlay(BODY_LAYER)

	handle_mutant_bodyparts(species_human)

/datum/species/handle_mutant_bodyparts(mob/living/carbon/human/H, forced_colour, force_update = FALSE)
	var/list/standing	= list()

	//Digitigrade legs are stuck in the phantom zone between true limbs and mutant bodyparts. Mainly it just needs more agressive updating than most limbs.
	var/update_needed = FALSE
	var/not_digitigrade = TRUE
	for(var/X in H.bodyparts)
		var/obj/item/bodypart/bodypart = X
		if(!bodypart.use_digitigrade)
			continue
		not_digitigrade = FALSE
		if(!(DIGITIGRADE in species_traits)) //Someone cut off a digitigrade leg and tacked it on
			species_traits += DIGITIGRADE
		var/should_be_squished = FALSE
		if((H.wear_suit && H.wear_suit.flags_inv & HIDEJUMPSUIT && !(H.wear_suit.mutant_variants & STYLE_DIGITIGRADE) && (H.wear_suit.body_parts_covered & LEGS)) || (H.w_uniform && (H.w_uniform.body_parts_covered & LEGS|FEET) && !(H.w_uniform.mutant_variants & STYLE_DIGITIGRADE)))
			should_be_squished = TRUE
		if(bodypart.use_digitigrade == FULL_DIGITIGRADE && should_be_squished)
			bodypart.use_digitigrade = SQUISHED_DIGITIGRADE
			update_needed = TRUE
		else if(bodypart.use_digitigrade == SQUISHED_DIGITIGRADE && !should_be_squished)
			bodypart.use_digitigrade = FULL_DIGITIGRADE
			update_needed = TRUE
	if(update_needed)
		H.update_body_parts()
	//Curse is lifted
	if(not_digitigrade && (DIGITIGRADE in species_traits))
		species_traits -= DIGITIGRADE

	if(!length(mutant_bodyparts) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
		H.remove_overlay(BODY_BEHIND_LAYER)
		H.remove_overlay(BODY_ADJ_LAYER)
		H.remove_overlay(BODY_FRONT_LAYER)
		return

	var/list/bodyparts_to_add = list()
	var/new_renderkey = "[id]"
	for(var/key in mutant_bodyparts)
		var/datum/sprite_accessory/S
		var/name = LAZYACCESSASSOC(mutant_bodyparts, key, MUTANT_INDEX_NAME)
		var/colors = LAZYACCESSASSOC(mutant_bodyparts, key, MUTANT_INDEX_COLOR)
		if(name)
			S = LAZYACCESSASSOC(GLOB.sprite_accessories, key, name)
		if(!S || isnull(S.icon_state))
			continue
		var/obj/item/bodypart/associated_part = H.get_bodypart_nostump(S.body_zone)
		if(S.is_hidden(H, associated_part))
			continue
		var/render_state
		if(S.special_render_case)
			render_state = S.get_special_render_state(H)
		else
			render_state = S.icon_state
		new_renderkey += "-[key]-[render_state]"
		if(colors)
			if(islist(colors))
				for(var/this_color in colors)
					var/final_color = sanitize_hexcolor(this_color, 6, FALSE)
					new_renderkey += "-[final_color]"
			else
				var/final_color = sanitize_hexcolor(colors, 6, FALSE)
				new_renderkey += "-[final_color]"
		bodyparts_to_add[S] = render_state

	if(new_renderkey == H.mutant_renderkey && !force_update)
		return

	H.mutant_renderkey = new_renderkey

	H.remove_overlay(BODY_BEHIND_LAYER)
	H.remove_overlay(BODY_ADJ_LAYER)
	H.remove_overlay(BODY_FRONT_LAYER)

	var/gender = (H.body_type == FEMALE) ? "f" : "m"
	for(var/bodypart in bodyparts_to_add)
		var/datum/sprite_accessory/S = bodypart
		var/key = S.key

		var/icon_to_use
		var/x_shift
		var/render_state = bodyparts_to_add[S]

		var/override_color = forced_colour
		if(!override_color && S.special_colorize)
			override_color = S.get_special_render_colour(H, render_state)

		if(S.special_icon_case)
			icon_to_use = S.get_special_icon(H, render_state)
		else
			icon_to_use = S.icon

		if(S.special_x_dimension)
			x_shift = S.get_special_x_dimension(H, render_state)
		else
			x_shift = S.dimension_x

		if(S.gender_specific)
			render_state = "[gender]_[key]_[render_state]"
		else
			render_state = "m_[key]_[render_state]"

		for(var/layer in S.relevant_layers)
			var/layertext = mutant_bodyparts_layertext(layer)

			var/mutable_appearance/accessory_overlay = mutable_appearance(icon_to_use, layer = -layer)

			accessory_overlay.icon_state = "[render_state]_[layertext]"

			if(S.center)
				accessory_overlay = center_image(accessory_overlay, x_shift, S.dimension_y)

			if(!override_color)
				if(HAS_TRAIT(H, TRAIT_HUSK))
					if(S.color_src == USE_MATRIXED_COLORS) //Matrixed+husk needs special care, otherwise we get sparkle dogs
						accessory_overlay.color = HUSK_COLOR_LIST
					else
						accessory_overlay.color = "#AAAAAA" //The gray husk color
				else
					switch(S.color_src)
						if(USE_ONE_COLOR)
							///Matrix
							if(islist(mutant_bodyparts[key][MUTANT_INDEX_COLOR]))
								accessory_overlay.color = sanitize_hexcolor(mutant_bodyparts[key][MUTANT_INDEX_COLOR][1], 6, TRUE)
							///Hex
							else
								accessory_overlay.color = sanitize_hexcolor(mutant_bodyparts[key][MUTANT_INDEX_COLOR], 6, TRUE)
						if(USE_MATRIXED_COLORS)
							var/list/color_list = mutant_bodyparts[key][MUTANT_INDEX_COLOR]
							//this is here and not with the alpha setting code below as setting the alpha on a matrix color mutable appearance breaks it (at least in this case)
							var/alpha_value = bodypart_alpha
							var/list/finished_list = list()
							if(length(color_list) == 1)
								color_list = color_list[1]
							//Matrix
							if(istype(color_list))
								finished_list += ReadRGB("[sanitize_hexcolor(color_list[min(1, length(color_list))], 6, FALSE)]00")
								finished_list += ReadRGB("[sanitize_hexcolor(color_list[min(2, length(color_list))], 6, FALSE)]00")
								finished_list += ReadRGB("[sanitize_hexcolor(color_list[min(3, length(color_list))], 6, FALSE)]00")
								finished_list += list(0,0,0,alpha_value)
							//Hex
							else
								var/color = ReadRGB("[sanitize_hexcolor(color_list, 6, FALSE)]00")
								var/list/final_color = ReadRGB(color)
								finished_list = list(final_color, final_color, final_color)
								finished_list += list(0,0,0,alpha_value)
							for(var/index in 1 to length(finished_list))
								finished_list[index] /= 255
							accessory_overlay.color = finished_list
						if(MUTCOLORS)
							if(fixed_mut_color)
								accessory_overlay.color = sanitize_hexcolor(fixed_mut_color, 6, TRUE)
							else
								accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
						if(HAIR)
							if(hair_color == "mutcolor")
								accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
							else if(hair_color == "fixedmutcolor")
								accessory_overlay.color = sanitize_hexcolor(fixed_mut_color, 6, TRUE)
							else
								accessory_overlay.color = sanitize_hexcolor(H.hair_color, 6, TRUE)
						if(FACEHAIR)
							accessory_overlay.color = sanitize_hexcolor(H.facial_hair_color, 6, TRUE)
						if(EYECOLOR)
							accessory_overlay.color = sanitize_hexcolor(H.left_eye_color, 6, TRUE)
			else
				accessory_overlay.color = sanitize_hexcolor(override_color, 6, TRUE)
			standing += accessory_overlay

			if(S.hasinner)
				var/mutable_appearance/inner_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
				if(S.gender_specific)
					inner_accessory_overlay.icon_state = "[gender]_[key]inner_[S.icon_state]_[layertext]"
				else
					inner_accessory_overlay.icon_state = "m_[key]inner_[S.icon_state]_[layertext]"

				if(S.center)
					inner_accessory_overlay = center_image(inner_accessory_overlay, S.dimension_x, S.dimension_y)

				standing += inner_accessory_overlay

			//Here's EXTRA parts of accessories which I should get rid of sometime TODO i guess
			if(S.extra) //apply the extra overlay, if there is one
				var/mutable_appearance/extra_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
				if(S.gender_specific)
					extra_accessory_overlay.icon_state = "[gender]_[key]_extra_[S.icon_state]_[layertext]"
				else
					extra_accessory_overlay.icon_state = "m_[key]_extra_[S.icon_state]_[layertext]"
				if(S.center)
					extra_accessory_overlay = center_image(extra_accessory_overlay, S.dimension_x, S.dimension_y)

				switch(S.extra_color_src) //change the color of the extra overlay
					if(MUTCOLORS)
						if(fixed_mut_color)
							extra_accessory_overlay.color = sanitize_hexcolor(fixed_mut_color, 6, TRUE)
						else
							extra_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
					if(MUTCOLORS2)
						extra_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor2"], 6, TRUE)
					if(MUTCOLORS3)
						extra_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor3"], 6, TRUE)
					if(HAIR)
						if(hair_color == "mutcolor")
							extra_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor3"], 6, TRUE)
						else
							extra_accessory_overlay.color = sanitize_hexcolor(H.hair_color, 6, TRUE)
					if(FACEHAIR)
						extra_accessory_overlay.color = sanitize_hexcolor(H.facial_hair_color, 6, TRUE)
					if(EYECOLOR)
						extra_accessory_overlay.color = sanitize_hexcolor(H.left_eye_color, 6, TRUE)

				standing += extra_accessory_overlay

			if(S.extra2) //apply the extra overlay, if there is one
				var/mutable_appearance/extra2_accessory_overlay = mutable_appearance(S.icon, layer = -layer)
				if(S.gender_specific)
					extra2_accessory_overlay.icon_state = "[gender]_[key]_extra2_[S.icon_state]_[layertext]"
				else
					extra2_accessory_overlay.icon_state = "m_[key]_extra2_[S.icon_state]_[layertext]"
				if(S.center)
					extra2_accessory_overlay = center_image(extra2_accessory_overlay, S.dimension_x, S.dimension_y)

				switch(S.extra2_color_src) //change the color of the extra overlay
					if(MUTCOLORS)
						if(fixed_mut_color)
							extra2_accessory_overlay.color = sanitize_hexcolor(fixed_mut_color, 6, TRUE)
						else
							extra2_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
					if(MUTCOLORS2)
						extra2_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor2"], 6, TRUE)
					if(MUTCOLORS3)
						extra2_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor3"], 6, TRUE)
					if(HAIR)
						if(hair_color == "mutcolor3")
							extra2_accessory_overlay.color = sanitize_hexcolor(H.dna.features["mcolor"], 6, TRUE)
						else
							extra2_accessory_overlay.color = sanitize_hexcolor(H.hair_color, 6, TRUE)

				standing += extra2_accessory_overlay
			if (bodypart_alpha != 255 && !override_color)
				for(var/ov in standing)
					var/image/overlay = ov
					if(!istype(overlay.color, /list)) //check for a list because setting the alpha of the matrix colors breaks the color (the matrix alpha is set above inside the matrix)
						overlay.alpha = bodypart_alpha

			if(!H.overlays_standing[layer])
				H.overlays_standing[layer] = list()
			H.overlays_standing[layer] += standing
			standing = list()

	H.apply_overlay(BODY_BEHIND_LAYER)
	H.apply_overlay(BODY_ADJ_LAYER)
	H.apply_overlay(BODY_FRONT_LAYER)

/datum/species/proc/get_random_features()
	var/list/returned = MANDATORY_FEATURE_LIST
	returned["mcolor"] = random_color()
	returned["mcolor2"] = random_color()
	returned["mcolor3"] = random_color()
	return returned

/datum/species/proc/get_random_mutant_bodyparts(list/features) //Needs features to base the colour off of
	var/list/mutantpart_list = list()
	var/list/bodyparts_to_add = default_mutant_bodyparts.Copy()
	for(var/key in bodyparts_to_add)
		var/datum/sprite_accessory/SP
		if(bodyparts_to_add[key] == ACC_RANDOM)
			SP = random_accessory_of_key_for_species(key, src)
		else
			SP = LAZYACCESSASSOC(GLOB.sprite_accessories, key, bodyparts_to_add[key])
			if(!SP)
				continue
		var/list/color_list = SP.get_default_color(features, src)
		var/list/final_list = list()
		final_list[MUTANT_INDEX_NAME] = SP.name
		final_list[MUTANT_INDEX_COLOR] = color_list
		mutantpart_list[key] = final_list

	return mutantpart_list

/datum/species/proc/get_random_body_markings(list/features) //Needs features to base the colour off of
	return list()


code:
/mob/living/carbon/human/become_husk(source)
	if(HAS_TRAIT(src, TRAIT_NO_HUSK))
		return
	return ..()

repo_name: AnalWerewolf/sundown

license: mit


code:
/mob/living/carbon/needs_heart()
	if(HAS_TRAIT(src, TRAIT_STABLEHEART))
		return FALSE
	if((NOBLOOD in dna?.species?.species_traits) || (NOHEART in dna?.species?.species_traits))
		return FALSE
	return TRUE

/// Handling organs
/mob/living/carbon/handle_organs(delta_time, times_fired)
	if(stat < DEAD)
		var/list/already_processed_life = list()
		for(var/organ_slot in GLOB.organ_process_order)
			var/list/organlist = getorganslotlist(organ_slot)
			for(var/thing in organlist)
				var/obj/item/organ/organ = thing
				if(!QDELETED(organ) && !(organ in already_processed_life) && (organ.owner == src)) // This exists mostly because reagent metabolization can cause organ reshuffling
					organ.on_life(delta_time, times_fired)
					already_processed_life |= organ
		for(var/thing in GLOB.organ_process_datum_order)
			if(QDELETED(src))
				break
			var/datum/organ_process/organ_process = GLOB.organ_processes_by_slot[thing]
			if(organ_process && organ_process.needs_process(src))
				organ_process.handle_process(src, delta_time, times_fired)
	else
		for(var/V in internal_organs)
			var/obj/item/organ/O = V
			O.on_death(delta_time, times_fired) //Needed so organs decay while inside the body.

/// Handling bodyparts
/mob/living/carbon/handle_bodyparts(delta_time, times_fired)
	if(stat < DEAD)
		for(var/thing in bodyparts)
			var/obj/item/bodypart/living_part = thing
			if(living_part.needs_processing)
				. |= living_part.on_life(delta_time, times_fired, TRUE)
	else
		for(var/thing in bodyparts)
			var/obj/item/bodypart/rotting_part = thing
			rotting_part.on_death(delta_time, times_fired)

/// Sleeping
/mob/living/carbon/Life(delta_time = SSMOBS_DT, times_fired)
	. = ..()
	if(!.) // We're dead
		return

	if(HAS_TRAIT(src, TRAIT_TRYINGTOSLEEP))
		handle_trying_to_sleep()

/mob/living/carbon/proc/handle_trying_to_sleep()
	if(getShock() >= max(PAIN_NO_SLEEP * (GET_MOB_ATTRIBUTE_VALUE(src, STAT_ENDURANCE)/ATTRIBUTE_MIDDLING), 1))
		REMOVE_TRAIT(src, TRAIT_TRYINGTOSLEEP, src)
		hud_used.sleeping.update_appearance()
	return Sleeping(8 SECONDS)

/// Immunity & disease
/mob/living/carbon/handle_diseases()
	. = ..()
	if(immunity >= 0.25 * default_immunity && immunity < default_immunity)
		immunity = min(immunity + 0.25, default_immunity)

/// Being burned to a crisp
/mob/living/carbon/proc/check_cremation(delta_time, times_fired)
	//Only cremate while actively on fire
	if(!on_fire)
		return

	//Only starts when the chest has taken full damage
	var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST)
	if(!(chest.get_damage() >= chest.max_damage))
		return

	//Burn off limbs one by one
	var/obj/item/bodypart/limb
	var/list/limb_list = LIMB_BODYPARTS
	var/still_has_limbs = FALSE
	for(var/zone in limb_list)
		limb = get_bodypart_nostump(zone)
		if(limb)
			still_has_limbs = TRUE
			if(limb.get_damage() >= limb.max_damage)
				limb.damage_integrity(WOUND_BURN, rand(0.5 * delta_time, 2 * delta_time))
				if(limb.limb_integrity <= 0)
					if(limb.status == BODYPART_ORGANIC) //Non-organic limbs don't burn
						limb.visible_message(span_warning("[src]'s [limb.name] crumbles into ash!"))
					else
						limb.visible_message(span_warning("[src]'s [limb.name] melts away!"))
					qdel(limb)

	if(still_has_limbs)
		return

	//Burn the head last
	var/obj/item/bodypart/head = get_bodypart_nostump(BODY_ZONE_HEAD)
	if(head)
		if(head.get_damage() >= head.max_damage)
			head.damage_integrity(WOUND_BURN, rand(0.5 * delta_time, 2 * delta_time))
			if(head.limb_integrity <= 0)
				if(limb.status == BODYPART_ORGANIC) //Non-organic limbs don't burn
					limb.visible_message(span_warning("[src]'s head crumbles into ash!"))
				else
					limb.visible_message(span_warning("[src]'s head melts away!"))
				qdel(limb)
		return

	//Nothing left: dust the body, drop the items (if they're flammable they'll burn on their own)
	chest.damage_integrity(WOUND_BURN, rand(0.5 * delta_time, 2 * delta_time))
	if(chest.limb_integrity <= 0)
		visible_message(span_warning("[src]'s body crumbles into a pile of ash!"))
		dust(TRUE, TRUE)


code:
/mob/living/carbon/proc/shit(intentional = FALSE)
	var/list/intestines = getorganslotlist(ORGAN_SLOT_INTESTINES)
	var/intestinal_efficiency = getorganslotefficiency(ORGAN_SLOT_INTESTINES)
	if(!length(intestines))
		if(intentional)
			to_chat(src, span_warning("I have no bowels."))
		return
	if(intestinal_efficiency < ORGAN_FAILING_EFFICIENCY)
		if(intentional)
			to_chat(src, span_warning("My bowels dilate in agonizing pain as they try to shit it's contents out!"))
		return
	var/collective_shit_amount = 0
	for(var/obj/item/organ/intestines/intestine as anything in intestines)
		collective_shit_amount += intestine.reagents.get_reagent_amount(/datum/reagent/consumable/shit)
	if(collective_shit_amount < 10)
		if(intentional)
			to_chat(src, span_warning("I don't need to shit."))
		return
	//we'll try to shit a maximum of 40u from each bowel
	var/obj/item/shit/stool = new(get_turf(src))
	stool.add_shit_DNA(get_blood_dna_list())
	stool.reagents.remove_all(INFINITY)
	for(var/obj/item/organ/intestines/intestine as anything in intestines)
		intestine.reagents.trans_to(stool, 40, preserve_data = TRUE)
	var/obj/structure/toilet/toiler = locate() in get_turf(src)
	if(toiler)
		//mess gone
		qdel(stool)
		visible_message(span_notice("[src] shits on [toiler]."), \
					span_notice("I take a shit on [toiler]."))
	else
		visible_message(span_notice("[src] shits on [loc]."), \
					span_notice("I take a shit on [loc]."))
	playsound(get_turf(src), 'modular_septic/sound/effects/poo.ogg', 75)

/mob/living/carbon/proc/piss(intentional = FALSE)
	var/list/bladders = getorganslotlist(ORGAN_SLOT_BLADDER)
	var/bladderal_efficiency = getorganslotefficiency(ORGAN_SLOT_BLADDER)
	if(!length(bladders))
		if(intentional)
			to_chat(src, span_warning("I have no bladders."))
		return
	if(bladderal_efficiency < ORGAN_FAILING_EFFICIENCY)
		if(intentional)
			to_chat(src, span_warning("My bladder dilates in agonizing pain as it tries to piss it's contents out!"))
		return
	var/collective_piss_amount = 0
	for(var/obj/item/organ/bladder/bladder as anything in bladders)
		collective_piss_amount += bladder.reagents.get_reagent_amount(/datum/reagent/consumable/piss)
	if(collective_piss_amount < 15)
		if(intentional)
			to_chat(src, span_warning("I don't need to piss."))
		return
	var/turf/pissed = get_turf(src)
	//try to evacuate 40u out of each bladder
	var/obj/structure/toilet/toiler = locate() in get_turf(src)
	var/obj/structure/urinal/urinel = locate() in get_turf(src)
	if(urinel)
		visible_message(span_notice("[src] pisses on [urinel]."), \
					span_notice("I take a piss on [urinel]."))
		for(var/obj/item/organ/bladder/bladder as anything in bladders)
			var/amount = min(40, bladder.reagents.get_reagent_amount(/datum/reagent/consumable/piss) - bladder.food_reagents[/datum/reagent/consumable/piss])
			bladder.reagents.remove_reagent(/datum/reagent/consumable/piss, amount)
	else if(toiler)
		visible_message(span_notice("[src] pisses on [toiler]."), \
					span_notice("I take a piss on [toiler]."))
		for(var/obj/item/organ/bladder/bladder as anything in bladders)
			var/amount = min(40, bladder.reagents.get_reagent_amount(/datum/reagent/consumable/piss) - bladder.food_reagents[/datum/reagent/consumable/piss])
			bladder.reagents.remove_reagent(/datum/reagent/consumable/piss, amount)
	else
		for(var/obj/item/organ/bladder/bladder as anything in bladders)
			var/amount = min(40, bladder.reagents.get_reagent_amount(/datum/reagent/consumable/piss) - bladder.food_reagents[/datum/reagent/consumable/piss])
			bladder.reagents.remove_reagent(/datum/reagent/consumable/piss, amount)
			pissed.add_liquid(/datum/reagent/consumable/piss, amount)
		visible_message(span_notice("[src] pisses on [pissed]."), \
					span_notice("I take a piss on [pissed]."))
	playsound(get_turf(src), 'modular_septic/sound/effects/pee.ogg', 75)

/mob/living/carbon/proc/defecate()
	set name = "Defecate"
	set category = "IC.Hygiene"
	set desc = "Answer the call of nature."

	shit(TRUE)

/mob/living/carbon/proc/urinate()
	set name = "Urinate"
	set category = "IC.Hygiene"
	set desc = "Answer the call of nature."

	piss(TRUE)


code:
//Force mob to rest, does NOT do stamina damage.
//It's really not recommended to use this proc to give feedback, hence why silent is defaulting to true.
/mob/living/carbon/KnockToFloor(knockdown_amt = 1, ignore_canknockdown = FALSE, silent = TRUE)
	if(!silent && body_position != LYING_DOWN)
		to_chat(src, span_warning("You are knocked to the floor!"))
	Knockdown(knockdown_amt, ignore_canknockdown)

/mob/living/carbon/CombatKnockdown(stamina_damage, knockdown_amount, paralyze_amount, disarm = FALSE, ignore_canknockdown = FALSE)
	if(!stamina_damage && !knockdown_amount && !paralyze_amount)
		return
	if(!ignore_canknockdown && !(status_flags & CANKNOCKDOWN))
		return FALSE
	if(istype(buckled, /obj/vehicle/ridden)) //this is dumb...
		buckled.unbuckle_mob(src)
	if(isnull(knockdown_amount))
		knockdown_amount = stamina_damage
	KnockToFloor(max(1, knockdown_amount), ignore_canknockdown)
	adjustStaminaLoss(stamina_damage)
	if(disarm)
		drop_all_held_items()
	if(paralyze_amount)
		Paralyze(paralyze_amount)


code:
// I absolutely HATE this but i could not get this to work with defines, because i don't know how to get
// a return from a fucking define - i gave up and turned all of the defines into procs too
/mob/living/proc/increase_chem_effect(chem_effect, power, source)
	if(power < 0)
		return decrease_chem_effect(chem_effect, abs(power), source)
	else if(!(LAZYACCESSASSOC(chem_effects, chem_effect, source)))
		return add_chem_effect(chem_effect, power, source)
	chem_effects[chem_effect][source] += power
	SEND_SIGNAL(src, SIGNAL_INCREASECHEMEFFECT(chem_effect), power, source)

/mob/living/proc/decrease_chem_effect(chem_effect, power, source)
	if(power < 0)
		return increase_chem_effect(chem_effect, abs(power), source)
	else if(!(LAZYACCESSASSOC(chem_effects, chem_effect, source)))
		return remove_chem_effect(chem_effect, source)
	chem_effects[chem_effect][source] -= power
	SEND_SIGNAL(src, SIGNAL_DECREASECHEMEFFECT(chem_effect), power, source)

/mob/living/proc/add_chem_effect(chem_effect, power, source)
	if(!chem_effects)
		chem_effects = list()
		chem_effects[chem_effect] = list()
		chem_effects[source] = power
		SEND_SIGNAL(src, SIGNAL_ADDCHEMEFFECT(chem_effect), power, source)
	else
		if(chem_effects[chem_effect])
			chem_effects[chem_effect][source] = power
		else
			chem_effects[chem_effect] = list()
			chem_effects[chem_effect][source] = power
			SEND_SIGNAL(src, SIGNAL_ADDCHEMEFFECT(chem_effect), power, source)

/mob/living/proc/remove_chem_effect(chem_effect, sources)
	var/list/srcs
	if(sources && !islist(sources))
		srcs = list(sources)
	else
		srcs = sources
	if(LAZYACCESS(chem_effects, chem_effect))
		for(var/source in chem_effects[chem_effect])
			if(!srcs || (source in srcs))
				chem_effects[chem_effect] -= source
		if(!length(chem_effects[chem_effect]))
			chem_effects -= chem_effect
			SEND_SIGNAL(src, SIGNAL_REMOVECHEMEFFECT(chem_effect), sources)
		if(!length(chem_effects))
			chem_effects = null

/mob/living/proc/get_chem_effect(chem_effect)
	. = 0
	if(LAZYACCESS(chem_effects, chem_effect))
		for(var/source in chem_effects[chem_effect])
			. += chem_effects[chem_effect][source]

/mob/living/proc/get_chem_effect_from(chem_effect, list/sources)
	. = 0
	if(!islist(sources))
		sources = list(sources)
	if(LAZYACCESS(chem_effects, chem_effect))
		for(var/source in chem_effects[chem_effect])
			if(!(source in sources))
				continue
			. += chem_effects[chem_effect][source]


code:
/mob/living
	/// List of fatigue modifiers applying to this mob
	var/list/fatigue_modification //Lazy list, see fatigue_modifier.dm
	/// List of fatigue modifiers ignored by this mob. List -> List (id) -> List (sources)
	var/list/fatigue_mod_immunities //Lazy list, see fatigue_modifier.dm
	var/max_fatigue = DEFAULT_MAX_FATIGUE
	var/base_max_fatigue = DEFAULT_MAX_FATIGUE
	var/fatigue = DEFAULT_MAX_FATIGUE
	COOLDOWN_DECLARE(fatigue_cooldown)

/// Go through the list of fatigue modifiers and calculate a final max_fatigue. ANY ADD/REMOVE DONE IN UPDATE_MOVESPEED MUST HAVE THE UPDATE ARGUMENT SET AS FALSE!
/mob/living/proc/update_fatigue()
	. = base_max_fatigue
	var/list/conflict_tracker = list()
	for(var/key in get_fatigue_modifiers())
		var/datum/fatigue_modifier/M = fatigue_modification[key]
		var/conflict = M.conflicts_with
		if(conflict)
			// Conflicting modifiers prioritize the ghighest priority
			if(conflict_tracker[conflict] < M.priority)
				conflict_tracker[conflict] = M.priority
			else
				continue
		. += M.fatigue_add
	max_fatigue = .
	fatigue = clamp(fatigue, FATIGUE_MINIMUM, max_fatigue)

/// Life() processing of fatigue
/mob/living/proc/handle_fatigue(delta_time, times_fired)
	return

/mob/living/proc/getFatigueLoss()
	return max(0, base_max_fatigue - fatigue)

/mob/living/proc/adjustFatigueLoss(amount, updating_health = TRUE, forced = FALSE)
	if(!forced && (status_flags & GODMODE))
		return FALSE
	fatigue = clamp(fatigue - amount, FATIGUE_MINIMUM, max_fatigue)
	if(amount > 0)
		COOLDOWN_START(src, fatigue_cooldown, FATIGUE_REGEN_COOLDOWN)
	if(updating_health)
		update_stamina()
	return amount

/mob/living/proc/setFatigueLoss(amount, updating_health = TRUE, forced = FALSE)
	var/prev_fatigue = fatigue
	fatigue = clamp(base_max_fatigue - amount, FATIGUE_MINIMUM, max_fatigue)
	if((fatigue - prev_fatigue) < 0)
		COOLDOWN_START(src, fatigue_cooldown, FATIGUE_REGEN_COOLDOWN)
	if(updating_health)
		update_stamina()
	return amount


code:
/datum/fatigue_modifier
	/// Text to show to the player on examining fatigue button
	var/desc = span_danger("Something is fucked.")

	/// Whether or not this is a variable modifier. Variable modifiers can NOT be ever auto-cached. ONLY CHECKED VIA INITIAL(), EFFECTIVELY READ ONLY (and for very good reason)
	var/variable = FALSE

	/// Unique ID. You can never have different modifications with the same ID. By default, this SHOULD NOT be set. Only set it for cases where you're dynamically making modifiers/need to have two types overwrite each other. If unset, uses path (converted to text) as ID.
	var/id

	/// Higher ones override lower priorities. This is NOT used for ID, ID must be unique, if it isn't unique the newer one overwrites automatically if overriding.
	var/priority = 0
	var/flags = NONE

	/// How much we add/remove from max_fatigue
	var/fatigue_add = 0

	/// Other modification datums this conflicts with.
	var/conflicts_with

/datum/fatigue_modifier/New()
	. = ..()
	if(!id)
		id = "[type]" //We turn the path into a string.

/// Grabs a STATIC MODIFIER datum from cache. YOU MUST NEVER EDIT THESE DATUMS, OR IT WILL AFFECT ANYTHING ELSE USING IT TOO!
/proc/get_cached_fatigue_modifier(modtype)
	if(!ispath(modtype, /datum/fatigue_modifier))
		CRASH("[modtype] is not a movespeed modification typepath.")
	var/datum/fatigue_modifier/M = modtype
	if(initial(M.variable))
		CRASH("[modtype] is a variable modifier, and can never be cached.")
	M = GLOB.fatigue_modification_cache[modtype]
	if(!M)
		M = GLOB.fatigue_modification_cache[modtype] = new modtype
	return M

///Add a fatigue modifier to a mob. If a variable subtype is passed in as the first argument, it will make a new datum. If ID conflicts, it will overwrite the old ID.
/mob/living/proc/add_fatigue_modifier(datum/fatigue_modifier/type_or_datum, update = TRUE)
	if(ispath(type_or_datum))
		if(!initial(type_or_datum.variable))
			type_or_datum = get_cached_fatigue_modifier(type_or_datum)
		else
			type_or_datum = new type_or_datum
	var/datum/fatigue_modifier/existing = LAZYACCESS(fatigue_modification, type_or_datum.id)
	if(existing)
		if(existing == type_or_datum) //same thing don't need to touch
			return TRUE
		remove_fatigue_modifier(existing, FALSE)
	if(length(fatigue_modification))
		BINARY_INSERT(type_or_datum.id, fatigue_modification, /datum/fatigue_modifier, type_or_datum, priority, COMPARE_VALUE)
	LAZYSET(fatigue_modification, type_or_datum.id, type_or_datum)
	if(update)
		update_stamina()
	return TRUE

/// Remove a move speed modifier from a mob, whether static or variable.
/mob/living/proc/remove_fatigue_modifier(datum/fatigue_modifier/type_id_datum, update = TRUE)
	var/key
	if(ispath(type_id_datum))
		key = initial(type_id_datum.id) || "[type_id_datum]" //id if set, path set to string if not.
	else if(!istext(type_id_datum)) //if it isn't text it has to be a datum, as it isn't a type.
		key = type_id_datum.id
	else //assume it's an id
		key = type_id_datum
	if(!LAZYACCESS(fatigue_modification, key))
		return FALSE
	LAZYREMOVE(fatigue_modification, key)
	if(update)
		update_stamina()
	return TRUE

/*! Used for variable slowdowns like hunger/health loss/etc, works somewhat like the old list-based modification adds. Returns the modifier datum if successful
	How this SHOULD work is:
	1. Ensures type_id_datum one way or another refers to a /variable datum. This makes sure it can't be cached. This includes if it's already in the modification list.
	2. Instantiate a new datum if type_id_datum isn't already instantiated + in the list, using the type. Obviously, wouldn't work for ID only.
	3. Add the datum if necessary using the regular add proc
	4. If any of the rest of the args are not null (see: multiplicative slowdown), modify the datum
	5. Update if necessary
*/
/mob/living/proc/add_or_update_variable_fatigue_modifier(datum/fatigue_modifier/type_id_datum, update = TRUE, fatigue_add)
	var/modified = FALSE
	var/inject = FALSE
	var/datum/fatigue_modifier/final
	if(istext(type_id_datum))
		final = LAZYACCESS(fatigue_modification, type_id_datum)
		if(!final)
			CRASH("Couldn't find existing modification when provided a text ID.")
	else if(ispath(type_id_datum))
		if(!initial(type_id_datum.variable))
			CRASH("Not a variable modifier")
		final = LAZYACCESS(fatigue_modification, initial(type_id_datum.id) || "[type_id_datum]")
		if(!final)
			final = new type_id_datum
			inject = TRUE
			modified = TRUE
	else
		if(!initial(type_id_datum.variable))
			CRASH("Not a variable modifier")
		final = type_id_datum
		if(!LAZYACCESS(fatigue_modification, final.id))
			inject = TRUE
			modified = TRUE
	if(!isnull(fatigue_add))
		final.fatigue_add = fatigue_add
		modified = TRUE
	if(inject)
		add_fatigue_modifier(final, FALSE)
	if(update && modified)
		update_stamina()
	return final

///Is there a movespeed modifier for this mob
/mob/living/proc/has_fatigue_modifier(datum/fatigue_modifier/datum_type_id)
	var/key
	if(ispath(datum_type_id))
		key = initial(datum_type_id.id) || "[datum_type_id]"
	else if(istext(datum_type_id))
		key = datum_type_id
	else
		key = datum_type_id.id
	return LAZYACCESS(fatigue_modification, key)

/// Get the fatigue modifiers list of the mob
/mob/living/proc/get_fatigue_modifiers()
	. = LAZYCOPY(fatigue_modification)
	for(var/id in fatigue_mod_immunities)
		. -= id

/// Calculate the total max_fatigue reduction of all fatigue modifiers
/mob/living/proc/total_fatigue_reduction()
	. = 0
	for(var/id in get_fatigue_modifiers())
		var/datum/fatigue_modifier/M = fatigue_modification[id]
		. += M.fatigue_add

/// Checks if a move speed modifier is valid and not missing any data
/proc/fatigue_data_null_check(datum/fatigue_modifier/M) //Determines if a data list is not meaningful and should be discarded.
	. = TRUE
	if(M.fatigue_add)
		. = FALSE


code:
/datum/fatigue_modifier/weight
	variable = TRUE
	desc = "I am quite encumbered."


code:
// Mostly pain related stuff here
// Important note about painloss: Simplemobs don't feel pain, so instead they should receive 0.5 pain as brute damage

/// Get a sum of all physical damage, except pain and stamina
/mob/living/proc/get_physical_damage()
	return round(getBruteLoss() + getFireLoss() + getToxLoss() + getCloneLoss() + getOxyLoss(), DAMAGE_PRECISION)

/mob/living/proc/getPainLoss()
	return painloss

/mob/living/proc/adjustPainLoss(amount, updating_health = TRUE, forced = FALSE)
	if(!forced && (status_flags & GODMODE))
		return
	. = painloss
	painloss = clamp((painloss + (amount * CONFIG_GET(number/damage_multiplier))), 0, maxHealth * 2)
	if(updating_health)
		updatehealth()

/mob/living/proc/setPainLoss(amount, updating_health = TRUE, forced = FALSE)
	if(!forced && status_flags & GODMODE)
		return
	. = painloss
	painloss = amount
	if(updating_health)
		updatehealth()

/mob/living/proc/getShock(painkiller_included = TRUE)
	return traumatic_shock

/mob/living/proc/getShockStage()
	return shock_stage

/mob/living/proc/adjustShockStage(amount, updating_health = TRUE, forced = FALSE)
	if(!forced && (status_flags & GODMODE))
		return
	. = shock_stage
	shock_stage = clamp((shock_stage + (amount * CONFIG_GET(number/damage_multiplier))), 0, SHOCK_STAGE_MAX)
	if(updating_health)
		updatehealth()

/mob/living/proc/setShockStage(amount, updating_health = TRUE, forced = FALSE)
	if(!forced && status_flags & GODMODE)
		return
	. = painloss
	shock_stage = amount
	if(updating_health)
		updatehealth()

/mob/living/proc/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = NONE)
	SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone)
	var/hit_percent = (100-blocked)/100
	if(!damage || (!forced && hit_percent <= 0))
		return FALSE
	var/damage_amount =  forced ? damage : damage * hit_percent
	switch(damagetype)
		if(BRUTE)
			adjustBruteLoss(damage_amount, forced = forced)
		if(BURN)
			adjustFireLoss(damage_amount, forced = forced)
		if(TOX)
			adjustToxLoss(damage_amount, forced = forced)
		if(OXY)
			adjustOxyLoss(damage_amount, forced = forced)
		if(CLONE)
			adjustCloneLoss(damage_amount, forced = forced)
		if(STAMINA)
			adjustStaminaLoss(damage_amount, forced = forced)
		if(PAIN, SHOCK_PAIN)
			adjustPainLoss(damage_amount, forced = forced)
		if(SHOCK_STAGE)
			adjustShockStage(damage_amount, forced = forced)
	return TRUE

/mob/living/proc/apply_damage_type(damage = 0, damagetype = BRUTE)
	switch(damagetype)
		if(BRUTE)
			return adjustBruteLoss(damage)
		if(BURN)
			return adjustFireLoss(damage)
		if(TOX)
			return adjustToxLoss(damage)
		if(OXY)
			return adjustOxyLoss(damage)
		if(CLONE)
			return adjustCloneLoss(damage)
		if(STAMINA)
			return adjustStaminaLoss(damage)
		if(PAIN, SHOCK_PAIN)
			return adjustPainLoss(damage)
		if(SHOCK_STAGE)
			return adjustShockStage(damage)

/mob/living/proc/get_damage_amount(damagetype = BRUTE)
	switch(damagetype)
		if(BRUTE)
			return getBruteLoss()
		if(BURN)
			return getFireLoss()
		if(TOX)
			return getToxLoss()
		if(OXY)
			return getOxyLoss()
		if(CLONE)
			return getCloneLoss()
		if(STAMINA)
			return getStaminaLoss()
		if(PAIN)
			return getPainLoss()
		if(SHOCK_PAIN)
			return getShock()
		if(SHOCK_STAGE)
			return getShockStage()


code:
// Tilt
/datum/emote/living/tilt
	key = "tilt"
	key_third_person = "tilts"
	message = "tilts their head."

// Clap
/datum/emote/living/carbon/clap/get_sound(mob/living/user)
	return "modular_septic/sound/emotes/clap[rand(1,2)].ogg"

// Sneeze
/datum/emote/living/sneeze/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/sneeze_male[rand(1,3)].ogg"
		else
			return "modular_septic/sound/emotes/sneeze_female[rand(1,2)].ogg"
	else
		return ..()

// Sniffing
/datum/emote/living/sniff/run_emote(mob/user, params, type_override, intentional)
	. = ..()
	if(.)
		var/turf/open/current_turf = get_turf(user)
		if(istype(current_turf) && current_turf.pollution)
			if(iscarbon(user))
				var/mob/living/carbon/carbon_user = user
				if(carbon_user.internal) //Breathing from internals means we cant smell
					return
				carbon_user.next_smell = world.time + SMELL_COOLDOWN
			current_turf.pollution.smell_act(user)

// Sigh
/datum/emote/living/sigh/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/sigh_male[rand(1,2)].ogg"
		else
			return "modular_septic/sound/emotes/sigh_female[rand(1,2)].ogg"
	else
		return ..()

// Snore
/datum/emote/living/carbon/clap/get_sound(mob/living/user)
	if(ishuman(src))
		return "modular_septic/sound/emotes/snore[rand(1,7)].ogg"
	else
		return ..()

// Yawn
/datum/emote/living/yawn/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/yawn_male[rand(1,2)].ogg"
		else
			return "modular_septic/sound/emotes/yawn_female[rand(1,3)].ogg"
	else
		return ..()

// Laughing sound
/datum/emote/living/laugh/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/laugh_male[rand(1,5)].ogg"
		else
			return "modular_septic/sound/emotes/laugh_female[rand(1,5)].ogg"
	else
		return ..()

// Normal screaming
/datum/emote/living/scream/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/terror_scream_male[rand(1,6)].ogg"
		else
			return "modular_septic/sound/emotes/terror_scream_female[rand(1,7)].ogg"
	else
		return ..()

// Falling down screaming
/datum/emote/living/fallscream
	key = "fallscream"
	key_third_person = "fallscreams"
	message = "screams!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = FALSE
	hands_use_check = FALSE

/datum/emote/living/fallscream/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/falling_down_male[rand(1,3)].ogg"
		else
			return "modular_septic/sound/emotes/falling_down_female1.ogg"
	else
		return ..()

// Agony screaming
/datum/emote/living/agonyscream
	key = "agonyscream"
	key_third_person = "screamsinagony"
	message = "screams in agony!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = FALSE
	hands_use_check = FALSE

/datum/emote/living/agonyscream/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.on_fire)
			if(user.gender != FEMALE)
				return "modular_septic/sound/emotes/agony_male[rand(1,15)].ogg"
			else
				return "modular_septic/sound/emotes/agony_female[rand(1,8)].ogg"
		else
			if(user.gender != FEMALE)
				return "modular_septic/sound/emotes/pain_scream_male[rand(1,3)].ogg"
			else
				return "modular_septic/sound/emotes/pain_scream_female[rand(1,7)].ogg"
	else
		return ..()

// Death scream - I swear this aint the same as death rattle
/datum/emote/living/deathscream
	key = "deathscream"
	key_third_person = "deathscreams"
	message = "screams!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = FALSE
	hands_use_check = FALSE

/datum/emote/living/deathscream/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/death_scream_male[rand(1,5)].ogg"
		else
			return "modular_septic/sound/emotes/death_scream_female[rand(1,3)].ogg"
	else
		return ..()

// Death rattle, kinda like deathgasp i guess
/datum/emote/living/deathrattle
	key = "deathrattle"
	key_third_person = "deathrattles"
	message = "makes a disgusting noise."
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

/datum/emote/living/deathrattle/get_sound(mob/living/user)
	if(ishuman(user))
		return "modular_septic/sound/emotes/deathgasp.ogg"
	else
		return ..()

/datum/emote/living/deathgasp
	message = "makes a disgusting noise."

// Fuck it deathgasp does the same sound
/datum/emote/living/deathgasp/get_sound(mob/living/user)
	if(ishuman(user))
		return "modular_septic/sound/emotes/deathgasp.ogg"
	else
		return ..()

// Grunting, moaning, and groaning all make the same sound
/datum/emote/living/grunt
	key = "grunt"
	key_third_person = "grunts"
	message = "grunts."
	emote_type = EMOTE_AUDIBLE

/datum/emote/living/grunt/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/moan_male[rand(1, 6)].ogg"
		else
			return "modular_septic/sound/emotes/moan_female[rand(1, 8)].ogg"
	else
		return ..()

/datum/emote/living/groan/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/moan_male[rand(1, 6)].ogg"
		else
			return "modular_septic/sound/emotes/moan_female[rand(1, 8)].ogg"
	else
		return ..()

/datum/emote/living/carbon/moan/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/moan_male[rand(1, 6)].ogg"
		else
			return "modular_septic/sound/emotes/moan_female[rand(1, 8)].ogg"
	else
		return ..()

// Gasping
/datum/emote/living/gasp/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/gasp_male[rand(1, 7)].ogg"
		else
			return "modular_septic/sound/emotes/gasp_female[rand(1, 13)].ogg"
	else
		return ..()

// Throat gargling
/datum/emote/living/gargle
	key = "gargle"
	key_third_person = "gargles"
	message = "gargles their throat!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

/datum/emote/living/gargle/get_sound(mob/living/user)
	if(ishuman(user))
		return "modular_septic/sound/emotes/throat[rand(1, 3)].ogg"
	else
		return ..()

// Crying
/datum/emote/living/whimper/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/whimper_male[rand(1, 3)].ogg"
		else
			return "modular_septic/sound/emotes/whimper_female[rand(1, 3)].ogg"
	else
		return ..()

/datum/emote/living/carbon/human/cry/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/cry_male[rand(1, 4)].ogg"
		else
			return "modular_septic/sound/emotes/cry_female[rand(1, 6)].ogg"
	else
		return ..()

// Coughing makes a sound too
/datum/emote/living/cough/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/cough_male[rand(1, 16)].ogg"
		else
			return "modular_septic/sound/emotes/gasp_male[rand(1, 12)].ogg"
	else
		return ..()

// Sagging i guess
/datum/emote/living/sag
	key = "sag"
	key_third_person = "sags"
	message = "sag%s on the floor, they won't regain their conciousness soon."
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

/datum/emote/living/sag/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/deathgasp.ogg"
		else
			return "modular_septic/sound/emotes/deathgasp.ogg"
	else
		return ..()

// These are for when your lungs are fucked and you try to audibly emote
/datum/emote/living/quietnoise
	key = "quietnoise"
	key_third_person = "quietnoises"
	message = "makes a quiet noise."
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

/datum/emote/living/loudnoise
	key = "loudnoise"
	key_third_person = "loudnoises"
	message = "makes a loud noise!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

// Oooo sex
/datum/emote/living/sexymoan
	key = "sexymoan"
	key_third_person = "sexymoans"
	message = "moans!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE
	mob_type_allowed_typecache = /mob/living/carbon/human

/datum/emote/living/sexymoan/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/sexo/moan_m[rand(1, 7)].ogg"
		else
			return "modular_septic/sound/sexo/moan_f[rand(1, 7)].ogg"
	else
		return ..()

/datum/emote/living/sexymoan/run_emote(mob/living/carbon/human/user, params, type_override, intentional)
	. = TRUE
	if(!can_run_emote(user, TRUE, intentional))
		return FALSE
	if(!intentional && !prob(user.lust))
		return FALSE
	var/msg = select_message_type(user, message, intentional)
	if(params && message_param)
		msg = select_param(user, params)

	msg = replace_pronoun(user, msg)

	if(!msg)
		return

	user.log_message(msg, LOG_EMOTE)
	var/dchatmsg = span_horny("[user] [pick("moans", "moans in pleasure")]")

	var/tmp_sound = get_sound(user)
	if(tmp_sound && (!only_forced_audio || !intentional) && !TIMER_COOLDOWN_CHECK(user, type))
		TIMER_COOLDOWN_START(user, type, audio_cooldown)
		playsound(user, tmp_sound, 50, vary)

	var/user_turf = get_turf(user)
	for(var/mob/ghost in GLOB.dead_mob_list)
		if(!ghost.client || isnewplayer(ghost))
			continue
		if(ghost.stat == DEAD && ghost.client && user.client && (ghost.client.prefs.chat_toggles & CHAT_GHOSTSIGHT) && !(ghost in viewers(user_turf, null)))
			ghost.show_message("[FOLLOW_LINK(ghost, user)] [dchatmsg]")

	if(emote_type == EMOTE_AUDIBLE)
		user.audible_message(span_horny("[user] [msg]"))
	else
		user.visible_message(span_horny("[user] [msg]"))

// Le quake jump has arrive
/datum/emote/living/jumpgrunt
	key = "jumpgrunt"
	key_third_person = "jumpgrunts"
	message = "grunts!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

/datum/emote/living/jumpgrunt/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/jump_male[rand(1,2)].ogg"
		else
			return "modular_septic/sound/emotes/jump_female1.ogg"
	else
		return ..()

// Fatigue emote
/datum/emote/living/fatiguegrunt
	key = "fatiguegrunt"
	key_third_person = "fatiguegrunt"
	message = "grunts!"
	emote_type = EMOTE_AUDIBLE
	muzzle_ignore = TRUE
	hands_use_check = FALSE

/datum/emote/living/fatiguegrunt/get_sound(mob/living/user)
	if(ishuman(user))
		if(user.gender != FEMALE)
			return "modular_septic/sound/emotes/fatigue_male[rand(1, 4)].ogg"
		else
			return "modular_septic/sound/emotes/fatigue_female[rand(1,3)].ogg"
	else
		return ..()


code:
// scream for more
/mob/living/proc/agony_scream()
	return

// i have fallen and i cant get up
/mob/living/proc/fall_scream()
	return

// death scream
/mob/living/proc/death_scream()
	return

// gargle
/mob/living/proc/agony_gargle()
	return

// gasp
/mob/living/proc/agony_gasp()
	return

// death rattle
/mob/living/proc/death_rattle()
	return

// sexy moan
/mob/living/proc/sexy_moan()
	return

// jump grunt
/mob/living/proc/jump_grunt()
	return

// fatigue grunt
/mob/living/proc/fatigue_grunt()
	return


code:
/// Things that happen when we examine an atom, duh
/mob/living/on_examine_atom(atom/examined, examine_more = FALSE)
	if(!client)
		return

	if((src != examined) || (!DirectAccess(examined) && get_dist(src, examined) > EYE_CONTACT_RANGE) || (stat >= UNCONSCIOUS) || is_blind())
		return

	if(!HAS_TRAIT(src, TRAIT_FLUORIDE_STARE))
		if(!ismob(examined))
			visible_message(span_emote(span_notice("[src] looks at [examined].")), \
							span_notice("I look at [examined]."), \
							vision_distance = EYE_CONTACT_RANGE)
		else
			var/mob/mob_examined = examined
			visible_message(span_emote(span_notice("[src] looks at \
							[examined].")), \
							span_notice("I look at [examined]."), \
							vision_distance = EYE_CONTACT_RANGE)
	else
		if(!ismob(examined))
			visible_message(span_emote(span_notice("[src] fluoride stares [examined].")), \
						span_notice("I fluoride stare [examined]."), \
						vision_distance = EYE_CONTACT_RANGE)
		else
			var/mob/mob_examined = examined
			visible_message(span_emote(span_notice("[src] fluoride stares \
						[examined].")), \
						span_notice("I fluoride stare [examined]."), \
						vision_distance = EYE_CONTACT_RANGE)


code:
/mob/living/carbon/attempt_self_grab()
	var/obj/item/bodypart/hand = get_active_hand()
	if(hand && (zone_selected in list(hand.body_zone, hand.parent_body_zone)))
		to_chat(src, span_warning("I can't grab my [parse_zone(zone_selected)] with my [hand]!"))
		return
	return grippedby(src, TRUE)

/mob/living/carbon/grippedby(mob/living/carbon/user, instant = FALSE)
	// We need to be pulled man
	if(src != user)
		if(!user.pulling || user.pulling != src)
			return
	var/obj/item/grab/active_grab = user.get_active_held_item()
	if(active_grab)
		if(istype(active_grab))
			to_chat(user, span_warning("I'm already grabbing something!"))
		else
			to_chat(user, span_warning("My hand is busy!"))
		return
	var/obj/item/bodypart/affected = get_bodypart_nostump(check_zone(user.zone_selected))
	if(!affected)
		to_chat(user, span_warning("[p_they(TRUE)] do[p_es()]n't have a [parse_zone(user.zone_selected)]!"))
		return
	var/hit_modifier = affected.hit_modifier
	//easy to kick people when they are down
	if(body_position == LYING_DOWN)
		hit_modifier += 6
	//very hard to miss when hidden by fov
	if(!(src in fov_viewers(2, user)))
		hit_modifier += 4
	//epic grab fail
	if((user != src) && !user.diceroll(GET_MOB_ATTRIBUTE_VALUE(user, STAT_STRENGTH)-affected.hit_modifier))
		user.visible_message(span_warning("[user] tries to grab [src]!"), \
				span_userdanger("I fail to grab [src]!"), \
				blind_message = span_hear("I hear some loud shuffling!"), \
				ignored_mobs = src)
		to_chat(src, span_userdanger("[user] tries to grab me!"))
		user.changeNext_move(CLICK_CD_GRABBING)
		return FALSE
	active_grab = new()
	user.put_in_active_hand(active_grab, FALSE)
	if(QDELETED(active_grab))
		return
	user.changeNext_move(CLICK_CD_GRABBING)
	active_grab.registergrab(src, user, affected, instant)
	active_grab.create_hud_object()
	active_grab.update_grab_mode()
	active_grab.display_grab_message()

/mob/living/carbon/throw_item(atom/target)
	. = ..()
	if(!target || !isturf(loc))
		return
	if(istype(target, /atom/movable/screen))
		return
	throw_mode_off(THROW_MODE_TOGGLE)

	var/atom/movable/thrown_thing
	var/was_pulling = pulling
	var/was_grab_state = grab_state
	var/obj/item/I = get_active_held_item()

	if(!I)
		if(pulling && isliving(pulling) && grab_state >= GRAB_AGGRESSIVE)
			var/mob/living/throwable_mob = pulling
			if(!throwable_mob.buckled)
				thrown_thing = throwable_mob
				stop_pulling()
				if(HAS_TRAIT(src, TRAIT_PACIFISM))
					to_chat(src, span_notice("I gently let go of [throwable_mob]."))
					return
	else
		thrown_thing = I.on_thrown(src, target)

	if(!thrown_thing)
		return

	if(isliving(thrown_thing))
		stop_pulling()
		var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
		var/turf/end_T = get_turf(target)
		if(start_T && end_T)
			log_combat(src, thrown_thing, "thrown", addition="grab from tile in [AREACOORD(start_T)] towards tile at [AREACOORD(end_T)]")
	var/power_throw = 0
	if(HAS_TRAIT(src, TRAIT_HULK))
		power_throw++
	if(HAS_TRAIT(src, TRAIT_DWARF))
		power_throw--
	if(HAS_TRAIT(thrown_thing, TRAIT_DWARF))
		power_throw++
	if((thrown_thing == was_pulling) && (was_grab_state >= GRAB_NECK))
		power_throw++
	if(GET_MOB_ATTRIBUTE_VALUE(src, STAT_STRENGTH) >= (ATTRIBUTE_MIDDLING * 1.2))
		power_throw++
	else if(GET_MOB_ATTRIBUTE_VALUE(src, STAT_STRENGTH) <= (ATTRIBUTE_MIDDLING * 0.8))
		power_throw--
	if(GET_MOB_ATTRIBUTE_VALUE(src, SKILL_THROWING) >= SKILL_MIDDLING)
		power_throw++
	var/parsed_target = target.name
	if(get_dist(src, target) > world.view)
		var/angle = get_angle(src, target)
		var/turf/new_target = get_turf_in_angle(angle, get_turf(src), world.view)
		parsed_target = new_target.name
	if(ismob(thrown_thing))
		visible_message(span_danger("[src] throws [thrown_thing] at [parsed_target][power_throw ? " really hard!" : "."]"), \
						span_userdanger("I throw [thrown_thing] at [parsed_target][power_throw ? " really hard!" : "."]"), \
						ignored_mobs = thrown_thing)
		to_chat(thrown_thing, span_userdanger("[src] throws me really hard!"))
	else
		visible_message(span_danger("[src] throws [thrown_thing] at [parsed_target][power_throw ? " really hard!" : "."]"), \
						span_userdanger("I throw [thrown_thing] at [parsed_target][power_throw ? " really hard!" : "."]"))
	log_message("has thrown [thrown_thing] at [parsed_target][power_throw ? " really hard" : ""]", LOG_ATTACK)
	sound_hint()
	newtonian_move(get_dir(target, src))
	thrown_thing.safe_throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed + power_throw, src, force = move_force)


code:
/client/Process_Grab()
	if(!mob.pulledby)
		return FALSE
	if(mob.pulledby == mob.pulling && mob.pulledby.grab_state == GRAB_PASSIVE) //Don't autoresist passive grabs if we're grabbing them too.
		return FALSE
	if(mob.incapacitated(ignore_restraints = TRUE))
		COOLDOWN_START(src, move_delay, 1 SECONDS)
		return TRUE
	else if(HAS_TRAIT(mob, TRAIT_RESTRAINED))
		COOLDOWN_START(src, move_delay, 1 SECONDS)
		to_chat(src, span_warning("You're restrained! You can't move!"))
		return TRUE
	return mob.resist_grab(TRUE)


code:
/atom/movable/screen/grab
	name = "grab"
	icon = 'modular_septic/icons/hud/quake/grab.dmi'
	icon_state = "grab_wrench"
	base_icon_state = "grab"
	layer = MOB_LAYER
	plane = HUD_PLANE
	//Grab item we are allied to
	var/obj/item/grab/parent
	//Strangle active overlay
	var/static/strangle_active_image
	//Takedown active overlay
	var/static/takedown_active_image

/atom/movable/screen/grab/Click(location, control, params)
	if(parent)
		var/list/modifiers = params2list(params)
		var/icon_y = text2num(LAZYACCESS(modifiers, ICON_Y))
		switch(parent.grab_mode)
			if(GM_TEAROFF)
				if(usr.next_move <= world.time)
					if(icon_y <= 16)
						return parent.tear_off_limb()
					else
						return parent.wrench_limb()
			if(GM_EMBEDDED)
				if(usr.next_move <= world.time)
					if(icon_y <= 16)
						return parent.pull_embedded()
					else
						return parent.twist_embedded()
			else
				return usr.ClickOn(parent, params)
	else
		return ..()

/atom/movable/screen/grab/update_name(updates)
	. = ..()
	if(parent)
		name = "grabbing [parent.victim]"
	else
		name = "grab"

/atom/movable/screen/grab/examine(mob/user)
	if(parent)
		return parent.examine(user)
	else
		return ..()

/atom/movable/screen/grab/update_icon_state()
	. = ..()
	if(!parent?.grab_mode)
		icon_state = base_icon_state
	else
		icon_state = "[base_icon_state]_[parent.grab_mode]"

/atom/movable/screen/grab/update_overlays()
	. = ..()
	if(parent?.active)
		switch(parent.grab_mode)
			if(GM_STRANGLE)
				. += image(icon, src, "strangle_active")
			if(GM_TAKEDOWN)
				. += image(icon, src, "takedown_active")


code:
/obj/item/grab/proc/strangle()
	//Wtf?
	if(!grasped_part)
		return FALSE
	//You can't strangle yourself!
	if(owner == victim)
		return FALSE
	//You can't double strangle, sorry!
	var/obj/item/grab/other_grab = owner.get_inactive_held_item()
	if(istype(other_grab) && (other_grab.grab_mode == GM_STRANGLE) && other_grab.active)
		to_chat(owner, span_danger("I'm already strangling [victim.p_them()]!"))
		return FALSE
	//Due to shitcode reasons, i cannot support strangling and taking down simultaneously
	else if(istype(other_grab) && (other_grab.grab_mode == GM_TAKEDOWN) && other_grab.active)
		to_chat(owner, span_danger("I'm too focused on taking [victim.p_them()] down!"))
		return FALSE
	active = !active
	if(!active)
		owner.setGrabState(GRAB_AGGRESSIVE)
		owner.set_pull_offsets(victim, owner.grab_state)
		victim.visible_message(span_danger("[owner] stops strangling [victim]!"), \
						span_userdanger("[owner] stops strangling me!"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
	else
		to_chat(owner, span_danger("I stop strangling [victim]!"))
		owner.setGrabState(GRAB_KILL)
		owner.set_pull_offsets(victim, owner.grab_state)
		victim.visible_message(span_danger("[owner] starts strangling [victim]!"), \
						span_userdanger("[owner] starts strangling me!"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I start strangling [victim]!"))
		actions_done++
	grab_hud?.update_appearance()
	owner.changeNext_move(CLICK_CD_STRANGLE)
	playsound(victim, 'modular_septic/sound/attack/twist.wav', 75, FALSE)
	return TRUE

/obj/item/grab/proc/takedown()
	//Wtf?
	if(!grasped_part)
		return FALSE
	//You can't takedown yourself!
	if(owner == victim)
		return FALSE
	//Only one hand can be the master of puppets!
	var/obj/item/grab/other_grab = owner.get_inactive_held_item()
	if(istype(other_grab) && (other_grab.grab_mode == GM_TAKEDOWN) && other_grab.active)
		to_chat(owner, span_danger("I'm already taking [victim.p_them()] down!"))
		return FALSE
	//Due to shitcode reasons, i cannot support strangling and taking down simultaneously
	else if(istype(other_grab) && (other_grab.grab_mode == GM_STRANGLE) && other_grab.active)
		to_chat(owner, span_danger("I'm too focused on strangling [victim.p_them()]!"))
		return FALSE
	if(active)
		active = FALSE
		owner.setGrabState(GRAB_AGGRESSIVE)
		owner.set_pull_offsets(victim, owner.grab_state)
		victim.visible_message(span_danger("[owner] stops pinning [victim] down!"), \
						span_userdanger("[owner] stops pinning me down!"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I stop pinning [victim] down!"))
	else
		//We need to do a lil' wrenching first!
		if(!istype(other_grab) || (other_grab.actions_done <= 0))
			to_chat(owner, span_danger("I need to subdue them more first!"))
			return FALSE
		active = TRUE
		owner.setGrabState(GRAB_NECK) //don't take GRAB_NECK literally
		owner.set_pull_offsets(victim, owner.grab_state)
		victim.visible_message(span_danger("[owner] starts pinning [victim] down!"), \
						span_userdanger("[owner] starts pinning me down!"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I start pinning [victim] down!"))
		actions_done++
	grab_hud?.update_appearance()
	owner.changeNext_move(CLICK_CD_TAKEDOWN)
	playsound(victim, 'modular_septic/sound/attack/twist.wav', 75, FALSE)
	return TRUE

/obj/item/grab/proc/wrench_limb()
	//God damn fucking simple mobs
	if(!grasped_part)
		return FALSE
	if(IS_HELP_INTENT(owner, null) && grasped_part.is_dislocated())
		if(DOING_INTERACTION_WITH_TARGET(owner, victim))
			return FALSE
		return relocate_limb()
	var/mob/living/carbon/carbon_victim = victim
	var/nonlethal = (!owner.combat_mode && (actions_done <= 0))
	var/epic_success = DICE_FAILURE
	var/modifier = 0
	if(victim.combat_mode && (GET_MOB_ATTRIBUTE_VALUE(victim, STAT_STRENGTH) >= GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH)))
		modifier -= 6
	if(nonlethal)
		epic_success = owner.diceroll(GET_MOB_ATTRIBUTE_VALUE(owner, SKILL_MELEE)+modifier)
	else
		epic_success = owner.diceroll(GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH)+modifier)
	if(epic_success >= DICE_SUCCESS)
		var/wrench_verb = "wrenches"
		if(nonlethal)
			wrench_verb = "twists"
		var/damage = GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH)/2
		var/deal_wound_bonus = 10
		if(epic_success >= DICE_CRIT_SUCCESS)
			deal_wound_bonus += 10
		if(!nonlethal)
			grasped_part.receive_damage(brute = damage, wound_bonus = deal_wound_bonus, sharpness = NONE)
		victim.visible_message(span_danger("[owner] [wrench_verb] [victim]'s [grasped_part.name]![carbon_victim.wound_message]"), \
						span_userdanger("[owner] [wrench_verb] my [grasped_part.name]![carbon_victim.wound_message]"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I [wrench_verb] [victim]'s [grasped_part.name]![carbon_victim.wound_message]"))
		SEND_SIGNAL(carbon_victim, COMSIG_CARBON_CLEAR_WOUND_MESSAGE)
		actions_done++
	else
		var/wrench_verb = "wrench"
		if(nonlethal)
			wrench_verb = "twist"
		victim.visible_message(span_danger("[owner] tries to [wrench_verb] [victim]'s [grasped_part.name]!"), \
						span_userdanger("[owner] tries to [wrench_verb] my [grasped_part.name]!"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I try to [wrench_verb] [victim]'s [grasped_part.name]!"))
	owner.changeNext_move(CLICK_CD_WRENCH)
	playsound(victim, 'modular_septic/sound/attack/twist.wav', 75, FALSE)
	return TRUE

/obj/item/grab/proc/relocate_limb()
	var/mob/living/carbon/carbon_victim = victim
	victim.visible_message(span_danger("[owner] tries to relocate [victim]'s [grasped_part.name]!"), \
					span_userdanger("[owner] tries to relocate my [grasped_part.name]!"), \
					vision_distance = COMBAT_MESSAGE_RANGE, \
					ignored_mobs = owner)
	to_chat(owner, span_userdanger("I try to relocate [victim]'s [grasped_part.name]!"))
	var/time = 12 SECONDS //Worst case scenario
	time -= (GET_MOB_ATTRIBUTE_VALUE(owner, SKILL_MEDICINE)/2 SECONDS)
	if(!do_mob(owner, carbon_victim, time))
		to_chat(owner, span_userdanger("I must stand still!"))
		return
	var/epic_success = DICE_FAILURE
	if(grasped_part.status == BODYPART_ORGANIC)
		epic_success = owner.diceroll(GET_MOB_ATTRIBUTE_VALUE(owner, SKILL_MEDICINE))
	else
		epic_success = owner.diceroll(GET_MOB_ATTRIBUTE_VALUE(owner, SKILL_ELECTRONICS))
	if(epic_success >= DICE_SUCCESS)
		var/damage = GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH)/2
		grasped_part.receive_damage(brute = damage, sharpness = NONE)
		for(var/obj/item/organ/bone/bone as anything in grasped_part.getorganslotlist(ORGAN_SLOT_BONE))
			if(bone.bone_flags & BONE_JOINTED)
				bone.relocate()
		victim.agony_scream()
		victim.visible_message(span_danger("[owner] relocates [victim]'s [grasped_part.name]![carbon_victim.wound_message]"), \
						span_userdanger("[owner] relocates my [grasped_part.name]![carbon_victim.wound_message]"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I relocate [victim]'s [grasped_part.name]![carbon_victim.wound_message]"))
		SEND_SIGNAL(carbon_victim, COMSIG_CARBON_CLEAR_WOUND_MESSAGE)
	else
		var/damage = GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH)/2
		var/deal_wound_bonus = 0
		if(epic_success <= DICE_CRIT_FAILURE)
			deal_wound_bonus += 10
		grasped_part.receive_damage(brute = damage, wound_bonus = deal_wound_bonus, sharpness = NONE)
		victim.visible_message(span_danger("[owner] painfully twists [victim]'s [grasped_part.name]![carbon_victim.wound_message]"), \
						span_userdanger("[owner] painfully twists my [grasped_part.name]![carbon_victim.wound_message]"), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I painfully twist [victim]'s [grasped_part.name]![carbon_victim.wound_message]"))
		SEND_SIGNAL(carbon_victim, COMSIG_CARBON_CLEAR_WOUND_MESSAGE)
	owner.changeNext_move(CLICK_CD_WRENCH)
	playsound(victim, 'modular_septic/sound/attack/twist.wav', 75, FALSE)
	return TRUE

/obj/item/grab/proc/tear_off_limb()
	//God damn fucking simple mobs
	if(!grasped_part)
		return FALSE
	for(var/obj/item/organ/bone/bone in grasped_part)
		if(!(bone.damage >= bone.medium_threshold))
			return FALSE
	var/epic_success = owner.diceroll(GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH))
	if(epic_success >= DICE_SUCCESS)
		victim.visible_message(span_danger("[owner] tears [victim]'s [grasped_part.name] off!"), \
						span_userdanger("[owner] tears my [grasped_part.name] off!"), \
						span_hear("I hear a disgusting sound of flesh being torn apart."), \
						vision_distance = COMBAT_MESSAGE_RANGE, \
						ignored_mobs = owner)
		to_chat(owner, span_userdanger("I tear [victim]'s [grasped_part.name] off!"))
		var/mob/living/victim_will_get_nulled = victim
		var/mob/living/carbon/owner_will_get_nulled = owner
		var/obj/item/bodypart/part_will_get_nulled = grasped_part
		grasped_part.drop_limb(FALSE, TRUE, FALSE, FALSE, WOUND_SLASH)
		//If nothing went bad, we should be qdeleted by now
		owner_will_get_nulled.changeNext_move(CLICK_CD_WRENCH)
		playsound(victim_will_get_nulled, 'modular_septic/sound/gore/tear.ogg', 100, FALSE)
		if(QDELETED(part_will_get_nulled))
			return TRUE
		owner_will_get_nulled.put_in_hands(part_will_get_nulled)
		return TRUE
	else
		return wrench_limb()

/obj/item/grab/proc/twist_embedded()
	//Wtf?
	if(!grasped_part || !LAZYACCESS(grasped_part.embedded_objects, 1))
		return FALSE
	return TRUE

/obj/item/grab/proc/pull_embedded()
	//Wtf?
	if(!grasped_part || !LAZYACCESS(grasped_part.embedded_objects, 1))
		return FALSE
	SEND_SIGNAL(victim, COMSIG_CARBON_EMBED_RIP, grasped_part.embedded_objects[1], grasped_part, owner)
	return TRUE


code:
/obj/item/grab
	name = "grab"
	icon = 'modular_septic/icons/hud/quake/grab.dmi'
	icon_state = "blank"
	base_icon_state = "blank"
	item_flags = DROPDEL | NOBLUDGEON | ABSTRACT | HAND_ITEM
	carry_weight = 0
	/// A typecache of items that should use attack_hand() instead of item attack procs
	var/static/list/attack_hand_typecache
	/// Our screen object, which is actually used for interactions and shit
	var/atom/movable/screen/grab/grab_hud
	/// Our current "mode"
	var/grab_mode = GM_STAUNCH
	/// The body zone we're grabbing - not necessarily equal to the grasped bodypart
	var/grasped_zone
	/// The bodypart we're grabbing
	var/obj/item/bodypart/grasped_part
	/// The mob being grabbed
	var/mob/living/victim
	/// The carbon who owns all of this mess
	var/mob/living/carbon/owner
	/// How much we've twisted/strangled the affected limb
	var/actions_done = 0
	/// Boolean used for strangling and takedowns
	var/active = FALSE

/// Initializing the typecache
/obj/item/grab/Initialize()
	if(!attack_hand_typecache)
		//expand this later
		attack_hand_typecache = typecacheof(/obj/structure/table)
	return ..()

/// No wield grab
/obj/item/grab/get_wield_component()
	return

/// Outline looks weird on grabs
/obj/item/grab/apply_outline(outline_color)
	return

/// Examining (examining the grab hud thing will examine this instead)
/obj/item/grab/examine(mob/user)
	. = ..()
	if(grasped_part)
		if(LAZYLEN(grasped_part.embedded_objects))
			. += span_alert("Grabbing [grasped_part.embedded_objects[1]] embedded in [victim]'s [grasped_part.name].")
		else
			. += span_alert("Grabbing [victim] by [victim.p_their()] [parse_zone(grasped_zone)].")
	else if(victim)
		. += span_alert("Grabbing [victim].")
	switch(grab_mode)
		if(GM_STAUNCH)
			. += span_info("Keep holding to staunch the bleeding on [victim][grasped_part ? "'s [grasped_part.name]" : ""].")
		if(GM_WRENCH)
			. += span_info("Use to wrench [victim][grasped_part ? "'s [parse_zone(grasped_zone)]" : ""].")
		if(GM_TEAROFF)
			. += span_info("Use to tear off [victim][grasped_part ? "'s [parse_zone(grasped_zone)]" : ""].")
		if(GM_STRANGLE)
			. += span_info("Use to strangle [victim].")
		if(GM_TAKEDOWN)
			. += span_info("Use to perform a takedown on [victim].")

/obj/item/grab/Destroy()
	. = ..()
	if(owner)
		if(victim)
			playsound(victim, 'modular_septic/sound/attack/thudswoosh.ogg', 50, 1, -1)
			if(owner == victim)
				victim.visible_message(span_warning("[owner] stops grabbing [victim.p_themselves()] by [victim.p_their()] [grasped_part.name]!"),\
											span_notice("I stop grabbing myself by my [grasped_part.name]!"))
			else
				victim.visible_message(span_danger("[owner] stops grabbing [victim][grasped_part ? " by [victim.p_their()] [grasped_part.name]": ""]!"),\
										span_userdanger("[owner] stops grabbing me[grasped_part ? " by my [grasped_part.name]" : ""]!"),\
										ignored_mobs = owner)
				to_chat(owner, span_danger("I stop grabbing [victim][grasped_part ? " by [victim.p_their()] [grasped_part.name]" : ""]!"))
		//Let's only stop pulling if we have no other hand grasping the victim
		var/obj/item/grab/other_grab = owner.get_inactive_held_item()
		if(!istype(other_grab) || QDELETED(other_grab) || (other_grab.victim != victim))
			owner.stop_pulling()
		//Stop strangling!
		else if(grab_mode == GM_STRANGLE && active)
			strangle()
		//Stop taking down!
		else if(grab_mode == GM_TAKEDOWN && active)
			takedown()
		//If the other grab was a pin, too bad retard you broke it you ARE STUPID!!!
		if(istype(other_grab) && (other_grab.grab_mode == GM_TAKEDOWN) && other_grab.active)
			qdel(other_grab)
		UnregisterSignal(owner, list(COMSIG_PARENT_QDELETING, COMSIG_MOVABLE_SET_GRAB_STATE))
	if(victim)
		UnregisterSignal(victim, list(COMSIG_PARENT_QDELETING, COMSIG_CARBON_REMOVE_LIMB))
		victim.cure_blind(GRAB_TRAIT)
	if(grasped_part)
		UnregisterSignal(grasped_part, COMSIG_PARENT_QDELETING)
		LAZYREMOVE(grasped_part.grasped_by, src)
	if(grab_hud)
		vis_contents -= grab_hud
		QDEL_NULL(grab_hud)
	owner = null
	owner = null
	victim = null
	grasped_part = null

//Attack hand typecache checks
/obj/item/grab/pre_attack(atom/A, mob/living/user, params)
	if(is_type_in_typecache(A, attack_hand_typecache))
		var/list/modifiers = params2list(params)
		A.attack_hand(user, modifiers)
		return TRUE
	return ..()

/obj/item/grab/attack_self(mob/user, modifiers)
	. = ..()
	if(!isliving(user))
		return
	var/mob/living/living_user = user
	if(iscarbon(victim) && (user.next_move <= world.time))
		switch(grab_mode)
			if(GM_STRANGLE)
				strangle()
			if(GM_TAKEDOWN)
				takedown()
			if(GM_EMBEDDED)
				twist_embedded()
			if(GM_TEAROFF)
				if(IS_HARM_INTENT(living_user, modifiers))
					tear_off_limb()
				else
					wrench_limb()
			if(GM_WRENCH)
				wrench_limb()

/// Throw the mob, not us
/obj/item/grab/on_thrown(mob/living/carbon/user, atom/target)
	if(victim == user)
		user.dropItemToGround(src, silent = TRUE)
		return
	if(victim != user.pulling)
		return
	if(HAS_TRAIT(user, TRAIT_PACIFISM))
		user.stop_pulling()
		to_chat(user, span_notice("I gently let go of [victim]."))
		return
	return victim

/// Updates the grab mode based on several circumstances
/obj/item/grab/proc/update_grab_mode()
	switch(grasped_zone)
		if(BODY_ZONE_PRECISE_NECK)
			grab_mode = GM_STRANGLE
		if(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN)
			grab_mode = GM_TAKEDOWN
			if(owner == victim)
				grab_mode = GM_STAUNCH
		else
			if(grasped_part?.can_dismember() && (GET_MOB_ATTRIBUTE_VALUE(owner, STAT_STRENGTH) - GET_MOB_ATTRIBUTE_VALUE(victim, STAT_STRENGTH)) >= GM_TEAROFF_DIFF)
				grab_mode = GM_TEAROFF
			else
				grab_mode = GM_WRENCH
	if(LAZYLEN(grasped_part?.embedded_objects))
		grab_mode = GM_EMBEDDED
	update_hud()

/obj/item/grab/proc/display_grab_message(silent = FALSE)
	if(!silent)
		playsound(victim, 'modular_septic/sound/attack/grapple.wav', 75, FALSE)
	/// The owner always has to be a carbon - Thus selfgrab always has a bodypart being grasped
	if(owner == victim)
		victim.visible_message(span_danger("[owner] grasps at [owner.p_their()] [grasped_part.name]."), \
						span_notice("I grab hold of my [grasped_part.name] tightly."), \
						vision_distance=COMBAT_MESSAGE_RANGE)
	else
		victim.visible_message(span_danger("[owner] grasps [victim][grasped_part ? " by [victim.p_their()] [grasped_part.name]" : ""]!"),\
								span_userdanger("I am grasped [grasped_part ? "on my [grasped_part.name] " : ""]by [owner]!"), \
								span_warning("I hear a shuffling sound."),\
								vision_distance = COMBAT_MESSAGE_RANGE, \
								ignored_mobs = owner)
		to_chat(owner, span_danger("I grab [victim][grasped_part ? " by [victim.p_their()] [grasped_part.name]" : ""]!"))
	return TRUE

/// Registers signals and variables and stuff
/obj/item/grab/proc/registergrab(mob/new_victim, mob/new_owner, obj/item/bodypart/new_part, instant = FALSE)
	if(!new_victim || !new_owner)
		return
	owner = new_owner
	RegisterSignal(new_owner, COMSIG_PARENT_QDELETING, .proc/qdel_void)
	victim = new_victim
	if(owner != victim)
		RegisterSignal(owner, COMSIG_MOVABLE_SET_GRAB_STATE, .proc/new_grab_state)
		if(owner.grab_state < GRAB_AGGRESSIVE)
			owner.setGrabState(GRAB_AGGRESSIVE)
			owner.set_pull_offsets(new_victim, owner.grab_state)
		RegisterSignal(victim, COMSIG_PARENT_QDELETING, .proc/qdel_void)
		RegisterSignal(victim, COMSIG_ATOM_NO_LONGER_PULLED, .proc/qdel_void)
		RegisterSignal(victim, COMSIG_CARBON_REMOVE_LIMB, .proc/check_delimb)

	grasped_zone = new_owner.zone_selected
	if(grasped_zone == BODY_ZONE_HEAD)
		new_part = victim.get_bodypart(BODY_ZONE_PRECISE_NECK)
	if(grasped_zone in list(BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE))
		victim.become_blind(GRAB_TRAIT)
	if(new_part)
		grasped_part = new_part
		LAZYADD(grasped_part.grasped_by, src)
		RegisterSignal(grasped_part, COMSIG_PARENT_QDELETING, .proc/qdel_void)
		// Bloody hands if the part is bleeding
		if(grasped_part.get_bleed_rate(FALSE))
			if(owner.gloves)
				owner.gloves.add_mob_blood(victim)
			else
				owner.add_mob_blood(victim)

/// Creates the hud object we are tied to
/obj/item/grab/proc/create_hud_object()
	grab_hud = new()
	grab_hud.parent = src
	vis_contents += grab_hud

/// Updates the appearance of the hud object
/obj/item/grab/proc/update_hud()
	if(!grab_hud)
		return
	grab_hud.update_appearance()

/// The victim got deleted, owner got deleted, or the bodypart we're grasping got deleted
/obj/item/grab/proc/qdel_void()
	if(!QDELETED(src))
		return qdel(src)

/// Handling grab state changes
/obj/item/grab/proc/new_grab_state(mob/living/source, new_state)
	if(new_state <= GRAB_PASSIVE)
		qdel_void()
		return

/// Update the grab mode, which then updates the hud icon, too lazy to implement a limb check
/obj/item/grab/proc/handle_embed_removal()
	update_grab_mode()

/// Delete ourselves, but only if the limb removed was our associated limb
/obj/item/grab/proc/check_delimb(mob/living/carbon/source, obj/item/bodypart/limb_removed)
	if(grasped_part == limb_removed)
		qdel_void()
		return


code:
/mob/living/start_pulling(atom/movable/AM, state, force = pull_force, supress_message = FALSE)
	if(!AM || !src)
		return FALSE
	if(!(AM.can_be_pulled(src, state, force)))
		return FALSE
	if(throwing || !(mobility_flags & MOBILITY_PULL))
		return FALSE
	if(SEND_SIGNAL(src, COMSIG_LIVING_TRY_PULL, AM, force) & COMSIG_LIVING_CANCEL_PULL)
		return FALSE

	AM.add_fingerprint(src)

	// If we're pulling something then drop what we're currently pulling and pull this instead.
	if(pulling)
		// Are we trying to pull something we are already pulling? Then just stop here, no need to continue.
		if(AM == pulling)
			return
		stop_pulling()

	changeNext_move(CLICK_CD_PULLING)
	if(AM.pulledby)
		if(!supress_message)
			AM.visible_message(span_danger("[src] pulls [AM] from [AM.pulledby]'s grip."), \
							span_danger("[src] pulls me from [AM.pulledby]'s grip."), \
							ignored_mobs = src)
			to_chat(src, span_notice("I pull [AM] from [AM.pulledby]'s grip!"))
		log_combat(AM, AM.pulledby, "pulled from", src)
		AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.

	pulling = AM
	AM.set_pulledby(src)

	SEND_SIGNAL(src, COMSIG_LIVING_START_PULL, AM, state, force)

	if(!supress_message)
		var/sound_to_play = 'sound/weapons/thudswoosh.ogg'
		if(ishuman(src))
			var/mob/living/carbon/human/H = src
			if(H.dna.species.grab_sound)
				sound_to_play = H.dna.species.grab_sound
			if(HAS_TRAIT(H, TRAIT_STRONG_GRABBER))
				sound_to_play = null
		if(sound_to_play)
			playsound(src.loc, sound_to_play, 50, TRUE, -1)

	update_pull_hud_icon()
	if(ismob(AM))
		var/mob/M = AM
		log_combat(src, M, "grabbed", addition="passive grab")
		if(!supress_message && !(iscarbon(AM) && HAS_TRAIT(src, TRAIT_STRONG_GRABBER)))
			M.visible_message(span_warning("[src] grabs [M] passively!"), \
							span_userdanger("[src] grabs me passively!"), \
							ignored_mobs = src)
			to_chat(src, span_notice("I grab [M] passively!"))
		M.LAssailant = WEAKREF(src)
		if(isliving(M))
			var/mob/living/L = M

			SEND_SIGNAL(M, COMSIG_LIVING_GET_PULLED, src)
			//Share diseases that are spread by touch
			for(var/thing in diseases)
				var/datum/disease/D = thing
				if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
					L.ContactContractDisease(D)

			for(var/thing in L.diseases)
				var/datum/disease/D = thing
				if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
					ContactContractDisease(D)

			update_pull_movespeed()

		set_pull_offsets(M, state)

/mob/living/grabbedby(mob/living/carbon/user, supress_message = FALSE)
	if(user == src)
		attempt_self_grab()
		return FALSE
	if(anchored || !isturf(user.loc))
		return FALSE
	if(!user.pulling || user.pulling != src)
		var/helpful = IS_HELP_INTENT(user, null)
		user.start_pulling(src, supress_message = !helpful)
		if(helpful)
			return
	if(!(status_flags & CANPUSH) || HAS_TRAIT(src, TRAIT_PUSHIMMUNE))
		to_chat(user, span_warning("[src] can't be grabbed more aggressively!"))
		return FALSE
	if(user.grab_state >= GRAB_PASSIVE && HAS_TRAIT(user, TRAIT_PACIFISM))
		to_chat(user, span_warning("You don't want to risk hurting [src]!"))
		return FALSE
	grippedby(user)

/mob/living/grippedby(mob/living/carbon/user, instant = FALSE)
	// We need to be pulled man
	if(src != user)
		if(!user.pulling || user.pulling != src)
			return
	var/obj/item/grab/active_grab = user.get_active_held_item()
	if(active_grab)
		return
	active_grab = new()
	user.put_in_active_hand(active_grab, FALSE)
	if(QDELETED(active_grab))
		return
	active_grab.registergrab(src, user, null, instant)
	active_grab.create_hud_object()
	active_grab.update_grab_mode()
	active_grab.display_grab_message()

/mob/living/resist_grab(moving_resist)
	. = TRUE
	if(pulledby.grab_state || body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS))
		var/altered_grab_state = pulledby.grab_state
		if((body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_GRABWEAKNESS)) && pulledby.grab_state < GRAB_KILL) //If prone, resisting out of a grab is equivalent to 1 grab state higher. won't make the grab state exceed the normal max, however
			altered_grab_state++
		var/resist_chance = BASE_GRAB_RESIST_CHANCE /// see defines/combat.dm, this should be baseline 60%
		resist_chance = (resist_chance/altered_grab_state) ///Resist chance divided by the value imparted by your grab state. It isn't until you reach neckgrab that you gain a penalty to escaping a grab.
		if(prob(resist_chance))
			adjustFatigueLoss(5)
			visible_message(span_danger("[src] breaks free of [pulledby]'s grip!"), \
							span_danger("You break free of [pulledby]'s grip!"), \
							ignored_mobs = pulledby)
			to_chat(pulledby, span_warning("[src] breaks free of your grip!"))
			log_combat(pulledby, src, "broke grab")
			pulledby.stop_pulling()
			return FALSE
		else
			adjustFatigueLoss(5)//failure to escape still imparts a pretty serious penalty
			visible_message(span_danger("[src] struggles as they fail to break free of [pulledby]'s grip!"), \
							span_warning("You struggle as you fail to break free of [pulledby]'s grip!"), \
							ignored_mobs = pulledby)
			to_chat(pulledby, span_danger("[src] struggles as they fail to break free of your grip!"))
		if(moving_resist && client) //we resisted by trying to move
			client.move_delay = world.time + CLICK_CD_RESIST * 2
	else
		pulledby.stop_pulling()
		return FALSE

/mob/living/proc/attempt_self_grab()
	return


code:
/atom/movable/setGrabState(newstate)
	if(newstate == grab_state)
		return
	SEND_SIGNAL(src, COMSIG_MOVABLE_SET_GRAB_STATE, newstate)
	. = grab_state
	grab_state = newstate
	if(pulling)
		switch(grab_state) // Current state.
			if(GRAB_PASSIVE)
				REMOVE_TRAIT(pulling, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT)
				REMOVE_TRAIT(pulling, TRAIT_HANDS_BLOCKED, CHOKEHOLD_TRAIT)
				if(. >= GRAB_NECK) // Previous state was a a neck-grab or higher.
					REMOVE_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT)
			if(GRAB_AGGRESSIVE)
				if(. >= GRAB_NECK) // Grab got downgraded.
					REMOVE_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT)
				else // Grab got upgraded from a passive one.
					ADD_TRAIT(pulling, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT)
					ADD_TRAIT(pulling, TRAIT_HANDS_BLOCKED, CHOKEHOLD_TRAIT)
			if(GRAB_NECK, GRAB_KILL)
				if(. <= GRAB_AGGRESSIVE)
					ADD_TRAIT(pulling, TRAIT_FLOORED, CHOKEHOLD_TRAIT)

/atom/movable/stop_pulling()
	if(pulling)
		var/atom/was_pulling = pulling
		pulling.set_pulledby(null)
		setGrabState(GRAB_PASSIVE)
		pulling = null
		//We want to call this once we actually stop pulling to avoid runtimes
		if(!QDELETED(was_pulling))
			SEND_SIGNAL(was_pulling, COMSIG_ATOM_NO_LONGER_PULLED, src)


code:
/mob/living/proc/lay_to_rest(msg_to_rester = DEFAULT_REST_TARGET_MSG, msg_to_observer = DEFAULT_REST_OBSERVER_MSG(src), forced)
	if(!mind)
		return
	var/mob/dead/observer/our_ghost = grab_ghost(FALSE)
	if(!our_ghost)
		return
	our_ghost.can_rest = TRUE
	if(HAS_TRAIT(mind, TRAIT_NO_PARRY) && !forced)
		to_chat(our_ghost, span_warning("My vessel has been laid to rest, yet I still cannot leave this mortal plane... I am stuck down here."))
	else
		to_chat(our_ghost, span_notice("[msg_to_rester]"))
	for(var/mob/living/L in fov_viewers(src))
		to_chat(src, span_nicegreen("[msg_to_observer]"))
		SEND_SIGNAL(L, COMSIG_ADD_MOOD_EVENT, "properburial", /datum/mood_event/proper_burial)