code:
/obj/item/healthanalyzer
	carry_weight = 1
	var/datum/weakref/analyzing
	var/current_dummy_key
	var/current_funny_quote = ""

/obj/item/healthanalyzer/interact(mob/user)
	add_fingerprint(user)

/obj/item/healthanalyzer/attack_self(mob/user, modifiers)
	. = ..()
	if(user.is_blind())
		to_chat(user, span_warning("The scanner doesn't support braille. Damn."))
		return
	if(analyzing)
		ui_interact(user)

/obj/item/healthanalyzer/attack(mob/living/M, mob/living/user, params)
	. = ..()
	if(user.is_blind())
		to_chat(user, span_warning("The scanner doesn't support braille. Damn."))
		return
	if(DOING_INTERACTION_WITH_TARGET(user, M))
		return
	user.visible_message(span_notice("[user] tries to scan [M] with [src]."),
					span_notice("I try to scan [M] with [src]."),
					ignored_mobs = M)
	to_chat(M, span_notice("[user] tries to scan me with [src]."))
	if(!do_mob(user, M, 1 SECONDS))
		to_chat(user, span_warning("I must stay still!"))
		return
	user.visible_message(span_notice("[user] scans [M] with [src]."),
					span_notice("I scan [M] with [src]."),
					ignored_mobs = M)
	to_chat(M, span_notice("[user] scans me with [src]."))
	analyzing = WEAKREF(M)
	var/mob/living/carbon/human/human_mob
	if(ishuman(M))
		human_mob = M
	var/patient_initials = get_name_initials(M.name)
	var/patient_age = (human_mob ? human_mob.age : 1)
	var/list/funny_quotes = list(
		"-emia meaning presence in blood.",
		"\"[patient_initials]\" is a [patient_age] year old [M.gender] presenting to the emergency room...",
		"This is what happened to [M.p_their()] [pick("heart", "liver", "stomach", "brain", "kidneys", "organs", "intestines")].",
		"This is how [M.p_their()] [pick("organs", "kidneys", "stomach", "intestines", "liver")] shut down.",
		"Heme Review!",
		"150 gummy vitamins.",
		"25 packs of silica gel.",
		"2 liters of fiber supplement.",
		"3 laundry pods.",
		"An apple a day keeps the doctor away.",
		"Surgeon Simulator [GLOB.year_integer]",
	)
	current_funny_quote = pick(funny_quotes)
	ui_interact(user)

/obj/item/healthanalyzer/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "HealthAnalyzer")
		ui.open()

/obj/item/healthanalyzer/ui_status(mob/user)
	var/mob/living/analyzed_mob = analyzing?.resolve()
	if(!analyzed_mob)
		return UI_CLOSE
	if(!(user.is_holding(src)))
		return UI_CLOSE
	return min(
		ui_status_user_is_advanced_tool_user(user),
		ui_status_only_living(user, src),
		ui_status_user_has_free_hands(user, src),
		ui_status_user_is_adjacent(user, src),
		ui_status_user_is_adjacent(user, analyzed_mob),
		max(
			ui_status_user_is_conscious_and_lying_down(user),
			ui_status_user_is_abled(user, src),
		),
	)

/obj/item/healthanalyzer/ui_state(mob/user)
	return GLOB.always_state

/obj/item/healthanalyzer/ui_data(mob/user)
	var/list/data = list()
	var/mob/living/analyzed_mob = analyzing?.resolve()
	if(!istype(analyzed_mob))
		return data
	var/mob/living/carbon/carbon_mob
	if(iscarbon(analyzed_mob))
		carbon_mob = analyzed_mob
	var/mob/living/carbon/human/human_mob
	if(ishuman(analyzed_mob))
		human_mob = analyzed_mob
	var/is_advanced = (advanced || analyzed_mob.has_reagent(/datum/reagent/inverse/technetium))
	switch(analyzed_mob.stat)
		if(DEAD)
			data["status"] = "Dead"
		if(UNCONSCIOUS, HARD_CRIT)
			data["status"] = "Unconcious"
		else
			if(HAS_TRAIT(analyzed_mob, TRAIT_FAKEDEATH) && !is_advanced)
				data["status"] = "Dead"
			else
				data["status"] = "Conscious"
	data["catatonic"] = FALSE
	if(!analyzed_mob.key && !(analyzed_mob.ai_controller?.ai_status == AI_STATUS_ON))
		data["catatonic"] = "Catatonic"
	else if(!analyzed_mob.client)
		data["catatonic"] = "Space Sleep Disorder"
	data["anencephalic"] = (!analyzed_mob.getorganslot(ORGAN_SLOT_BRAIN) ? "Anencephalic" : FALSE)
	data["brain_activity"] = CEILING(analyzed_mob.health/analyzed_mob.maxHealth*100, 1)
	data["overall_damage"] = analyzed_mob.get_physical_damage()
	data["oxyloss"] = analyzed_mob.getOxyLoss()
	data["toxloss"] = analyzed_mob.getToxLoss()
	data["cloneloss"] = analyzed_mob.getCloneLoss()
	data["fireloss"] = analyzed_mob.getFireLoss()
	data["bruteloss"] = analyzed_mob.getBruteLoss()
	data["staminaloss"] = analyzed_mob.getStaminaLoss()
	data["traumatic_shock"] = analyzed_mob.getShock()
	data["shock_stage"] = analyzed_mob.getShockStage()
	data["husked"] = FALSE
	if(is_advanced && HAS_TRAIT_FROM(analyzed_mob, TRAIT_HUSK, BURN))
		data["husked"] = "Husked by burns"
	else if(is_advanced && HAS_TRAIT_FROM(analyzed_mob, TRAIT_HUSK, CHANGELING_DRAIN))
		data["husked"] = "Husked by dessication"
	else if(HAS_TRAIT(analyzed_mob, TRAIT_HUSK))
		data["husked"] = "Husked by unknown source"
	data["disfigured"] = FALSE
	if(is_advanced && HAS_TRAIT_FROM(analyzed_mob, TRAIT_DISFIGURED, BRUTE))
		data["disfigured"] = "Disfigured by brute trauma"
	else if(is_advanced && HAS_TRAIT_FROM(analyzed_mob, TRAIT_DISFIGURED, BURN))
		data["disfigured"] = "Disfigured by burns"
	else if(is_advanced && HAS_TRAIT_FROM(analyzed_mob, TRAIT_DISFIGURED, GERM_LEVEL))
		data["disfigured"] = "Disfigured by gangrene"
	else if(HAS_TRAIT(analyzed_mob, TRAIT_DISFIGURED))
		data["disfigured"] = "Disfigured by unknown source"
	data["brain_traumas"] = list()
	for(var/datum/brain_trauma/brain_trauma as anything in carbon_mob.get_traumas())
		var/list/this_trauma = list()
		this_trauma["name"] = brain_trauma.name
		switch(brain_trauma.resilience)
			if(TRAUMA_RESILIENCE_BASIC)
				this_trauma["resilience"] = "Moderate"
			if(TRAUMA_RESILIENCE_SURGERY)
				this_trauma["resilience"] = "Severe"
			if(TRAUMA_RESILIENCE_LOBOTOMY)
				this_trauma["resilience"] = "Deep-Rooted "
			if(TRAUMA_RESILIENCE_WOUND)
				this_trauma["resilience"] = "Trauma-derived "
			if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
				this_trauma["resilience"] = "Permanent"
		this_trauma["scan_desc"] = brain_trauma.desc

		data["brain_traumas"] += list(this_trauma)
	data["major_disabilities"] = list()
	if(carbon_mob)
		for(var/datum/quirk/disability in carbon_mob.get_quirks(CAT_QUIRK_MAJOR_DISABILITY))
			var/list/this_disability = list()
			this_disability["name"] = disability.name
			this_disability["desc"] = disability.desc
			this_disability["scan_desc"] = disability.medical_record_text

			data["major_disabilities"] += list(this_disability)
	data["minor_disabilities"] = list()
	if(carbon_mob)
		for(var/datum/quirk/disability in carbon_mob.get_quirks(CAT_QUIRK_MINOR_DISABILITY))
			var/list/this_disability = list()
			this_disability["name"] = disability.name
			this_disability["desc"] = disability.desc
			this_disability["scan_desc"] = disability.medical_record_text

			data["minor_disabilities"] += list(this_disability)
	data["neutral_disabilities"] = list()
	if(is_advanced && carbon_mob)
		for(var/datum/quirk/disability in carbon_mob.get_quirks(CAT_QUIRK_NOTES))
			var/list/this_disability = list()
			this_disability["name"] = disability.name
			this_disability["desc"] = disability.desc
			this_disability["scan_desc"] = disability.medical_record_text

			data["neutral_disabilities"] += list(this_disability)
	data["cardiac_arrest"] = (carbon_mob?.undergoing_cardiac_arrest() ? "Cardiac Arest" : FALSE)
	data["radiation"] = analyzed_mob.radiation
	data["hallucinating"] = FALSE
	if(is_advanced)
		data["hallucinating"] = (analyzed_mob.hallucinating() ? "Hallucinating" : FALSE)
	data["bodyparts"] = list()
	data["organs"] = list()
	if(carbon_mob)
		data["bodyparts"] = list()
		for(var/zone in ALL_BODYPARTS)
			var/obj/item/bodypart/bodypart = carbon_mob.get_bodypart_nostump(zone)
			var/list/this_bodypart = list()
			this_bodypart["zone"] = capitalize_like_old_man(parse_zone(zone))
			if(!bodypart)
				this_bodypart["name"] = "Missing"
			else
				this_bodypart["name"] = capitalize_like_old_man(bodypart.name)
				switch(bodypart.status)
					if(BODYPART_ORGANIC)
						this_bodypart["status"] = "Organic"
					else
						this_bodypart["status"] = "Robotic"
				this_bodypart["max_damage"] = bodypart.max_damage
				this_bodypart["overall_damage"] = (bodypart.brute_dam + bodypart.burn_dam)
				this_bodypart["bruteloss"] = bodypart.brute_dam
				this_bodypart["fireloss"] = bodypart.burn_dam
				this_bodypart["max_stam_damage"] = bodypart.max_stamina_damage
				this_bodypart["staminaloss"] = bodypart.stamina_dam
				this_bodypart["max_pain"] = bodypart.max_pain_damage
				this_bodypart["traumatic_shock"] = bodypart.get_shock(TRUE, TRUE)
				this_bodypart["germ_level"] = CEILING(bodypart.germ_level/INFECTION_LEVEL_THREE*100, 1)
				this_bodypart["organs"] = list()
				for(var/obj/item/organ/organ as anything in bodypart.get_organs())
					if(organ.scanner_hidden)
						continue
					var/list/this_organ = list()
					this_organ["name"] = capitalize_like_old_man(organ.name)
					switch(organ.status)
						if(ORGAN_ORGANIC)
							this_organ["status"] = "Organic"
						else
							this_organ["status"] = "Robotic"
					this_organ["overall_damage"] = "Healthy"
					if(organ.is_necrotic())
						this_organ["overall_damage"] = "Necrotic"
					else if(organ.is_dead())
						this_organ["overall_damage"] = "Destroyed"
					else if(organ.is_failing())
						this_organ["overall_damage"] = "Failing"
					else if(organ.damage >= organ.high_threshold)
						this_organ["overall_damage"] = "Severely Damaged"
					else if(organ.damage >= organ.low_threshold)
						this_organ["overall_damage"] = "Mildly Damaged"
					else if(organ.damage && is_advanced)
						this_organ["overall_damage"] = "Minorly Damaged"
					this_organ["max_damage"] = organ.maxHealth
					this_organ["damage"] = organ.damage
					this_organ["germ_level"] = CEILING(organ.germ_level/INFECTION_LEVEL_THREE*100, 1)
					var/list/efficiencies = list()
					for(var/slot in organ.organ_efficiency)
						var/list/this_efficiency = list()
						this_efficiency["slot"] = capitalize_like_old_man(slot)
						this_efficiency["value"] = organ.organ_efficiency[slot]

						efficiencies += list(this_efficiency)
					this_organ["efficiencies"] = efficiencies

					this_bodypart["organs"] += list(this_organ)
			data["bodyparts"] += list(this_bodypart)

	data["genetic_stability"] = 100
	if(is_advanced && carbon_mob)
		data["genetic_stability"] = carbon_mob.dna.stability
	data["species"] = "Unknown"
	data["core_temperature"] = analyzed_mob.bodytemperature
	if(human_mob)
		data["core_temperature"] = human_mob.coretemperature
		data["species"] = human_mob.dna.species.name
	data["body_temperature"] = analyzed_mob.bodytemperature
	data["timeofdeath"] = DisplayTimeText(analyzed_mob.timeofdeath)
	data["diseases"] = list()
	for(var/datum/disease/disease as anything in analyzed_mob.diseases)
		var/list/disease_data = list()
		disease_data["name"] = disease.name
		disease_data["form"] = disease.form
		disease_data["agent"] = disease.agent
		disease_data["type"] = disease.spread_text
		disease_data["stage"] = disease.stage
		disease_data["max_stage"] = disease.max_stages
		disease_data["cure"] = disease.cure_text
		disease_data["severity"] = disease.severity
		disease_data["hopelessness"] = disease.hopelessness

		data["diseases"] += list(disease_data)
	var/blood_id = analyzed_mob.get_blood_id()
	var/datum/reagent/bloodreagent = blood_id
	data["blood_id"] = blood_id
	data["blood_name"] = "Blood"
	if(blood_id)
		data["blood_name"] = initial(bloodreagent.name)
	data["blood_type"] = "Unknown"
	data["bleed_rate"] = 0
	if(carbon_mob)
		data["bleed_rate"] = carbon_mob.get_total_bleed_rate()
		if(carbon_mob.has_dna())
			var/blood_type = carbon_mob.dna.blood_type
			if(blood_id != /datum/reagent/blood) // special blood substance
				var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
				blood_type = R ? R.name : blood_id
			data["blood_type"] = blood_type
		data["pulse"] = carbon_mob.get_pulse(is_advanced ? GETPULSE_ADVANCED : GETPULSE_BASIC)
		var/normal_volume = GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_NORMAL, carbon_mob.total_blood_req)
		data["normal_blood_volume"] = normal_volume
		var/okay_volume = GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_NORMAL, carbon_mob.total_blood_req)
		data["okay_blood_volume"] = okay_volume
		var/safe_volume = GET_EFFECTIVE_BLOOD_VOL(BLOOD_VOLUME_SAFE, carbon_mob.total_blood_req)
		data["safe_blood_volume"] = safe_volume
		var/volume = carbon_mob.blood_volume
		data["blood_volume"] = volume
		data["blood_volume_perc"] = CEILING(volume/normal_volume*100, 1)
		var/circulation = carbon_mob.get_blood_circulation()
		data["blood_circulation"] = circulation
		data["blood_circulation_perc"] = CEILING(circulation/normal_volume*100, 1)
		var/oxygenation = carbon_mob.get_blood_oxygenation()
		data["blood_oxygenation"] = oxygenation
		data["blood_oxygenation_perc"] = CEILING(oxygenation/normal_volume*100, 1)
	if(analyzed_mob.reagents)
		data["blood_reagents"] = list()
		for(var/datum/reagent/reagent as anything in analyzed_mob.reagents.reagent_list)
			//Don't show hidden chems
			if(reagent.chemical_flags & REAGENT_INVISIBLE)
				continue
			else if(!is_advanced && (reagent.chemical_flags & REAGENT_VISIBLE_ADVANCED))
				continue
			var/list/this_reagent = list()
			this_reagent["name"] = capitalize_like_old_man(reagent.name)
			this_reagent["volume"] = CEILING(reagent.volume, 0.001)
			this_reagent["overdosing"] = reagent.overdosed

			data["blood_reagents"] += list(this_reagent)
		data["stomach_reagents"] = list()
		var/list/stomachs = analyzed_mob.getorganslotlist(ORGAN_SLOT_STOMACH)
		for(var/obj/item/organ/belly as anything in stomachs)
			for(var/datum/reagent/nommed as anything in belly.reagents?.reagent_list)
				//Don't show hidden chems
				if(nommed.chemical_flags & REAGENT_INVISIBLE)
					continue
				var/list/this_reagent = list()
				var/this_volume = nommed.volume
				if(belly.food_reagents[nommed.type])
					this_volume = max(this_volume - belly.food_reagents[nommed.type], 0)
				if(!this_volume)
					continue
				this_reagent["name"] = capitalize_like_old_man(nommed.name)
				this_reagent["volume"] = CEILING(this_volume, 0.001)
				this_reagent["overdosing"] = nommed.overdosed

				data["stomach_reagents"] += list(this_reagent)
		data["intestine_reagents"] = list()
		var/list/intestines = analyzed_mob.getorganslotlist(ORGAN_SLOT_INTESTINES)
		for(var/obj/item/organ/shitter as anything in intestines)
			for(var/datum/reagent/shitted as anything in shitter.reagents?.reagent_list)
				//Don't show hidden chems
				if(shitted.chemical_flags & REAGENT_INVISIBLE)
					continue
				var/list/this_reagent = list()
				var/this_volume = shitted.volume
				if(shitter.food_reagents[shitted.type])
					this_volume = max(this_volume - shitter.food_reagents[shitted.type], 0)
				if(!this_volume)
					continue
				this_reagent["name"] = capitalize_like_old_man(shitted.name)
				this_reagent["volume"] = CEILING(this_volume, 0.001)
				this_reagent["overdosing"] = shitted.overdosed

				data["intestine_reagents"] += list(this_reagent)
		data["bladder_reagents"] = list()
		var/list/bladder = analyzed_mob.getorganslotlist(ORGAN_SLOT_BLADDER)
		for(var/obj/item/organ/pisser as anything in bladder)
			for(var/datum/reagent/pissed as anything in pisser.reagents?.reagent_list)
				//Don't show hidden chems
				if(pissed.chemical_flags & REAGENT_INVISIBLE)
					continue
				var/list/this_reagent = list()
				var/this_volume = pissed.volume
				if(pisser.food_reagents[pissed.type])
					this_volume = max(this_volume - pisser.food_reagents[pissed.type], 0)
				if(!this_volume)
					continue
				this_reagent["name"] = capitalize_like_old_man(pissed.name)
				this_reagent["volume"] = CEILING(this_volume, 0.001)
				this_reagent["overdosing"] = pissed.overdosed

				data["bladder_reagents"] += list(this_reagent)
		data["addictions"] = list()
		for(var/datum/addiction/addiction as anything in analyzed_mob.mind?.active_addictions)
			var/list/this_addiction = list()
			this_addiction["name"] = capitalize_like_old_man(initial(addiction.name))
			this_addiction["addiction_points"] = analyzed_mob.mind.addiction_points[addiction]

			data["addictions"] += list(this_addiction)
	return data

