code:
// Here be the hexadecimal color lists for the suit tail sprites.
// The list gets passed on to var/list/special_colour_list in species.dm, turned to RGB values, and overlayed onto the uncolored tailsprite in tails_hardsuit.dmi
/obj/item/clothing/suit/space/hardsuit/engine
	suit_tail_colors = list("974", "A62", "C95")

/obj/item/clothing/suit/space/hardsuit/engine/atmos
	suit_tail_colors = list("974", "487", "498")

/obj/item/clothing/suit/space/hardsuit/engine/elite
	suit_tail_colors = list("321", "CCB", "EEE")

/obj/item/clothing/suit/space/hardsuit/mining
	suit_tail_colors = list("877", "BA9", "655")

/obj/item/clothing/suit/space/hardsuit/syndi
	suit_tail_colors = list("A11", "322", "c45")

/obj/item/clothing/suit/space/hardsuit/syndi/elite
	suit_tail_colors = list("222", "454", "443")

/obj/item/clothing/suit/space/hardsuit/medical
	suit_tail_colors = list("DDD", "A75", "FFF")

/obj/item/clothing/suit/space/hardsuit/rd
	suit_tail_colors = list("CB7", "839", "995")

/obj/item/clothing/suit/space/hardsuit/security
	suit_tail_colors = list("222", "C23", "335")

/obj/item/clothing/suit/space/hardsuit/security/hos
	suit_tail_colors = list("212", "C32", "234")

/obj/item/clothing/suit/space/hardsuit/swat
	suit_tail_colors = list("333", "345", "335")

/obj/item/clothing/suit/space/hardsuit/swat/captain
	suit_tail_colors = list("368", "CA0", "030")

/obj/item/clothing/suit/space/hardsuit/shielded/syndi
	suit_tail_colors = list("A11", "322", "c45")

/obj/item/clothing/suit/space/hardsuit/shielded/swat
	suit_tail_colors = list("222", "454", "443")

/obj/item/clothing/suit/space/hardsuit/deathsquad
	suit_tail_colors = list("222", "510", "443")

/obj/item/clothing/suit/space/hardsuit/ert
	suit_tail_colors = list("112", "553", "28F")

/obj/item/clothing/suit/space/hardsuit/ert/sec
	suit_tail_colors = list("411", "D34", "D34")

/obj/item/clothing/suit/space/hardsuit/ert/engi
	suit_tail_colors = list("333", "FA0", "FA0")

/obj/item/clothing/suit/space/hardsuit/ert/med
	suit_tail_colors = list("321", "FFF", "FFF")

/obj/item/clothing/suit/space/hardsuit/ert/jani
	suit_tail_colors = list("112", "93E", "93E")

/obj/item/clothing/suit/space/hardsuit/ert/debug
	suit_tail_colors = list("037", "222", "2AF")

/obj/item/clothing/suit/space/hardsuit/ert/paranormal
	suit_tail_colors = list("9AA", "A23", "222")

/obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
	suit_tail_colors = list("223", "521", "721")

/obj/item/clothing/suit/space/hardsuit/cybersun
	suit_tail_colors = list("333", "345", "335")


code:
/obj/item/clothing/under
	worn_icon_digi = 'modular_septic/icons/mob/clothing/under/uniform_digi.dmi'
	carry_weight = 0.5

repo_name: AnalWerewolf/sundown

license: mit


code:
/obj/item/clothing/under/rank/captain
	carry_weight = 1.2

/obj/item/clothing/under/rank/captain/zoomtech
	name = "ZoomTech royal jumpsuit"
	desc = "A tight fitting orange and grey jumpsuit. You'd expect better for a king..."
	icon = 'modular_septic/icons/obj/clothing/under/captain.dmi'
	icon_state = "zcap_under"
	worn_icon = 'modular_septic/icons/mob/clothing/under/captain.dmi'
	worn_icon_state = "zcap_under"
	lefthand_file = 'modular_septic/icons/mob/inhands/clothing/clothing_lefthand.dmi'
	righthand_file = 'modular_septic/icons/mob/inhands/clothing/clothing_righthand.dmi'
	inhand_icon_state = "zcap_under"


code:
/obj/item/clothing/under/rank/cargo
	carry_weight = 1

/obj/item/clothing/under/rank/cargo/tech/zoomtech
	name = "alien jumpsuit"
	desc = "Tight fitting jeans and an alien stamped shirt."
	icon = 'modular_septic/icons/obj/clothing/under/cargo.dmi'
	icon_state = "postal_cargo"
	worn_icon = 'modular_septic/icons/mob/clothing/under/cargo.dmi'
	worn_icon_state = "postal_cargo"


code:
/obj/item/clothing/under/color/grey/ancient
	icon = 'modular_septic/icons/obj/clothing/under/color.dmi'
	icon_state = "grey_ancient"
	worn_icon = 'modular_septic/icons/mob/clothing/under/color.dmi'
	worn_icon_state = "grey_ancient"


code:
/obj/item/clothing/under/rank/engineering
	carry_weight = 1

/obj/item/clothing/under/rank/engineering/engineer
	name = "mechanist's jumpsuit"
	desc = "It's an orange high visibility jumpsuit worn by mechanists. It has minor radiation shielding."
	icon = 'modular_septic/icons/obj/clothing/under/mechanist.dmi'
	icon_state = "mechanist_jumpsuit"
	worn_icon = 'modular_septic/icons/mob/clothing/under/mechanist.dmi'
	worn_icon_state = "mechanist_jumpsuit"
	inhand_icon_state = "engi_suit"

/obj/item/clothing/under/rank/engineering/chief_engineer
	name = "arch-mechanist's jumpsuit"
	desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Arch-Mechanist\". It has minor radiation shielding."
	icon = 'modular_septic/icons/obj/clothing/under/mechanist.dmi'
	icon_state = "mechanist_jumpsuit"
	worn_icon = 'modular_septic/icons/mob/clothing/under/mechanist.dmi'
	worn_icon_state = "mechanist_jumpsuit"
	inhand_icon_state = "engi_suit"


code:
/obj/item/clothing/under/rank/security
	carry_weight = 1

/obj/item/clothing/under/rank/security/ordinator
	name = "ordinator jumpsuit"
	desc = "A \"tactical\" grey jumpsuit."
	icon = 'modular_septic/icons/obj/clothing/under/ordinator.dmi'
	icon_state = "ordie_jumpsuit"
	worn_icon = 'modular_septic/icons/mob/clothing/under/ordinator.dmi'
	worn_icon_state = "ordie_jumpsuit"
	lefthand_file = 'modular_septic/icons/mob/inhands/clothing/clothing_lefthand.dmi'
	righthand_file = 'modular_septic/icons/mob/inhands/clothing/clothing_righthand.dmi'
	inhand_icon_state = "ordie_jumpsuit"

/obj/item/clothing/under/rank/security/ordinator/coordinator
	name = "\proper coordinator's turtleneck"
	desc = "A \"tactical\" black armored turtleneck suit denoting the rank of coordinator."
	icon = 'modular_septic/icons/obj/clothing/under/ordinator.dmi'
	icon_state = "coordie_jumpsuit"
	worn_icon = 'modular_septic/icons/mob/clothing/under/ordinator.dmi'
	worn_icon_state = "coordie_jumpsuit"
	lefthand_file = 'modular_septic/icons/mob/inhands/clothing/clothing_lefthand.dmi'
	righthand_file = 'modular_septic/icons/mob/inhands/clothing/clothing_righthand.dmi'
	inhand_icon_state = "coordie_jumpsuit"
	armor = list(MELEE = 10, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 50, ACID = 50, WOUND = 10)
	carry_weight = 1.5


code:
//SHIT
/atom/proc/return_shit_DNA()
	var/datum/component/forensics/D = GetComponent(/datum/component/forensics)
	if(D)
		. = D.shit_DNA

/atom/proc/shit_DNA_length()
	var/datum/component/forensics/D = GetComponent(/datum/component/forensics)
	if(D)
		. = length(D.shit_DNA)

/atom/proc/add_shit_DNA(list/dna)
	return FALSE

/atom/proc/transfer_mob_shit_dna(mob/living/L)
	// Returns 0 if we have that blood already
	var/new_blood_dna = L.get_blood_dna_list()
	if(!new_blood_dna)
		return FALSE
	var/old_length = shit_DNA_length()
	add_shit_DNA(new_blood_dna)
	if(shit_DNA_length() == old_length)
		return FALSE
	return TRUE

/atom/proc/add_mob_shit(mob/living/M)
	var/list/blood_dna = M.get_blood_dna_list()
	if(!blood_dna)
		return FALSE
	return add_shit_DNA(blood_dna)

/obj/add_shit_DNA(list/dna)
	. = ..()
	if(length(dna))
		. = AddComponent(/datum/component/forensics, null, null, null, null, dna)

/obj/add_shit_DNA(list/dna)
	if(!QDELETED(src))
		return ..()

/mob/living/carbon/human/add_shit_DNA(list/blood_dna, list/datum/disease/diseases)
	if(!QDELETED(src))
		return ..()

//CUM
/atom/proc/return_cum_DNA()
	var/datum/component/forensics/D = GetComponent(/datum/component/forensics)
	if(D)
		. = D.cum_DNA

/atom/proc/cum_DNA_length()
	var/datum/component/forensics/D = GetComponent(/datum/component/forensics)
	if(D)
		. = length(D.cum_DNA)

/atom/proc/add_cum_DNA(list/dna)
	return FALSE

/atom/proc/transfer_mob_cum_dna(mob/living/L)
	// Returns 0 if we have that blood already
	var/new_blood_dna = L.get_blood_dna_list()
	if(!new_blood_dna)
		return FALSE
	var/old_length = cum_DNA_length()
	add_cum_DNA(new_blood_dna)
	if(cum_DNA_length() == old_length)
		return FALSE
	return TRUE

/atom/proc/add_mob_cum(mob/living/M)
	var/list/blood_dna = M.get_blood_dna_list()
	if(!blood_dna)
		return FALSE
	return add_cum_DNA(blood_dna)

/obj/add_cum_DNA(list/dna)
	. = ..()
	if(length(dna))
		. = AddComponent(/datum/component/forensics, null, null, null, null, null, dna)

/obj/add_cum_DNA(list/dna)
	if(!QDELETED(src))
		return ..()

/mob/living/carbon/human/add_cum_DNA(list/blood_dna, list/datum/disease/diseases)
	if(!QDELETED(src))
		return ..()

//FEMCUM
/atom/proc/return_femcum_DNA()
	var/datum/component/forensics/D = GetComponent(/datum/component/forensics)
	if(D)
		. = D.femcum_DNA

/atom/proc/femcum_DNA_length()
	var/datum/component/forensics/D = GetComponent(/datum/component/forensics)
	if(D)
		. = length(D.femcum_DNA)

/atom/proc/add_femcum_DNA(list/dna)
	return FALSE

/atom/proc/transfer_mob_femcum_dna(mob/living/L)
	// Returns 0 if we have that blood already
	var/new_blood_dna = L.get_blood_dna_list()
	if(!new_blood_dna)
		return FALSE
	var/old_length = femcum_DNA_length()
	add_cum_DNA(new_blood_dna)
	if(femcum_DNA_length() == old_length)
		return FALSE
	return TRUE

/atom/proc/add_mob_femcum(mob/living/M)
	var/list/blood_dna = M.get_blood_dna_list()
	if(!blood_dna)
		return FALSE
	return add_femcum_DNA(blood_dna)

/obj/add_femcum_DNA(list/dna)
	. = ..()
	if(length(dna))
		. = AddComponent(/datum/component/forensics, null, null, null, null, null, null, dna)

/obj/add_femcum_DNA(list/dna)
	if(!QDELETED(src))
		return ..()

/mob/living/carbon/human/add_femcum_DNA(list/blood_dna, list/datum/disease/diseases)
	if(!QDELETED(src))
		return ..()

//BLOOD
/obj/add_blood_DNA(list/dna)
	if(!QDELETED(src))
		return ..()

/mob/living/carbon/human/add_blood_DNA(list/blood_dna, list/datum/disease/diseases)
	if(QDELETED(src))
		return
	if(head)
		head.add_blood_DNA(blood_dna)
		update_inv_head()
	else if(wear_mask)
		wear_mask.add_blood_DNA(blood_dna)
		update_inv_wear_mask()
	else if(glasses)
		glasses.add_blood_DNA(blood_dna)
		update_inv_glasses()
	if(wear_suit)
		wear_suit.add_blood_DNA(blood_dna)
		update_inv_wear_suit()
	else if(w_uniform)
		w_uniform.add_blood_DNA(blood_dna)
		update_inv_w_uniform()
	if(gloves)
		gloves.add_blood_DNA(blood_dna)
		update_inv_gloves()
	else if(length(blood_dna))
		AddComponent(/datum/component/forensics, null, null, blood_dna)
		blood_in_hands += rand(2, 4)
		update_inv_gloves()
	return TRUE


code:
/atom/movable/fire
	name = "Fire"
	desc = "I'll take you to burn."
	icon = 'modular_septic/icons/effects/fire/fire.dmi'
	icon_state = "fire_small"
	plane = MOB_PLANE
	layer = ABOVE_MOB_LAYER
	anchored = TRUE
	move_resist = INFINITY
	light_range = 1.5
	light_power = 1.5
	light_color = LIGHT_COLOR_FIRE
	/// Is it magical, if it is then it wont interact with atmos, and it will not loose power by itself. Mainly for adminbus events or mapping
	var/magical = FALSE
	/// How much power have we got. This is treated like fuel, be it flamethrower liquid or any random thing you could come up with
	var/fire_power = 20
	/// Visual state of the fire. Kept track to not do too many updates.
	var/current_fire_state
	/// Used for connect_loc element
	var/static/list/loc_connections = list(
		COMSIG_ATOM_ENTERED = .proc/on_entered,
	)

/atom/movable/fire/Initialize(mapload, power)
	. = ..()
	var/turf/our_turf = loc
	if(our_turf.turf_fire)
		return INITIALIZE_HINT_QDEL
	AddElement(/datum/element/connect_loc, src, loc_connections)
	our_turf.turf_fire = src
	SSturf_fire.fires[src] = TRUE
	if(power)
		fire_power = min(TURF_FIRE_MAX_POWER, power)
	update_fire_state()

/atom/movable/fire/Destroy()
	var/turf/turf_loc = get_turf(src)
	RemoveElement(/datum/element/connect_loc, src, loc_connections)
	turf_loc?.turf_fire = null
	SSturf_fire.fires -= src
	return ..()