/obj/item/healthanalyzer/ui_static_data(mob/user)
	var/list/data = list()

	var/mob/living/analyzed_mob = analyzing?.resolve()
	if(!istype(analyzed_mob))
		data["patient"] = "literally no one"
		return data
	var/is_advanced = (advanced || analyzed_mob.has_reagent(/datum/reagent/inverse/technetium))
	data["advanced"] = is_advanced
	data["patient"] = analyzed_mob.name
	data["funnyquote"] = current_funny_quote
	if(!current_dummy_key)
		init_dummy()
	//We tried to init dummykey, but this may be a simple mob
	if(current_dummy_key)
		var/icon/dummysprite = get_flat_human_icon(null,
			dummy_key = current_dummy_key,
			showDirs = list(SOUTH))
		data["icon64"] = icon2base64(dummysprite)

	return data

/obj/item/healthanalyzer/ui_close(mob/user)
	. = ..()
	clear_human_dummy(current_dummy_key)

/obj/item/healthanalyzer/proc/init_dummy()
	var/mob/living/carbon/human/analyzed_human = analyzing?.resolve()
	if(!istype(analyzed_human))
		return
	current_dummy_key = "healthanalyzerUI_[analyzed_human]"
	generate_dummy_lookalike(current_dummy_key, analyzed_human)
	unset_busy_human_dummy(current_dummy_key)
	return


code:
/obj/item/fireaxe/Initialize()
	. = ..()
	AddElement(/datum/element/fireaxe_breaking)

repo_name: AnalWerewolf/sundown

license: mit


code:
/obj/item/food
	carry_weight = 0.5


code:
/obj/item/food/fishmeat/moonfish/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/grilled_moonfish, rand(40 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_fish)

/obj/item/food/spiderleg/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/boiledspiderleg, rand(50 SECONDS, 60 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/raw_meatball/MakeGrillable()
	AddComponent(/datum/component/grillable, meatball_type, rand(30 SECONDS, 40 SECONDS), TRUE, pollutant_type = /datum/pollutant/food/fried_meat)

/obj/item/food/raw_patty/MakeGrillable()
	AddComponent(/datum/component/grillable, patty_type, rand(30 SECONDS, 40 SECONDS), TRUE, TRUE, pollutant_type = /datum/pollutant/food/fried_meat)

/obj/item/food/raw_sausage/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/sausage, rand(60 SECONDS, 75 SECONDS), TRUE, TRUE, pollutant_type = /datum/pollutant/food/fried_meat)

/obj/item/food/meat/slab/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/plain, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat) //Add medium rare later maybe?

/obj/item/food/meat/slab/human/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/plain/human, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat) //Add medium rare later maybe?

/obj/item/food/meat/slab/human/mutant/lizard/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/plain/human/lizard, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/slab/meatproduct/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/meatproduct, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/slab/bear/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/bear, rand(40 SECONDS, 70 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/slab/xeno/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/xeno, rand(40 SECONDS, 70 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/slab/spider/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/spider, rand(40 SECONDS, 70 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawbacon/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/bacon, rand(25 SECONDS, 45 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_bacon)

/obj/item/food/meat/slab/gondola/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/gondola, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat) //Add medium rare later maybe?

/obj/item/food/meat/slab/penguin/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/penguin, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_chicken) //Add medium rare later maybe?

/obj/item/food/meat/slab/rawcrab/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/crab, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_fish) //Add medium rare later maybe?

/obj/item/food/meat/slab/chicken/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/steak/chicken, rand(30 SECONDS, 90 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_chicken) //Add medium rare later maybe? (no this is chicken)

/obj/item/food/meat/rawcutlet/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/plain, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawcutlet/plain/human/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/plain/human, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawcutlet/bear/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/bear, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawcutlet/xeno/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/xeno, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawcutlet/spider/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/spider, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawcutlet/gondola/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/gondola, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_meat)

/obj/item/food/meat/rawcutlet/penguin/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/penguin, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_chicken)

/obj/item/food/meat/rawcutlet/chicken/MakeGrillable()
	AddComponent(/datum/component/grillable, /obj/item/food/meat/cutlet/penguin, rand(35 SECONDS, 50 SECONDS), TRUE, TRUE, /datum/pollutant/food/fried_chicken)


code:
/obj/item/food/pancakes/raw/MakeGrillable()
	AddComponent(/datum/component/grillable,\
				cook_result = /obj/item/food/pancakes,\
				required_cook_time = rand(30 SECONDS, 40 SECONDS),\
				positive_result = TRUE,\
				use_large_steam_sprite = TRUE,\
				pollutant_type = /datum/pollutant/food/pancakes)


code:
/obj/item/mop
	carry_weight = 1.5
	mopcap = 50

/obj/item/mop/proc/attack_on_liquids_turf(obj/item/mop/the_mop, turf/T, mob/user, atom/movable/liquid/liquids)
	var/free_space = the_mop.reagents.maximum_volume - the_mop.reagents.total_volume
	if(free_space <= 0)
		to_chat(user, span_warning("My mop can't absorb any more!"))
		return TRUE
	var/datum/reagents/temporary_holder = liquids.take_reagents_flat(free_space)
	temporary_holder.trans_to(the_mop.reagents, temporary_holder.total_volume)
	to_chat(user, span_notice("I soak the mop with some liquids."))
	qdel(temporary_holder)
	user.changeNext_move(attack_delay)
	return TRUE

/obj/item/mop/advanced
	mopcap = 100


code:
/obj/item/shit
	name = "shit"
	desc = "Human fecal matter. How pleasant."
	icon = 'modular_septic/icons/obj/items/shit.dmi'
	icon_state = "poo1"
	base_icon_state = "poo"
	germ_level = MAXIMUM_GERM_LEVEL
	//crewmates be putting out massive turds
	carry_weight = 0.5

/obj/item/shit/Initialize()
	. = ..()
	icon_state = "[base_icon_state][rand(1, 6)]"
	AddComponent(/datum/component/edible, \
		initial_reagents = list(/datum/reagent/consumable/shit = 15), \
		foodtypes = TOXIC|GROSS|SEWAGE, \
		volume = 115, \
		after_eat = CALLBACK(src, .proc/on_eat_from))

/obj/item/shit/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
	. = ..()
	if(HAS_SHIT_DNA(src))
		hit_atom.add_shit_DNA(return_shit_DNA())
	if(reagents.total_volume)
		var/modifier = (10/reagents.total_volume)
		reagents.expose(hit_atom, TOUCH, modifier)

/obj/item/shit/proc/on_eat_from(mob/living/carbon/eater, mob/living/feeder)
	//contract bad
	var/datum/disease/advance/random/random_dysentery = new()
	var/static/list/funny_names
	if(!funny_names)
		funny_names = list("Ligma", "Sugma", "Colon Cancer", "Dysentery", "Hemorrhoids", "Taeniasis", "Anal Prolapse", "Megacolon", "Acidic Reflux", "Constipation", "Anal Fistula", "Spastic Colon", "Indigestion", "Peptic Ulcers", "Gastritis")
	random_dysentery.name = pick(funny_names)
	random_dysentery.form = "Parasitic Infection"
	random_dysentery.agent = "Parasites"
	random_dysentery.spread_text = "Human Waste"
	random_dysentery.hopelessness = "Considerable"
	random_dysentery.severity = DISEASE_SEVERITY_HARMFUL
	random_dysentery.try_infect(eater)


code:
/obj/item/stack
	/// If this gets applied as a splint or gauze, this is the overlay icon_state prefix
	var/medicine_overlay_prefix = ""


code:
/obj/item/stack/medical
	icon = 'modular_septic/icons/obj/items/stack_medical.dmi'
	lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
	righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
	amount = 10
	max_amount = 10
	/// Limb status we can get used on
	var/required_status = BODYPART_ORGANIC