/atom/movable/fire/proc/process_waste()
	if(isclosedturf(loc))
		return TRUE
	var/turf/open/open_turf = loc
	open_turf.pollute_list_turf(list(/datum/pollutant/smoke = 15, /datum/pollutant/carbon_air_pollution = 5))
	if(open_turf.planetary_atmos)
		return TRUE
	var/list/air_gases = open_turf.air?.gases
	if(!air_gases)
		return FALSE
	var/oxy = air_gases[/datum/gas/oxygen] ? air_gases[/datum/gas/oxygen][MOLES] : 0
	if (oxy < 0.5)
		return FALSE
	var/datum/gas_mixture/cached_air = open_turf.air
	var/temperature = cached_air.temperature
	var/old_heat_capacity = cached_air.heat_capacity()
	var/burn_rate = TURF_FIRE_BURN_RATE_BASE + fire_power * TURF_FIRE_BURN_RATE_PER_POWER
	if(burn_rate > oxy)
		burn_rate = oxy
	air_gases[/datum/gas/oxygen][MOLES] = air_gases[/datum/gas/oxygen][MOLES] - burn_rate
	ASSERT_GAS(/datum/gas/carbon_dioxide,cached_air)
	air_gases[/datum/gas/carbon_dioxide][MOLES] += burn_rate * TURF_FIRE_BURN_CARBON_DIOXIDE_MULTIPLIER
	var/new_heat_capacity = cached_air.heat_capacity()
	var/energy_released = burn_rate * TURF_FIRE_ENERGY_PER_BURNED_OXY_MOL
	cached_air.temperature = (temperature * old_heat_capacity + energy_released) / new_heat_capacity
	open_turf.air_update_turf(TRUE)
	return TRUE

/atom/movable/fire/process()
	var/turf/turf_loc = loc
	if(!istype(turf_loc)) //This can happen, how I'm not sure
		qdel(src)
		return
	if(isclosedturf(turf_loc))
		if(iswallturf(turf_loc))
			var/turf/closed/wall/wall_loc = turf_loc
			if(!magical)
				if(prob(max(0, fire_power - (100 - wall_loc.hardness)/4)))
					wall_loc.dismantle_wall(FALSE, FALSE)
				if(prob(6))
					playsound(wall_loc, 'sound/effects/comfyfire.ogg', 40, TRUE)
		if(!magical)
			reduce_power(1)
		return
	var/turf/open/open_turf = turf_loc
	if(open_turf.active_hotspot) //If we have an active hotspot, let it do the damage instead and lets not loose power
		return
	if(!magical)
		if(!process_waste())
			qdel(src)
			return
		if(open_turf.air.temperature < TURF_FIRE_REQUIRED_TEMP)
			fire_power -= TURF_FIRE_POWER_LOSS_ON_LOW_TEMP
		if(reduce_power(1))
			return
	open_turf.hotspot_expose(TURF_FIRE_TEMP_BASE + (TURF_FIRE_TEMP_INCREMENT_PER_POWER*fire_power), TURF_FIRE_VOLUME)
	for(var/A in open_turf)
		var/atom/AT = A
		AT.fire_act(TURF_FIRE_TEMP_BASE + (TURF_FIRE_TEMP_INCREMENT_PER_POWER*fire_power), TURF_FIRE_VOLUME)
	if(!magical)
		if(prob(6))
			playsound(open_turf, 'sound/effects/comfyfire.ogg', 40, TRUE)
		if(prob(fire_power))
			open_turf.burn_tile()
		if(prob(fire_power))
			var/list/arthur_brown = list()
			for(var/turf/closed/wall/wall_turf in range(1, src))
				if(wall_turf.turf_fire || !wall_turf.flammable)
					continue
				if(prob(100 - wall_turf.hardness))
					arthur_brown += wall_turf
			for(var/turf/open/open_neighbor in range(1, src))
				if(open_neighbor.turf_fire || !open_neighbor.flammable)
					continue
				if(istype(open_neighbor, /turf/open/openspace) || isspaceturf(open_neighbor))
					continue
				if(prob(IGNITE_NEIGHBOR_TURF_CHANCE))
					arthur_brown += open_neighbor
			if(length(arthur_brown))
				var/turf/god_of_hellfire = pick(arthur_brown)
				god_of_hellfire.ignite_turf_fire(CEILING(fire_power/2, 1))
				if(reduce_power(FLOOR(fire_power/2, 1)))
					return
		update_fire_state()

/atom/movable/fire/proc/on_entered(datum/source, atom/movable/AM)
	var/turf/open/open_turf = loc
	if(open_turf.active_hotspot) //If we have an active hotspot, let it do the damage instead
		return
	AM.fire_act(TURF_FIRE_TEMP_BASE + (TURF_FIRE_TEMP_INCREMENT_PER_POWER*fire_power), TURF_FIRE_VOLUME)
	return

/atom/movable/fire/proc/add_power(power)
	fire_power = clamp(fire_power + power, 0, TURF_FIRE_MAX_POWER)
	update_fire_state()
	if(fire_power <= 0)
		qdel(src)
		return TRUE

/atom/movable/fire/proc/reduce_power(power)
	fire_power = clamp(fire_power - power, 0, TURF_FIRE_MAX_POWER)
	update_fire_state()
	if(fire_power <= 0)
		qdel(src)
		return TRUE

/atom/movable/fire/proc/update_fire_state()
	var/new_state
	switch(fire_power)
		if(0 to 10)
			new_state = TURF_FIRE_STATE_SMALL
		if(11 to 24)
			new_state = TURF_FIRE_STATE_MEDIUM
		if(25 to INFINITY)
			new_state = TURF_FIRE_STATE_LARGE

	if(new_state == current_fire_state)
		return
	current_fire_state = new_state

	SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
	switch(current_fire_state)
		if(TURF_FIRE_STATE_SMALL)
			icon_state = "fire_small"
			SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/fire/fire_overlays.dmi', "fire_small", LYING_MOB_LAYER, MOB_PLANE, add_appearance_flags = RESET_COLOR|RESET_ALPHA)
			plane = MOB_PLANE
			layer = BELOW_MOB_LAYER
			set_light_range(1.5)
			add_filter("fire_filter", 10, drop_shadow_filter(x=0, y=0, size=32, offset=3, color=light_color))
		if(TURF_FIRE_STATE_MEDIUM)
			icon_state = "fire_medium"
			SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/fire/fire_overlays.dmi', "fire_medium", ON_EDGED_TURF_LAYER, MOB_PLANE, add_appearance_flags = RESET_COLOR|RESET_ALPHA)
			plane = MOB_PLANE
			layer = BELOW_MOB_LAYER
			set_light_range(2.5)
			add_filter("fire_filter", 10, drop_shadow_filter(x=0, y=0, size=32, offset=6, color=light_color))
		if(TURF_FIRE_STATE_LARGE)
			icon_state = "fire_big"
			SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/fire/fire_overlays.dmi', "fire_big", ABOVE_ALL_MOB_LAYER, MOB_PLANE, add_appearance_flags = RESET_COLOR|RESET_ALPHA)
			plane = MOB_PLANE
			layer = ABOVE_MOB_LAYER
			set_light_range(3.5)
			add_filter("fire_filter", 10, drop_shadow_filter(x=0, y=0, size=32, offset=10, color=light_color))

///All the subtypes are for adminbussery and or mapping
/atom/movable/fire/magical
	magical = TRUE

/atom/movable/fire/small
	fire_power = 10

/atom/movable/fire/small/magical
	magical = TRUE

/atom/movable/fire/inferno
	fire_power = 30

/atom/movable/fire/inferno/magical
	magical = TRUE


code:
/turf
	///Fire movable of this turf
	var/atom/movable/fire/turf_fire
	///Whether or not we can have a turf fire going on
	var/flammable = TRUE

/turf/examine(mob/user)
	. = ..()
	if(turf_fire)
		. += span_warning("Holy shit, [src] is on <b>fire</b>!")

/turf/proc/ignite_turf_fire(power)
	if(!flammable)
		return
	if(turf_fire)
		turf_fire.add_power(power)
		return
	if(istype(src, /turf/open/openspace) || isspaceturf(src))
		return
	new /atom/movable/fire(src, power)


code:
/obj/item/paper/code/syringe_gun
	name = "\improper important-looking paper"
	info = "You shouldn't see this. If you do, it means Null did a fucky wucky."

/obj/item/paper/code/syringe_gun/Initialize()
	. = ..()
	info = "Code for the emergency syringe gun: [SSid_access.safe_codes[CODE_SYRINGE_GUN]]"
	update_appearance()

/obj/item/paper/code/nuke_disk
	name = "\improper important-looking paper"
	info = "You shouldn't see this. If you do, it means Null did a fucky wucky."

/obj/item/paper/code/nuke_disk/Initialize()
	. = ..()
	info = "Current authentication signature for the NAD: [get_authcode()]"
	update_appearance()


code:
/obj/item/food/raw_meatball
	name = "raw faggot"
	desc = "A great meal all round. Not a cord of wood."

/obj/item/food/raw_meatball/human
	name = "strange raw faggot"

/obj/item/food/raw_meatball/corgi
	name = "raw corgi faggot"

/obj/item/food/raw_meatball/xeno
	name = "raw xeno faggot"

/obj/item/food/raw_meatball/bear
	name = "raw bear faggot"

/obj/item/food/raw_meatball/chicken
	name = "raw chicken faggot"

/obj/item/food/meatball
	name = "faggot"

/obj/item/food/meatball/human
	name = "strange faggot"

/obj/item/food/meatball/corgi
	name = "corgi faggot"

/obj/item/food/meatball/bear
	name = "bear faggot"

/obj/item/food/meatball/xeno
	name = "xenomorph faggot"

/obj/item/food/meatball/chicken
	name = "chicken faggot"


code:
/obj/item/reagent_containers/food/drinks/mug/tea/Initialize()
	. = ..()
	AddComponent(/datum/component/temporary_pollution_emission, /datum/pollutant/food/tea, 5, 3 MINUTES)

/obj/item/reagent_containers/food/drinks/mug/coco/Initialize()
	. = ..()
	AddComponent(/datum/component/temporary_pollution_emission, /datum/pollutant/food/chocolate, 5, 3 MINUTES)


code:
/datum/chemical_reaction/drink/coffee
	pollutant_type = /datum/pollutant/food/coffee

/datum/chemical_reaction/drink/tea
	pollutant_type = /datum/pollutant/food/tea

/datum/chemical_reaction/food/hot_ramen
	pollutant_type = /datum/pollutant/food/spicy_noodles

/datum/chemical_reaction/food/hell_ramen
	pollutant_type = /datum/pollutant/food/spicy_noodles


code:
/obj/machinery/computer/holodeck/Initialize()
	. = ..()
	AddElement(/datum/element/multitool_emaggable)


code:
/obj/item/seeds/starthistle/corpse_flower
	genes = list(/datum/plant_gene/trait/pollutant_production)


code:
/datum/plant_gene/trait/pollutant_production
	name = "Fragrance Production"

	var/obj/machinery/hydroponics/home_tray
	var/datum/pollutant/pollution_type = /datum/pollutant/miasma

/datum/plant_gene/trait/pollutant_production/on_new_seed(obj/item/seeds/new_seed)
	if(!..())
		return
	RegisterSignal(new_seed, COMSIG_SEED_ON_GROW, .proc/try_pollute)
	RegisterSignal(new_seed, COMSIG_PARENT_PREQDELETED, .proc/stop_polluting)

/datum/plant_gene/trait/pollutant_production/proc/try_pollute(obj/item/seeds/our_seed, obj/machinery/hydroponics/grown_tray)
	SIGNAL_HANDLER

	if(grown_tray.age < our_seed.maturation)
		return

	home_tray.AddElement(/datum/element/pollution_emitter, pollution_type, ((our_seed.potency - 50) / 25) * 15)

/datum/plant_gene/trait/pollutant_production/proc/stop_polluting()
	SIGNAL_HANDLER

	home_tray.RemoveElement(/datum/element/pollution_emitter)
	home_tray = null


code:
/datum/job
	/// Stat sheet this job uses, if any
	var/attribute_sheet
	/// Minimum cock size for this role (cm)
	var/min_dicksize = 10
	/// Maximum cock size for this role (cm)
	var/max_dicksize = 15
	/// Whether or not this job has a circumcised penis
	var/circumcised = FALSE
	/// Minimum breast size for this role (gets converted to cup size)
	var/min_breastsize = 1
	/// Maximum breast size for this role (gets converted to cup size)
	var/max_breastsize = 3
	/// Whether or not this job has lactating breasts
	var/lactating = FALSE
	/// With this set to TRUE, the loadout will be applied before a job clothing will be
	var/no_dresscode = FALSE
	/// Whether the job can use the loadout system
	var/loadout_enabled = TRUE
	/// List of banned quirks in their names(dont blame me, that's how they're stored), players can't join as the job if they have the quirk. Associative for the purposes of performance
	var/list/banned_quirks
	/// A list of slots that can't have loadout items assigned to them if no_dresscode is applied, used for important items such as ID, PDA, backpack and headset
	var/list/blacklist_dresscode_slots
	/// Whitelist of allowed species for this job. If not specified then all roundstart races can be used. Associative with TRUE
	var/list/species_whitelist
	/// Blacklist of species for this job.
	var/list/species_blacklist
	/// Which languages does the job require, associative to LANGUAGE_UNDERSTOOD or LANGUAGE_SPOKEN
	var/list/required_languages = list(/datum/language/common = LANGUAGE_SPOKEN)

/datum/job/after_spawn(mob/living/spawned, client/player_client)
	. = ..()
	if(spawned.attributes)
		assign_attributes(spawned, player_client)
	if(ishuman(spawned))
		assign_genitalia(spawned, player_client)

/datum/job/proc/assign_genitalia(mob/living/carbon/human/spawned, client/player_client)
	/* In case of emergency, break glass
	if(!LAZYLEN(spawned.dna?.features))
		return
	spawned.dna.features["penis_size"] = clamp(rand(min_dicksize, max_dicksize), PENIS_MIN_LENGTH, PENIS_MAX_LENGTH)
	spawned.dna.features["penis_girth"] = clamp(spawned.dna.features["penis_size"] - 3, PENIS_MIN_GIRTH, PENIS_MAX_GIRTH)
	spawned.dna.features["breasts_size"] = clamp(rand(min_breastsize, max_breastsize), BREASTS_MIN_SIZE, BREASTS_MAX_SIZE)
	spawned.dna.features["breasts_lactation"] = lactating
	for(var/obj/item/organ/genital/genital in spawned.internal_organs)
		genital.build_from_dna(spawned.dna, genital.mutantpart_key)
	if(circumcised)
		for(var/obj/item/organ/genital/penis/penis in spawned.internal_organs)
			penis.circumcised = TRUE
	*/