/obj/item/stack/medical/try_heal(mob/living/patient, mob/user, silent = FALSE)
	if(!patient.try_inject(user, injection_flags = INJECT_TRY_SHOW_ERROR_MESSAGE))
		return
	if(patient == user)
		if(self_delay)
			if(!silent)
				user.visible_message(span_notice("[user] starts to apply [src] on [user.p_themselves()]..."), \
					span_notice("I begin applying [src] on myself..."))
			if(!do_mob(user, patient, self_delay, extra_checks=CALLBACK(patient, /mob/living/proc/try_inject, user, null, INJECT_TRY_SHOW_ERROR_MESSAGE)))
				return
	else if(other_delay)
		if(!silent)
			user.visible_message(span_notice("[user] starts to apply [src] on [patient]."), \
					span_notice("I begin applying [src] on [patient]..."))
		if(!do_mob(user, patient, other_delay, extra_checks=CALLBACK(patient, /mob/living/proc/try_inject, user, null, INJECT_TRY_SHOW_ERROR_MESSAGE)))
			return

	if(heal(patient, user))
		log_combat(user, patient, "healed", src.name)
		use(1)
		if(repeating && amount > 0)
			try_heal(patient, user, TRUE)

/obj/item/stack/medical/heal(mob/living/patient, mob/user)
	if(iscarbon(patient))
		return heal_carbon(patient, user, heal_brute, heal_burn)
	else if(isanimal(patient) && heal_brute) // only brute can heal
		var/mob/living/simple_animal/critter = patient
		if (!critter.healable)
			to_chat(user, span_warning("I cannot use [src] on [patient]!"))
			return FALSE
		else if (critter.health == critter.maxHealth)
			to_chat(user, span_notice("[patient] is at full health."))
			return FALSE
		user.visible_message(span_green("[user] applies [src] on [patient]."), \
					span_green("I apply [src] on [patient]."))
		patient.heal_bodypart_damage(heal_brute)
		return TRUE
	to_chat(user, span_warning("I can't heal [patient] with [src]!"))

/obj/item/stack/medical/heal_carbon(mob/living/carbon/C, mob/user, brute, burn)
	var/obj/item/bodypart/affecting = C.get_bodypart(check_zone(user.zone_selected))
	if(!affecting) //Missing limb?
		to_chat(user, span_warning("[C] doesn't have \a [parse_zone(user.zone_selected)]!"))
		return FALSE
	if(required_status && (affecting.status != required_status))
		to_chat(user, span_warning("[src] won't work on that limb!"))
		return FALSE
	if(affecting.brute_dam && brute || affecting.burn_dam && burn)
		user.visible_message(span_green("[user] applies [src] on [C]'s [affecting.name]."), \
						span_green("I apply [src] on [C]'s [affecting.name]."))
		var/previous_damage = affecting.get_damage()
		if(affecting.heal_damage(brute, burn))
			C.update_damage_overlays()
		post_heal_effects(max(previous_damage - affecting.get_damage(), 0), C, user)
		return TRUE
	to_chat(user, span_warning("[C]'s [affecting.name] can not be healed with [src]!"))
	return FALSE

/obj/item/stack/medical/bruise_pack
	name = "bandage pack"
	singular_name = "bandage pack"
	desc = "A simple pack of small compression bandages and gauze roller bandages."
	amount = 10
	max_amount = 10
	self_delay = 0.3 SECONDS //These will get multiplied by damage
	other_delay = 0.15 SECONDS
	repeating = TRUE