/datum/job/proc/assign_attributes(mob/living/spawned, client/player_client)
	if(attribute_sheet)
		spawned.attributes.add_sheet(attribute_sheet)
	var/datum/preferences/prefs = player_client?.prefs
	if(prefs?.birthsign)
		var/datum/cultural_info/cinfo = GLOB.culture_birthsigns[prefs.birthsign]
		if(cinfo)
			cinfo.apply(spawned)
	//Woman moment
	if(spawned.gender == FEMALE)
		spawned.attributes.add_sheet(/datum/attribute_holder/sheet/woman)
	spawned.attributes.update_attributes()

/datum/job/proc/has_banned_quirk(datum/preferences/pref)
	if(!pref) //No preferences? We'll let you pass, this time (just a precautionary check,you dont wanna mess up gamemode setting logic)
		return FALSE
	if(banned_quirks)
		for(var/Q in pref.all_quirks)
			if(banned_quirks[Q])
				return TRUE
	return FALSE

/datum/job/proc/has_banned_species(datum/preferences/pref)
	var/my_id = pref.read_preference(/datum/preference/choiced/species)
	if(species_whitelist && !species_whitelist[my_id])
		return TRUE
	else if(!GLOB.roundstart_races[my_id])
		return TRUE
	if(species_blacklist && species_blacklist[my_id])
		return TRUE
	return FALSE

/datum/job/proc/has_required_languages(datum/preferences/pref)
	if(!required_languages)
		return TRUE
	for(var/lang in required_languages)
		//Doesnt have language, or the required "level" is too low (understood, while needing spoken)
		if(!pref.languages[lang] || pref.languages[lang] < required_languages[lang])
			return FALSE
	return TRUE


code:
/datum/job/ai
	total_positions = 0
	spawn_positions = 0
	current_positions = 0


code:
/datum/job/assistant
	title = "Beggar"
	no_dresscode = TRUE
	blacklist_dresscode_slots = list(ITEM_SLOT_LEAR,ITEM_SLOT_REAR,ITEM_SLOT_BELT,ITEM_SLOT_ID,ITEM_SLOT_BACK) //headset, PDA, ID, backpack are important items
	required_languages = null

	outfit = /datum/outfit/job/assistant/zoomtech

	//bums are chad thundercocks
	min_dicksize = 16
	max_dicksize = 25

	//and e-thots
	min_breastsize = 3
	max_breastsize = 6

/datum/outfit/job/assistant/zoomtech
	name = "ZoomTech Assistant"
	uniform = /obj/item/clothing/under/color/grey/ancient


code:
/datum/job/atmospheric_technician
	total_positions = 0
	spawn_positions = 0
	current_positions = 0
	department_head = list("Arch-Mechanist")
	supervisors = "the arch-mechanist"

	//decent cock
	min_dicksize = 12
	max_dicksize = 18

	//decent breasts
	min_breastsize = 2
	max_breastsize = 4


code:
/datum/job/bartender
	title = "Innkeeper"
	department_head = list("Doge")
	supervisors = "your greed"

	//decent cock
	min_dicksize = 12
	max_dicksize = 18

	//decent breasts
	min_breastsize = 2
	max_breastsize = 4
	//lactation so they can feed customers milk
	lactating = TRUE


code:
/datum/job/botanist
	title = "Seeder"
	department_head = list("Gatekeeper")
	supervisors = "your greed"

	//large cock
	min_dicksize = 14
	max_dicksize = 20

	//large breasts
	min_breastsize = 2
	max_breastsize = 5


code:
/datum/job/captain
	title = "Doge"
	department_head = list("CentCom")
	supervisors = "ZoomTech"

	outfit = /datum/outfit/job/captain/zoomtech

	//lol micropenis
	min_dicksize = 8
	max_dicksize = 10

	//flat as fuck
	min_breastsize = 0
	max_breastsize = 1

/datum/job/captain/get_captaincy_announcement(mob/living/captain)
	return "[title] [captain.real_name] on deck!"

/datum/outfit/job/captain/zoomtech
	name = "ZoomTech Doge"

	glasses = /obj/item/clothing/glasses/hud/security/sunglasses/zoomtech
	gloves = /obj/item/clothing/gloves/combat/zoomtech
	uniform =  /obj/item/clothing/under/rank/captain/zoomtech
	suit = /obj/item/clothing/suit/armor/vest/capcarapace/zoomtech
	shoes = /obj/item/clothing/shoes/jackboots
	head = /obj/item/clothing/head/caphat/zoomtech
	backpack_contents = list(/obj/item/melee/baton/telescopic=1, /obj/item/station_charter=1)

	skillchips = null


code:
/datum/job/cargo_technician
	title = "Freighter"
	department_head = list("Merchant")
	supervisors = "the merchant"

	outfit = /datum/outfit/job/cargo_tech/zoomtech

	//large cock
	min_dicksize = 14
	max_dicksize = 20

	//large breasts
	min_breastsize = 2
	max_breastsize = 5

/datum/outfit/job/cargo_tech/zoomtech
	name = "Cargo Technician (Postal)"
	suit = /obj/item/clothing/suit/postal
	uniform = /obj/item/clothing/under/rank/cargo/tech/zoomtech


code:
/datum/job/chaplain
	total_positions = 0
	spawn_positions = 0

	//small cock
	min_dicksize = 8
	max_dicksize = 12

	//small breasts
	min_breastsize = 0
	max_breastsize = 2

/datum/outfit/job/chaplain
	skillchips = null


code:
/datum/job/chemist
	title = "Pharmacist"
	department_head = list("Hippocrite")
	supervisors = "the hippocrite"

	//okay but circumcised cock
	min_dicksize = 12
	max_dicksize = 16
	circumcised = TRUE

	//okay lactating breasts
	min_breastsize = 2
	max_breastsize = 4
	lactating = TRUE


code:
/datum/job/chief_engineer
	title = "Foreman"
	department_head = list("Caretakeer")
	supervisors = "the doge"

	//below average cock
	min_dicksize = 10
	max_dicksize = 15

	//below average breasts
	min_breastsize = 1
	max_breastsize = 3

/datum/outfit/job/ce
	skillchips = null


code:
/datum/job/chief_medical_officer
	title = "Hippocrite"
	department_head = list("Doge")
	supervisors = "the doge"

	outfit = /datum/outfit/job/cmo/zoomtech

	//below average but circumcised cock
	min_dicksize = 10
	max_dicksize = 14
	circumcised = TRUE

	//below average lactating breasts
	min_breastsize = 1
	max_breastsize = 3
	lactating = TRUE

/datum/outfit/job/cmo/zoomtech
	name = "Hippocrite"
	suit = /obj/item/clothing/suit/hooded/medical/cmo
	belt = /obj/item/storage/belt/medical/humorist
	backpack_contents = list(/obj/item/melee/baton/telescopic=1,
							/obj/item/modular_computer/tablet/preset/advanced/command=1,
							/obj/item/pda/medical=1,
							/obj/item/paper/code/syringe_gun)
	skillchips = null


code:
/datum/job/clown
	title = "Jester"
	department_head = list("Gatekeeper")
	supervisors = "the gatekeeper"

	//batshit insanity
	min_dicksize = PENIS_MIN_LENGTH
	max_dicksize = PENIS_MAX_LENGTH

	//batshit insanity
	min_breastsize = BREASTS_MIN_SIZE
	max_breastsize = BREASTS_MAX_SIZE


code:
/datum/job/cook
	total_positions = 0
	spawn_positions = 0
	current_positions = 0

	//large cock
	min_dicksize = 14
	max_dicksize = 20

	//large breasts
	min_breastsize = 2
	max_breastsize = 5

/datum/outfit/job/cook
	skillchips = null


code:
/datum/job/curator
	total_positions = 0
	spawn_positions = 0


code:
/datum/job/cyborg
	total_positions = 0
	spawn_positions = 0
	current_positions = 0


code:
/datum/job/detective
	title = "Sheriff"
	department_head = list("Coordinator")
	supervisors = "the coordinator"
	total_positions = 0
	spawn_positions = 0

	//okay cock
	min_dicksize = 12
	max_dicksize = 16

	//okay breasts
	min_breastsize = 2
	max_breastsize = 4


code:
/datum/job/geneticist
	total_positions = 0
	spawn_positions = 0
	current_positions = 0

	//below average cock
	min_dicksize = 10
	max_dicksize = 14

	//below average breasts
	min_breastsize = 1
	max_breastsize = 3


code:
/datum/job/head_of_personnel
	title = "Gatekeeper"
	department_head = list("Doge")
	supervisors = "the doge"

	//below average cock
	min_dicksize = 10
	max_dicksize = 14

	//below average breasts
	min_breastsize = 1
	max_breastsize = 3


code:
/datum/job/head_of_security
	title = "Coordinator"
	department_head = list("Doge")
	supervisors = "the doge"

	outfit = /datum/outfit/job/hos/zoomtech

	//below average cock
	min_dicksize = 10
	max_dicksize = 14

	//below average breasts
	min_breastsize = 1
	max_breastsize = 3

/datum/outfit/job/hos/zoomtech
	name = "Coordinator"

	head = /obj/item/clothing/head/helmet/ordinator
	mask = /obj/item/clothing/mask/gas/ordinator/coordinator
	neck = /obj/item/clothing/neck/ordinator/coordinator
	suit = /obj/item/clothing/suit/armor/vest/alt
	uniform = /obj/item/clothing/under/rank/security/ordinator/coordinator
	gloves = /obj/item/clothing/gloves/color/black
	shoes = /obj/item/clothing/shoes/jackboots
	backpack_contents = list(/obj/item/melee/truncheon/black=1, \
						/obj/item/modular_computer/tablet/preset/advanced/command=1)


code:
/client/get_exp_living(pure_numeric = FALSE)
	if(!prefs.exp || !prefs.exp[EXP_TYPE_LIVING])
		return pure_numeric ? 0 : "No data"
	var/exp_living = text2num(prefs.exp[EXP_TYPE_LIVING])
	return pure_numeric ? exp_living : get_exp_format(exp_living)


code:
/datum/job/lawyer
	total_positions = 0
	spawn_positions = 0
	current_positions = 0


code:
/datum/job/doctor
	title = "Humorist"
	department_head = list("Hippocrite")
	supervisors = "the hippocrite"
	outfit = /datum/outfit/job/doctor/zoomtech

	//okay but circumcised cock
	min_dicksize = 12
	max_dicksize = 16
	circumcised = TRUE

	//okay lactating breasts
	min_breastsize = 2
	max_breastsize = 4
	lactating = TRUE

/datum/outfit/job/doctor/zoomtech
	name = "Humorist"
	suit = /obj/item/clothing/suit/hooded/medical
	belt = /obj/item/storage/belt/medical/humorist
	backpack_contents = list(/obj/item/pda/medical=1)
	skillchips = null


code:
/datum/job/mime
	total_positions = 0
	spawn_positions = 0
	current_positions = 0

	//batshit insanity
	min_dicksize = PENIS_MIN_LENGTH
	max_dicksize = PENIS_MAX_LENGTH

	//batshit insanity
	min_breastsize = BREASTS_MIN_SIZE
	max_breastsize = BREASTS_MAX_SIZE


code:
/datum/job/paramedic
	title = "Sanitar"
	department_head = list("Hippocrite")
	supervisors = "the hippocrite"

	//okay but circumcised cock
	min_dicksize = 12
	max_dicksize = 16
	circumcised = TRUE

	//okay lactating breasts
	min_breastsize = 2
	max_breastsize = 4
	lactating = TRUE


code:
/datum/job/prisoner
	total_positions = 0
	spawn_positions = 0
	current_positions = 0
	required_languages = null

	//prisoners are chad thundercocks
	min_dicksize = 16
	max_dicksize = 25

	//and e-thots
	min_breastsize = 3
	max_breastsize = 6


code:
/datum/job/psychologist
	total_positions = 0
	spawn_positions = 0
	current_positions = 0

	//okay but circumcised cock
	min_dicksize = 12
	max_dicksize = 16
	circumcised = TRUE

	//okay lactating breasts
	min_breastsize = 2
	max_breastsize = 4
	lactating = TRUE

/datum/outfit/job/psychologist
	skillchips = null


code:
/datum/job/quartermaster
	title = "Merchant"
	department_head = list("Doge")
	supervisors = "the doge"
	mind_traits = list(TRAIT_CAPITALIST_MOOD)

	//below average circumcised cock (jew)
	min_dicksize = 10
	max_dicksize = 14
	circumcised = TRUE

	//below average lactating breasts
	min_breastsize = 1
	max_breastsize = 3


code:
/datum/job/research_director
	title = "Technocrat"
	department_head = list("Doge")
	supervisors = "the doge"

	//below average cock
	min_dicksize = 10
	max_dicksize = 14

	//below average breasts
	min_breastsize = 1
	max_breastsize = 3

/datum/outfit/job/rd
	skillchips = null


code:
/datum/job/roboticist
	total_positions = 0
	spawn_positions = 0
	current_positions = 0

	//okay cock
	min_dicksize = 12
	max_dicksize = 16

	//okay breasts
	min_breastsize = 2
	max_breastsize = 4

/datum/outfit/job/roboticist
	skillchips = null


code:
/datum/job/scientist
	title = "Technologist"
	department_head = list("Technocrat")
	supervisors = "the technocrat"

	//okay cock
	min_dicksize = 12
	max_dicksize = 16

	//okay breasts
	min_breastsize = 2
	max_breastsize = 4


code:
/datum/job/security_officer
	title = "Ordinator"
	department_head = list("Coordinator")
	supervisors = "the coordinator"
	total_positions = 3
	spawn_positions = 3

	outfit = /datum/outfit/job/security/zoomtech

	//okay cock
	min_dicksize = 12
	max_dicksize = 16

	//okay breasts
	min_breastsize = 2
	max_breastsize = 4

/datum/job/security_officer/setup_department(mob/living/carbon/human/spawning, client/player_client)
	return

/datum/outfit/job/security/zoomtech
	name = "Ordinator"

	head = /obj/item/clothing/head/helmet/ordinator
	mask = /obj/item/clothing/mask/gas/ordinator
	neck = /obj/item/clothing/neck/ordinator
	suit = /obj/item/clothing/suit/armor/vest/alt
	uniform = /obj/item/clothing/under/rank/security/ordinator
	gloves = /obj/item/clothing/gloves/color/black
	shoes = /obj/item/clothing/shoes/jackboots
	backpack_contents = list(/obj/item/melee/truncheon=1)


code:
/datum/job/shaft_miner
	title = "Pioneer"
	department_head = list("Merchant")
	supervisors = "the merchant"

	//large cock
	min_dicksize = 14
	max_dicksize = 20

	//large breasts
	min_breastsize = 2
	max_breastsize = 5


code:
/datum/job/assistant
	attribute_sheet = /datum/attribute_holder/sheet/job/hobo

/datum/job/prisoner
	attribute_sheet = /datum/attribute_holder/sheet/job/hobo

/datum/job/lawyer
	attribute_sheet = /datum/attribute_holder/sheet/job/hobo

/datum/job/curator
	attribute_sheet = /datum/attribute_holder/sheet/job/hobo

/datum/job/captain
	attribute_sheet = /datum/attribute_holder/sheet/job/doge

/datum/job/chief_engineer
	attribute_sheet = /datum/attribute_holder/sheet/job/chief_engi

/datum/job/atmospheric_technician
	attribute_sheet = /datum/attribute_holder/sheet/job/engi

/datum/job/station_engineer
	attribute_sheet = /datum/attribute_holder/sheet/job/engi

/datum/job/chief_medical_officer
	attribute_sheet = /datum/attribute_holder/sheet/job/master_practitioner

/datum/job/chemist
	attribute_sheet = /datum/attribute_holder/sheet/job/apothecary

/datum/job/doctor
	attribute_sheet = /datum/attribute_holder/sheet/job/practitioner

/datum/job/virologist
	attribute_sheet = /datum/attribute_holder/sheet/job/practitioner

/datum/job/psychologist
	attribute_sheet = /datum/attribute_holder/sheet/job/practitioner

/datum/job/doctor
	attribute_sheet = /datum/attribute_holder/sheet/job/practitioner

/datum/job/geneticist
	attribute_sheet = /datum/attribute_holder/sheet/job/practitioner

/datum/job/paramedic
	attribute_sheet = /datum/attribute_holder/sheet/job/paramedic

/datum/job/quartermaster
	attribute_sheet = /datum/attribute_holder/sheet/job/merchant

/datum/job/cargo_technician
	attribute_sheet = /datum/attribute_holder/sheet/job/freighter

/datum/job/shaft_miner
	attribute_sheet = /datum/attribute_holder/sheet/job/miner

/datum/job/head_of_security
	attribute_sheet = /datum/attribute_holder/sheet/job/head_ordinator

/datum/job/warden
	attribute_sheet = /datum/attribute_holder/sheet/job/ordinator

/datum/job/detective
	attribute_sheet = /datum/attribute_holder/sheet/job/sheriff

/datum/job/security_officer
	attribute_sheet = /datum/attribute_holder/sheet/job/ordinator

/datum/job/research_director
	attribute_sheet = /datum/attribute_holder/sheet/job/technocrat

/datum/job/scientist
	attribute_sheet = /datum/attribute_holder/sheet/job/technologist

/datum/job/roboticist
	attribute_sheet = /datum/attribute_holder/sheet/job/machinist

/datum/job/head_of_personnel
	attribute_sheet = /datum/attribute_holder/sheet/job/gatekeeper

/datum/job/bartender
	attribute_sheet = /datum/attribute_holder/sheet/job/innkeeper

/datum/job/botanist
	attribute_sheet = /datum/attribute_holder/sheet/job/farmer

/datum/job/cook
	attribute_sheet = /datum/attribute_holder/sheet/job/chef

/datum/job/chaplain
	attribute_sheet = /datum/attribute_holder/sheet/job/chaplain

/datum/job/clown
	attribute_sheet = /datum/attribute_holder/sheet/job/jester

/datum/job/mime
	attribute_sheet = /datum/attribute_holder/sheet/job/jester

/datum/job/janitor
	attribute_sheet = /datum/attribute_holder/sheet/job/janitor


code:
/datum/job/station_engineer
	title = "Mechanist"
	department_head = list("Arch-mechanist")
	supervisors = "the arch-mechanist"

	total_positions = 0
	spawn_positions = 0

	//decent cock
	min_dicksize = 12
	max_dicksize = 18

	//decent breasts
	min_breastsize = 2
	max_breastsize = 4

/datum/outfit/job/engineer
	skillchips = null


code:
/datum/job/virologist
	total_positions = 0
	spawn_positions = 0
	current_positions = 0

	//okay but circumcised cock
	min_dicksize = 12
	max_dicksize = 16
	circumcised = TRUE

	//okay lactating breasts
	min_breastsize = 2
	max_breastsize = 4
	lactating = TRUE


code:
/datum/job/warden
	total_positions = 0
	spawn_positions = 0

	//okay cock
	min_dicksize = 12
	max_dicksize = 16

	//okay breasts
	min_breastsize = 2
	max_breastsize = 4


code:
/datum/language
	// Secret languages should not be shown on the character setup
	var/secret = FALSE


code:
/datum/language_holder/New(_owner, datum/preferences/pref_load)
	if(pref_load)
		//If we're loading a holder from prefs, override the languages
		understood_languages.Cut()
		spoken_languages.Cut()
		for(var/lang_path in pref_load.languages)
			understood_languages[lang_path] = list(LANGUAGE_ATOM)
			if(pref_load.languages[lang_path] == LANGUAGE_SPOKEN)
				spoken_languages[lang_path] = list(LANGUAGE_ATOM)
	owner = _owner
	if(istype(owner, /datum/mind))
		var/datum/mind/M = owner
		if(M.current)
			update_atom_languages(M.current)
	get_selected_language()


code:
/datum/language/aphasia
	name = "Retardspeak"


code:
//Default nevado language
/datum/language/common
	name = "Nulatin"
	desc = "A language derived from the unification of portuguese, spanish and other modern-ish latin-derived languages. \
			Used to be the main language of Itobe, until it was officially replaced with Newspeak in 2600."
	default_priority = 100
	icon = 'modular_septic/icons/misc/language.dmi'
	icon_state = "nulatin"
	space_chance = 50

/**
 * As implied by the desc, the syllables are mostly taken from spanish, portuguese and italian
 * I have no fucking idea on how reliable these sources are.
 *
 * https://www.sttmedia.com/syllablefrequency-spanish
 * https://www.sttmedia.com/syllablefrequency-portuguese
 * https://www.sttmedia.com/syllablefrequency-italian
 */
/datum/language/common/syllables = list(
// Two party syllables
"ad", "al", "am", "an", "ar", "as", "ão", "ci", "co", "de", "di", "do", "du", "el", "en", "er", "es", "he", "ia", "ie",
"io", "il", "in", "la", "lo", "le", "ll", "ma", "me", "na", "ne", "no", "nt", "om", "on", "or", "os", "ol", "pa", "pe",
"qu", "ra", "re", "ri", "ro", "sa", "se", "so", "su", "st", "ta", "te", "ti", "to", "tr", "tt", "ue", "um", "un", "ve",
// Three party syllables
"aci", "ada", "ado", "ara", "ale", "att", "ato", "ame", "and", "ant", "ara", "are", "che", "ció", "com", "con", "des",
"dos", "ela", "ele", "ent", "era", "ero", "eri", "est", "ida", "ido", "ito", "ien", "ier", "ión", "inh", "las", "los",
"mas", "men", "não", "ndo", "nha", "nte", "nto", "ocê", "par", "por", "per", "qua", "que", "res", "ria", "sta", "ste",
"ten", "tos", "tra", "uma", "una", "ver", "voc",
)


code:
//New Omsk language
/datum/language/cyrillic
	name = "Post-Cyrillic"
	desc = "Although ever since the third world war russian has sharply declined in popularity, \
		especially due to the forced adoption of Newspeak, \
		the language never truly died - Being now used in ruski settlements of Nevado."
	key = "3"
	default_priority = 97
	icon = 'modular_septic/icons/misc/language.dmi'
	icon_state = "commie"
	space_chance = 80

/**
 * Shamelessly stolen from Eris' technorussian.
 */
/datum/language/cyrillic/syllables = list(
"zhena", "reb", "kot", "tvoy", "vodka", "blyad", "verbovka", "ponimat", "zhit", "kley", "sto", "yat", "si", "det", \
"re", "be", "nok", "chto", "techno", "kak", "govor", "navernoe", "da", "net", "horosho", "pochemu", "privet","lubov", \
"ebat", "krovat", "stol", "za", "ryad", "ka", "voyna", "dumat", "patroni", "tarakanu", "zdorovie", "day", "dengi", \
"pizdec", "mat", "tvoyu", "suka", "ayblya", "uebok", "sosi", "ebi", "huyar", "trahat", "pizda", "uebu", "zaebal", "zgorela", \
"pizduy", "srat", "naydu", "ubyi", "uebishe", "blyadina", "priebali", "prosrali", "suche", "voituyay", "tupoy", "daun", "churka", \
"nelez", "sovershenstvo", "viju", "stradaniye", "smusl", "spaseniye", "pomosh", "zvezdu", "kosmos", "pokorim", "lublu", "bereza",  \
"zashishu", "luna", "planeta", "voshod", "mercaet", "smeshno", "razum", "trud", "mucheniya", "chudo", "borba", "sudba", "svoboda", \
"provodimost", "inicializaciya", "compilaciya", "izolaciya", "teplootdacha", "izlucheniye", "osnasheniye", \
"vidimost", "indukciya", "ionizaciya", "laser", "svyaz", "provodka", "atmosfera", "davleniye", "temperatura", \
"obyem", "massa", "scorost", "uskoreniye", "radiaciya", "ves", "neobhodimost", "dokozatelstvo", "teorema", "kipeniye", \
"inovaciya", "proruv", "turbulentnost", "zashita", "pitaniye", "zamukaniye", "korotkoye", "dlennoye", "verticalno", \
"portal", "systema", "electronika", "nigilizm", "anarhizm", "communistu", "tupuye", "sopla", "obshivka", "obtekaemost", \
"dinamica", "statica", "organizacuya", "yeyenet", "radio", "peredacha", "priem", "slushno", "chastota", "gerts", "stantiya", \
"suda", "huyar", "odin", "dva", "tri", "holod", "granata", "ne", "re", "ru", "korabl",
)


code:
//just changing the key
/datum/language/monkey
	key = "9"
	secret = TRUE


code:
//Itobe language
/datum/language/newspeak
	name = "Newspeak"
	desc = "An oversimplified, artificial bastardization of latin. \
			Adopted fully by all under control of the Itobe empire since 2600, \
			and considered the standard tongue of humanity."
	key = "1"
	default_priority = 99
	icon = 'modular_septic/icons/misc/language.dmi'
	icon_state = "itobe"
	space_chance = 50

/**
 * As implied by the desc, the language is based on latin.
 * I have no fucking idea on how reliable this source is.
 *
 * https://www.sttmedia.com/syllablefrequency-latin
 */
/datum/language/newspeak/syllables = list(
// two party syllables
"ae", "am", "an", "ar", "at", "ci", "co", "de", "di", "em", "en", "er", "es", "et", "ia", "in", "is",
"it", "iu", "ni", "li", "ne", "ni", "nt", "on", "or", "os", "pe", "qu", "ra", "re", "ri", "ru", "se",
"si", "st", "ta", "te", "ti", "tu", "ue", "ui", "um", "ur", "us",
// three party syllables
"ant", "ati", "atu", "bus", "con", "cum", "ent", "era", "ere", "eri", "est", "iam", "ibu", "ili", "iss",
"ita", "itu", "ium", "ius", "nte", "nti", "oru", "per", "pro", "qua", "que", "qui", "quo", "rat", "rum",
"sse", "tat", "ter", "tis", "tum", "tur", "tus", "unt",
)


code:
//STICK language
/datum/language/uncommon
	name = "Anglic Common"
	desc = "The official language of the STICK States, \
			formed after the catastrophic dissolution of the United States, \
			Anglic Common is a result of the increasing demand \
			for an unified English language."
	key = "2"
	default_priority = 98
	icon = 'modular_septic/icons/misc/language.dmi'
	icon_state = "british"
	space_chance = 50

/datum/language/uncommon/syllables = list(
		"al", "an", "ar", "as", "at", "ea", "ed", "en", "er", "es", "ha", "he", "hi", "in", "is", "it",
		"le", "me", "nd", "ne", "ng", "nt", "on", "or", "ou", "re", "se", "st", "te", "th", "ti", "to",
		"ve", "wa", "all", "and", "are", "but", "ent", "era", "ere", "eve", "for", "had", "hat", "hen", "her", "hin",
		"ch", "de", "ge", "be", "ach", "abe", "ich", "ein", "die", "sch", "auf", "aus", "ber", "che", "ent", "que",
		"ait", "les", "lle", "men", "ais", "ans", "ait", "ave", "con", "com", "des", "tre", "eta", "eur", "est",
		"ing", "the", "ver", "was", "ith", "hin",
	)


code:
/datum/language/xenocommon
	key = "9"
	secret = TRUE


code:
//Stray language
/datum/language/yoinky
	name = "Yoinky Sploinky"
	desc = "An incomprehensible language composed of mostly guttural vowel sounds."
	key = "5"
	default_priority = 96
	icon = 'modular_septic/icons/misc/language.dmi'
	icon_state = "sploinky"
	space_chance = 65
	secret = TRUE

/**
 * It's literally just nonsensical meme shit
 */
/datum/language/yoinky/syllables = list(
"splurg", "plab", "yoink", "oink", "splonk", "splunk", "spunk", "bonk", "sploink", "splorg", "garg",
"glug", "chung", "chang", "spok", "spork", "ork", "unk", "sperg", "pog", "poggers", "poge", "gloop",
"glab", "glob", "galab", "lalab", "gab", "dink", "tink", "swig", "swag", "curb", "swab", "go", "swib",
"dab", "gabble", "gag", "oinky", "sploinky",
)


code:
/obj/effect/temp_visual/liquid_splash
	icon = 'modular_septic/icons/effects/liquids/splash.dmi'
	icon_state = "splash"
	layer = FLY_LAYER
	randomdir = FALSE


code:
/***************************************************/
/********************PROPER GROUPING**************/

//Whenever you add a liquid cell add its contents to the group, have the group hold the reference to total reagents for processing sake
//Have the liquid turfs point to a partial liquids reference in the group for any interactions
//Have the liquid group handle the total reagents datum, and reactions too (apply fraction?)

/datum/liquids_group
	var/list/members = list()
	var/color
	var/next_share = 0
	var/dirty = TRUE
	var/amount_of_active_turfs = 0
	var/decay_counter = 0
	var/expected_turf_height = 0
	var/cached_color
	var/list/last_cached_fraction_share
	var/last_cached_total_volume = 0
	var/last_cached_thermal = 0
	var/last_cached_overlay_state = LIQUID_STATE_PUDDLE

/datum/liquids_group/New(height)
	SSliquids.active_groups[src] = TRUE
	color = "#[random_color()]"
	expected_turf_height = height