/obj/item/stack/medical/bruise_pack/heal_carbon(mob/living/carbon/C, mob/user, brute, burn)
	var/obj/item/bodypart/affecting = C.get_bodypart(check_zone(user.zone_selected))
	if(!affecting) //Missing limb?
		to_chat(user, span_warning("[C] doesn't have \a [parse_zone(user.zone_selected)]!"))
		return FALSE
	if(required_status && (affecting.status != required_status))
		to_chat(user, span_warning("[src] won't work on that limb!"))
		return FALSE
	for(var/thing in affecting.injuries)
		var/datum/injury/injury = thing
		if(injury.is_bandaged())
			continue
		var/time = (user == C ? self_delay : other_delay ) * injury.damage
		time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_DEXTERITY), 1))
		if(!do_mob(user, C, time))
			to_chat(user, span_warning("I must stand still!"))
			return
		if(!use(1))
			to_chat(user, span_warning("All used up..."))
			return
		//bandage packs are VERY EASY to use
		if(user.diceroll(GET_MOB_ATTRIBUTE_VALUE(user, SKILL_MEDICINE)+GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE)+10, 20, 2, 20) <= DICE_FAILURE)
			to_chat(user, span_warning(fail_msg()))
			return
		if(injury.current_stage <= injury.max_bleeding_stage)
			user.visible_message(span_green("[user] bandages \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
								span_green("I bandage \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		else if(injury.damage_type == WOUND_BLUNT)
			user.visible_message(span_green("[user] places an compression bandage over \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
								span_green("I place an compression bandage over \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		else if(injury.damage_type == WOUND_BURN)
			user.visible_message(span_green("[user] dresses \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
								span_green("I dress \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		else
			user.visible_message(span_green("[user] places a bandaid over \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
								span_green("I place a bandaid over \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		injury.bandage_injury()
		if(!repeating)
			return TRUE
	to_chat(user, span_warning("[C]'s [affecting.name] can not be healed with [src]!"))
	return FALSE

/obj/item/stack/medical/ointment
	name = "ointment pack"
	singular_name = "ointment pack"
	desc = "Simple antibacterial ointment designed to disinfect and promote wound healing."
	amount = 10
	max_amount = 10
	self_delay = 0.2 SECONDS //These will get multiplied by damage
	other_delay = 0.1 SECONDS
	repeating = TRUE

/obj/item/stack/medical/ointment/heal_carbon(mob/living/carbon/C, mob/user, brute, burn)
	var/obj/item/bodypart/affecting = C.get_bodypart(check_zone(user.zone_selected))
	if(!affecting) //Missing limb?
		to_chat(user, span_warning("[C] doesn't have \a [parse_zone(user.zone_selected)]!"))
		return FALSE
	if(required_status && (affecting.status != required_status))
		to_chat(user, span_warning("[src] won't work on that limb!"))
		return FALSE
	for(var/thing in affecting.injuries)
		var/datum/injury/injury = thing
		if(injury.is_salved())
			continue
		var/time = (user == C ? self_delay : other_delay ) * injury.damage
		time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_DEXTERITY), 1))
		if(!do_mob(user, C, time))
			to_chat(user, span_warning("I must stand still!"))
			return
		if(!use(1))
			to_chat(user, span_warning("All used up..."))
			return
		//ointment is VERY EASY to use
		if(user.diceroll(GET_MOB_ATTRIBUTE_VALUE(user, SKILL_MEDICINE)+GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE)+10, 20, 2, 20) <= DICE_FAILURE)
			to_chat(user, span_warning(fail_msg()))
			return
		user.visible_message(span_green("[user] salves \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
							span_green("I salve \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		injury.salve_injury()
		injury.disinfect_injury()
		if(!repeating)
			return TRUE
	to_chat(user, span_warning("[C]'s [affecting.name] can not be healed with [src]!"))
	return FALSE

/obj/item/stack/medical/suture
	name = "sutures"
	singular_name = "suture"
	desc = "Basic sterile sutures used to stitch up bleeding cuts and lacerations."
	amount = 10
	max_amount = 10
	self_delay = 0.4 SECONDS //These will get multiplied by damage
	other_delay = 0.2 SECONDS
	heal_brute = 10
	repeating = TRUE

/obj/item/stack/medical/suture/heal_carbon(mob/living/carbon/C, mob/user, brute, burn)
	var/obj/item/bodypart/affecting = C.get_bodypart(check_zone(user.zone_selected))
	if(!affecting) //Missing limb?
		to_chat(user, span_warning("[C] doesn't have \a [parse_zone(user.zone_selected)]!"))
		return FALSE
	if(required_status && (affecting.status != required_status))
		to_chat(user, span_warning("[src] won't work on that limb!"))
		return FALSE
	// try fixing arteries first
	if(affecting.get_incision(TRUE))
		if(affecting.is_artery_torn())
			var/time = (user == C ? self_delay : other_delay ) * 20
			time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_DEXTERITY), 1))
			playsound(C, 'modular_septic/sound/gore/suture.ogg', 65, FALSE)
			if(!do_mob(user, C, time))
				to_chat(user, span_warning("I must stand still!"))
				return
			if(!use(1))
				to_chat(user, span_warning("All used up..."))
				return
			//suturing arteries is VERY hard
			if(user.diceroll(GET_MOB_ATTRIBUTE_VALUE(user, SKILL_SURGERY)+GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE), 20, 2, 20) <= DICE_FAILURE)
				to_chat(user, span_warning(fail_msg()))
				return FALSE
			user.visible_message(span_green("[user] sutures [C]'s [affecting.name] arteries with \the [src]."), \
								span_green("I suture [C]'s [affecting.name] arteries with \the [src]."))
			for(var/obj/item/organ/artery in affecting.getorganslotlist(ORGAN_SLOT_ARTERY))
				if(artery.damage)
					artery.applyOrganDamage(-min(artery.maxHealth/2, 50))
					return TRUE
			return TRUE
		// tendons second
		if(affecting.is_tendon_torn())
			var/time = (user == C ? self_delay : other_delay ) * 20
			time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_DEXTERITY), 1))
			playsound(C, 'modular_septic/sound/gore/suture.ogg', 65, FALSE)
			if(!do_mob(user, C, time))
				to_chat(user, span_warning("I must stand still!"))
				return
			if(!use(1))
				to_chat(user, span_warning("All used up..."))
				return
			//suturing tendons is hard, but not as hard as arteries
			if(user.diceroll(GET_MOB_ATTRIBUTE_VALUE(user, SKILL_SURGERY)+GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE)+3, 20, 2, 20) <= DICE_FAILURE)
				to_chat(user, span_warning(fail_msg()))
				return FALSE
			user.visible_message(span_green("[user] sutures [C]'s [affecting.name] tendons with \the [src]."), \
								span_green("I suture [C]'s [affecting.name] tendons with \the [src]."))
			for(var/obj/item/organ/tendon in affecting.getorganslotlist(ORGAN_SLOT_TENDON))
				if(tendon.damage)
					tendon.applyOrganDamage(-min(tendon.maxHealth/2, 50))
					return TRUE
			return TRUE
	for(var/thing in affecting.injuries)
		var/datum/injury/injury = thing
		if(!(injury.damage_type in list(WOUND_SLASH, WOUND_PIERCE)) || (injury.damage_per_injury() <= injury.autoheal_cutoff))
			continue
		var/time = (user == C ? self_delay : other_delay ) * injury.damage
		time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_DEXTERITY), 1))
		playsound(C, 'modular_septic/sound/gore/suture.ogg', 65, FALSE)
		if(!do_mob(user, C, time))
			to_chat(user, span_warning("I must stand still!"))
			return
		if(!use(1))
			to_chat(user, span_warning("All used up..."))
			return
		//sutures aren't easy to use
		if(user.diceroll(GET_MOB_ATTRIBUTE_VALUE(user, SKILL_SURGERY)+GET_MOB_ATTRIBUTE_VALUE(user, STAT_INTELLIGENCE)+6, 20, 2, 20) <= DICE_FAILURE)
			to_chat(user, span_warning(fail_msg()))
			return
		injury.heal_damage(brute)
		affecting.update_damages()
		if(affecting.update_bodypart_damage_state())
			C.update_damage_overlays()
		if(injury.damage_per_injury() > injury.autoheal_cutoff)
			user.visible_message(span_green("[user] partially stitches \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
								span_green("I partially stitch \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		else
			user.visible_message(span_green("[user] stitches \a [injury.get_desc()] shut on [C]'s [affecting.name] with \the [src]."), \
								span_green("I stitch \a [injury.get_desc()] shut on \the [affecting.name] with \the [src]."))
		injury.suture_injury()
		if(!repeating)
			return TRUE
	to_chat(user, span_warning("[C]'s [affecting.name] can not be healed with [src]!"))
	return FALSE

/obj/item/stack/medical/mesh
	name = "hydrogel meshes"
	singular_name = "hydrogel mesh"
	desc = "A bacteriostatic mesh of moist hydrophillic gel, capable of salving and dressing wounds while maintaining moisture."
	self_delay = 0.3 SECONDS //These will get multiplied by damage
	other_delay = 0.15 SECONDS
	amount = 10
	max_amount = 10
	heal_burn = 10
	repeating = TRUE

/obj/item/stack/medical/mesh/heal_carbon(mob/living/carbon/C, mob/user, brute, burn)
	var/obj/item/bodypart/affecting = C.get_bodypart(check_zone(user.zone_selected))
	if(!affecting) //Missing limb?
		to_chat(user, span_warning("[C] doesn't have \a [parse_zone(user.zone_selected)]!"))
		return FALSE
	if(required_status && (affecting.status != required_status))
		to_chat(user, span_warning("[src] won't work on that limb!"))
		return FALSE
	for(var/thing in affecting.injuries)
		var/datum/injury/injury = thing
		if(injury.is_bandaged())
			continue
		var/time = (user == C ? self_delay : other_delay ) * injury.damage
		time *= (ATTRIBUTE_MIDDLING/max(GET_MOB_ATTRIBUTE_VALUE(user, STAT_DEXTERITY), 1))
		if(!do_mob(user, C, time))
			to_chat(user, span_warning("I must stand still!"))
			return
		if(!use(1))
			to_chat(user, span_warning("All used up..."))
			return
		if(injury.damage_type == WOUND_BURN)
			injury.heal_damage(burn)
			affecting.update_damages()
			if(affecting.update_bodypart_damage_state())
				C.update_damage_overlays()
		user.visible_message(span_green("[user] dresses \a [injury.get_desc()] on [C]'s [affecting.name] with \the [src]."), \
							span_green("I dress \a [injury.get_desc()] on \the [affecting.name] with \the [src]."))
		injury.salve_injury()
		injury.bandage_injury()
		if(!repeating)
			return TRUE
	to_chat(user, span_warning("[C]'s [affecting.name] can not be healed with [src]!"))
	return FALSE

/obj/item/stack/medical/bone_gel
	icon = 'modular_septic/icons/obj/items/stack_medical.dmi'
	desc = "A potent medical gel that, when applied to a fractured bone, triggers an intense melding reaction. A red warning label says: \"Do not ingest.\"."
	self_delay = 4 SECONDS
	other_delay = 2 SECONDS
	amount = 1
	max_amount = 6

/obj/item/stack/medical/bone_gel/four //this is dumb lol
	amount = 6

/obj/item/stack/medical/gauze
	desc = "A long roll of medical gauze, perfect for dressing cuts and burns."
	self_delay = 4 SECONDS
	other_delay = 2 SECONDS
	amount = 8
	max_amount = 12
	splint_factor = 0.75
	absorption_capacity = 100
	absorption_rate = 0.2
	medicine_overlay_prefix = "gauze"

/obj/item/stack/medical/gauze/try_heal(mob/living/M, mob/user, silent)
	var/obj/item/bodypart/limb = M.get_bodypart_nostump(check_zone(user.zone_selected))
	if(!limb)
		to_chat(user, span_notice("There's nothing there to bandage!"))
		return
	if(limb.current_gauze)
		to_chat(user, span_warning("[user == M ? "My" : "[M]'s"] [limb.name] is already bandaged!"))
		return
	user.visible_message(span_warning("[user] begins wrapping [M]'s [limb.name] with [src]..."), \
				span_warning("I begin wrapping [user == M ? "my" : "[M]'s"] [limb.name] with [src]..."))
	if(!do_after(user, (user == M ? self_delay : other_delay), target=M))
		return

	user.visible_message(span_green("[user] applies [src] to [M]'s [limb.name]."), \
			span_green("I bandage [user == M ? "my" : "[M]'s"] [limb.name]."))
	limb.apply_gauze(src)
	return TRUE

/obj/item/stack/medical/gauze/improvised
	absorption_capacity = 100
	absorption_rate = 0.4

/obj/item/stack/medical/splint
	name = "medical splints"
	singular_name = "medical splint"
	desc = "Hard plastic splints that help stabilize fractured limbs."
	icon_state = "splint"
	self_delay = 4 SECONDS
	other_delay = 2 SECONDS
	amount = 1
	max_amount = 5
	medicine_overlay_prefix = "splint"
	custom_price = PAYCHECK_ASSISTANT * 3
	splint_factor = 0.3
	merge_type = /obj/item/stack/medical/splint

/obj/item/stack/medical/splint/five
	amount = 5

/obj/item/stack/medical/splint/try_heal(mob/living/M, mob/user, silent)
	var/obj/item/bodypart/limb = M.get_bodypart_nostump(check_zone(user.zone_selected))
	if(!limb)
		to_chat(user, span_notice("There's nothing there to splint!"))
		return
	if(limb.current_splint)
		to_chat(user, span_warning("[user == M ? "My" : "[M]'s"] [limb.name] is already splinted!"))
		return
	user.visible_message(span_warning("[user] begins splinting [M]'s [limb.name] with \the [src]..."), \
				span_warning("I begin splinting [user == M ? "my" : "[M]'s"] [limb.name] with \the [src]..."))
	if(!do_after(user, (user == M ? self_delay : other_delay), target=M))
		return

	user.visible_message(span_green("[user] applies [src] to [M]'s [limb.name]."), \
			span_green("I splint [user == M ? "my" : "[M]'s"] [limb.name]."))
	limb.apply_splint(src)
	return TRUE

/obj/item/stack/medical/splint/tribal
	name = "tribal splints"
	singular_name = "tribal splint"
	desc = "A few bones wrapped tightly in sinew... Should be enough to keep a limb in place."
	icon_state = "splint_tribal"
	amount = 1
	self_delay = 6 SECONDS
	other_delay = 3 SECONDS
	custom_price = PAYCHECK_ASSISTANT
	splint_factor = 0.5
	merge_type = /obj/item/stack/medical/splint/tribal

/obj/item/stack/medical/splint/tribal/five
	amount = 5

/obj/item/stack/medical/splint/improvised
	name = "improvised splints"
	singular_name = "improvised splint"
	desc = "Crudely made out splints with wood and some cloth. Better than nothing."
	amount = 1
	self_delay = 6 SECONDS
	other_delay = 3 SECONDS
	custom_price = PAYCHECK_ASSISTANT * 0.6
	splint_factor = 0.6
	merge_type = /obj/item/stack/medical/splint/improvised

/obj/item/stack/medical/splint/improvised/five
	amount = 5

/obj/item/stack/medical/nanopaste
	name = "nanite paste"
	singular_name = "nanite paste"
	icon_state = "nanopaste"
	desc = "A paste composed of silicon and healing nanites. Very efficient at healing robotic limbs."
	novariants = TRUE
	amount = 10
	max_amount = 10
	self_delay = 4 SECONDS
	other_delay = 2 SECONDS
	heal_brute = 20
	heal_burn = 20
	required_status = BODYPART_ROBOTIC
	grind_results = list(/datum/reagent/silicon = 10, /datum/reagent/silver = 10)
	merge_type = /obj/item/stack/medical/nanopaste

/obj/item/stack/medical/nervemend
	name = "nerve mender"
	singular_name = "nerve mender"
	desc = "A tube filled with stem cells, capable of rapidly assimilating to damaged nerve endings."
	icon_state = "fixovein"
	novariants = TRUE
	amount = 1
	max_amount = 5
	self_delay = 4 SECONDS
	other_delay = 2 SECONDS
	grind_results = list(/datum/reagent/medicine/c2/libital = 10, /datum/reagent/medicine/coagulant = 10)
	merge_type = /obj/item/stack/medical/nervemend

/obj/item/stack/medical/nervemend/five
	amount = 5


code:
/obj/item/stack/sticky_tape
	icon = 'modular_septic/icons/obj/items/tapes.dmi'


code:
/obj/item/storage
	var/storage_flags = NONE

/obj/item/storage/Initialize()
	. = ..()
	var/datum/component/storage/STR = GetComponent(/datum/component/storage)
	if(STR)
		STR.storage_flags |= storage_flags


code:
/obj/item/storage/backpack
	slowdown = 0
	storage_flags = STORAGE_NO_WORN_ACCESS
	carry_weight = 2

/obj/item/storage/backpack/satchel
	slowdown = 0
	slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_SUITSTORE
	storage_flags = NONE
	carry_weight = 1.5

/obj/item/storage/backpack/satchel/Initialize()
	. = ..()
	var/datum/component/storage/STR = GetComponent(/datum/component/storage)
	if(STR)
		STR.max_combined_w_class = 12

/obj/item/storage/backpack/duffelbag
	slowdown = 0
	storage_flags = STORAGE_NO_WORN_ACCESS|STORAGE_NO_EQUIPPED_ACCESS
	carry_weight = 3

/obj/item/storage/backpack/duffelbag/Initialize()
	. = ..()
	var/datum/component/storage/STR = GetComponent(/datum/component/storage)
	STR.max_combined_w_class = 32

/obj/item/storage/backpack/satchel/flat/Initialize(mapload)
	. = ..()
	var/datum/component/storage/STR = GetComponent(/datum/component/storage)
	STR.max_combined_w_class = 10

/obj/item/storage/backpack/holding/Initialize()
	. = ..()
	var/datum/component/storage/STR = GetComponent(/datum/component/storage)
	STR.max_combined_w_class = 38

/obj/item/storage/backpack/industrial
	icon = 'modular_septic/icons/obj/clothing/back.dmi'
	icon_state = "industrial_backpack"
	worn_icon = 'modular_septic/icons/mob/clothing/back.dmi'
	worn_icon_state = "industrial_backpack"


code:
/obj/item/storage/firstaid
	carry_weight = 1

/obj/item/storage/firstaid/regular/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/stack/medical/gauze = 1,
		/obj/item/stack/medical/suture = 2,
		/obj/item/stack/medical/mesh = 2,
		/obj/item/reagent_containers/hypospray/medipen = 1)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/medical/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/healthanalyzer = 1,
		/obj/item/stack/medical/gauze/twelve = 1,
		/obj/item/stack/medical/splint/five = 1,
		/obj/item/stack/medical/suture = 2,
		/obj/item/stack/medical/mesh = 2,
		/obj/item/stack/medical/ointment = 2,
		/obj/item/reagent_containers/hypospray/medipen = 1,
		/obj/item/reagent_containers/hypospray/medipen/ekit = 1)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/ancient/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/stack/medical/gauze/twelve = 1,
		/obj/item/stack/medical/bruise_pack = 3,
		/obj/item/stack/medical/ointment= 3)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/brute/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/stack/medical/gauze/twelve = 1,
		/obj/item/stack/medical/bruise_pack= 3,
		/obj/item/stack/medical/suture = 2,
		/obj/item/reagent_containers/medigel/libital = 1)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/fire/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/stack/medical/gauze/twelve = 1,
		/obj/item/stack/medical/ointment= 3,
		/obj/item/stack/medical/mesh = 2,
		/obj/item/reagent_containers/medigel/aiuri = 1)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/toxin/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
	    /obj/item/storage/pill_bottle/multiver = 1,
		/obj/item/reagent_containers/syringe/syriniver = 3,
		/obj/item/reagent_containers/syringe/antiviral = 1,
		/obj/item/storage/pill_bottle/potassiodide = 1,
		/obj/item/reagent_containers/hypospray/medipen/penacid = 1)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/o2/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/reagent_containers/syringe/convermol = 3,
		/obj/item/reagent_containers/syringe/tirimol = 1,
		/obj/item/reagent_containers/hypospray/medipen/salbutamol = 1,
		/obj/item/reagent_containers/hypospray/medipen = 1,
		/obj/item/storage/pill_bottle/iron = 1)
	generate_items_inside(items_inside,src)

/obj/item/storage/firstaid/advanced/Initialize()
	. = ..()
	var/datum/component/storage/STR = GetComponent(/datum/component/storage)
	STR.max_w_class = WEIGHT_CLASS_NORMAL //holds the same equipment as a medibelt
	STR.max_items = 12
	STR.max_combined_w_class = 24
	STR.set_holdable(list(
		/obj/item/healthanalyzer,
		/obj/item/dnainjector,
		/obj/item/reagent_containers/dropper,
		/obj/item/reagent_containers/glass/beaker,
		/obj/item/reagent_containers/glass/bottle,
		/obj/item/reagent_containers/pill,
		/obj/item/reagent_containers/syringe,
		/obj/item/reagent_containers/medigel,
		/obj/item/reagent_containers/spray,
		/obj/item/lighter,
		/obj/item/storage/fancy/cigarettes,
		/obj/item/storage/pill_bottle,
		/obj/item/stack/medical,
		/obj/item/flashlight/pen,
		/obj/item/extinguisher/mini,
		/obj/item/reagent_containers/hypospray,
		/obj/item/sensor_device,
		/obj/item/radio,
		/obj/item/clothing/gloves/,
		/obj/item/lazarus_injector,
		/obj/item/bikehorn/rubberducky,
		/obj/item/clothing/mask/surgical,
		/obj/item/clothing/mask/breath,
		/obj/item/clothing/mask/breath/medical,
		/obj/item/surgical_drapes, //for true paramedics
		/obj/item/scalpel,
		/obj/item/circular_saw,
		/obj/item/bonesetter,
		/obj/item/surgicaldrill,
		/obj/item/retractor,
		/obj/item/cautery,
		/obj/item/hemostat,
		/obj/item/blood_filter,
		/obj/item/shears,
		/obj/item/geiger_counter,
		/obj/item/clothing/neck/stethoscope,
		/obj/item/stamp,
		/obj/item/clothing/glasses,
		/obj/item/wrench/medical,
		/obj/item/clothing/mask/muzzle,
		/obj/item/reagent_containers/blood,
		/obj/item/tank/internals/emergency_oxygen,
		/obj/item/gun/syringe/syndicate,
		/obj/item/implantcase,
		/obj/item/implant,
		/obj/item/implanter,
		/obj/item/pinpointer/crew,
		/obj/item/holosign_creator/medical,
		/obj/item/stack/sticky_tape //surgical tape
		))

/obj/item/storage/firstaid/advanced/PopulateContents()
	if(empty)
		return
	var/static/items_inside = list(
		/obj/item/stack/medical/gauze/twelve = 1,
		/obj/item/stack/medical/ointment= 1,
		/obj/item/stack/medical/bruise_pack= 1,
		/obj/item/reagent_containers/medigel/synthflesh = 1,
		/obj/item/reagent_containers/glass/bottle/copium = 1,
		/obj/item/reagent_containers/glass/bottle/tirimol = 1,
		/obj/item/reagent_containers/glass/bottle/syriniver = 1,
		/obj/item/reagent_containers/glass/bottle/spaceacillin = 1,
		/obj/item/reagent_containers/syringe = 1)
	generate_items_inside(items_inside,src)


code:
/obj/item/storage/secure/safe
	armor = list(MELEE = 70, BULLET = 70, LASER = 70, ENERGY = 70, BOMB = 70, BIO = 100, RAD = 100, FIRE = 70, ACID = 70)

/obj/item/storage/secure/Initialize()
	. = ..()
	AddElement(/datum/element/multitool_emaggable)

/obj/item/storage/secure/emag_act(mob/user, obj/item/card/emag/E)
	. = ..()
	l_set = FALSE
	to_chat(user, span_notice("You corrupt [src]'s internal memory."))

/obj/item/storage/secure/safe/caps_spare
	name = "captain's spare ID safe"
	desc = "Stores the spare full-access ID. I think."
	can_hack_open = TRUE
	armor = list(MELEE = 70, BULLET = 70, LASER = 70, ENERGY = 70, BOMB = 70, BIO = 100, RAD = 100, FIRE = 70, ACID = 70)
	max_integrity = 600
	color = null

/obj/item/storage/secure/safe/locked
	var/code_id

/obj/item/storage/secure/safe/locked/Initialize()
	. = ..()
	if(!code_id)
		CRASH("No code_id present")

	l_code = SSid_access.safe_codes[code_id]
	l_set = TRUE
	SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, TRUE)

/obj/item/storage/secure/safe/locked/syringe_gun
	code_id = CODE_SYRINGE_GUN
	desc = "There's a placard on it... \"In case of unruly patients.\""

/obj/item/storage/secure/safe/locked/syringe_gun/PopulateContents()
	new /obj/item/gun/syringe

	new /obj/item/reagent_containers/syringe/morphine
	new /obj/item/reagent_containers/syringe/morphine
	new /obj/item/reagent_containers/syringe/morphine


code:
//These weight an absurd amount irl
/obj/item/tank/internals
	carry_weight = 10

/obj/item/tank/internals/emergency_oxygen
	carry_weight = 5

/obj/item/tank/internals/plasmaman
	carry_weight = 5


code:
/obj/item/crowbar
	//They're pocketable i guess
	carry_weight = 7


code:
/obj/item/screwdriver
	carry_weight = 0.25


code:
/obj/item/weldingtool
	carry_weight = 4


code:
/obj/item/wirebrush
	carry_weight = 0.5


code:
/obj/item/wirecutters
	carry_weight = 0.25

/obj/item/wirecutters/attack(mob/living/carbon/attacked, mob/user, params)
	if(!istype(attacked))
		return
	var/mob/living/loser = user
	if(!istype(loser))
		return

	var/list/modifiers = params2list(params)
	if(IS_DISARM_INTENT(loser, modifiers))
		switch(loser.zone_selected)
			if(BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND)
				if(attacked.handcuffed)
					loser.visible_message(span_notice("[loser] attempts to cut the [attacked.handcuffed] from around [attacked]'s wrists."))
					if(do_mob(loser, attacked, 4 SECONDS))
						loser.visible_message(span_notice("[loser] cuts [attacked]'s restraints with [src]!"), \
											span_notice("I cut [attacked]'s restraints."))
						qdel(attacked.handcuffed)
					return
			if(BODY_ZONE_PRECISE_NECK)
				if(attacked.has_status_effect(STATUS_EFFECT_CHOKINGSTRAND))
					loser.visible_message(span_notice("[loser] attempts to cut the durathread strand from around [attacked]'s neck."), \
										span_notice("I cut [attacked]'s choking strand."))
					if(do_mob(loser, attacked, 2 SECONDS))
						loser.visible_message(span_notice("[loser] succesfully cuts the durathread strand from around [attacked]'s neck."))
						attacked.remove_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
						playsound(loc, usesound, 50, TRUE, -1)
					return
	else if(IS_GRAB_INTENT(loser, modifiers))
		switch(loser.zone_selected)
			if(BODY_ZONE_PRECISE_MOUTH)
				var/obj/item/bodypart/mouth/jaw = attacked.get_bodypart(BODY_ZONE_PRECISE_MOUTH)
				if(!jaw)
					to_chat(loser, span_danger("[attacked.p_they(TRUE)] have no mouth and they must scream!"))
					return
				if(!jaw.teeth_object?.amount)
					to_chat(loser, span_danger("[attacked.p_they(TRUE)] don't have any teeth left!"))
					return
				attacked.visible_message(span_warning("[loser] shoves [src] into [attacked]'s mouth!"), \
								span_userdanger("[loser] is trying to rip my teeth off!!"), \
								vision_distance = COMBAT_MESSAGE_RANGE, \
								ignored_mobs = loser)
				to_chat(loser, span_danger("I start ripping [attacked]'s tooth off!"))
				if(do_mob(loser, attacked, 3 SECONDS))
					attacked.visible_message(span_danger("[loser] rips a tooth out of [attacked]'s mouth!"), \
									span_userdanger("FUCK!!!"), \
									vision_distance = COMBAT_MESSAGE_RANGE, \
									ignored_mobs = loser)
					to_chat(loser, span_danger("I rip [attacked]'s tooth off!"))
					jaw.knock_out_teeth(1, pick(GLOB.alldirs))
					attacked.agony_scream()
					jaw.add_pain(25)
				return
	return ..()


code:
/obj/item/wrench
	carry_weight = 1


code:
/obj/item/knife
	carry_weight = 0.4

/obj/item/knife/combat
	carry_weight = 0.8

//Horrible
/obj/item/knife/combat/zhunter
	name = "z-hunter brand knife"
	desc = "Illegal in the Separated Kingdom, this surplus knife is barely able to cut through skin. It can, however, hunt many Z's."
	icon = 'modular_septic/icons/obj/items/melee/knife.dmi'
	icon_state = "zhunter"
	force = 10
	throwforce = 5
	w_class = WEIGHT_CLASS_SMALL
	wound_bonus = 0
	bare_wound_bonus = 5

//Nice sexy sex
/obj/item/melee/truncheon
	name = "truncheon"
	desc = "A tool to beat the melanin out of criminals."
	icon = 'modular_septic/icons/obj/items/melee/baton.dmi'
	icon_state = "truncheon"
	lefthand_file = 'modular_septic/icons/obj/items/melee/inhands/baton_lefthand.dmi'
	righthand_file = 'modular_septic/icons/obj/items/melee/inhands/baton_righthand.dmi'
	inhand_icon_state = "truncheon"
	force = 15
	wound_bonus = 3
	bare_wound_bonus = 0
	carry_weight = 2.5
	slot_flags = ITEM_SLOT_BELT
	worn_icon_state = "classic_baton"

/obj/item/melee/truncheon/black
	name = "black truncheon"
	icon = 'modular_septic/icons/obj/items/melee/baton.dmi'
	icon_state = "truncheon_black"
	lefthand_file = 'modular_septic/icons/obj/items/melee/inhands/baton_lefthand.dmi'
	righthand_file = 'modular_septic/icons/obj/items/melee/inhands/baton_righthand.dmi'
	inhand_icon_state = "truncheon_black"


code:
// Wielding any item very poggers
/obj/item
	var/wield_info = /datum/wield_info/standard

/obj/item/proc/get_wield_component()
	var/datum/component/two_handed/wield_component = GetComponent(/datum/component/two_handed)
	if(wield_component)
		return wield_component
	else if(!wield_info || QDELETED(src))
		return
	var/datum/wield_info/wield_datum = GLOB.path_to_wield_info[wield_info]
	if(!wield_datum)
		return
	if(wield_datum.wield_flags & WIELD_WIELDABLE)
		wield_component = LoadComponent(/datum/component/two_handed, \
					require_twohands = (wield_datum.wield_flags & WIELD_ALWAYS_TWOHANDED),\
					wieldsound = wield_datum.wield_sound, \
					unwieldsound = wield_datum.unwield_sound, \
					attacksound = wield_datum.wield_hitsound,\
					force_multiplier = wield_datum.force_multiplier,\
					force_wielded = wield_datum.force_wielded,\
					force_unwielded = wield_datum.force_unwielded,\
					icon_wielded = wield_datum.icon_wielded,\
					)
		if(wield_datum.wield_flags & WIELD_NO_ATTACK_SELF)
			wield_component.UnregisterSignal(src, COMSIG_ITEM_ATTACK_SELF)
		return wield_component

/obj/item/proc/wield_act(mob/living/carbon/user)
	// Check if we can even wield the item, if not we error out
	var/datum/component/two_handed/wield_comp = get_wield_component()
	if(!wield_comp)
		to_chat(user, span_warning("\The [src] cannot be wielded in any way!"))
		return
	if(wield_comp.wielded)
		wield_comp.unwield(user)
	else
		wield_comp.wield(user)


code:
/obj/structure/ladder
	travel_time = 2 SECONDS


code:
/obj/on_rammed(mob/living/carbon/rammer)
	. = ..()
	take_damage(GET_MOB_ATTRIBUTE_VALUE(rammer, STAT_STRENGTH))


code:
/obj/structure/closet/Initialize(mapload)
	. = ..()
	AddElement(/datum/element/multitool_emaggable)


code:
/obj/structure/door_assembly
	icon = 'modular_septic/icons/obj/doors/airlocks/station/public.dmi'
	overlays_file = 'modular_septic/icons/obj/doors/airlocks/station/overlays.dmi'


code:
/obj/structure/falsewall
	icon = 'modular_septic/icons/turf/walls/wall.dmi'
	var/can_open = TRUE

/obj/structure/falsewall/attack_hand(mob/user, list/modifiers)
	if(opening)
		return
	. = ..()
	if(.)
		return

	if(!can_open)
		user.changeNext_move(CLICK_CD_MELEE)
		to_chat(user, span_notice("You push the wall but nothing happens!"))
		playsound(src, 'sound/weapons/genhit.ogg', 25, TRUE)
		return

	opening = TRUE
	update_appearance()
	if(!density)
		var/srcturf = get_turf(src)
		for(var/mob/living/obstacle in srcturf) //Stop people from using this as a shield
			opening = FALSE
			return
	addtimer(CALLBACK(src, /obj/structure/falsewall/proc/toggle_open), 5)

/obj/structure/falsewall/reinforced
	icon = 'modular_septic/icons/turf/walls/reinforced_wall.dmi'


code:
/obj/structure/falsewall/tram_wall
	name = "tram wall"
	desc = "A huge chunk of metal used to encase trams."
	icon = 'modular_septic/icons/turf/train/train_wall.dmi'
	icon_state = "reinforced_wall-0"
	base_icon_state = "reinforced_wall"
	can_open = FALSE
	smoothing_flags = SMOOTH_BITMASK
	smoothing_groups = list(SMOOTH_GROUP_TRAM)
	canSmoothWith = list(SMOOTH_GROUP_TRAM)


code:
/obj/structure/grille
	icon = 'modular_septic/icons/obj/structures/structures.dmi'

/obj/structure/grille/Initialize()
	. = ..()
	AddElement(/datum/element/fireaxe_brittle)


code:
// i don't want lattice to be taken away by the train lol
/obj/structure/industrial_lift/AddItemOnLift(datum/source, atom/movable/AM)
	var/static/list/bad_types_typecache
	if(!LAZYLEN(bad_types_typecache))
		bad_types_typecache = list()
		bad_types_typecache |= typecacheof(/obj/structure/fluff/tram_rail)
		bad_types_typecache |= typecacheof(/obj/structure/lattice)
		bad_types_typecache |= typecacheof(/atom/movable/liquid)
		bad_types_typecache |= typecacheof(/atom/movable/fire)
		bad_types_typecache |= typecacheof(/obj/structure/holosign/barrier/atmos/sturdy)
	if((AM.invisibility == INVISIBILITY_ABSTRACT) || is_type_in_typecache(AM, bad_types_typecache)) //prevents the tram from stealing things like landmarks
		return
	if(AM in lift_load)
		return
	LAZYADD(lift_load, AM)
	RegisterSignal(AM, COMSIG_PARENT_QDELETING, .proc/RemoveItemFromLift)

/obj/effect/landmark/tram/arrival_station
	name = "Arrivals"
	destination_id = "arrival_station"
	tgui_icons = list("Arrival Station" = "subway")

/obj/effect/landmark/tram/entrance_station
	name = "Entrance"
	destination_id = "entrance_station"
	tgui_icons = list("Train Station" = "subway")


code:
/obj/structure/ladder/examine(mob/user)
	. = ..()
	if(up && down)
		. += span_info("[src] can lead you upwards or downwards.")
	else if(up)
		. += span_info("[src] can lead you upwards.")
	else if(down)
		. += span_info("[src] can lead you downwards.")
	else
		. += span_info("[src] is like arguing with a woman - It leads you nowhere.")

/obj/structure/ladder/attack_hand(mob/user, list/modifiers)
	. = ..()
	if(.)
		return
	use(user, TRUE)

/obj/structure/ladder/attack_hand_secondary(mob/user, list/modifiers)
	. = ..()
	if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
		return
	use(user, FALSE)
	return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN

/obj/structure/ladder/attack_paw(mob/user, list/modifiers)
	return use(user, !LAZYACCESS(modifiers, RIGHT_CLICK))

/obj/structure/ladder/attack_alien(mob/user, list/modifiers)
	return use(user, !LAZYACCESS(modifiers, RIGHT_CLICK))

/obj/structure/ladder/attack_larva(mob/user, list/modifiers)
	return use(user, !LAZYACCESS(modifiers, RIGHT_CLICK))

/obj/structure/ladder/attack_animal(mob/user, list/modifiers)
	return use(user, !LAZYACCESS(modifiers, RIGHT_CLICK))

/obj/structure/ladder/attack_slime(mob/user, list/modifiers)
	return use(user, !LAZYACCESS(modifiers, RIGHT_CLICK))

/obj/structure/ladder/attack_robot(mob/living/silicon/robot/user)
	return use(user, TRUE)

/obj/structure/ladder/attack_robot_secondary(mob/user, list/modifiers)
	use(user, FALSE)
	return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN

/obj/structure/ladder/attack_ghost(mob/dead/observer/user, list/modifiers)
	. = ..()
	if(.)
		return
	use(user, !LAZYACCESS(modifiers, RIGHT_CLICK), TRUE)

/obj/structure/ladder/proc/use(mob/user, going_up = TRUE, is_ghost = FALSE)
	if(!is_ghost)
		if(!in_range(src, user) || user.incapacitated())
			return
		add_fingerprint(user)
	if(!((going_up && up) || (!going_up && down))) //if you're going up and there's no up / if you're going down and there's no down
		to_chat(user, span_warning("This ladder does not go [going_up ? "up" : "down"]."))
		return

	travel(going_up, user, is_ghost, going_up ? up : down)


code:
/obj/structure/moisture_trap/Initialize()
	. = ..()
	AddElement(/datum/element/pollution_emitter, /datum/pollutant/decaying_waste, 30)


code:
#define NOTICES_CMO list(\
	/obj/item/paper/code/syringe_gun\
)
#define MAX_NOTICES 5
/obj/structure/noticeboard
	var/list/spawn_notices

/obj/structure/noticeboard/Initialize(mapload)
	. = ..()
	if(!mapload)
		return

	for(var/notice in spawn_notices)
		new notice(src)
		notices++

	if(notices > MAX_NOTICES)
		icon_state = "nboard05"
		CRASH("notice board has more mapload notices than allowed")

	icon_state = "nboard0[notices]"

/obj/structure/noticeboard/cmo
	spawn_notices = NOTICES_CMO

#undef NOTICES_CMO
#undef MAX_NOTICES


code:
/obj/structure/table/optable
	icon = 'modular_septic/icons/obj/structures/optable.dmi'


code:
/obj/structure/table/Initialize(mapload, _buildstack)
	. = ..()
	AddElement(/datum/element/liquids_height, 20)


code:
//lifeweb sinks wtf
/obj/structure/sink
	var/can_bite = TRUE

/obj/structure/sink/attack_jaw(mob/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(busy)
		to_chat(user, span_warning("Someone is using \the [src]."))
		return
	if(reagents.total_volume <= 0)
		to_chat(user, span_warning("\The [src] is empty. Fuck."))
		return
	to_chat(user, span_notice("I start drinking from [src]..."))
	busy = TRUE
	if(!do_after(user, 1.5 SECONDS, target = src))
		busy = FALSE
		return 1
	busy = FALSE
	reagents.trans_to(user, 5, methods = INGEST)


code:
/obj/structure/window
	icon = 'modular_septic/icons/obj/structures/structures.dmi'

/obj/structure/window/Initialize()
	. = ..()
	AddElement(/datum/element/fireaxe_brittle)

/obj/structure/window/fulltile
	icon = 'modular_septic/icons/obj/smooth_structures/window.dmi'

/obj/structure/window/reinforced
	icon = 'modular_septic/icons/obj/structures/structures.dmi'
	desc = "A window that is reinforced with metal rods. Flimsier than you'd expect."
	armor = list(MELEE = 75, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 25, BIO = 100, RAD = 100, FIRE = 80, ACID = 100)
	damage_deflection = 7.5

/obj/structure/window/reinforced/fulltile
	icon = 'modular_septic/icons/obj/smooth_structures/reinforced_window.dmi'

/obj/structure/window/reinforced/examine(mob/user)
	. = ..()
	switch(state)
		if(RWINDOW_BOLTS_HEATED, RWINDOW_SECURE)
			. += span_notice("The screws are tight, but you'll likely be able to unscrew them.")
		if(RWINDOW_BOLTS_OUT)
			. += span_notice("The screws have been removed, revealing a small gap you could fit a prying tool in.")
		if(RWINDOW_POPPED)
			. += span_notice("The main plate of the window has popped out of the frame, exposing some bars that look like they can be cut.")
		if(RWINDOW_BARS_CUT)
			. += span_notice("The main pane can be easily moved out of the way to reveal some bolts holding the frame in.")

/obj/structure/window/reinforced/attackby_secondary(obj/item/tool, mob/user, params)
	switch(state)
		if(RWINDOW_BOLTS_HEATED, RWINDOW_SECURE)
			if(tool.tool_behaviour == TOOL_SCREWDRIVER)
				user.visible_message(span_notice("[user] digs into the security screws and starts removing them..."),
										span_notice("You dig into the screws hard and they start turning..."))
				if(tool.use_tool(src, user, 50, volume = 50))
					state = RWINDOW_BOLTS_OUT
					to_chat(user, span_notice("The screws come out, and a gap forms around the edge of the pane."))
			else if (tool.tool_behaviour)
				to_chat(user, span_warning("The security screws need to be removed first!"))

		if(RWINDOW_BOLTS_OUT)
			if(tool.tool_behaviour == TOOL_CROWBAR)
				user.visible_message(span_notice("[user] wedges \the [tool] into the gap in the frame and starts prying..."),
										span_notice("You wedge \the [tool] into the gap in the frame and start prying..."))
				if(tool.use_tool(src, user, 40, volume = 50))
					state = RWINDOW_POPPED
					to_chat(user, span_notice("The panel pops out of the frame, exposing some thin metal bars that looks like they can be cut."))
			else if (tool.tool_behaviour)
				to_chat(user, span_warning("The gap needs to be pried first!"))

		if(RWINDOW_POPPED)
			if(tool.tool_behaviour == TOOL_WIRECUTTER)
				user.visible_message(span_notice("[user] starts cutting the exposed bars on \the [src]..."),
										span_notice("You start cutting the exposed bars on \the [src]"))
				if(tool.use_tool(src, user, 20, volume = 50))
					state = RWINDOW_BARS_CUT
					to_chat(user, span_notice("The panels falls out of the way exposing the frame bolts."))
			else if (tool.tool_behaviour)
				to_chat(user, span_warning("The bars need to be cut first!"))

		if(RWINDOW_BARS_CUT)
			if(tool.tool_behaviour == TOOL_WRENCH)
				user.visible_message(span_notice("[user] starts unfastening \the [src] from the frame..."),
					span_notice("You start unfastening the bolts from the frame..."))
				if(tool.use_tool(src, user, 40, volume = 50))
					to_chat(user, span_notice("You unscrew the bolts from the frame and the window pops loose."))
					state = WINDOW_OUT_OF_FRAME
					set_anchored(FALSE)
			else if (tool.tool_behaviour)
				to_chat(user, span_warning("The bolts need to be loosened first!"))

	return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN

/obj/structure/window/on_rammed(mob/living/carbon/rammer)
	rammer.ram_stun()
	var/smash_sound = pick('modular_septic/sound/gore/smash1.ogg',
						'modular_septic/sound/gore/smash2.ogg',
						'modular_septic/sound/gore/smash3.ogg')
	playsound(src, smash_sound, 75)
	rammer.sound_hint()
	sound_hint()


code:
/atom/movable/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), face_name = FALSE)
	//This proc uses text() because it is faster than appending strings. Thanks BYOND.
	//Basic span
	var/spanpart1 = ""
	//Start name span.
	var/spanpart2 = ""
	//Radio freq/name display
	var/freqpart = radio_freq ? "\[[get_radio_name(radio_freq)]\] " : ""
	//Job name display
	var/jobpart = compose_job(speaker, message_language, raw_message, radio_freq)
	//Speaker name
	var/namepart = "[speaker.GetVoice()][speaker.get_alt_name()]"
	if(face_name && ishuman(speaker))
		var/mob/living/carbon/human/H = speaker
		namepart = "[H.get_face_name()]" //So "fake" speaking like in hallucinations does not give the speaker away if disguised
	if(ismob(speaker))
		var/mob/mob_speaker = speaker
		namepart = "[namepart]"
	//End name span.
	var/endspanpart = ""

	//Message
	var/messagepart = " [lang_treat(speaker, message_language, raw_message, spans, message_mods)]"

	var/languageicon = ""
	var/datum/language/D = GLOB.language_datum_instances[message_language]
	if(istype(D) && D.display_icon(src))
		languageicon = "[D.get_icon()] "

	return "[spanpart1][spanpart2][freqpart][languageicon][compose_track_href(speaker, namepart)][namepart][jobpart][endspanpart][messagepart]"

/atom/movable/compose_job(atom/movable/speaker, message_langs, raw_message, radio_freq)
	. = ""
	if(!radio_freq)
		return
	var/anonymize = FALSE
	var/radio_icon
	var/real_speaker = speaker
	if(istype(real_speaker, /atom/movable/virtualspeaker))
		var/atom/movable/virtualspeaker/virtual_speaker = real_speaker
		real_speaker = virtual_speaker.GetSource()
		if(ismob(src) && virtual_speaker.radio)
			var/mob/mob_source = src
			if(mob_source.client)
				radio_icon = icon2html(virtual_speaker.radio, mob_source.client)
		anonymize = virtual_speaker.radio?.anonymize
	if(anonymize)
		return " \[Unknown\]"
	if(ishuman(real_speaker))
		var/mob/living/carbon/human/human_speaker = real_speaker
		var/speaker_age_gender = human_speaker.get_aged_gender()
		var/speaker_job = human_speaker.get_assignment("", "", FALSE)
		if(speaker_age_gender)
			return " [radio_icon ? "\[[radio_icon]\]" : null]\[[speaker_job ? "[speaker_job] " : ""][speaker_age_gender]\]"
	if(istype(real_speaker, /obj/machinery/announcement_system))
		return " [radio_icon ? radio_icon : null]\[AAS\]"
	return " [radio_icon ? "\[[radio_icon]\]" : null]\[Unknown\]"


code:
/turf/attack_hand(mob/user, list/modifiers)
	. = ..()
	var/mob/living/living_user = user
	if(istype(living_user) && living_user.client && living_user.movement_locked && living_user.body_position == LYING_DOWN)
		if(living_user.client.Move(src, get_dir(living_user, src)))
			user.visible_message(span_warning("<b>[user]</b> crawls on [src]."), \
								span_warning("I crawl on [src]."))


code:
/turf/closed/Initialize(mapload)
	. = ..()
	if(clingable)
		AddElement(/datum/element/clingable, SKILL_CLIMBING, 6)

/turf/closed/on_rammed(mob/living/carbon/rammer)
	rammer.ram_stun()
	var/smash_sound = pick('modular_septic/sound/gore/smash1.ogg',
						'modular_septic/sound/gore/smash2.ogg',
						'modular_septic/sound/gore/smash3.ogg')
	playsound(src, smash_sound, 75)
	rammer.sound_hint()
	sound_hint()


code:
/turf/closed/mineral
	clingable = TRUE

/turf/closed/mineral/snowmountain/nevado_surface
	baseturfs = /turf/open/floor/plating/asteroid/snow/nevado_surface
	turf_type = /turf/open/floor/plating/asteroid/snow/nevado_surface
	initial_gas_mix = NEVADO_SURFACE_DEFAULT_ATMOS


code:
/turf/closed/indestructible/splashscreen/Initialize(mapload)
	. = ..()
	pixel_x = -76
	pixel_y = -16


code:
/turf/closed/wall
	icon = 'modular_septic/icons/turf/walls/wall.dmi'
	clingable = TRUE

/turf/closed/wall/r_wall
	icon = 'modular_septic/icons/turf/walls/reinforced_wall.dmi'


code:
//Consider making all of these behaviours a smart component/element? Something that's only applied wherever it needs to be
//Could probably have the variables on the turf level, and the behaviours being activated/deactived on the component level as the vars are updated
/turf/open/CanPass(atom/movable/mover, border_dir)
	if(isliving(mover))
		var/turf/mover_turf = get_turf(mover)
		if(turf_height - mover_turf?.turf_height >= TURF_HEIGHT_BLOCK_THRESHOLD)
			return FALSE
	return ..()

/turf/open/Exit(atom/movable/leaving, direction)
	. = ..()
	if(. && isliving(leaving) && leaving.has_gravity())
		var/mob/living/living_mover = leaving
		var/turf/new_turf = get_step(leaving, direction)
		if(!istype(new_turf))
			return
		if(turf_height - new_turf?.turf_height >= TURF_HEIGHT_BLOCK_THRESHOLD)
			living_mover.on_fall()
			living_mover.onZImpact(new_turf, 1)

/turf/open/MouseDrop_T(atom/movable/dropping, mob/living/user)
	. = ..()
	if(!isliving(dropping) || !isliving(user) || !dropping.has_gravity() || !Adjacent(user) || !dropping.Adjacent(user) || (user.stat > CONSCIOUS) || user.body_position == LYING_DOWN)
		return
	if(!dropping.has_gravity())
		return
	var/turf/dropping_turf = get_turf(dropping)
	if(!dropping_turf)
		return
	if(dropping_turf.turf_height - turf_height >= TURF_HEIGHT_BLOCK_THRESHOLD)
		//Climb down
		if(user == dropping)
			dropping.visible_message(span_notice("[user] is descending down to [src]"), \
								span_notice("I start lowering myself to [src]."))
		else
			dropping.visible_message(span_warning("[user] is lowering [dropping] down to [src]"), \
								span_notice("I start lowering [dropping] down to [src]."))
		if(do_mob(user, dropping, 2 SECONDS))
			dropping.forceMove(src)
		return
	else if(turf_height - dropping_turf.turf_height >= TURF_HEIGHT_BLOCK_THRESHOLD)
		//Climb up
		if(user == dropping)
			dropping.visible_message(span_notice("[user] is climbing onto [src]"), \
								span_notice("I start climbing onto [src]."))
		else
			dropping.visible_message(span_warning("[user] is pulling [dropping] onto [src]"), \
								span_notice("I start pulling [dropping] onto [src]."))
		if(do_mob(user, dropping, 2 SECONDS))
			dropping.forceMove(src)
		return


code:
/turf/open/floor/crowbar_act(mob/living/user, obj/item/tool)
	return

/turf/open/floor/crowbar_act_secondary(mob/living/user, obj/item/tool)
	if(intact && pry_tile(tool, user))
		return TRUE

/turf/open/floor/plating
	icon = 'modular_septic/icons/turf/floors.dmi'

/turf/open/floor/iron
	icon = 'modular_septic/icons/turf/floors.dmi'

/turf/open/floor/mineral/plastitanium
	icon = 'modular_septic/icons/turf/floors.dmi'


code:
/turf/open/floor/plating/asteroid/snow/nevado_surface
	name = "snow"
	desc = "Looks cold."
	baseturfs = /turf/open/floor/plating/asteroid/snow/nevado_surface
	initial_gas_mix = NEVADO_SURFACE_DEFAULT_ATMOS
	planetary_atmos = TRUE

/turf/open/floor/plating/asteroid/snow/ice/nevado_surface
	name = "ice"
	desc = "Looks very cold."
	baseturfs = /turf/open/floor/plating/asteroid/snow/ice/nevado_surface
	initial_gas_mix = NEVADO_SURFACE_DEFAULT_ATMOS
	planetary_atmos = TRUE

/turf/open/floor/plating/asteroid/snow/river
	name = "icy river"
	desc = "Looks wet and cold."
	icon_state = "snow-ice"
	base_icon_state = "snow-ice"
	baseturfs = /turf/open/floor/plating/asteroid/snow
	turf_height = -TURF_HEIGHT_BLOCK_THRESHOLD
	var/initial_liquid = /datum/reagent/water

/turf/open/floor/plating/asteroid/snow/river/Initialize()
	. = ..()
	if(!liquids && initial_liquid)
		// rivers are always -10 celsius, which is the maximum surface temperature
		add_liquid(initial_liquid, 600, FALSE, 263.15)

/turf/open/floor/plating/asteroid/snow/river/nevado_surface
	baseturfs = /turf/open/floor/plating/asteroid/snow/nevado_surface
	initial_gas_mix = NEVADO_SURFACE_DEFAULT_ATMOS
	planetary_atmos = TRUE

/turf/open/floor/plating/asteroid/snow/river/nevado_surface/acid
	name = "infernal river of dissolution"
	desc = "Ah, an acid bath. Delicious."
	initial_liquid = /datum/reagent/toxin/acid/fluacid

/turf/open/floor/plating/asteroid/nevado_caves
	name = "cave floor"
	baseturfs = /turf/open/floor/plating/asteroid/nevado_caves
	icon = 'modular_septic/icons/turf/floors/redrock.dmi'
	icon_state = "redrock"
	base_icon_state = "redrock"
	floor_variance = 25
	initial_gas_mix = NEVADO_CAVES_DEFAULT_ATMOS
	planetary_atmos = TRUE
	digResult = /obj/item/stack/ore/glass/basalt

/turf/closed/mineral/random/nevado_caves
	name = "cave wall"
	icon = 'modular_septic/icons/turf/mining.dmi'
	smooth_icon = 'modular_septic/icons/turf/walls/redrock.dmi'
	icon_state = "redrock"
	base_icon_state = "redrock"
	environment_type = "redrock"
	turf_type = /turf/open/floor/plating/asteroid/nevado_caves
	baseturfs = /turf/open/floor/plating/asteroid/nevado_caves
	initial_gas_mix = NEVADO_CAVES_DEFAULT_ATMOS
	defer_change = TRUE

	mineralChance = 10
	mineralSpawnChanceList = list(
		/obj/item/stack/ore/uranium = 5, /obj/item/stack/ore/diamond = 1, /obj/item/stack/ore/gold = 10, /obj/item/stack/ore/titanium = 11,
		/obj/item/stack/ore/silver = 12, /obj/item/stack/ore/plasma = 20, /obj/item/stack/ore/iron = 40,
		/turf/closed/mineral/gibtonite/volcanic = 4, /obj/item/stack/ore/bluespace_crystal = 1)


code:
/turf/open/floor/trapdoor
	name = "trapdoor"
	desc = "Watch the FUCK out!"
	baseturfs = /turf/open/openspace
	icon = 'modular_septic/icons/turf/floors.dmi'
	icon_state = "trapdoor"
	base_icon_state = "trapdoor"

/turf/open/floor/trapdoor/crowbar_act(mob/living/user, obj/item/tool)
	return

/turf/open/floor/trapdoor/Initialize(mapload)
	. = ..()
	var/obj/item/assembly/trapdoor/our_assembly = find_nearest_assembly()
	AddComponent(/datum/component/trapdoor, FALSE, baseturfs, our_assembly)

/turf/open/floor/trapdoor/proc/find_nearest_assembly()
	for(var/turf/maybe_here in range(6, src))
		for(var/obj/item/trapdoor_remote/remote in maybe_here)
			if(remote.internals)
				return remote.internals
		for(var/obj/item/assembly/assembly in maybe_here)
			return assembly
	//if we didn't return before, fuck these niggas i be movin' on my own
	var/obj/item/trapdoor_remote/remote = new /obj/item/trapdoor_remote/preloaded(src)
	return remote.internals


code:
/atom/movable/openspace_backdrop
	name = "open space"
	desc = "Watch your step!"
	icon = 'modular_septic/icons/turf/floors.dmi'
	icon_state = "openspace"
	mouse_opacity = MOUSE_OPACITY_TRANSPARENT

/turf/open/openspace
	icon = 'modular_septic/icons/turf/floors.dmi'
	icon_state = "transparent"
	mouse_opacity = MOUSE_OPACITY_TRANSPARENT
	light_range = 2
	light_power = 1
	light_color = COLOR_WHITE

/turf/open/openspace/return_screentip(mob/user, params)
	if(flags_1 & NO_SCREENTIPS_1)
		return ""
	return SCREENTIP_OPENSPACE(uppertext(name))


code:
/turf
	vis_flags = VIS_INHERIT_PLANE
	var/clingable = FALSE

/turf/return_screentip(mob/user, params)
	if(flags_1 & NO_SCREENTIPS_1)
		return ""
	if(user.z != src.z)
		return SCREENTIP_OPENSPACE("OPEN SPACE")
	else
		return SCREENTIP_TURF(uppertext(name))

/turf/handle_fall(mob/faller)
	if(has_gravity(src))
		playsound(src, "modular_septic/sound/effects/collapse[rand(1,5)].wav", 50, TRUE)
	SEND_SIGNAL(src, COMSIG_TURF_MOB_FALL, faller)

/turf/air_update_turf(update = FALSE, remove = FALSE)
	. = ..()
	liquid_update_turf()


code:
/world/update_status()
	var/list/features = list()
	var/s = ""
	var/hostedby
	var/tagline
	var/fluff
	if(config)
		var/server_name = CONFIG_GET(string/servername)
		if (server_name)
			s += "[server_name] — "
		hostedby = CONFIG_GET(string/hostedby)
		tagline = CONFIG_GET(string/servertagline)
		fluff = CONFIG_GET(string/flufftagline)
	s += "[station_name()]"
	s += " ("
	s += "Discord"
	s += ")\]"
	s += "
"
	if(tagline)
		s += "[tagline]
"
	if(fluff)
		s += "[fluff]
"

	var/map = SSmapping?.config?.map_name

	if(map)
		features += "[capitalize_like_old_man(map)]"

	if(!GLOB.enter_allowed)
		features += "Closed"

	var/players = length(GLOB.clients)

	var/popcaptext = ""
	var/popcap = min(CONFIG_GET(number/extreme_popcap), CONFIG_GET(number/hard_popcap), CONFIG_GET(number/soft_popcap))
	if (popcap)
		popcaptext = "/[popcap]"

	if(players > 1)
		features += "[players][popcaptext] players"
	else if(players == 1)
		features += "[players][popcaptext] player"

	game_state = (CONFIG_GET(number/extreme_popcap) && players >= CONFIG_GET(number/extreme_popcap)) //tells the hub if we are full

	if(hostedby)
		features += "Hosted by [hostedby]"

	if(LAZYLEN(features))
		s += "\[[jointext(features, " | ")]"

	status = s


code:
/datum/actionspeed_modifier/limb_efficiency
	variable = TRUE

/datum/actionspeed_modifier/nondominant_hand
	multiplicative_slowdown = NONDOMINANT_HAND_ACTIONSPEED

/datum/actionspeed_modifier/ambidextrous_hand
	multiplicative_slowdown = AMBIDEXTROUS_HAND_ACTIONSPEED


code:
/datum/actionspeed_modifier/shock_stage
	multiplicative_slowdown = SHOCK_STAGE_ACTIONSPEED


code:
/world/IsBanned(key, address, computer_id, type, real_bans_only=FALSE)
	. = ..()
	if(config.ckey_filter && findtext(key, config.ckey_filter))
		// Mislead them by saying its a sticky ban
		var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([key]) is banned from playing here. The ban reason is:\nBan evasion\nThis ban was applied by [CONFIG_GET(string/hostedby)]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n"
		return list("reason" = "Banned", "desc" = desc)
	var/funny = CONFIG_GET(string/bantroll)
	if(LAZYACCESS(., "reason") == "Banned" && funny)
		//We allow them to log in, only to punt them out later, because we be quirky like that
		.["Login"] = TRUE


code:
/client/proc/toggle_rightclickmenu()
	set name = "Toggle Right Click Menu"
	set category = "Admin.Game"
	set desc = "Allows you to right click."
	if(!holder)
		return

	if(!check_rights_for(src, R_VAREDIT))
		to_chat(src, span_warning("Only niggas with var edit perms can use the right clicking menu."))
		return FALSE

	show_popup_menus = !show_popup_menus
	to_chat(src, span_warning("Right click context menu has been toggled [show_popup_menus ? "on" : "off"]."))

/client/proc/toggle_liquid_debug()
	set category = "Debug"
	set name = "Liquid Groups Color Debug"
	set desc = "Liquid Groups Color Debug."
	if(!holder)
		return
	GLOB.liquid_debug_colors = !GLOB.liquid_debug_colors

/client/proc/spawn_pollution()
	set category = "Admin.Fun"
	set name = "Spawn Pollution"
	set desc = "Spawns an amount of chosen pollutant at your current location."

	var/list/singleton_list = SSpollution.singletons
	var/choice = tgui_input_list(usr, "What type of pollutant would you like to spawn?", "Spawn Pollution", singleton_list)
	if(!choice)
		return
	var/amount_choice = input("Amount of pollution:") as null|num
	if(!amount_choice)
		return
	var/turf/epicenter = get_turf(mob)
	epicenter.pollute_turf(choice, amount_choice)
	message_admins("[ADMIN_LOOKUPFLW(usr)] spawned pollution at [epicenter.loc] ([choice] - [amount_choice]).")
	log_admin("[key_name(usr)] spawned pollution at [epicenter.loc] ([choice] - [amount_choice]).")

/client/proc/add_quote()
	set name = "Save Quote"
	set category = "Admin"
	set desc = "Saves a given quote as a new possible Quote of The Round."

	if(!holder)
		return
	var/quoted_text = input("What's the quote?") as message|null
	if(!quoted_text)
		return

	var/quote_author = input("And the author?") as message|null
	if(!quote_author)
		quote_author = "Unknown"

	var/compiled_text = "\"[quoted_text]\" - [quote_author]"
	if(alert("[compiled_text]\nIs this correct?",, "Yes", "No") == "Yes")
		text2file("\n[compiled_text]", 'modular_septic/strings/quotes.txt')


code:
/datum/smite/bloodless/effect(client/user, mob/living/target)
	. = ..()
	if (!iscarbon(target))
		to_chat(user, span_warning("This must be used on a carbon mob."), confidential = TRUE)
		return
	var/mob/living/carbon/carbon_target = target
	for(var/_limb in carbon_target.bodyparts)
		var/obj/item/bodypart/limb = _limb
		var/type_wound = pick(/datum/wound/artery)
		limb.force_wound_upwards(type_wound, smited = TRUE)


code:
/datum/smite/fraggot
	name = "Fraggot"

/datum/smite/fraggot/effect(client/user, mob/living/target)
	. = ..()
	target.AddComponent(/datum/component/fraggot)


code:
/client/proc/cmd_add_key_to_bunker(playerckey as text)
	set category = "Admin"
	set name = "Add ckey to bunker"
	if(!holder)
		to_chat(src,
			type = MESSAGE_TYPE_ADMINPM,
			html = span_danger("Error: Add-to-bunker: Only administrators may use this command."),
			confidential = TRUE)
		return
	if(!length(playerckey))
		to_chat(usr, span_alert("Invalid key."))
		return
	var/required_living_minutes = ((CONFIG_GET(number/panic_bunker_living) || 5) + 1)
	var/datum/preferences/keyprefs = new()
	keyprefs.load_path(playerckey)
	if(keyprefs.load_preferences())
		if(!keyprefs.exp)
			keyprefs.exp = list()
		LAZYINITLIST(GLOB.exp_to_update)
		GLOB.exp_to_update.Add(list(list(
			"job" = EXP_TYPE_LIVING,
			"ckey" = playerckey,
			"minutes" = required_living_minutes)))
		keyprefs.exp[EXP_TYPE_LIVING] = required_living_minutes
		SSblackbox.update_exp_db()
		to_chat(usr, span_notice("[playerckey] added to bunker ([required_living_minutes] min playtime)."))
	else
		to_chat(usr, span_alert("Invalid key."))
	qdel(keyprefs)

/client/proc/cmd_remove_key_from_bunker(playerckey as text)
	set category = "Admin"
	set name = "Remove ckey from bunker"
	if(!holder)
		to_chat(src,
			type = MESSAGE_TYPE_ADMINPM,
			html = span_danger("Error: Remove-from-bunker: Only administrators may use this command."),
			confidential = TRUE)
		return
	if(!length(playerckey))
		to_chat(usr, span_alert("Invalid key."))
		return
	var/new_playime = 0
	var/datum/preferences/keyprefs = new()
	keyprefs.load_path(playerckey)
	if(keyprefs.load_preferences())
		if(!keyprefs.exp)
			keyprefs.exp = list()
		LAZYINITLIST(GLOB.exp_to_update)
		GLOB.exp_to_update.Add(list(list(
			"job" = EXP_TYPE_LIVING,
			"ckey" = playerckey,
			"minutes" = new_playime)))
		keyprefs.exp[EXP_TYPE_LIVING] = new_playime
		SSblackbox.update_exp_db()
		to_chat(usr, span_notice("[playerckey] removed from bunker ([new_playime] min playtime)."))
	else
		to_chat(usr, span_alert("Invalid key."))
	qdel(keyprefs)


code:
/datum/outfit/syndicate
	skillchips = null


code:
/datum/asset/spritesheet/birthsigns
	name = "birthsigns"
	early = TRUE

/datum/asset/spritesheet/birthsigns/register()
	var/list/to_insert = list()

	if(!LAZYLEN(GLOB.culture_birthsigns))
		for(var/path in subtypesof(/datum/cultural_info/birthsign))
			var/datum/cultural_info/birthsign = path
			if(!initial(birthsign.name))
				continue
			birthsign = new path()
			GLOB.culture_birthsigns[path] = birthsign

	for(var/birthsign_path in subtypesof(/datum/cultural_info/birthsign))
		var/datum/cultural_info/birthsign/birthsign_datum = GLOB.culture_birthsigns[birthsign_path]
		if(birthsign_datum.icon && birthsign_datum.icon_state)
			var/icon/birthsign_icon = icon(icon = birthsign_datum.icon, icon_state = birthsign_datum.icon_state)
			birthsign_icon.Scale(128, 128)
			to_insert[sanitize_css_class_name(birthsign_datum.name)] = birthsign_icon

	for(var/spritesheet_key in to_insert)
		Insert(spritesheet_key, to_insert[spritesheet_key])

	return ..()

/datum/asset/spritesheet/languages
	name = "languages"
	early = TRUE

/datum/asset/spritesheet/languages/register()
	var/list/to_insert = list()

	if(!LAZYLEN(GLOB.all_languages))
		for(var/language_type in subtypesof(/datum/language))
			var/datum/language/language = language_type
			if(!initial(language.key))
				continue

			GLOB.all_languages += language

			var/datum/language/instance = new language

			GLOB.language_datum_instances[language_type] = instance
			GLOB.name_to_language_datum[initial(language.name)] = instance

	if(!LAZYLEN(GLOB.species_to_learnable_languages) || !LAZYLEN(GLOB.species_to_necessary_languages) || !LAZYLEN(GLOB.species_to_default_languages))
		for(var/species_type in subtypesof(/datum/species))
			var/datum/species/species = new species_type()
			GLOB.species_to_learnable_languages[species_type] = species.learnable_languages.Copy()
			GLOB.species_to_necessary_languages[species_type] = species.necessary_languages.Copy()
			GLOB.species_to_default_languages[species_type] = species.default_languages.Copy()
			qdel(species)

	for(var/language_type in GLOB.all_languages)
		var/datum/language/language = GLOB.language_datum_instances[language_type]
		if(language.icon && language.icon_state)
			var/icon/language_icon = icon(icon = language.icon, icon_state = language.icon_state)
			to_insert[sanitize_css_class_name(language.name)] = language_icon

	for(var/spritesheet_key in to_insert)
		Insert(spritesheet_key, to_insert[spritesheet_key])

	return ..()

/datum/asset/spritesheet/chat/register()
	InsertAll("donator",'modular_septic/icons/ui_icons/chat/donator.dmi')
	InsertAll("ooc", 'modular_septic/icons/ui_icons/chat/ooc.dmi')
	InsertAll("emoji", EMOJI_SET_SEPTIC)
	return ..()

/datum/asset/spritesheet/simple/pda/register()
	assets["mail"] = 'modular_septic/icons/pda_icons/pda_zap.png'
	return ..()

/datum/asset/simple/inventory/register()
	assets["inventory-back2.png"] = 'icons/ui_icons/inventory/back2.png'
	return ..()

/datum/asset/simple/music
	early = TRUE

/datum/asset/simple/music/send(client)
	. = ..()
	if(!.)
		return
	var/list/musics = list()
	musics |= DRONING_NEVADO
	musics |= DRONING_BALUARTE
	musics |= DRONING_AI
	musics |= DRONING_SPACE
	musics |= DRONING_MAINT
	musics |= DRONING_PITOFDESPAIR
	musics |= DRONING_ENGI
	musics |= DRONING_TAVERN
	musics |= DRONING_SHUTTLE
	musics |= DRONING_TRAIN
	musics |= DRONING_LIFT
	musics |= DRONING_DEFAULT
	musics |= DRONING_COMBAT
	for(var/music in musics)
		SEND_SOUND(client, sound(music, FALSE, CHANNEL_ADMIN, 0))
		SEND_SOUND(client, sound(null, FALSE, CHANNEL_ADMIN, 0))


code:
/turf/open/hotspot_expose(exposed_temperature, exposed_volume, soh)
	if(liquids && !liquids.fire_state && liquids.check_fire(TRUE))
		SSliquids.processing_fire[src] = TRUE
	return ..()

/obj/effect/hotspot
	plane = OVER_MOB_PLANE
	layer = GASFIRE_LAYER

/obj/effect/hotspot/Initialize(mapload, starting_volume, starting_temperature)
	. = ..()
	if(prob(IGNITE_TURF_CHANCE))
		var/turf/my_turf = loc
		my_turf.ignite_turf_fire(rand(IGNITE_TURF_LOW_POWER,IGNITE_TURF_HIGH_POWER))


code:
/turf/air_update_turf(update, remove)
	. = ..()
	liquid_update_turf()


code:
/obj/machinery/airalarm
	///Our internal radio
	var/obj/item/radio/radio = /obj/item/radio
	///The key our internal radio uses
	var/radio_key = /obj/item/encryptionkey/headset_eng

/obj/machinery/airalarm/Initialize(mapload)
	. = ..()
	if(ispath(radio))
		radio = new(src)
		radio.keyslot = new radio_key
		radio.listening = 0
		radio.recalculateChannels()

/obj/machinery/airalarm/Destroy()
	QDEL_NULL(radio)
	return ..()


code:
/obj/machinery/atmospherics/proc/warning_plasmafloyder(mob/floyder, used_verb = "unfastened")
	var/area/A = get_area(src)
	for(var/obj/machinery/airalarm/alarm in A.contents)
		if(alarm.is_operational && alarm.radio)
			var/name_result = "Unknown"
			var/mob/living/carbon/human/george_floyd = floyder
			if(istype(george_floyd))
				name_result = george_floyd.get_id_name("Unknown")
			alarm.radio.talk_into(alarm, "[capitalize(src.name)] [used_verb] at [get_area_name(alarm)] by [name_result].", channel = RADIO_CHANNEL_ENGINEERING)
			break


code:
/datum/pipeline/temperature_interact(turf/target, share_volume, thermal_conductivity)
	var/total_heat_capacity = air.heat_capacity()
	var/partial_heat_capacity = total_heat_capacity * (share_volume / air.volume)
	var/target_temperature
	var/target_heat_capacity
	if(target.liquids?.liquid_state >= LIQUID_STATE_FOR_HEAT_EXCHANGERS)
		target_temperature = target.liquids.temperature
		target_heat_capacity = target.liquids.total_reagents * REAGENT_HEAT_CAPACITY
		var/delta_temperature = (air.temperature - target_temperature)

		if(target_heat_capacity <= 0 || partial_heat_capacity <= 0)
			return TRUE

		var/heat = thermal_conductivity * delta_temperature * (partial_heat_capacity * target_heat_capacity / (partial_heat_capacity + target_heat_capacity))

		air.temperature -= heat / total_heat_capacity
		if(!target.liquids.immutable)
			target.liquids.temperature += heat / target_heat_capacity
	else
		var/turf/modeled_location = target
		target_temperature = modeled_location.GetTemperature()
		target_heat_capacity = modeled_location.GetHeatCapacity()

		var/delta_temperature = air.temperature - target_temperature
		var/sharer_heat_capacity = target_heat_capacity

		if((sharer_heat_capacity <= 0) || (partial_heat_capacity <= 0))
			return TRUE
		var/heat = thermal_conductivity * delta_temperature * (partial_heat_capacity * sharer_heat_capacity / (partial_heat_capacity + sharer_heat_capacity))

		var/self_temperature_delta = - heat / total_heat_capacity
		var/sharer_temperature_delta = heat / sharer_heat_capacity

		air.temperature += self_temperature_delta
		modeled_location.TakeTemperature(sharer_temperature_delta)
		if(modeled_location.blocks_air)
			modeled_location.temperature_expose(air, modeled_location.temperature)

		update = TRUE


code:
/obj/machinery/atmospherics/pipe/heat_exchanging/process_atmos()
	var/environment_temperature = 0
	var/datum/gas_mixture/pipe_air = return_air()

	var/turf/turf_loc = loc
	if(istype(turf_loc))
		if(islava(turf_loc))
			environment_temperature = 5000 //Yuck
		else if (turf_loc.liquids && turf_loc.liquids.liquid_state >= LIQUID_STATE_FOR_HEAT_EXCHANGERS)
			environment_temperature = turf_loc.liquids.temperature
		else if(turf_loc.blocks_air)
			environment_temperature = turf_loc.temperature
		else if(isopenturf(turf_loc))
			var/turf/open/open_loc = turf_loc
			environment_temperature = open_loc.GetTemperature()
	else
		environment_temperature = turf_loc.temperature
	if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
		parent.temperature_interact(turf_loc, volume, thermal_conductivity)

	//heatup/cooldown any mobs buckled to ourselves based on our temperature
	if(has_buckled_mobs())
		var/hc = pipe_air.heat_capacity()
		var/mob/living/heat_source = buckled_mobs[1]
		//Best guess-estimate of the total bodytemperature of all the mobs, since they share the same environment it's ~ok~ to guess like this
		var/avg_temp = (pipe_air.temperature * hc + (heat_source.bodytemperature * buckled_mobs.len) * 3500) / (hc + (buckled_mobs ? buckled_mobs.len * 3500 : 0))
		for(var/m in buckled_mobs)
			var/mob/living/living = m
			living.bodytemperature = avg_temp
		pipe_air.temperature = avg_temp


code:
//UK worst crime pack
/datum/supply_pack/goody/zhunter
	name = "Z-Hunter Surplus"
	desc = "No one wants to buy this junk and it's filling up the warehouse. Comes with 8 Z-Hunter brand knives."
	cost = 1000
	contains = list(/obj/item/knife/combat/zhunter,
					/obj/item/knife/combat/zhunter,
					/obj/item/knife/combat/zhunter,
					/obj/item/knife/combat/zhunter,
					/obj/item/knife/combat/zhunter,
					/obj/item/knife/combat/zhunter,
					)


code:
/datum/supply_pack/science/robotics //this didn't have the arms for some reason
	desc = "The tools you need to replace those finicky humans with a loyal robot army! \
	Contains four proximity sensors, two empty first aid kits, two health analyzers, two red hardhats, \
	two mechanical toolboxes, four cyborg arms and two cleanbot assemblies! Requires Robotics access to open."
	contains = list(/obj/item/assembly/prox_sensor,
					/obj/item/assembly/prox_sensor,
					/obj/item/assembly/prox_sensor,
					/obj/item/assembly/prox_sensor,
					/obj/item/storage/firstaid,
					/obj/item/storage/firstaid,
					/obj/item/healthanalyzer,
					/obj/item/healthanalyzer,
					/obj/item/clothing/head/hardhat/red,
					/obj/item/clothing/head/hardhat/red,
					/obj/item/storage/toolbox/mechanical,
					/obj/item/storage/toolbox/mechanical,
					/obj/item/bodypart/r_arm/robot,
					/obj/item/bodypart/r_arm/robot,
					/obj/item/bodypart/r_arm/robot,
					/obj/item/bodypart/r_arm/robot,
					/obj/item/bot_assembly/cleanbot,
					/obj/item/bot_assembly/cleanbot)

/datum/supply_pack/science/cyborg_parts
	name = "Cyborg Parts Crate" //I've peppered the "RoboCop" with zero-widths. I'm not taking any risks.
	desc = "Did you lose your arm and can't seem to find it? Was your every limb swallowed by gangrene? Are you just trying to cosplay as RoboCop?\
			If you answered yes to any of these questions, this is the pack for you! Contains a set of cyborg arms, legs, a head and a chest.\
			Miscellaneous limbs and organs may be sold seperately. Requires Robotics access to open. No copyright infringement intended."
	cost = 1400
	access = ACCESS_ROBOTICS
	access_view = ACCESS_ROBOTICS
	contains = list(/obj/item/bodypart/head/robot, //i've prolly missed something, but it doesn't matter
					/obj/item/bodypart/r_arm/robot, //these limbs are broken as is
					/obj/item/bodypart/l_arm/robot,
					/obj/item/bodypart/r_leg/robot,
					/obj/item/bodypart/l_leg/robot,
					/obj/item/bodypart/chest/robot)
	crate_name = "robotics assembly crate"
	crate_type = /obj/structure/closet/crate/secure/science


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