/datum/liquids_group/proc/add_to_group(turf/turf_added)
	members[turf_added] = TRUE
	turf_added.liquids_group = src
	if(SSliquids.active_turfs[turf_added])
		amount_of_active_turfs++
	if(turf_added.liquids)
		turf_added.liquids.has_cached_share = FALSE

/datum/liquids_group/proc/remove_from_group(turf/turf_removed)
	members -= turf_removed
	turf_removed.liquids_group = null
	if(SSliquids.active_turfs[turf_removed])
		amount_of_active_turfs--
	if(!length(members))
		qdel(src)

/datum/liquids_group/proc/can_merge_group(datum/liquids_group/other_group)
	if(expected_turf_height == other_group.expected_turf_height)
		return TRUE
	return FALSE

/datum/liquids_group/proc/merge_group(datum/liquids_group/other_group)
	amount_of_active_turfs += other_group.amount_of_active_turfs
	var/turf/member //faster declaration
	for(var/t in other_group.members)
		member = t
		member.liquids_group = src
		members[member] = TRUE
		if(member.liquids)
			member.liquids.has_cached_share = FALSE
	other_group.members = list()
	qdel(other_group)
	share()

/datum/liquids_group/proc/break_group()
	//Flag puddles to the evaporation queue
	var/turf/member //faster declaration
	for(var/t in members)
		member = t
		if(member.liquids?.liquid_state >= LIQUID_STATE_PUDDLE)
			SSliquids.evaporation_queue[member] = TRUE

	share(TRUE)
	qdel(src)

/datum/liquids_group/Destroy()
	SSliquids.active_groups -= src
	var/turf/member //faster declaration
	for(var/t in members)
		member = t
		member.liquids_group = null
	members = null
	return ..()

/datum/liquids_group/proc/check_adjacency(turf/checked)
	var/list/recursive_adjacent = list()
	var/list/current_adjacent = list()
	current_adjacent[checked] = TRUE
	recursive_adjacent[checked] = TRUE
	var/getting_new_turfs = TRUE
	var/indef_loop_safety = 0
	var/turf/adjacent_cell //faster declaration
	while(getting_new_turfs && indef_loop_safety < LIQUID_RECURSIVE_LOOP_SAFETY)
		indef_loop_safety++
		getting_new_turfs = FALSE
		var/list/new_adjacent = list()
		for(var/neighbor in current_adjacent)
			adjacent_cell = neighbor
			for(var/neighbor_of_neighbor in adjacent_cell.get_atmos_adjacent_turfs())
				if(!recursive_adjacent[neighbor_of_neighbor])
					new_adjacent[neighbor_of_neighbor] = TRUE
					recursive_adjacent[neighbor_of_neighbor] = TRUE
					getting_new_turfs = TRUE
		current_adjacent = new_adjacent
	//All adjacent, somehow
	if(LAZYLEN(recursive_adjacent) == length(members))
		return
	var/datum/liquids_group/new_group = new(expected_turf_height)
	for(var/member in members)
		if(!recursive_adjacent[member])
			remove_from_group(member)
			new_group.add_to_group(member)

/datum/liquids_group/proc/share(use_liquids_color = FALSE)
	var/any_share = FALSE
	var/cached_shares = 0
	var/list/cached_add = list()
	var/cached_volume = 0
	var/cached_thermal = 0

	var/atom/movable/liquid/cached_liquids
	var/turf/member //faster declaration
	for(var/cell in members)
		member = cell
		if(member.liquids)
			any_share = TRUE
			cached_liquids = member.liquids

			if(cached_liquids.has_cached_share && last_cached_fraction_share)
				cached_shares++
				continue

			for(var/reagent_type in cached_liquids.reagent_list)
				if(!cached_add[reagent_type])
					cached_add[reagent_type] = 0
				cached_add[reagent_type] += cached_liquids.reagent_list[reagent_type]
			cached_volume += cached_liquids.total_reagents
			cached_thermal += cached_liquids.total_reagents * cached_liquids.temperature
	if(!any_share)
		return

	decay_counter = 0

	if(cached_shares)
		for(var/reagent_type in last_cached_fraction_share)
			if(!cached_add[reagent_type])
				cached_add[reagent_type] = 0
			cached_add[reagent_type] += last_cached_fraction_share[reagent_type] * cached_shares
		cached_volume += last_cached_total_volume * cached_shares
		cached_thermal += cached_shares * last_cached_thermal

	for(var/reagent_type in cached_add)
		cached_add[reagent_type] = cached_add[reagent_type] / length(members)
	cached_volume = cached_volume / length(members)
	cached_thermal = cached_thermal / length(members)
	var/temp_to_set = cached_thermal / cached_volume
	last_cached_thermal = cached_thermal
	last_cached_fraction_share = cached_add
	last_cached_total_volume = cached_volume
	var/mixed_color = use_liquids_color ? mix_color_from_reagent_list(cached_add) : color
	if(use_liquids_color)
		mixed_color = mix_color_from_reagent_list(cached_add)
	else if (GLOB.liquid_debug_colors)
		mixed_color = color
	else
		if(!cached_color)
			cached_color = mix_color_from_reagent_list(cached_add)
		mixed_color = cached_color

	var/height = CEILING(cached_volume/LIQUID_HEIGHT_DIVISOR, 1)

	var/determined_new_state
	var/state_height = height
	switch(state_height)
		if(0 to LIQUID_ANKLES_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_PUDDLE
		if(LIQUID_ANKLES_LEVEL_HEIGHT to LIQUID_WAIST_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_ANKLES
		if(LIQUID_WAIST_LEVEL_HEIGHT to LIQUID_SHOULDERS_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_WAIST
		if(LIQUID_SHOULDERS_LEVEL_HEIGHT to LIQUID_FULLTILE_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_SHOULDERS
		if(LIQUID_FULLTILE_LEVEL_HEIGHT to INFINITY)
			determined_new_state = LIQUID_STATE_FULLTILE

	var/new_liquids = FALSE
	for(var/cell in members)
		member = cell
		new_liquids = FALSE
		if(!member.liquids)
			new_liquids = TRUE
			member.liquids = new(member)
		cached_liquids = member.liquids

		cached_liquids.reagent_list = cached_add.Copy()
		cached_liquids.total_reagents = cached_volume
		cached_liquids.temperature = temp_to_set

		cached_liquids.has_cached_share = TRUE
		cached_liquids.attrition = 0

		cached_liquids.color = mixed_color
		cached_liquids.set_height(height)

		if(determined_new_state != cached_liquids.liquid_state)
			cached_liquids.set_new_liquid_state(determined_new_state)

		//Only simulate a turf exposure when we had to create a new liquid tile
		if(new_liquids)
			cached_liquids.expose_my_turf()

/datum/liquids_group/proc/process_cell(turf/cell)
	var/turf/adjacent_cell //faster declaration
	for(var/neighbor in cell.get_atmos_adjacent_turfs())
		adjacent_cell = neighbor
		var/my_liquid_height = TOTAL_LIQUID_HEIGHT(cell)
		var/target_liquid_height = TOTAL_LIQUID_HEIGHT(adjacent_cell)
		//IMMUTABLE MADNESS
		if(adjacent_cell.liquids?.immutable)
			if(cell.z != adjacent_cell.z)
				var/turf/z_turf_below = SSmapping.get_turf_below(cell)
				if(adjacent_cell == z_turf_below)
					qdel(cell.liquids, TRUE)
					return
				else
					continue

			//CHECK DIFFERENT TURF HEIGHT THING
			if(cell.liquid_height != adjacent_cell.liquid_height)
				var/difference = (my_liquid_height - target_liquid_height)
				if(difference > 1)
					var/coeff = (cell.liquids.liquid_height / (cell.liquids.liquid_height + abs(cell.liquid_height)))
					var/height_diff = min(0.4, abs(target_liquid_height / my_liquid_height) * coeff)
					cell.liquid_fraction_delete(height_diff)
					. = TRUE
				continue

			if((adjacent_cell.liquid_height - cell.liquid_height) > 1)
				SSliquids.active_immutables[adjacent_cell] = TRUE
				. = TRUE
				continue
		//END OF IMMUTABLE MADNESS

		//Don't try to share liquids normally if we can't
		if(!cell.can_share_liquids_with(adjacent_cell))
			continue

		//DIFFERING Z LEVEL HANDLING
		if(cell.z != adjacent_cell.z)
			//Only interact with cell directly below us
			var/turf/z_turf_below = SSmapping.get_turf_below(cell)
			if(adjacent_cell == z_turf_below)
				if(!(target_liquid_height >= LIQUID_HEIGHT_CONSIDER_FULL_TILE))
					cell.liquid_fraction_share(adjacent_cell, 1)
					qdel(cell.liquids, TRUE)
					. = TRUE
			continue
		//DIFFERENT HEIGHT HANDLING
		if(cell.liquid_height != adjacent_cell.liquid_height)
			//Give liquids to lower liquid heights, but not higher
			var/difference = (my_liquid_height - target_liquid_height)
			if(difference > 1)
				var/coeff = (cell.liquids.liquid_height / (cell.liquids.liquid_height + abs(cell.liquid_height)))
				var/fraction = min(0.4, abs(1 - (target_liquid_height / my_liquid_height)) * coeff)
				cell.liquid_fraction_share(adjacent_cell, fraction)
				. = TRUE
			continue
		//END OF HEIGHT
		if(!adjacent_cell.liquids_group)
			add_to_group(adjacent_cell)
		//Try merge groups if possible
		else if((adjacent_cell.liquids_group != cell.liquids_group) && cell.liquids_group.can_merge_group(adjacent_cell.liquids_group))
			cell.liquids_group.merge_group(adjacent_cell.liquids_group)
		. = TRUE
		SSliquids.add_active_turf(adjacent_cell)
	//Do we want it to spread once per process or many times?
	//Make sure to handle up/down z levels on adjacency properly
	if(.)
		dirty = TRUE


code:
/turf
	var/atom/movable/liquid/liquids
	var/datum/liquids_group/liquids_group
	/// This height is used exclusively for checking if liquids should fall or not!
	var/liquid_height = 0
	/// This height is used exclusively for checking if mobs should fall or not!
	var/turf_height = 0

/turf/examine(mob/user)
	. = ..()
	if(liquids)
		switch(liquids.liquid_state)
			if(LIQUID_STATE_PUDDLE)
				. += span_notice("[capitalize(name)] is coated with a puddle of liquids.")
			if(LIQUID_STATE_ANKLES)
				. += span_notice("[capitalize(name)] is flooded with an ankle-level pool of liquids.")
			if(LIQUID_STATE_WAIST)
				. += span_warning("[capitalize(name)] is flooded with a waist-level pool of liquids.")
			if(LIQUID_STATE_SHOULDERS)
				. += span_danger("[capitalize(name)] is flooded with a shoulder-level pool of liquids.")
			if(LIQUID_STATE_FULLTILE)
				. += span_danger("[capitalize(name)] is completely flooded with a pool of liquids.")

/turf/proc/convert_immutable_liquids()
	if(!liquids || !liquids.immutable)
		return
	var/datum/reagents/tempr = liquids.take_reagents_flat(liquids.total_reagents)
	var/cached_height = liquids.liquid_height
	liquids.remove_turf(src)
	liquids = new(src)
	liquids.liquid_height = cached_height //Prevent height effects
	add_liquid_from_reagents(tempr)
	qdel(tempr)

/turf/proc/reasses_liquids()
	if(!liquids)
		return
	if(liquids_group)
		liquids_group.remove_from_group(src)
	SSliquids.add_active_turf(src)

/turf/proc/liquid_fraction_delete(fraction)
	for(var/reagent_type in liquids.reagent_list)
		var/volume_change = liquids.reagent_list[reagent_type] * fraction
		liquids.reagent_list[reagent_type] -= volume_change
		liquids.total_reagents -= volume_change

/turf/proc/liquid_fraction_share(turf/taker, fraction)
	if(!liquids)
		return
	if(fraction > 1)
		CRASH("Liquid fraction share more than 100%")
	var/list/new_liquid_list = list()
	for(var/reagent_type in liquids.reagent_list)
		var/volume_change = liquids.reagent_list[reagent_type] * fraction
		liquids.reagent_list[reagent_type] -= volume_change
		liquids.total_reagents -= volume_change
		new_liquid_list[reagent_type] = volume_change
	taker.add_liquid_list(new_liquid_list, FALSE, liquids.temperature)
	liquids.has_cached_share = FALSE

/turf/proc/liquid_update_turf()
	if(liquids?.immutable)
		SSliquids.active_immutables[src] = TRUE
		return
	//Check atmos adjacency to cut off any disconnected groups
	if(liquids_group)
		var/assoc_atmos_turfs = list()
		for(var/neighbor in get_atmos_adjacent_turfs())
			assoc_atmos_turfs[neighbor] = TRUE
		//Check any cardinals that may have a matching group
		var/turf/connection //faster declaration
		for(var/direction in GLOB.cardinals)
			connection = get_step(src, direction)
			//Same group of which we do not share atmos adjacency
			if(!assoc_atmos_turfs[connection] && connection.liquids_group && connection.liquids_group == liquids_group)
				connection.liquids_group.check_adjacency(connection)

	SSliquids.add_active_turf(src)

/turf/proc/add_liquid_from_reagents(datum/reagents/giver, no_react = FALSE)
	var/list/compiled_list = list()
	var/list/has_solvent = list()
	var/datum/reagent/reagent //faster declaration
	for(var/reagent_type in giver.reagent_list)
		reagent = reagent_type
		if((reagent.reagent_state == LIQUID) && (reagent.dissolve_per_unit))
			if(has_solvent[reagent.polarity])
				has_solvent[reagent.polarity] += reagent.dissolve_per_unit * reagent.volume
			else
				has_solvent[reagent.polarity] = reagent.dissolve_per_unit * reagent.volume
	for(var/reagent_type in giver.reagent_list)
		reagent = reagent_type
		//It's not a liquid, can only pool if we happen to have the right solvent
		if(reagent.reagent_state != LIQUID)
			if(!has_solvent[reagent.polarity])
				continue
			var/can_dissolve = min(has_solvent[reagent.polarity], reagent.volume)
			has_solvent[reagent.polarity] -= can_dissolve
			compiled_list[reagent.type] = can_dissolve
		else
			compiled_list[reagent.type] = reagent.volume
	//Let's say the beaker is full of non-liquids - Then we can't pool up
	if(length(compiled_list))
		add_liquid_list(compiled_list, no_react, giver.chem_temp)

//More efficient than add_liquid for multiples
/turf/proc/add_liquid_list(list/reagent_list, no_react = FALSE, chem_temp = 300)
	if(!liquids)
		liquids = new(src)
	if(liquids.immutable)
		return

	var/prev_total_reagents = liquids.total_reagents
	var/prev_thermal_energy = prev_total_reagents * liquids.temperature

	for(var/reagent in reagent_list)
		if(!liquids.reagent_list[reagent])
			liquids.reagent_list[reagent] = 0
		liquids.reagent_list[reagent] += reagent_list[reagent]
		liquids.total_reagents += reagent_list[reagent]

	var/recieved_thermal_energy = (liquids.total_reagents - prev_total_reagents) * chem_temp
	liquids.temperature = (recieved_thermal_energy + prev_thermal_energy) / liquids.total_reagents

	if(!no_react)
		//We do react so, make a simulation
		liquids.create_reagents(10000)
		var/datum/reagents/reagent_holder = liquids.reagents
		reagent_holder.add_reagent_list(liquids.reagent_list)
		reagent_holder.chem_temp = liquids.temperature
		if(reagent_holder.handle_reactions())//Any reactions happened, so re-calculate our reagents
			liquids.reagent_list = list()
			liquids.total_reagents = 0
			var/datum/reagent/reagent //faster declaration
			for(var/reagent_type in reagent_holder.reagent_list)
				reagent = reagent_type
				liquids.reagent_list[reagent.type] = reagent.volume
				liquids.total_reagents += reagent.volume
			liquids.temperature = reagent_holder.chem_temp
			if(!liquids.total_reagents) //Our reaction exerted all of our reagents, remove self
				qdel(reagent_holder)
				qdel(liquids)
				return
		qdel(reagent_holder)
		//Expose turf
		liquids.expose_my_turf()

	liquids.calculate_height()
	liquids.set_color_from_reagents()
	liquids.has_cached_share = FALSE
	SSliquids.add_active_turf(src)
	if(liquids_group)
		liquids_group.dirty = TRUE

/turf/proc/add_liquid(reagent, amount, no_react = FALSE, chem_temp = 300)
	if(amount <= 0)
		return
	if(!liquids)
		liquids = new(src)
	if(liquids.immutable)
		return

	var/prev_thermal_energy = liquids.total_reagents * liquids.temperature

	if(!liquids.reagent_list[reagent])
		liquids.reagent_list[reagent] = 0
	liquids.reagent_list[reagent] += amount
	liquids.total_reagents += amount

	liquids.temperature = ((amount * chem_temp) + prev_thermal_energy) / max(1, liquids.total_reagents)

	if(!no_react)
		//We do react so, make a simulation
		liquids.create_reagents(10000)
		var/datum/reagents/reagent_holder = liquids.reagents
		reagent_holder.add_reagent_list(liquids.reagent_list)
		//Any reactions happened, so re-calculate our reagents
		if(reagent_holder.handle_reactions())
			liquids.reagent_list = list()
			liquids.total_reagents = 0
			var/datum/reagent/liquid //faster declaration
			for(var/reagent_type in reagents.reagent_list)
				liquid = reagent_type
				liquids.reagent_list[liquid.type] = liquid.volume
				liquids.total_reagents += liquid.volume
			liquids.temperature = reagent_holder.chem_temp
			if(!liquids.total_reagents) //Our reaction exerted all of our reagents, remove self
				qdel(reagent_holder)
				qdel(liquids)
				return
		qdel(reagent_holder)
		//Expose turf
		liquids.expose_my_turf()

	liquids.calculate_height()
	liquids.set_color_from_reagents()
	liquids.has_cached_share = FALSE
	SSliquids.add_active_turf(src)
	if(liquids_group)
		liquids_group.dirty = TRUE

/turf/proc/can_share_liquids_with(turf/target)
	//One of us is a spaceturf
	if(isspaceturf(target) || isspaceturf(src))
		return FALSE
	//Target is immutable
	if(target.liquids?.immutable)
		return FALSE
	//We're just a puddle, we can't spread
	if(liquids?.liquid_height <= 1)
		return FALSE

	//Only share with turf right below us, if z level differs
	if(z != target.z)
		var/turf/z_turf_below = SSmapping.get_turf_below(src)
		if(target == z_turf_below)
			return TRUE
		return FALSE
	else
		//Varied heights handling
		var/my_liquid_height = TOTAL_LIQUID_HEIGHT(src)
		var/target_liquid_height = TOTAL_LIQUID_HEIGHT(target)
		var/difference = (my_liquid_height - target_liquid_height)
		//Give liquids to lower liquid heights, but not higher
		if(difference > 1)
			return TRUE

	return FALSE

/turf/proc/process_liquid_cell()
	if(!liquids)
		if(!liquids_group)
			var/turf/neighbor //faster declaration
			for(var/t in get_atmos_adjacent_turfs())
				neighbor = t
				if(neighbor.liquids)
					if(neighbor.liquids.immutable)
						SSliquids.active_immutables[neighbor] = TRUE
					else if(neighbor.can_share_liquids_with(src))
						if(neighbor.liquids_group)
							liquids_group = new(liquid_height)
							liquids_group.add_to_group(src)
						SSliquids.add_active_turf(neighbor)
						SSliquids.remove_active_turf(src)
						break
		SSliquids.remove_active_turf(src)
		return
	if(!liquids_group)
		liquids_group = new(liquid_height)
		liquids_group.add_to_group(src)
	var/shared = liquids_group.process_cell(src)
	if(QDELETED(liquids)) //Liquids may be deleted in process cell
		SSliquids.remove_active_turf(src)
		return
	if(!shared)
		liquids.attrition++
	if(liquids.attrition >= LIQUID_ATTRITION_TO_STOP_ACTIVITY)
		SSliquids.remove_active_turf(src)

/turf/proc/process_immutable_liquid()
	var/any_share = FALSE
	var/turf/neighbor //faster declaration
	for(var/turf in get_atmos_adjacent_turfs())
		neighbor = turf
		if(can_share_liquids_with(neighbor))
			//Move this elsewhere sometime later?
			if(neighbor.liquids && (neighbor.liquids.liquid_height > liquids.liquid_height))
				continue
			any_share = TRUE
			neighbor.add_liquid_list(liquids.reagent_list, TRUE, liquids.temperature)
	if(!any_share)
		SSliquids.active_immutables -= src


code:
/atom/movable/liquid/immutable
	immutable = TRUE

/atom/movable/liquid/immutable/ocean
	icon_state = "ocean"
	base_icon_state = "ocean"
	smoothing_flags = NONE
	plane = BLACKNESS_PLANE //Same as weather, etc.
	layer = ABOVE_MOB_LAYER
	temperature = T20C-150
	no_effects = TRUE
	vis_flags = NONE

/atom/movable/liquid/immutable/ocean/warm
	temperature = T20C

/atom/movable/liquid/immutable/ocean/warm/warmer
	temperature = T20C+20

/atom/movable/liquid/immutable/ocean/nevado
	temperature = T0C


code:
/atom/movable/liquid
	name = "liquid"
	desc = "It's a liquid, how incredibly observant you are."
	icon = 'modular_septic/icons/effects/liquids/liquid.dmi'
	icon_state = "liquid-0"
	base_icon_state = "liquid"
	anchored = TRUE
	plane = FLOOR_PLANE
	layer = LIQUID_LAYER
	color = "#DDDDFF"

	move_force = MOVE_FORCE_VERY_STRONG
	move_resist = MOVE_FORCE_VERY_STRONG

	//For being on fire
	light_range = 0
	light_power = 1
	light_color = LIGHT_COLOR_FIRE

	smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
	smoothing_groups = list(SMOOTH_GROUP_LIQUID)
	canSmoothWith = list(SMOOTH_GROUP_WALLS, SMOOTH_GROUP_WINDOW_FULLTILE, SMOOTH_GROUP_LIQUID)

	mouse_opacity = MOUSE_OPACITY_TRANSPARENT

	var/turf/my_turf
	var/liquid_height = 1
	var/only_big_diffs = 1
	var/has_cached_share = FALSE
	var/attrition = 0
	var/immutable = FALSE
	var/no_effects = FALSE
	var/fire_state = LIQUID_FIRE_STATE_NONE
	var/liquid_state = LIQUID_STATE_PUDDLE

	var/list/starting_mixture
	var/list/reagent_list = list()
	var/total_reagents = 0
	var/temperature = T20C

/atom/movable/liquid/Initialize(mapload)
	. = ..()
	if(!SSliquids)
		stack_trace("Liquid movable created with the liquids sybsystem not yet initialized at [loc]!")
		return INITIALIZE_HINT_QDEL
	if(isspaceturf(my_turf))
		return INITIALIZE_HINT_QDEL
	if(!immutable)
		my_turf = get_turf(loc)
		if(!istype(my_turf))
			stack_trace("Liquid movable created inside non-turf [my_turf]!")
			return INITIALIZE_HINT_QDEL
		if(my_turf.liquids)
			stack_trace("Redundant liquid movable created at [my_turf]!")
			return INITIALIZE_HINT_QDEL
		if(SEND_SIGNAL(my_turf, COMSIG_TURF_LIQUIDS_CREATION, src) & COMPONENT_DENY_LIQUID_CREATION)
			return INITIALIZE_HINT_QDEL
		RegisterSignal(my_turf, COMSIG_ATOM_ENTERED, .proc/movable_entered)
		RegisterSignal(my_turf, COMSIG_TURF_MOB_FALL, .proc/mob_fall)
		SSliquids.add_active_turf(my_turf)

	update_liquid_vis()
	if(length(starting_mixture))
		reagent_list = starting_mixture.Copy()
		total_reagents = 0
		for(var/key in reagent_list)
			total_reagents += reagent_list[key]
		calculate_height()
		set_color_from_reagents()
	QUEUE_SMOOTH(src)
	QUEUE_SMOOTH_NEIGHBORS(src)

/atom/movable/liquid/Destroy(force)
	. = ..()
	if(force)
		if(immutable)
			stack_trace("Something has force destroyed an immutable liquid movable at [my_turf].")
		if(my_turf)
			UnregisterSignal(my_turf, list(COMSIG_ATOM_ENTERED, COMSIG_TURF_MOB_FALL))
			my_turf.liquids_group?.remove_from_group(my_turf)
		if(SSliquids.evaporation_queue[my_turf])
			SSliquids.evaporation_queue -= my_turf
		if(SSliquids.processing_fire[my_turf])
			SSliquids.processing_fire -= my_turf
		//Is added because it could invoke a change to neighboring liquids
		SSliquids.add_active_turf(my_turf)
		my_turf.liquids = null
		my_turf = null
		QUEUE_SMOOTH_NEIGHBORS(src)
	else
		return QDEL_HINT_LETMELIVE
	return ..()

/atom/movable/liquid/forceMove(atom/destination, forced = FALSE)
	if(forced)
		return ..()
	return FALSE

/atom/movable/liquid/singularity_act()
	qdel(src)

/atom/movable/liquid/singularity_pull(obj/singularity/S, current_size)
	return

/atom/movable/liquid/ex_act(severity, target)
	return //minecraft reference

/atom/movable/liquid/fire_act(exposed_temperature, exposed_volume)
	if(!fire_state)
		if(check_fire(TRUE))
			SSliquids.processing_fire[my_turf] = TRUE
			return TRUE
	return FALSE

/atom/movable/liquid/onShuttleMove()
	return

/atom/movable/liquid/return_temperature()
	return temperature

/atom/movable/liquid/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta)
	return

/atom/movable/liquid/has_gravity(turf/T)
	return FALSE

/atom/movable/liquid/proc/check_fire(hotspotted = FALSE)
	var/my_burn_power = get_burn_power(hotspotted)
	if(!my_burn_power)
		if(fire_state)
			//Set state to 0
			set_fire_state(LIQUID_FIRE_STATE_NONE)
		return FALSE
	//Calculate appropriate state
	var/new_state = LIQUID_FIRE_STATE_SMALL
	switch(my_burn_power)
		if(0 to 7)
			new_state = LIQUID_FIRE_STATE_SMALL
		if(7 to 8)
			new_state = LIQUID_FIRE_STATE_MILD
		if(8 to 9)
			new_state = LIQUID_FIRE_STATE_MEDIUM
		if(9 to 10)
			new_state = LIQUID_FIRE_STATE_HUGE
		if(10 to INFINITY)
			new_state = LIQUID_FIRE_STATE_INFERNO

	if(fire_state != new_state)
		set_fire_state(new_state)

	return TRUE

/atom/movable/liquid/proc/set_fire_state(new_state)
	fire_state = new_state
	switch(fire_state)
		if(LIQUID_FIRE_STATE_NONE)
			set_light_range(initial(light_range))
		if(LIQUID_FIRE_STATE_SMALL)
			set_light_range(LIGHT_RANGE_FIRE)
		if(LIQUID_FIRE_STATE_MILD)
			set_light_range(LIGHT_RANGE_FIRE)
		if(LIQUID_FIRE_STATE_MEDIUM)
			set_light_range(LIGHT_RANGE_FIRE)
		if(LIQUID_FIRE_STATE_HUGE)
			set_light_range(LIGHT_RANGE_FIRE)
		if(LIQUID_FIRE_STATE_INFERNO)
			set_light_range(LIGHT_RANGE_FIRE)
	update_light()
	update_liquid_vis()

/atom/movable/liquid/proc/get_burn_power(hotspotted = FALSE)
	//We are not on fire and werent ignited by a hotspot exposure, no fire pls
	if(!hotspotted && !fire_state)
		return FALSE
	var/total_burn_power = 0
	var/datum/reagent/reagent //Faster declaration
	for(var/reagent_type in reagent_list)
		reagent = reagent_type
		var/burn_power = initial(reagent.liquid_fire_power)
		if(burn_power)
			total_burn_power += burn_power * reagent_list[reagent_type]
	if(!total_burn_power)
		return FALSE
	//We get burn power per unit
	total_burn_power /= total_reagents
	if(total_burn_power <= REQUIRED_FIRE_POWER_PER_UNIT)
		return FALSE
	//Finally, we burn
	return total_burn_power

/atom/movable/liquid/proc/process_fire()
	if(!fire_state)
		SSliquids.processing_fire -= my_turf
	var/old_state = fire_state
	if(!check_fire())
		SSliquids.processing_fire -= my_turf
	//Try spreading
	//If an extinguisher made our fire smaller, dont spread, else it's too hard to put out
	if(fire_state == old_state)
		var/turf/adjacent_cell //faster declaration
		for(var/neighbor in my_turf.atmos_adjacent_turfs)
			adjacent_cell = neighbor
			if(adjacent_cell.liquids && !adjacent_cell.liquids.fire_state && adjacent_cell.liquids.check_fire(TRUE))
				SSliquids.processing_fire[adjacent_cell] = TRUE
	//Burn our resources
	var/burn_rate = 0 //Faster declaration
	var/datum/reagent/reagent //Faster declaration
	for(var/reagent_type in reagent_list)
		reagent = reagent_type
		burn_rate = initial(reagent.liquid_fire_burnrate)
		if(burn_rate)
			var/amt = reagent_list[reagent_type]
			if(burn_rate >= amt)
				reagent_list -= reagent_type
				total_reagents -= amt
			else
				reagent_list[reagent_type] -= burn_rate
				total_reagents -= burn_rate

	my_turf.hotspot_expose((T20C+50) + (50*fire_state), 125)
	var/atom/atom //faster declaration
	for(var/A in my_turf.contents)
		atom = A
		if(!QDELETED(atom))
			atom.fire_act((T20C+50) + (50*fire_state), 125)

	if(reagent_list.len == 0)
		qdel(src, TRUE)
	else
		has_cached_share = FALSE
		if(!my_turf.liquids_group)
			calculate_height()
			set_color_from_reagents()

/atom/movable/liquid/proc/process_evaporation()
	if(immutable)
		SSliquids.evaporation_queue -= my_turf
		return
	//We're in a group. dont try and evaporate
	if(my_turf.liquids_group)
		SSliquids.evaporation_queue -= my_turf
		return
	if(liquid_state != LIQUID_STATE_PUDDLE)
		SSliquids.evaporation_queue -= my_turf
		return
	//See if any of our reagents evaporates
	var/any_change = FALSE
	var/datum/reagent/reagent //Faster declaration
	for(var/reagent_type in reagent_list)
		//We evaporate - bye bye
		reagent = reagent_type
		var/evaporation_rate = initial(reagent.liquid_evaporation_rate)
		if(evaporation_rate)
			var/evaporated = min(evaporation_rate, reagent_list[reagent_type])
			total_reagents -= evaporated
			reagent_list[reagent_type] -= evaporated
			if(reagent_list[reagent_type] <= 0)
				reagent_list -= reagent_type
			any_change = TRUE
	if(!any_change)
		SSliquids.evaporation_queue -= my_turf
		return
	//No total reagents. Commit death
	if(!length(reagent_list))
		qdel(src, TRUE)
	//Reagents still left - Recalculte height and color
	else
		has_cached_share = FALSE
		calculate_height()
		set_color_from_reagents()

/atom/movable/liquid/proc/set_new_liquid_state(new_state)
	liquid_state = new_state
	cut_overlays()
	if(no_effects)
		return
	var/image/stage_overlay = image('modular_septic/icons/effects/liquids/liquid_overlays.dmi', src, "stage[liquid_state-1]_bottom")
	stage_overlay.plane = MOB_PLANE
	stage_overlay.layer = ABOVE_MOB_LAYER
	var/image/stage_underlay = image('modular_septic/icons/effects/liquids/liquid_overlays.dmi', src, "stage[liquid_state-1]_top")
	stage_underlay.plane = MOB_PLANE
	stage_underlay.layer = BELOW_MOB_LAYER
	add_overlay(stage_overlay)
	add_overlay(stage_underlay)

/atom/movable/liquid/proc/update_liquid_vis()
	if(no_effects)
		return
	SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
	SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/liquids/liquid_overlays.dmi', "shine", layer+0.01, plane, add_appearance_flags = RESET_COLOR|RESET_ALPHA)
	//Add a fire overlay too
	switch(fire_state)
		if(LIQUID_FIRE_STATE_SMALL, LIQUID_FIRE_STATE_MILD)
			SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/liquids/liquid_overlays.dmi', "fire_small", BELOW_MOB_LAYER, OVER_MOB_PLANE, add_appearance_flags = RESET_COLOR|RESET_ALPHA)
		if(LIQUID_FIRE_STATE_MEDIUM)
			SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/liquids/liquid_overlays.dmi', "fire_medium", BELOW_MOB_LAYER, OVER_MOB_PLANE, add_appearance_flags = RESET_COLOR|RESET_ALPHA)
		if(LIQUID_FIRE_STATE_HUGE, LIQUID_FIRE_STATE_INFERNO)
			SSvis_overlays.add_vis_overlay(src, 'modular_septic/icons/effects/liquids/liquid_overlays.dmi', "fire_big", BELOW_MOB_LAYER, OVER_MOB_PLANE, add_appearance_flags = RESET_COLOR|RESET_ALPHA)

//Deletes reagents without doing any sort of interaction
/atom/movable/liquid/proc/delete_reagents_flat(flat_amount)
	if(flat_amount >= total_reagents)
		qdel(src, TRUE)
		return
	var/fraction = flat_amount/total_reagents
	for(var/reagent_type in reagent_list)
		var/amount = fraction * reagent_list[reagent_type]
		reagent_list[reagent_type] -= amount
		total_reagents -= amount
	has_cached_share = FALSE
	if(!my_turf.liquids_group)
		calculate_height()

//Takes a flat of our reagents and returns it, possibly qdeling our liquids
/atom/movable/liquid/proc/take_reagents_flat(flat_amount)
	if(immutable)
		return simulate_reagents_flat(flat_amount)
	var/datum/reagents/temporary_holder = new(10000)
	if(flat_amount >= total_reagents)
		temporary_holder.add_reagent_list(reagent_list)
		qdel(src, TRUE)
	else
		var/fraction = flat_amount/total_reagents
		var/passed_list = list()
		for(var/reagent_type in reagent_list)
			var/amount = fraction * reagent_list[reagent_type]
			reagent_list[reagent_type] -= amount
			total_reagents -= amount
			passed_list[reagent_type] = amount
		temporary_holder.add_reagent_list(passed_list)
		has_cached_share = FALSE
	temporary_holder.chem_temp = temperature
	return temporary_holder

//Returns a reagents holder with all the reagents with a higher volume than the threshold
/atom/movable/liquid/proc/simulate_reagents_threshold(amount_threshold)
	var/datum/reagents/temporary_holder = new(10000)
	var/passed_list = list()
	for(var/reagent_type in reagent_list)
		var/amount = reagent_list[reagent_type]
		if(amount_threshold && amount < amount_threshold)
			continue
		passed_list[reagent_type] = amount
	temporary_holder.add_reagent_list(passed_list)
	temporary_holder.chem_temp = temperature
	return temporary_holder

//Returns a flat of our reagents without any effects on the liquids
/atom/movable/liquid/proc/simulate_reagents_flat(flat_amount)
	var/datum/reagents/temporary_holder = new(10000)
	if(flat_amount >= total_reagents)
		temporary_holder.add_reagent_list(reagent_list)
	else
		var/fraction = flat_amount/total_reagents
		var/passed_list = list()
		for(var/reagent_type in reagent_list)
			var/amount = fraction * reagent_list[reagent_type]
			passed_list[reagent_type] = amount
		temporary_holder.add_reagent_list(passed_list)
	temporary_holder.chem_temp = temperature
	return temporary_holder

/atom/movable/liquid/proc/set_color_from_reagents()
	color = mix_color_from_reagent_list(reagent_list)
	return color

/atom/movable/liquid/proc/calculate_height()
	var/new_height = total_reagents/LIQUID_HEIGHT_DIVISOR
	set_height(new_height)
	var/determined_new_state
	//We add the turf height if it's possible to state calculations
	new_height =  CEILING(new_height, 1)
	switch(new_height)
		if(0 to LIQUID_ANKLES_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_PUDDLE
		if(LIQUID_ANKLES_LEVEL_HEIGHT to LIQUID_WAIST_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_ANKLES
		if(LIQUID_WAIST_LEVEL_HEIGHT to LIQUID_SHOULDERS_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_WAIST
		if(LIQUID_SHOULDERS_LEVEL_HEIGHT to LIQUID_FULLTILE_LEVEL_HEIGHT-1)
			determined_new_state = LIQUID_STATE_SHOULDERS
		if(LIQUID_FULLTILE_LEVEL_HEIGHT to INFINITY)
			determined_new_state = LIQUID_STATE_FULLTILE
	if(determined_new_state != liquid_state)
		set_new_liquid_state(determined_new_state)

/atom/movable/liquid/proc/set_height(new_height)
	var/prev_height = liquid_height
	liquid_height = new_height
	if(immutable)
		return
	if(abs(liquid_height - prev_height) >= WATER_HEIGHT_DIFFERENCE_SPLASH)
		//Splash
		if(prob(WATER_HEIGHT_DIFFERENCE_SPLASH))
			var/sound_to_play = "modular_septic/sound/liquids/wade[rand(1,4)].ogg"
			playsound(my_turf, sound_to_play, 60, 0)
		var/obj/splashy = new /obj/effect/temp_visual/liquid_splash(my_turf)
		splashy.color = color
		if(liquid_height >= LIQUID_WAIST_LEVEL_HEIGHT)
			//Push things into some direction, like space wind
			var/turf/dest_turf
			var/last_height = liquid_height
			var/turf/adjacent_cell //faster declaration
			for(var/neighbor in my_turf.atmos_adjacent_turfs)
				adjacent_cell = neighbor
				//Automatic winner
				if(!adjacent_cell.liquids)
					dest_turf = adjacent_cell
					break
				else if(adjacent_cell.liquids.liquid_height < last_height)
					dest_turf = adjacent_cell
					last_height = adjacent_cell.liquids.liquid_height
			if(dest_turf)
				var/direction = get_dir(my_turf, dest_turf)
				var/atom/movable/movable
				for(var/thing in my_turf)
					movable = thing
					if(movable.anchored || movable.pulledby || isdead(movable) || iscameramob(movable) || isliquid(movable) || (movable.move_resist > move_force))
						continue
					else if((dest_turf.z != my_turf.z) && !movable.can_zTravel(dest_turf, direction))
						continue
					if(ismob(movable))
						var/mob/mob_movable = movable
						if(mob_movable.buckled)
							continue
					if(iscarbon(movable))
						var/mob/living/carbon/carbon = movable
						if(!(carbon.shoes?.clothing_flags & NOSLIP))
							carbon.Move(dest_turf, direction)
							if(prob(60) && carbon.body_position != LYING_DOWN)
								to_chat(carbon, span_userdanger("The current knocks me down!"))
								carbon.CombatKnockdown(25, 5 SECONDS, 5 SECONDS)
					else
						movable.Move(dest_turf, direction)
						if(isitem(movable))
							var/obj/item/messy = movable
							messy.do_messy(duration = 4)
	return liquid_height

/atom/movable/liquid/proc/movable_entered(datum/source, atom/movable/movable)
	SIGNAL_HANDLER

	var/turf/source_turf = source
	if(isdead(movable) || iscameramob(movable))
		return //ghosts, camera eyes, etc - don't make water splashy splashy
	if(liquid_state >= LIQUID_STATE_ANKLES)
		if(prob(30))
			var/sound_to_play = "modular_septic/sound/liquids/wade[rand(1,4)].ogg"
			playsound(source_turf, sound_to_play, 50, 0)
		if(iscarbon(movable))
			var/mob/living/carbon/carbon = movable
			carbon.apply_status_effect(/datum/status_effect/water_affected)
	else if(isliving(movable))
		var/mob/living/living = movable
		if(prob(5) && !(living.movement_type & FLYING|FLOATING))
			living.slip(3 SECONDS, source_turf, NO_SLIP_WHEN_WALKING, 2 SECONDS)
	if(fire_state)
		movable.fire_act((T20C+50) + (50*fire_state), 125)

/atom/movable/liquid/proc/mob_fall(datum/source, mob/fell)
	var/turf/source_turf = source
	if(liquid_state >= LIQUID_STATE_ANKLES && source_turf.has_gravity(source_turf))
		playsound(source_turf, "modular_septic/sound/liquids/splash[rand(1, 2)].ogg", 50, 0)
		if(iscarbon(fell))
			var/mob/living/carbon/carbon = fell
			if(carbon.wear_mask && carbon.wear_mask.flags_cover & MASKCOVERSMOUTH)
				to_chat(carbon, span_userdanger("I fall in the liquid!"))
			else
				var/datum/reagents/temporary_holder = take_reagents_flat(CHOKE_REAGENTS_INGEST_ON_FALL_AMOUNT)
				temporary_holder.trans_to(carbon, temporary_holder.total_volume, methods = INGEST)
				qdel(temporary_holder)
				carbon.losebreath += 2
				carbon.agony_gargle()
				to_chat(carbon, span_userdanger("I fall in and swallow some liquid!"))
		else
			to_chat(fell, span_userdanger("I fall in the liquid!"))

//Exposes my_turf with simulated reagents
/atom/movable/liquid/proc/expose_my_turf()
	var/datum/reagents/temporary_holder = simulate_reagents_threshold(LIQUID_REAGENT_THRESHOLD_TURF_EXPOSURE)
	temporary_holder.expose(my_turf, TOUCH, temporary_holder.total_volume)
	qdel(temporary_holder)

/atom/movable/liquid/proc/change_to_new_turf(turf/new_turf)
	if(new_turf.liquids)
		stack_trace("Liquids tried to change to turf [new_turf], that already has liquids on it!")
		return FALSE

	UnregisterSignal(my_turf, list(COMSIG_ATOM_ENTERED, COMSIG_TURF_MOB_FALL))
	if(SSliquids.active_turfs[my_turf])
		SSliquids.active_turfs -= my_turf
		SSliquids.active_turfs[new_turf] = TRUE
	if(SSliquids.evaporation_queue[my_turf])
		SSliquids.evaporation_queue -= my_turf
		SSliquids.evaporation_queue[new_turf] = TRUE
	if(SSliquids.processing_fire[my_turf])
		SSliquids.processing_fire -= my_turf
		SSliquids.processing_fire[new_turf] = TRUE
	my_turf.liquids = null
	my_turf = new_turf
	new_turf.liquids = src
	loc = new_turf
	RegisterSignal(my_turf, COMSIG_ATOM_ENTERED, .proc/movable_entered)
	RegisterSignal(my_turf, COMSIG_TURF_MOB_FALL, .proc/mob_fall)

//STRICTLY FOR IMMUTABLE USE
/atom/movable/liquid/proc/add_turf(turf/added_turf)
	added_turf.liquids = src
	added_turf.vis_contents += src
	SSliquids.active_immutables[added_turf] = TRUE
	RegisterSignal(added_turf, COMSIG_ATOM_ENTERED, .proc/movable_entered)
	RegisterSignal(added_turf, COMSIG_TURF_MOB_FALL, .proc/mob_fall)

/atom/movable/liquid/proc/remove_turf(turf/removed_turf)
	SSliquids.active_immutables -= removed_turf
	removed_turf.liquids = null
	removed_turf.vis_contents -= src
	UnregisterSignal(removed_turf, list(COMSIG_ATOM_ENTERED, COMSIG_TURF_MOB_FALL))


code:
//Structure as this doesn't need any power to work
/obj/structure/drain
	name = "drain"
	icon = 'modular_septic/icons/obj/structures/liquid_drain.dmi'
	icon_state = "drain"
	desc = "Drainage inlet embedded in the floor to prevent flooding."
	resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
	density = FALSE
	plane = FLOOR_PLANE
	layer = GAS_SCRUBBER_LAYER
	anchored = TRUE
	var/processing = FALSE
	var/drain_flat = 5
	var/drain_percent = 0.1
	var/welded = FALSE
	var/turf/my_turf //need to keep track of it for the signal, if in any bizarre cases something would be moving the drain

/obj/structure/drain/update_icon()
	. = ..()
	if(welded)
		icon_state = "[initial(icon_state)]_welded"
	else
		icon_state = "[initial(icon_state)]"

/obj/structure/drain/welder_act(mob/living/user, obj/item/tool)
	. = ..()
	if(!tool.tool_start_check(user, amount=0))
		return TRUE

	playsound(src, 'sound/items/welder2.ogg', 50, TRUE)
	to_chat(user, span_notice("I start [welded ? "unwelding" : "welding"] [src]..."))
	if(tool.use_tool(src, user, 20))
		to_chat(user, span_notice("I [welded ? "unweld" : "weld"] [src]."))
		welded = !welded
		update_icon()
		if(welded)
			if(processing)
				STOP_PROCESSING(SSobj, src)
				processing = FALSE
		else if (my_turf.liquids)
			START_PROCESSING(SSobj, src)
			processing = TRUE
	return TRUE

/obj/structure/drain/process()
	if(!my_turf?.liquids || my_turf.liquids.immutable)
		STOP_PROCESSING(SSobj, src)
		processing = FALSE
		return
	my_turf.liquids.delete_reagents_flat(drain_flat + (drain_percent * my_turf.liquids.total_reagents))

/obj/structure/drain/Initialize()
	. = ..()
	if(!isturf(loc))
		stack_trace("Drain structure initialized not on a turf")
	else
		my_turf = loc
		RegisterSignal(my_turf, COMSIG_TURF_LIQUIDS_CREATION, .proc/liquids_signal)
		if(my_turf.liquids)
			START_PROCESSING(SSobj, src)
			processing = TRUE

/obj/structure/drain/proc/liquids_signal()
	SIGNAL_HANDLER
	if(processing || welded)
		return
	START_PROCESSING(SSobj, src)
	processing = TRUE

/obj/structure/drain/Destroy()
	if(processing)
		STOP_PROCESSING(SSobj, src)
	UnregisterSignal(my_turf, COMSIG_TURF_LIQUIDS_CREATION)
	my_turf = null
	return ..()

/obj/structure/drain/big
	desc = "Drainage inlet embedded in the floor to prevent flooding. This one seems large."
	icon_state = "bigdrain"
	drain_percent = 0.3
	drain_flat = 15


code:
/obj/machinery/plumbing/liquid_input_pump
	name = "liquid input pump"
	desc = "Pump used to siphon liquids from a location into the plumbing pipenet."
	icon = 'icons/obj/plumbing/plumbers.dmi'
	icon_state = "pump"
	anchored = FALSE
	density = TRUE
	idle_power_usage = 10
	active_power_usage = 1000
	buffer = 300

	var/turned_on = FALSE
	var/height_regulator = 0

	var/is_working = FALSE

	var/drain_flat = 20
	var/drain_percent = 0.4

/obj/machinery/plumbing/liquid_input_pump/Initialize(mapload, bolt)
	. = ..()
	AddComponent(/datum/component/plumbing/simple_supply, bolt)

/obj/machinery/plumbing/liquid_input_pump/update_icon_state()
	. = ..()
	if(panel_open)
		icon_state = initial(icon_state) + "-open"
	else if(is_working)
		icon_state = initial(icon_state) + "-on"
	else
		icon_state = initial(icon_state)

/obj/machinery/plumbing/liquid_input_pump/examine(mob/user)
	. = ..()
	. += span_notice("It's currently [turned_on ? "ON" : "OFF"].")
	. += span_notice("It's height regulator [height_regulator ? "points at [height_regulator]" : "is disabled"]. (Ctrl-click to change)")

/obj/machinery/plumbing/liquid_input_pump/default_unfasten_wrench(mob/user, obj/item/I, time)
	. = ..()
	if(. == SUCCESSFUL_UNFASTEN)
		turned_on = FALSE

/obj/machinery/plumbing/liquid_input_pump/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(!anchored)
		to_chat(user, span_warning("[src] needs to be anchored first!"))
		return
	to_chat(user, span_notice("I turn [src] [turned_on ? "off" : "on"]."))
	turned_on = !turned_on

/obj/machinery/plumbing/liquid_input_pump/CtrlClick(mob/user)
	. = ..()
	if(anchored)
		return ..()
	if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE, TRUE))
		return
	var/new_height = input(user, "Set new height regulation:\n([0]-[LIQUID_HEIGHT_CONSIDER_FULL_TILE]. Use 0 to disable the regulation)\nThe pump will only siphon if environment is above the regulation", "Liquid Pump") as num|null
	if(QDELETED(src))
		return
	if(new_height)
		height_regulator = sanitize_integer(new_height, 0, LIQUID_HEIGHT_CONSIDER_FULL_TILE, 0)

/obj/machinery/plumbing/liquid_input_pump/proc/can_pump()
	if(!turned_on || !anchored || panel_open || !isturf(loc) || (reagents.total_volume == reagents.maximum_volume))
		return FALSE

	var/turf/turf_loc = loc

	if(!turf_loc.liquids)
		return FALSE

	if(height_regulator && turf_loc.liquids.liquid_height < height_regulator)
		return FALSE

	return TRUE

/obj/machinery/plumbing/liquid_input_pump/process(delta_time)
	var/last_pumped = is_working
	is_working = can_pump()
	if(is_working != last_pumped)
		update_icon_state()
	if(!is_working)
		return

	var/turf/turf_loc = loc

	var/target_value = delta_time * (drain_flat + (turf_loc.liquids.total_reagents * drain_percent))
	//Free space handling
	var/free_space = reagents.maximum_volume - reagents.total_volume
	if(target_value > free_space)
		target_value = free_space

	var/datum/reagents/temporary_holder = turf_loc.liquids.take_reagents_flat(target_value)
	temporary_holder.trans_to(src, temporary_holder.total_volume)
	qdel(temporary_holder)

/obj/machinery/plumbing/liquid_output_pump
	name = "liquid output pump"
	desc = "Pump used to dump liquids out from a plumbing pipenet into a location."
	icon = 'icons/obj/plumbing/plumbers.dmi'
	icon_state = "pump"
	anchored = FALSE
	density = TRUE
	idle_power_usage = 10
	active_power_usage = 1000
	buffer = 300

	var/turned_on = FALSE
	var/height_regulator = 0

	var/is_working = FALSE

	var/drain_flat = 20
	var/drain_percent = 0.4

/obj/machinery/plumbing/liquid_output_pump/Initialize(mapload, bolt)
	. = ..()
	AddComponent(/datum/component/plumbing/simple_demand, bolt)

/obj/machinery/plumbing/liquid_output_pump/update_icon_state()
	. = ..()
	if(panel_open)
		icon_state = initial(icon_state) + "-open"
	else if(is_working)
		icon_state = initial(icon_state) + "-on"
	else
		icon_state = initial(icon_state)

/obj/machinery/plumbing/liquid_output_pump/examine(mob/user)
	. = ..()
	. += span_notice("It's currently [turned_on ? "ON" : "OFF"].")
	. += span_notice("It's height regulator [height_regulator ? "points at [height_regulator]" : "is disabled"]. (Ctrl-click to change)")

/obj/machinery/plumbing/liquid_output_pump/default_unfasten_wrench(mob/user, obj/item/I, time)
	. = ..()
	if(. == SUCCESSFUL_UNFASTEN)
		turned_on = FALSE

/obj/machinery/plumbing/liquid_output_pump/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(!anchored)
		to_chat(user, span_warning("[src] needs to be anchored first!"))
		return
	to_chat(user, span_notice("I turn [src] [turned_on ? "off" : "on"]."))
	turned_on = !turned_on

/obj/machinery/plumbing/liquid_output_pump/CtrlClick(mob/user)
	. = ..()
	if(anchored)
		return
	if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE, TRUE))
		return
	var/new_height = input(user, "Set new height regulation:\n([0]-[LIQUID_HEIGHT_CONSIDER_FULL_TILE]. Use 0 to disable the regulation)\nThe pump will only siphon if environment is below the regulation", "Liquid Pump") as num|null
	if(QDELETED(src))
		return
	if(new_height)
		height_regulator = sanitize_integer(new_height, 0, LIQUID_HEIGHT_CONSIDER_FULL_TILE, 0)

/obj/machinery/plumbing/liquid_output_pump/process(delta_time)
	var/last_pumped = is_working
	is_working = can_pump()
	if(is_working != last_pumped)
		update_icon_state()
	if(!is_working)
		return

	var/turf/turf_loc = loc

	var/target_value = delta_time * (drain_flat + (reagents.total_volume * drain_percent))
	if(target_value > reagents.total_volume)
		target_value = reagents.total_volume

	var/datum/reagents/tempr = new(10000)
	reagents.trans_to(tempr, target_value, no_react = TRUE)
	turf_loc.add_liquid_from_reagents(tempr)
	qdel(tempr)

/obj/machinery/plumbing/liquid_output_pump/proc/can_pump()
	if(!turned_on || !anchored || panel_open || !isturf(loc) || reagents.total_volume == 0)
		return FALSE
	if(height_regulator)
		var/turf/T = loc
		if(T.liquids && T.liquids.liquid_height >= height_regulator)
			return FALSE
	return TRUE

/obj/item/construction/plumbing/engineering
	name = "engineering plumbing constructor"
	desc = "A type of plumbing constructor designed to rapidly deploy the machines needed for logistics regarding fluids."
	icon = 'modular_septic/icons/obj/items/tools.dmi'
	icon_state = "plumberer_engi"
	has_ammobar = TRUE

/obj/item/construction/plumbing/engineering/set_plumbing_designs()
	plumbing_design_types = list(
	/obj/machinery/plumbing/input = 5,
	/obj/machinery/plumbing/output = 5,
	/obj/machinery/plumbing/tank = 20,
	/obj/machinery/plumbing/acclimator = 10,
	/obj/machinery/plumbing/filter = 5,
	/obj/machinery/plumbing/splitter = 5,
	/obj/machinery/plumbing/disposer = 10,
	/obj/machinery/plumbing/liquid_input_pump = 20,
	/obj/machinery/plumbing/liquid_output_pump = 20
)


code:
//Right now it's a structure that works off of magic, as it'd require an internal power source for what its supposed to do
/obj/structure/liquid_pump
	name = "portable liquid pump"
	desc = "An industrial grade pump, capable of either siphoning or spewing liquids. Needs to be anchored first to work. Has a limited capacity internal storage."
	icon = 'modular_septic/icons/obj/structures/liquid_pump.dmi'
	icon_state = "liquid_pump"
	density = TRUE
	max_integrity = 500
	anchored = FALSE
	resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
	/// How many reagents at maximum can it hold
	var/max_volume = 10000
	/// Whether spewing reagents out, instead of siphoning them
	var/spewing_mode = FALSE
	/// Whether its turned on and processing
	var/turned_on = FALSE
	/// How fast does the pump work, in percentages relative to the volume we're working with
	var/pump_speed_percentage = 0.4
	/// How fast does the pump work, in flat values. Flat values on top of percentages to help processing
	var/pump_speed_flat = 20

/obj/structure/liquid_pump/wrench_act(mob/living/user, obj/item/tool)
	. = ..()
	default_unfasten_wrench(user, tool, 40)
	if(!anchored && turned_on)
		toggle_working()
	return TRUE

/obj/structure/liquid_pump/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(!anchored)
		to_chat(user, span_warning("[src] needs to be anchored first!"))
		return
	to_chat(user, span_notice("I turn [src] [turned_on ? "off" : "on"]."))
	toggle_working()

/obj/structure/liquid_pump/AltClick(mob/living/user)
	if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE, TRUE))
		return
	to_chat(user, span_notice("I flick [src]'s spewing mode [spewing_mode ? "off" : "on"]."))
	spewing_mode = !spewing_mode
	update_icon()

/obj/structure/liquid_pump/examine(mob/user)
	. = ..()
	. += span_notice("It's anchor bolts are [anchored ? "down and secured" : "up"].")
	. += span_notice("It's currently [turned_on ? "ON" : "OFF"].")
	. += span_notice("It's mode currently is set to [spewing_mode ? "SPEWING" : "SIPHONING"].</span> (Alt-click to switch)")
	. += span_notice("The pressure gauge shows [reagents.total_volume]/[reagents.maximum_volume].")

/obj/structure/liquid_pump/process()
	if(!isturf(loc))
		return
	var/turf/turf_loc = loc
	if(spewing_mode)
		if(!reagents.total_volume)
			return
		var/datum/reagents/tempr = new(10000)
		reagents.trans_to(tempr, (reagents.total_volume * pump_speed_percentage) + pump_speed_flat, no_react = TRUE)
		turf_loc.add_liquid_from_reagents(tempr)
		qdel(tempr)
	else
		if(!turf_loc.liquids)
			return
		var/free_space = reagents.maximum_volume - reagents.total_volume
		if(!free_space)
			return
		var/target_siphon_amt = (turf_loc.liquids.total_reagents * pump_speed_percentage) + pump_speed_flat
		if(target_siphon_amt > free_space)
			target_siphon_amt = free_space
		var/datum/reagents/tempr = turf_loc.liquids.take_reagents_flat(target_siphon_amt)
		tempr.trans_to(reagents, tempr.total_volume)
		qdel(tempr)
	return

/obj/structure/liquid_pump/update_icon()
	. = ..()
	if(turned_on)
		if(spewing_mode)
			icon_state = "[initial(icon_state)]_spewing"
		else
			icon_state = "[initial(icon_state)]_siphoning"
	else
		icon_state = "[initial(icon_state)]"

/obj/structure/liquid_pump/proc/toggle_working()
	if(turned_on)
		STOP_PROCESSING(SSobj, src)
	else
		START_PROCESSING(SSobj, src)
	turned_on = !turned_on
	update_icon()

/obj/structure/liquid_pump/Initialize()
	. = ..()
	create_reagents(max_volume)

/obj/structure/liquid_pump/Destroy()
	if(turned_on)
		STOP_PROCESSING(SSobj, src)
	qdel(reagents)
	return ..()