code:
/datum/disease/magnitis
	name = "Magnitis"
	max_stages = 4
	spread_text = "Airborne"
	cure_text = "Iron"
	cures = list("iron")
	agent = "Fukkos Miracos"
	viable_mobtypes = list(/mob/living/carbon/human)
	disease_flags = CAN_CARRY|CAN_RESIST|CURABLE
	permeability_mod = 0.75
	desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field."
	severity = MEDIUM

/datum/disease/magnitis/stage_act()
	..()
	switch(stage)
		if(2)
			if(prob(2))
				affected_mob << "You feel a slight shock course through your body."
			if(prob(2))
				for(var/obj/M in orange(2,affected_mob))
					if(!M.anchored && (M.flags & CONDUCT))
						step_towards(M,affected_mob)
				for(var/mob/living/silicon/S in orange(2,affected_mob))
					if(istype(S, /mob/living/silicon/ai)) continue
					step_towards(S,affected_mob)
		if(3)
			if(prob(2))
				affected_mob << "You feel a strong shock course through your body."
			if(prob(2))
				affected_mob << "You feel like clowning around."
			if(prob(4))
				for(var/obj/M in orange(4,affected_mob))
					if(!M.anchored && (M.flags & CONDUCT))
						var/i
						var/iter = rand(1,2)
						for(i=0,i<iter,i++)
							step_towards(M,affected_mob)
				for(var/mob/living/silicon/S in orange(4,affected_mob))
					if(istype(S, /mob/living/silicon/ai)) continue
					var/i
					var/iter = rand(1,2)
					for(i=0,i<iter,i++)
						step_towards(S,affected_mob)
		if(4)
			if(prob(2))
				affected_mob << "You feel a powerful shock course through your body."
			if(prob(2))
				affected_mob << "You query upon the nature of miracles."
			if(prob(8))
				for(var/obj/M in orange(6,affected_mob))
					if(!M.anchored && (M.flags & CONDUCT))
						var/i
						var/iter = rand(1,3)
						for(i=0,i<iter,i++)
							step_towards(M,affected_mob)
				for(var/mob/living/silicon/S in orange(6,affected_mob))
					if(istype(S, /mob/living/silicon/ai)) continue
					var/i
					var/iter = rand(1,3)
					for(i=0,i<iter,i++)
						step_towards(S,affected_mob)
	return


code:
/datum/disease/pierrot_throat
	name = "Pierrot's Throat"
	max_stages = 4
	spread_text = "Airborne"
	cure_text = "Banana products, especially banana bread."
	cures = list("banana")
	cure_chance = 75
	agent = "H0NI<42 Virus"
	viable_mobtypes = list(/mob/living/carbon/human)
	permeability_mod = 0.75
	desc = "If left untreated the subject will probably drive others to insanity."
	severity = MEDIUM
	longevity = 400

/datum/disease/pierrot_throat/stage_act()
	..()
	switch(stage)
		if(1)
			if(prob(10)) affected_mob << "You feel a little silly."
		if(2)
			if(prob(10)) affected_mob << "You start seeing rainbows."
		if(3)
			if(prob(10)) affected_mob << "Your thoughts are interrupted by a loud HONK!"
		if(4)
			if(prob(5)) affected_mob.say( pick( list("HONK!", "Honk!", "Honk.", "Honk?", "Honk!!", "Honk?!", "Honk...") ) )

license: mit


code:
/datum/disease/dna_retrovirus
	name = "Retrovirus"
	max_stages = 4
	spread_text = "Contact"
	spread_flags = CONTACT_GENERAL
	cure_text = "Rest or an injection of mutadone"
	cure_chance = 6
	agent = ""
	viable_mobtypes = list(/mob/living/carbon/human)
	desc = "A DNA-altering retrovirus that scrambles the structural and unique enzymes of a host constantly."
	severity = DANGEROUS
	permeability_mod = 0.4
	stage_prob = 2
	var/SE
	var/UI
	var/restcure = 0

/datum/disease/dna_retrovirus/New()
	..()
	agent = "Virus class [pick("A","B","C","D","E","F")][pick("A","B","C","D","E","F")]-[rand(50,300)]"
	if(prob(40))
		cures = list("mutadone")
	else
		restcure = 1

/datum/disease/dna_retrovirus/stage_act()
	..()
	switch(stage)
		if(1)
			if(restcure)
				if(affected_mob.lying && prob(30))
					affected_mob << "You feel better."
					cure()
					return
			if (prob(8))
				affected_mob << "Your head hurts."
			if (prob(9))
				affected_mob << "You feel a tingling sensation in your chest."
			if (prob(9))
				affected_mob << "You feel angry."
		if(2)
			if(restcure)
				if(affected_mob.lying && prob(20))
					affected_mob << "You feel better."
					cure()
					return
			if (prob(8))
				affected_mob << "Your skin feels loose."
			if (prob(10))
				affected_mob << "You feel very strange."
			if (prob(4))
				affected_mob << "You feel a stabbing pain in your head!"
				affected_mob.Paralyse(2)
			if (prob(4))
				affected_mob << "Your stomach churns."
		if(3)
			if(restcure)
				if(affected_mob.lying && prob(20))
					affected_mob << "You feel better."
					cure()
					return
			if (prob(10))
				affected_mob << "Your entire body vibrates."

			if (prob(35))
				if(prob(50))
					scramble_dna(affected_mob, 1, 0, rand(15,45))
				else
					scramble_dna(affected_mob, 0, 1, rand(15,45))

		if(4)
			if(restcure)
				if(affected_mob.lying && prob(5))
					affected_mob << "You feel better."
					cure()
					return
			if (prob(60))
				if(prob(50))
					scramble_dna(affected_mob, 1, 0, rand(50,75))
				else
					scramble_dna(affected_mob, 0, 1, rand(50,75))


code:
/datum/disease/rhumba_beat
	name = "The Rhumba Beat"
	max_stages = 5
	spread_text = "On contact"
	spread_flags = CONTACT_GENERAL
	cure_text = "Chick Chicky Boom!"
	cures = list("plasma")
	agent = "Unknown"
	viable_mobtypes = list(/mob/living/carbon/human)
	permeability_mod = 1
	severity = BIOHAZARD

/datum/disease/rhumba_beat/stage_act()
	..()
	switch(stage)
		if(1)
			if(affected_mob.ckey == "rosham")
				src.cure()
		if(2)
			if(affected_mob.ckey == "rosham")
				src.cure()
			if(prob(45))
				affected_mob.adjustToxLoss(5)
				affected_mob.updatehealth()
			if(prob(1))
				affected_mob << "You feel strange..."
		if(3)
			if(affected_mob.ckey == "rosham")
				src.cure()
			if(prob(5))
				affected_mob << "You feel the urge to dance..."
			else if(prob(5))
				affected_mob.emote("gasp")
			else if(prob(10))
				affected_mob << "You feel the need to chick chicky boom..."
		if(4)
			if(affected_mob.ckey == "rosham")
				src.cure()
			if(prob(10))
				affected_mob.emote("gasp")
				affected_mob << "You feel a burning beat inside..."
			if(prob(20))
				affected_mob.adjustToxLoss(5)
				affected_mob.updatehealth()
		if(5)
			if(affected_mob.ckey == "rosham")
				src.cure()
			affected_mob << "Your body is unable to contain the Rhumba Beat..."
			if(prob(50))
				affected_mob.gib()
		else
			return


code:
/datum/disease/transformation
	name = "Transformation"
	max_stages = 5
	spread_text = "Acute"
	spread_flags = SPECIAL
	cure_text = "A coder's love (theoretical)."
	agent = "Shenanigans"
	viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey, /mob/living/carbon/alien)
	severity = HARMFUL
	stage_prob = 10
	visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC
	disease_flags = CURABLE
	var/list/stage1 = list("You feel unremarkable.")
	var/list/stage2 = list("You feel boring.")
	var/list/stage3 = list("You feel utterly plain.")
	var/list/stage4 = list("You feel white bread.")
	var/list/stage5 = list("Oh the humanity!")
	var/new_form = /mob/living/carbon/human

/datum/disease/transformation/stage_act()
	..()
	switch(stage)
		if(1)
			if (prob(stage_prob) && stage1)
				affected_mob << pick(stage1)
		if(2)
			if (prob(stage_prob) && stage2)
				affected_mob << pick(stage2)
		if(3)
			if (prob(stage_prob*2) && stage3)
				affected_mob << pick(stage3)
		if(4)
			if (prob(stage_prob*2) && stage4)
				affected_mob << pick(stage4)
		if(5)
			do_disease_transformation(affected_mob)

/datum/disease/transformation/proc/do_disease_transformation(mob/living/affected_mob)
	if(istype(affected_mob, /mob/living/carbon) && affected_mob.stat != DEAD)
		if(stage5)
			affected_mob << pick(stage5)
		if(jobban_isbanned(affected_mob, new_form))
			affected_mob.death(1)
			return
		if(affected_mob.notransform)
			return
		affected_mob.notransform = 1
		affected_mob.canmove = 0
		affected_mob.icon = null
		affected_mob.overlays.Cut()
		affected_mob.invisibility = 101
		for(var/obj/item/W in affected_mob)
			if(istype(W, /obj/item/weapon/implant))
				qdel(W)
				continue
			W.layer = initial(W.layer)
			W.loc = affected_mob.loc
			W.dropped(affected_mob)
		var/mob/living/new_mob = new new_form(affected_mob.loc)
		if(istype(new_mob))
			new_mob.a_intent = "harm"
			if(affected_mob.mind)
				affected_mob.mind.transfer_to(new_mob)
			else
				new_mob.key = affected_mob.key
		qdel(affected_mob)

/datum/disease/transformation/jungle_fever
	name = "Jungle Fever"
	cure_text = "Bananas"
	cures = list("banana")
	spread_text = "Monkey Bites"
	spread_flags = SPECIAL
	viable_mobtypes = list(/mob/living/carbon/monkey, /mob/living/carbon/human)
	permeability_mod = 1
	cure_chance = 1
	disease_flags = CAN_CARRY|CAN_RESIST
	longevity = 30
	desc = "Monkeys with this disease will bite humans, causing humans to mutate into a monkey."
	severity = BIOHAZARD
	stage_prob = 4
	visibility_flags = 0
	agent = "Kongey Vibrion M-909"
	new_form = /mob/living/carbon/monkey

	stage1	= null
	stage2	= null
	stage3	= null
	stage4	= list("Your back hurts.", "You breathe through your mouth.",
					"You have a craving for bananas.", "Your mind feels clouded.")
	stage5	= list("You feel like monkeying around.")

/datum/disease/transformation/jungle_fever/do_disease_transformation(mob/living/carbon/affected_mob)
	if(!ismonkey(affected_mob))
		ticker.mode.add_monkey(affected_mob.mind)
		affected_mob.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)

/datum/disease/transformation/jungle_fever/stage_act()
	..()
	switch(stage)
		if(2)
			if(prob(2))
				affected_mob << "Your [pick("back", "arm", "leg", "elbow", "head")] itches."
		if(3)
			if(prob(4))
				affected_mob << "You feel a stabbing pain in your head."
				affected_mob.confused += 10
		if(4)
			if(prob(3))
				affected_mob.say(pick("Eeek, ook ook!", "Eee-eeek!", "Eeee!", "Ungh, ungh."))

/datum/disease/transformation/jungle_fever/cure()
	ticker.mode.remove_monkey(affected_mob.mind)
	..()

/datum/disease/transformation/robot

	name = "Robotic Transformation"
	cure_text = "An injection of copper."
	cures = list("copper")
	cure_chance = 5
	agent = "R2D2 Nanomachines"
	desc = "This disease, actually acute nanomachine infection, converts the victim into a cyborg."
	severity = DANGEROUS
	visibility_flags = 0
	stage1	= null
	stage2	= list("Your joints feel stiff.", "Beep...boop..")
	stage3	= list("Your joints feel very stiff.", "Your skin feels loose.", "You can feel something move...inside.")
	stage4	= list("Your skin feels very loose.", "You can feel... something...inside you.")
	stage5	= list("Your skin feels as if it's about to burst off!")
	new_form = /mob/living/silicon/robot

/datum/disease/transformation/robot/stage_act()
	..()
	switch(stage)
		if(3)
			if (prob(8))
				affected_mob.say(pick("Beep, boop", "beep, beep!", "Boop...bop"))
			if (prob(4))
				affected_mob << "You feel a stabbing pain in your head."
				affected_mob.Paralyse(2)
		if(4)
			if (prob(20))
				affected_mob.say(pick("beep, beep!", "Boop bop boop beep.", "kkkiiiill mmme", "I wwwaaannntt tttoo dddiiieeee..."))

/datum/disease/transformation/xeno

	name = "Xenomorph Transformation"
	cure_text = "Spaceacillin & Glycerol"
	cures = list("spaceacillin", "glycerol")
	cure_chance = 5
	agent = "Rip-LEY Alien Microbes"
	desc = "This disease changes the victim into a xenomorph."
	severity = BIOHAZARD
	visibility_flags = 0
	stage1	= null
	stage2	= list("Your throat feels scratchy.", "Kill...")
	stage3	= list("Your throat feels very scratchy.", "Your skin feels tight.", "You can feel something move...inside.")
	stage4	= list("Your skin feels very tight.", "Your blood boils!", "You can feel... something...inside you.")
	stage5	= list("Your skin feels as if it's about to burst off!")
	new_form = /mob/living/carbon/alien/humanoid/hunter

/datum/disease/transformation/xeno/stage_act()
	..()
	switch(stage)
		if(3)
			if (prob(4))
				affected_mob << "You feel a stabbing pain in your head."
				affected_mob.Paralyse(2)
		if(4)
			if (prob(20))
				affected_mob.say(pick("You look delicious.", "Going to... devour you...", "Hsssshhhhh!"))

/datum/disease/transformation/slime
	name = "Advanced Mutation Transformation"
	cure_text = "frost oil"
	cures = list("frostoil")
	cure_chance = 80
	agent = "Advanced Mutation Toxin"
	desc = "This highly concentrated extract converts anything into more of itself."
	severity = BIOHAZARD
	visibility_flags = 0
	stage1	= list("You don't feel very well.")
	stage2	= list("Your skin feels a little slimy.")
	stage3	= list("Your appendages are melting away.", "Your limbs begin to lose their shape.")
	stage4	= list("You are turning into a slime.")
	stage5	= list("You have become a slime.")
	new_form = /mob/living/simple_animal/slime

/datum/disease/transformation/slime/stage_act()
	..()
	switch(stage)
		if(1)
			if(ishuman(affected_mob) && affected_mob.dna && affected_mob.dna.species.id == "slime")
				stage = 5
		if(3)
			if(ishuman(affected_mob))
				var/mob/living/carbon/human/human = affected_mob
				if(human.dna.species.id != "slime")
					human.set_species(/datum/species/jelly/slime)

/datum/disease/transformation/corgi
	name = "The Barkening"
	cure_text = "Death"
	cures = list("adminordrazine")
	agent = "Fell Doge Majicks"
	desc = "This disease transforms the victim into a corgi."
	visibility_flags = 0
	stage1	= list("BARK.")
	stage2	= list("You feel the need to wear silly hats.")
	stage3	= list("Must... eat... chocolate....", "YAP")
	stage4	= list("Visions of washing machines assail your mind!")
	stage5	= list("AUUUUUU!!!")
	new_form = /mob/living/simple_animal/pet/dog/corgi

/datum/disease/transformation/corgi/stage_act()
	..()
	switch(stage)
		if(3)
			if (prob(8))
				affected_mob.say(pick("YAP", "Woof!"))
		if(4)
			if (prob(20))
				affected_mob.say(pick("Bark!", "AUUUUUU"))

/datum/disease/transformation/morph
	name = "Gluttony's Blessing"
	cure_text = "nothing"
	cures = list("adminordrazine")
	agent = "Gluttony's Blessing"
	desc = "A 'gift' from somewhere terrible."
	stage_prob = 20
	severity = BIOHAZARD
	visibility_flags = 0
	stage1	= list("Your stomach rumbles.")
	stage2	= list("Your skin feels saggy.")
	stage3	= list("Your appendages are melting away.", "Your limbs begin to lose their shape.")
	stage4	= list("You're ravenous.")
	stage5	= list("You have become a morph.")
	new_form = /mob/living/simple_animal/hostile/morph


code:
/datum/disease/tuberculosis
	name = "Fungal tuberculosis"
	max_stages = 5
	spread_text = "Airborne"
	cure_text = "Spaceacillin & salbutamol"
	cures = list("spaceacillin", "salbutamol")
	agent = "Fungal Tubercle bacillus Cosmosis"
	viable_mobtypes = list(/mob/living/carbon/human)
	cure_chance = 5//like hell are you getting out of hell
	desc = "A rare highly transmittable virulent virus. Few samples exist, rumoured to be carefully grown and cultured by clandestine bio-weapon specialists. Causes fever, blood vomiting, lung damage, weight loss, and fatigue."
	required_organs = list(/obj/item/organ/limb/head)
	severity = DANGEROUS

/datum/disease/tuberculosis/stage_act() //it begins
	..()
	switch(stage)
		if(2)
			if(prob(2))
				affected_mob.emote("cough")
				affected_mob << "Your chest hurts."
			if(prob(2))
				affected_mob << "Your stomach violently rumbles!"
			if(prob(5))
				affected_mob << "You feel a cold sweat form."
		if(4)
			if(prob(2))
				affected_mob << "You see four of everything"
				affected_mob.Dizzy(5)
			if(prob(2))
				affected_mob << "You feel a sharp pain from your lower chest!"
				affected_mob.adjustOxyLoss(5)
				affected_mob.emote("gasp")
			if(prob(10))
				affected_mob << "You feel air escape from your lungs painfully."
				affected_mob.adjustOxyLoss(25)
				affected_mob.emote("gasp")
		if(5)
			if(prob(2))
				affected_mob << "[pick("You feel your heart slowing...", "You relax and slow your heartbeat.")]"
				affected_mob.adjustStaminaLoss(70)
			if(prob(10))
				affected_mob.adjustStaminaLoss(100)
				affected_mob.visible_message("[affected_mob] faints!", "You surrender yourself and feel at peace...")
				affected_mob.AdjustSleeping(5)
			if(prob(2))
				affected_mob << "You feel your mind relax and your thoughts drift!"
				affected_mob.confused = min(100, affected_mob.confused + 8)
			if(prob(10))
				affected_mob.vomit(20)
			if(prob(3))
				affected_mob << "[pick("Your stomach silently rumbles...", "Your stomach seizes up and falls limp, muscles dead and lifeless.", "You could eat a crayon")]"
				affected_mob.overeatduration = max(affected_mob.overeatduration - 100, 0)
				affected_mob.nutrition = max(affected_mob.nutrition - 100, 0)
			if(prob(15))
				affected_mob << "[pick("You feel uncomfortably hot...", "You feel like unzipping your jumpsuit", "You feel like taking off some clothes...")]"
				affected_mob.bodytemperature += 40
	return


code:
/datum/disease/wizarditis
	name = "Wizarditis"
	max_stages = 4
	spread_text = "Airborne"
	cure_text = "The Manly Dorf"
	cures = list("manlydorf")
	cure_chance = 100
	agent = "Rincewindus Vulgaris"
	viable_mobtypes = list(/mob/living/carbon/human)
	disease_flags = CAN_CARRY|CAN_RESIST|CURABLE
	permeability_mod = 0.75
	desc = "Some speculate, that this virus is the cause of Wizard Federation existance. Subjects affected show the signs of mental retardation, yelling obscure sentences or total gibberish. On late stages subjects sometime express the feelings of inner power, and, cite, 'the ability to control the forces of cosmos themselves!' A gulp of strong, manly spirits usually reverts them to normal, humanlike, condition."
	severity = HARMFUL
	required_organs = list(/obj/item/organ/limb/head)

/*
BIRUZ BENNAR
SCYAR NILA - teleport
NEC CANTIO - dis techno
EI NATH - shocking grasp
AULIE OXIN FIERA - knock
TARCOL MINTI ZHERI - forcewall
STI KALY - blind
*/

/datum/disease/wizarditis/stage_act()
	..()

	switch(stage)
		if(2)
			if(prob(1)&&prob(50))
				affected_mob.say(pick("You shall not pass!", "Expeliarmus!", "By Merlins beard!", "Feel the power of the Dark Side!"))
			if(prob(1)&&prob(50))
				affected_mob << "You feel [pick("that you don't have enough mana", "that the winds of magic are gone", "an urge to summon familiar")]."

		if(3)
			if(prob(1)&&prob(50))
				affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!", "STI KALY!", "TARCOL MINTI ZHERI!"))
			if(prob(1)&&prob(50))
				affected_mob << "You feel [pick("the magic bubbling in your veins","that this location gives you a +1 to INT","an urge to summon familiar")]."

		if(4)

			if(prob(1))
				affected_mob.say(pick("NEC CANTIO!","AULIE OXIN FIERA!","STI KALY!","EI NATH!"))
				return
			if(prob(1)&&prob(50))
				affected_mob << "You feel [pick("the tidal wave of raw power building inside","that this location gives you a +2 to INT and +1 to WIS","an urge to teleport")]."
				spawn_wizard_clothes(50)
			if(prob(1)&&prob(1))
				teleport()
	return

/datum/disease/wizarditis/proc/spawn_wizard_clothes(chance = 0)
	if(istype(affected_mob, /mob/living/carbon/human))
		var/mob/living/carbon/human/H = affected_mob
		if(prob(chance))
			if(!istype(H.head, /obj/item/clothing/head/wizard))
				if(!H.unEquip(H.head))
					qdel(H.head)
				H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(H), slot_head)
			return
		if(prob(chance))
			if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe))
				if(!H.unEquip(H.wear_suit))
					qdel(H.wear_suit)
				H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(H), slot_wear_suit)
			return
		if(prob(chance))
			if(!istype(H.shoes, /obj/item/clothing/shoes/sandal))
				if(!H.unEquip(H.shoes))
					qdel(H.shoes)
			H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes)
			return
	else
		var/mob/living/carbon/H = affected_mob
		if(prob(chance))
			if(!istype(H.r_hand, /obj/item/weapon/staff))
				H.drop_r_hand()
				H.put_in_r_hand( new /obj/item/weapon/staff(H) )
			return
	return

/datum/disease/wizarditis/proc/teleport()
	var/list/theareas = get_areas_in_range(80, affected_mob)
	for(var/area/space/S in theareas)
		theareas -= S

	if(!theareas||!theareas.len)
		return

	var/area/thearea = pick(theareas)

	var/list/L = list()
	for(var/turf/T in get_area_turfs(thearea.type))
		if(T.z != affected_mob.z) continue
		if(T.name == "space") continue
		if(!T.density)
			var/clear = 1
			for(var/obj/O in T)
				if(O.density)
					clear = 0
					break
			if(clear)
				L+=T

	if(!L)
		return

	affected_mob.say("SCYAR NILA [uppertext(thearea.name)]!")
	affected_mob.loc = pick(L)

	return


code:
#define FORWARD -1
#define BACKWARD 1

/datum/construction
	var/list/steps
	var/atom/holder
	var/result
	var/list/steps_desc

/datum/construction/New(atom)
	..()
	holder = atom
	if(!holder) //don't want this without a holder
		qdel(src)
	set_desc(steps.len)
	return

/datum/construction/proc/next_step()
	steps.len--
	if(!steps.len)
		spawn_result()
	else
		set_desc(steps.len)
	return

/datum/construction/proc/action(atom/used_atom,mob/user)
	return

/datum/construction/proc/check_step(atom/used_atom,mob/user) //check last step only
	var/valid_step = is_right_key(used_atom)
	if(valid_step)
		if(custom_action(valid_step, used_atom, user))
			next_step()
			return 1
	return 0

/datum/construction/proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type.
	var/list/L = steps[steps.len]
	if(istype(used_atom, L["key"]))
		return steps.len
	return 0

/datum/construction/proc/custom_action(step, used_atom, user)
	return 1

/datum/construction/proc/check_all_steps(atom/used_atom,mob/user) //check all steps, remove matching one.
	for(var/i=1;i<=steps.len;i++)
		var/list/L = steps[i];
		if(istype(used_atom, L["key"]))
			if(custom_action(i, used_atom, user))
				steps[i]=null;//stupid byond list from list removal...
				listclearnulls(steps);
				if(!steps.len)
					spawn_result()
				return 1
	return 0

/datum/construction/proc/spawn_result()
	if(result)
		new result(get_turf(holder))
		qdel(holder)
	return

/datum/construction/proc/set_desc(index as num)
	var/list/step = steps[index]
	holder.desc = step["desc"]
	return

/datum/construction/reversible
	var/index

/datum/construction/reversible/New(atom)
	..()
	index = steps.len
	return

/datum/construction/reversible/proc/update_index(diff as num)
	index+=diff
	if(index==0)
		spawn_result()
	else
		set_desc(index)
	return

/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step
	var/list/L = steps[index]
	if(istype(used_atom, L["key"]))
		return FORWARD //to the first step -> forward
	else if(L["backkey"] && istype(used_atom, L["backkey"]))
		return BACKWARD //to the last step -> backwards
	return 0

/datum/construction/reversible/check_step(atom/used_atom,mob/user)
	var/diff = is_right_key(used_atom)
	if(diff)
		if(custom_action(index, diff, used_atom, user))
			update_index(diff)
			return 1
	return 0

/datum/construction/reversible/custom_action(index, diff, used_atom, user)
	return 1


code:
/*
 * WARRANTY VOID IF CODE USED
 */

/datum/events
	var/list/events

/datum/events/New()
	..()
	events = new

/datum/events/proc/addEventType(event_type as text)
	if(!(event_type in events) || !islist(events[event_type]))
		events[event_type] = list()
		return 1
	return

//	Arguments: event_type as text, proc_holder as datum, proc_name as text
//	Returns: New event, null on error.
/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text)
	if(!event_type || !proc_holder || !proc_name)
		return
	addEventType(event_type)
	var/list/event = events[event_type]
	var/datum/event/E = new /datum/event(proc_holder,proc_name)
	event += E
	return E

//  Arguments: event_type as text, any number of additional arguments to pass to event handler
//  Returns: null
/datum/events/proc/fireEvent()
	//world << "Events in [args[1]] called"
	var/list/event = listgetindex(events,args[1])
	if(istype(event))
		spawn(0)
			for(var/datum/event/E in event)
				if(!E.Fire(arglist(args.Copy(2))))
					clearEvent(args[1],E)
	return

// Arguments: event_type as text, E as /datum/event
// Returns: 1 if event cleared, null on error

/datum/events/proc/clearEvent(event_type as text, datum/event/E)
	if(!event_type || !E)
		return
	var/list/event = listgetindex(events,event_type)
	event -= E
	return 1

/datum/event
	var/listener
	var/proc_name

/datum/event/New(tlistener,tprocname)
	listener = tlistener
	proc_name = tprocname
	return ..()

/datum/event/proc/Fire()
	//world << "Event fired"
	if(listener)
		call(listener,proc_name)(arglist(args))
		return 1
	return


code:
var/global/datum/getrev/revdata = new()

/datum/getrev
	var/parentcommit
	var/commit
	var/list/testmerge = list()
	var/date

/datum/getrev/New()
	var/head_file = return_file_text(".git/logs/HEAD")
	var/regex/head_log = new("(\\w{40}) (\\w{40}).+> (\\d{10}).+\n\\Z")
	head_log.Find(head_file)
	parentcommit = head_log.group[1]
	commit = head_log.group[2]
	var/unix_time = text2num(head_log.group[3])
	if(SERVERTOOLS && fexists("..\\prtestjob.lk"))
		testmerge = file2list("..\\prtestjob.lk")
	date = unix2date(unix_time)
	world.log << "Running /tg/ revision:"
	world.log << "[date]"
	world.log << commit
	if(testmerge.len)
		for(var/line in testmerge)
			if(line)
				world.log << "Test merge active of PR #[line]"
		world.log << "Based off master commit [parentcommit]"
	world.log << "Current map - [MAP_NAME]" //can't think of anywhere better to put it

/client/verb/showrevinfo()
	set category = "OOC"
	set name = "Show Server Revision"
	set desc = "Check the current server code revision"

	if(revdata.commit)
		src << "Server revision compiled on: [revdata.date]"
		if(revdata.testmerge.len)
			for(var/line in revdata.testmerge)
				if(line)
					src << "Test merge active of PR #[line]"
			src << "Based off master commit [revdata.parentcommit]"
		else
			src << "[revdata.commit]"
	else
		src << "Revision unknown"
	src << "Current Infomational Settings:"
	src << "Protect Authority Roles From Traitor: [config.protect_roles_from_antagonist]"
	src << "Protect Assistant Role From Traitor: [config.protect_assistant_from_antagonist]"
	src << "Enforce Human Authority: [config.enforce_human_authority]"
	src << "Allow Latejoin Antagonists: [config.allow_latejoin_antagonists]"
	src << "Enforce Continuous Rounds: [config.continuous.len] of [config.modes.len] roundtypes"
	src << "Allow Midround Antagonists: [config.midround_antag.len] of [config.modes.len] roundtypes"
	if(config.show_game_type_odds)
		src <<"Game Mode Odds:"
		var/sum = 0
		for(var/i=1,i<=config.probabilities.len,i++)
			sum += config.probabilities[config.probabilities[i]]
		for(var/i=1,i<=config.probabilities.len,i++)
			if(config.probabilities[config.probabilities[i]] > 0)
				var/percentage = round(config.probabilities[config.probabilities[i]] / sum * 100, 0.1)
				src << "[config.probabilities[i]] [percentage]%"
	return


code:
/datum/icon_snapshot
	var/name
	var/icon
	var/icon_state
	var/list/overlays

/datum/icon_snapshot/proc/makeImg()
	if(!icon || !icon_state)
		return
	var/obj/temp = new
	temp.icon = icon
	temp.icon_state = icon_state
	temp.overlays = overlays.Copy()
	var/icon/tempicon = getFlatIcon(temp) // TODO Actually write something less heavy-handed for this
	return tempicon


code:
/datum/map_template
	var/name = "Default Template Name"
	var/width = 0
	var/height = 0
	var/mappath = null
	var/mapfile = null
	var/loaded = 0 // Times loaded this round

/datum/map_template/New(path = null, map = null, rename = null)
	if(path)
		mappath = path
		preload_size(mappath)
	if(map)
		mapfile = map
	if(rename)
		name = rename

/datum/map_template/proc/preload_size(path)
	var/quote = ascii2text(34)
	var/map_file = file2text(path)
	var/key_len = length(copytext(map_file,2,findtext(map_file,quote,2,0)))
	//assuming one map per file since more makes no sense for templates anyway
	var/mapstart = findtext(map_file,"\n(1,1,") //todo replace with something saner
	var/content = copytext(map_file,findtext(map_file,quote+"\n",mapstart,0)+2,findtext(map_file,"\n"+quote,mapstart,0)+1)
	var/line_len = length(copytext(content,1,findtext(content,"\n",2,0)))

	width = line_len/key_len
	height = length(content)/(line_len+1)

/datum/map_template/proc/load(turf/T, centered = FALSE)
	if(centered)
		T = locate(T.x - round(width/2) , T.y - round(height/2) , T.z)
	if(!T)
		return
	if(T.x+width > world.maxx)
		return
	if(T.y+height > world.maxy)
		return

	maploader.load_map(get_file(), T.x-1, T.y-1, T.z)

	//initialize things that are normally initialized after map load
	var/list/obj/machinery/atmospherics/atmos_machines = list()
	var/list/obj/structure/cable/cables = list()
	var/list/atom/atoms = list()

	for(var/L in block(T,locate(T.x+width-1, T.y+height-1, T.z)))
		var/turf/B = L
		for(var/A in B)
			atoms += A
			if(istype(A,/obj/structure/cable))
				cables += A
				continue
			if(istype(A,/obj/machinery/atmospherics))
				atmos_machines += A
				continue

	SSobj.setup_template_objects(atoms)
	SSmachine.setup_template_powernets(cables)
	SSair.setup_template_machinery(atmos_machines)

	log_game("[name] loaded at at [T.x],[T.y],[T.z]")

/datum/map_template/proc/get_file()
	if(mapfile)
		return mapfile
	if(mappath)
		mapfile = file(mappath)
		return mapfile

/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
	var/turf/placement = T
	if(centered)
		var/turf/corner = locate(placement.x - round(width/2), placement.y - round(height/2), placement.z)
		if(corner)
			placement = corner
	return block(placement, locate(placement.x+width-1, placement.y+height-1, placement.z))

/proc/preloadTemplates(path = "_maps/templates/") //see master controller setup
	var/list/filelist = flist(path)
	for(var/map in filelist)
		var/datum/map_template/T = new(path = "[path][map]", rename = "[map]")
		map_templates[T.name] = T

	preloadRuinTemplates()

/proc/preloadRuinTemplates()
	var/list/potentialSpaceRuins = generateMapList(filename = "config/spaceRuinConfig.txt")
	for(var/ruin in potentialSpaceRuins)
		var/datum/map_template/T = new(path = "[ruin]", rename = "[ruin]")
		space_ruins_templates[T.name] = T

	var/list/potentialLavaRuins = generateMapList(filename = "config/lavaRuinConfig.txt")
	for(var/ruin in potentialLavaRuins)
		var/datum/map_template/T = new(path = "[ruin]", rename = "[ruin]")
		lava_ruins_templates[T.name] = T


code:
//wrapper
/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
	var/datum/teleport/instant/science/D = new
	if(D.start(arglist(args)))
		return 1
	return 0

/datum/teleport
	var/atom/movable/teleatom //atom to teleport
	var/atom/destination //destination to teleport to
	var/precision = 0 //teleport precision
	var/datum/effect_system/effectin //effect to show right before teleportation
	var/datum/effect_system/effectout //effect to show right after teleportation
	var/soundin //soundfile to play before teleportation
	var/soundout //soundfile to play after teleportation
	var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation)

/datum/teleport/proc/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
	if(!initTeleport(arglist(args)))
		return 0
	return 1

/datum/teleport/proc/initTeleport(ateleatom,adestination,aprecision,afteleport,aeffectin,aeffectout,asoundin,asoundout)
	if(!setTeleatom(ateleatom))
		return 0
	if(!setDestination(adestination))
		return 0
	if(!setPrecision(aprecision))
		return 0
	setEffects(aeffectin,aeffectout)
	setForceTeleport(afteleport)
	setSounds(asoundin,asoundout)
	return 1

//must succeed
/datum/teleport/proc/setPrecision(aprecision)
	if(isnum(aprecision))
		precision = aprecision
		return 1
	return 0

//must succeed
/datum/teleport/proc/setDestination(atom/adestination)
	if(istype(adestination))
		destination = adestination
		return 1
	return 0

//must succeed in most cases
/datum/teleport/proc/setTeleatom(atom/movable/ateleatom)
	if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon))
		qdel(ateleatom)
		return 0
	if(istype(ateleatom))
		teleatom = ateleatom
		return 1
	return 0

//custom effects must be properly set up first for instant-type teleports
//optional
/datum/teleport/proc/setEffects(datum/effect_system/aeffectin=null,datum/effect_system/aeffectout=null)
	effectin = istype(aeffectin) ? aeffectin : null
	effectout = istype(aeffectout) ? aeffectout : null
	return 1

//optional
/datum/teleport/proc/setForceTeleport(afteleport)
	force_teleport = afteleport
	return 1

//optional
/datum/teleport/proc/setSounds(asoundin=null,asoundout=null)
	soundin = isfile(asoundin) ? asoundin : null
	soundout = isfile(asoundout) ? asoundout : null
	return 1

//placeholder
/datum/teleport/proc/teleportChecks()
	return 1

/datum/teleport/proc/playSpecials(atom/location,datum/effect_system/effect,sound)
	if(location)
		if(effect)
			spawn(0)
				src = null
				effect.attach(location)
				effect.start()
		if(sound)
			spawn(0)
				src = null
				playsound(location,sound,60,1)
	return

//do the monkey dance
/datum/teleport/proc/doTeleport()

	var/turf/destturf
	var/turf/curturf = get_turf(teleatom)
	if(precision)
		var/list/posturfs = list()
		var/center = get_turf(destination)
		if(!center)
			center = destination
		for(var/turf/T in range(precision,center))
			posturfs.Add(T)
		destturf = safepick(posturfs)
	else
		destturf = get_turf(destination)

	if(!destturf || !curturf)
		return 0

	playSpecials(curturf,effectin,soundin)

	if(force_teleport)
		teleatom.forceMove(destturf)
		playSpecials(destturf,effectout,soundout)
	else
		if(teleatom.Move(destturf))
			playSpecials(destturf,effectout,soundout)
	return 1

/datum/teleport/proc/teleport()
	if(teleportChecks())
		return doTeleport()
	return 0

/datum/teleport/instant //teleports when datum is created

	start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null)
		if(..())
			if(teleport())
				return 1
		return 0

/datum/teleport/instant/science

/datum/teleport/instant/science/setEffects(datum/effect_system/aeffectin,datum/effect_system/aeffectout)
	if(aeffectin==null || aeffectout==null)
		var/datum/effect_system/spark_spread/aeffect = new
		aeffect.set_up(5, 1, teleatom)
		effectin = effectin || aeffect
		effectout = effectout || aeffect
		return 1
	else
		return ..()

/datum/teleport/instant/science/setPrecision(aprecision)
	..()
	if(istype(teleatom, /obj/item/weapon/storage/backpack/holding))
		precision = rand(1,100)

	var/list/bagholding = teleatom.search_contents_for(/obj/item/weapon/storage/backpack/holding)
	if(bagholding.len)
		precision = max(rand(1,100)*bagholding.len,100)
		if(istype(teleatom, /mob/living))
			var/mob/living/MM = teleatom
			MM << "The bluespace interface on your bag of holding interferes with the teleport!"
	return 1


code:
/datum/topic_input
	var/href
	var/list/href_list

/datum/topic_input/New(thref,list/thref_list)
	href = thref
	href_list = thref_list.Copy()
	return

/datum/topic_input/proc/get(i)
	return listgetindex(href_list,i)

/datum/topic_input/proc/getAndLocate(i)
	var/t = get(i)
	if(t)
		t = locate(t)
	if (istext(t))
		t = null
	return t || null

/datum/topic_input/proc/getNum(i)
	var/t = get(i)
	if(t)
		t = text2num(t)
	return isnum(t) ? t : null

/datum/topic_input/proc/getObj(i)
	var/t = getAndLocate(i)
	return isobj(t) ? t : null

/datum/topic_input/proc/getMob(i)
	var/t = getAndLocate(i)
	return ismob(t) ? t : null

/datum/topic_input/proc/getTurf(i)
	var/t = getAndLocate(i)
	return isturf(t) ? t : null

/datum/topic_input/proc/getAtom(i)
	return getType(i,/atom)

/datum/topic_input/proc/getArea(i)
	var/t = getAndLocate(i)
	return isarea(t) ? t : null

/datum/topic_input/proc/getStr(i)//params should always be text, but...
	var/t = get(i)
	return istext(t) ? t : null

/datum/topic_input/proc/getType(i,type)
	var/t = getAndLocate(i)
	return istype(t,type) ? t : null

/datum/topic_input/proc/getPath(i)
	var/t = get(i)
	if(t)
		t = text2path(t)
	return ispath(t) ? t : null

/datum/topic_input/proc/getList(i)
	var/t = getAndLocate(i)
	return islist(t) ? t : null


code:
/* HUD DATUMS */

//GLOBAL HUD LIST
var/datum/atom_hud/huds = list( \
	DATA_HUD_SECURITY_BASIC = new/datum/atom_hud/data/human/security/basic(), \
	DATA_HUD_SECURITY_ADVANCED = new/datum/atom_hud/data/human/security/advanced(), \
	DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(), \
	DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(), \
	DATA_HUD_DIAGNOSTIC = new/datum/atom_hud/data/diagnostic(), \
	ANTAG_HUD_CULT = new/datum/atom_hud/antag(), \
	ANTAG_HUD_REV = new/datum/atom_hud/antag(), \
	ANTAG_HUD_OPS = new/datum/atom_hud/antag(), \
	ANTAG_HUD_WIZ = new/datum/atom_hud/antag(), \
	ANTAG_HUD_SHADOW = new/datum/atom_hud/antag(), \
	ANTAG_HUD_HOG_BLUE = new/datum/atom_hud/antag(),\
	ANTAG_HUD_HOG_RED = new/datum/atom_hud/antag(),\
	ANTAG_HUD_TRAITOR = new/datum/atom_hud/antag/hidden(),\
	ANTAG_HUD_NINJA = new/datum/atom_hud/antag/hidden(),\
	ANTAG_HUD_CHANGELING = new/datum/atom_hud/antag/hidden(),\
	ANTAG_HUD_ABDUCTOR = new/datum/atom_hud/antag/hidden(),\
	)

/datum/atom_hud
	var/list/atom/hudatoms = list() //list of all atoms which display this hud
	var/list/mob/hudusers = list() //list with all mobs who can see the hud
	var/list/hud_icons = list() //these will be the indexes for the atom's hud_list

/datum/atom_hud/proc/remove_hud_from(mob/M)
	if(!M)
		return
	if(src in M.permanent_huds)
		return
	for(var/atom/A in hudatoms)
		remove_from_single_hud(M, A)
	hudusers -= M

/datum/atom_hud/proc/remove_from_hud(atom/A)
	if(!A)
		return
	for(var/mob/M in hudusers)
		remove_from_single_hud(M, A)
	hudatoms -= A

/datum/atom_hud/proc/remove_from_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
	if(!M || !M.client || !A)
		return
	for(var/i in hud_icons)
		M.client.images -= A.hud_list[i]

/datum/atom_hud/proc/add_hud_to(mob/M)
	if(!M)
		return
	hudusers |= M
	for(var/atom/A in hudatoms)
		add_to_single_hud(M, A)

/datum/atom_hud/proc/add_to_hud(atom/A)
	if(!A)
		return
	hudatoms |= A
	for(var/mob/M in hudusers)
		add_to_single_hud(M, A)

/datum/atom_hud/proc/add_to_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
	if(!M || !M.client || !A)
		return
	for(var/i in hud_icons)
		if(A.hud_list[i])
			M.client.images |= A.hud_list[i]

//MOB PROCS
/mob/proc/reload_huds()
	var/gang_huds = list()
	if(ticker.mode)
		for(var/datum/gang/G in ticker.mode.gangs)
			gang_huds += G.ganghud

	for(var/datum/atom_hud/hud in (huds|gang_huds))
		if(src in hud.hudusers)
			hud.add_hud_to(src)

/mob/new_player/reload_huds()
	return

/mob/proc/add_click_catcher()
	client.screen += client.void

/mob/new_player/add_click_catcher()
	return


code:
/datum/martial_art
	var/name = "Martial Art"
	var/streak = ""
	var/max_streak_length = 6
	var/current_target = null
	var/temporary = 0
	var/datum/martial_art/base = null // The permanent style
	var/deflection_chance = 0 //Chance to deflect projectiles
	var/help_verb = null

/datum/martial_art/proc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	return 0

/datum/martial_art/proc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	return 0

/datum/martial_art/proc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	return 0

/datum/martial_art/proc/add_to_streak(element,mob/living/carbon/human/D)
	if(D != current_target)
		current_target = D
		streak = ""
	streak = streak+element
	if(length(streak) > max_streak_length)
		streak = copytext(streak,2)
	return

/datum/martial_art/proc/basic_hit(mob/living/carbon/human/A,mob/living/carbon/human/D)

	A.do_attack_animation(D)
	var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh)

	var/atk_verb = A.dna.species.attack_verb
	if(D.lying)
		atk_verb = "kick"

	if(!damage)
		playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1)
		D.visible_message("[A] has attempted to [atk_verb] [D]!")
		add_logs(A, D, "attempted to [atk_verb]")
		return 0

	var/obj/item/organ/limb/affecting = D.get_organ(ran_zone(A.zone_selected))
	var/armor_block = D.run_armor_check(affecting, "melee")

	playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1)
	D.visible_message("[A] has [atk_verb]ed [D]!", \
								"[A] has [atk_verb]ed [D]!")

	D.apply_damage(damage, BRUTE, affecting, armor_block)

	add_logs(A, D, "punched")

	if((D.stat != DEAD) && damage >= A.dna.species.punchstunthreshold)
		D.visible_message("[A] has weakened [D]!!", \
								"[A] has weakened [D]!")
		D.apply_effect(4, WEAKEN, armor_block)
		D.forcesay(hit_appends)
	else if(D.lying)
		D.forcesay(hit_appends)
	return 1

/datum/martial_art/proc/teach(mob/living/carbon/human/H,make_temporary=0)
	if(help_verb)
		H.verbs += help_verb
	if(make_temporary)
		temporary = 1
	if(H.martial_art && H.martial_art.temporary)
		if(temporary)
			base = H.martial_art.base
		else
			H.martial_art.base = src //temporary styles have priority
			return
	H.martial_art = src

/datum/martial_art/proc/remove(mob/living/carbon/human/H)
	if(H.martial_art != src)
		return
	H.martial_art = base
	if(help_verb)
		H.verbs -= help_verb

/datum/martial_art/boxing
	name = "Boxing"

/datum/martial_art/boxing/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	A << "Can't disarm while boxing!"
	return 1

/datum/martial_art/boxing/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	A << "Can't grab while boxing!"
	return 1

/datum/martial_art/boxing/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)

	A.do_attack_animation(D)

	var/atk_verb = pick("left hook","right hook","straight punch")

	var/damage = rand(5, 8) + A.dna.species.punchdamagelow
	if(!damage)
		playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1)
		D.visible_message("[A] has attempted to hit [D] with a [atk_verb]!")
		add_logs(A, D, "attempted to hit", atk_verb)
		return 0

	var/obj/item/organ/limb/affecting = D.get_organ(ran_zone(A.zone_selected))
	var/armor_block = D.run_armor_check(affecting, "melee")

	playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1)

	D.visible_message("[A] has hit [D] with a [atk_verb]!", \
								"[A] has hit [D] with a [atk_verb]!")

	D.apply_damage(damage, STAMINA, affecting, armor_block)
	add_logs(A, D, "punched")
	if(D.getStaminaLoss() > 50)
		var/knockout_prob = D.getStaminaLoss() + rand(-15,15)
		if((D.stat != DEAD) && prob(knockout_prob))
			D.visible_message("[A] has knocked [D] out with a haymaker!", \
								"[A] has knocked [D] out with a haymaker!")
			D.apply_effect(10,WEAKEN,armor_block)
			D.SetSleeping(5)
			D.forcesay(hit_appends)
		else if(D.lying)
			D.forcesay(hit_appends)
	return 1

/datum/martial_art/wrestling
	name = "Wrestling"
	help_verb = /mob/living/carbon/human/proc/wrestling_help

//	combo refence since wrestling uses a different format to sleeping carp and plasma fist.
//	Clinch "G"
//	Suplex "GD"
//	Advanced grab "G"

/datum/martial_art/wrestling/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	D.grabbedby(A,1)
	var/obj/item/weapon/grab/G = A.get_active_hand()
	if(G && prob(50))
		G.state = GRAB_AGGRESSIVE
		D.visible_message("[A] has [D] in a clinch!", \
								"[A] has [D] in a clinch!")
	else
		D.visible_message("[A] fails to get [D] in a clinch!", \
								"[A] fails to get [D] in a clinch!")
	return 1

/datum/martial_art/wrestling/proc/Suplex(mob/living/carbon/human/A, mob/living/carbon/human/D)

	D.visible_message("[A] suplexes [D]!", \
								"[A] suplexes [D]!")
	D.forceMove(A.loc)
	var/armor_block = D.run_armor_check(null, "melee")
	D.apply_damage(30, BRUTE, null, armor_block)
	D.apply_effect(6, WEAKEN, armor_block)
	add_logs(A, D, "suplexed")

	A.SpinAnimation(10,1)

	D.SpinAnimation(10,1)
	spawn(3)
		armor_block = A.run_armor_check(null, "melee")
		A.apply_effect(4, WEAKEN, armor_block)
	return

/datum/martial_art/wrestling/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(istype(A.get_inactive_hand(),/obj/item/weapon/grab))
		var/obj/item/weapon/grab/G = A.get_inactive_hand()
		if(G.affecting == D)
			Suplex(A,D)
			return 1
	harm_act(A,D)
	return 1

/datum/martial_art/wrestling/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	D.grabbedby(A,1)
	D.visible_message("[A] holds [D] down!", \
								"[A] holds [D] down!")
	var/obj/item/organ/limb/affecting = D.get_organ(ran_zone(A.zone_selected))
	var/armor_block = D.run_armor_check(affecting, "melee")
	D.apply_damage(10, STAMINA, affecting, armor_block)
	return 1

/mob/living/carbon/human/proc/wrestling_help()
	set name = "Recall Teachings"
	set desc = "Remember how to wrestle."
	set category = "Wrestling"

	usr << "You flex your muscles and have a revelation..."
	usr << "Clinch: Grab. Passively gives you a chance to immediately aggressively grab someone. Not always successful."
	usr << "Suplex: Disarm someone you are grabbing. Suplexes your target to the floor. Greatly injures them and leaves both you and your target on the floor."
	usr << "Advanced grab: Grab. Passively causes stamina damage when grabbing someone."

#define TORNADO_COMBO "HHD"
#define THROWBACK_COMBO "DHD"
#define PLASMA_COMBO "HDDDH"

/datum/martial_art/plasma_fist
	name = "Plasma Fist"
	help_verb = /mob/living/carbon/human/proc/plasma_fist_help

/datum/martial_art/plasma_fist/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(findtext(streak,TORNADO_COMBO))
		streak = ""
		Tornado(A,D)
		return 1
	if(findtext(streak,THROWBACK_COMBO))
		streak = ""
		Throwback(A,D)
		return 1
	if(findtext(streak,PLASMA_COMBO))
		streak = ""
		Plasma(A,D)
		return 1
	return 0

/datum/martial_art/plasma_fist/proc/Tornado(mob/living/carbon/human/A, mob/living/carbon/human/D)
	A.say("TORNADO SWEEP!")
	spawn(0)
		for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
			A.dir = i
			playsound(A.loc, 'sound/weapons/punch1.ogg', 15, 1, -1)
			sleep(1)
	var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null)
	var/list/turfs = list()
	for(var/turf/T in range(1,A))
		turfs.Add(T)
	R.cast(turfs)
	return

/datum/martial_art/plasma_fist/proc/Throwback(mob/living/carbon/human/A, mob/living/carbon/human/D)
	D.visible_message("[A] has hit [D] with Plasma Punch!", \
								"[A] has hit [D] with Plasma Punch!")
	playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
	var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A)))
	D.throw_at(throw_target, 200, 4,A)
	A.say("HYAH!")
	return

/datum/martial_art/plasma_fist/proc/Plasma(mob/living/carbon/human/A, mob/living/carbon/human/D)
	A.do_attack_animation(D)
	playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
	A.say("PLASMA FIST!")
	D.visible_message("[A] has hit [D] with THE PLASMA FIST TECHNIQUE!", \
								"[A] has hit [D] with THE PLASMA FIST TECHNIQUE!")
	D.gib()
	return

/datum/martial_art/plasma_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	add_to_streak("H",D)
	if(check_streak(A,D))
		return 1
	basic_hit(A,D)
	return 1

/datum/martial_art/plasma_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	add_to_streak("D",D)
	if(check_streak(A,D))
		return 1
	basic_hit(A,D)
	return 1

/datum/martial_art/plasma_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	add_to_streak("G",D)
	if(check_streak(A,D))
		return 1
	basic_hit(A,D)
	return 1

/mob/living/carbon/human/proc/plasma_fist_help()
	set name = "Recall Teachings"
	set desc = "Remember the martial techniques of the Plasma Fist."
	set category = "Plasma Fist"

	usr << "You clench your fists and have a flashback of knowledge..."
	usr << "Tornado Sweep: Harm Harm Disarm. Repulses target and everyone back."
	usr << "Throwback: Disarm Harm Disarm. Throws the target and an item at them."
	usr << "The Plasma Fist: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body."

//Used by the gang of the same name. Uses combos. Basic attacks bypass armor and never miss
#define WRIST_WRENCH_COMBO "DD"
#define BACK_KICK_COMBO "HG"
#define STOMACH_KNEE_COMBO "GH"
#define HEAD_KICK_COMBO "DHH"
#define ELBOW_DROP_COMBO "HDHDH"
/datum/martial_art/the_sleeping_carp
	name = "The Sleeping Carp"
	deflection_chance = 100
	help_verb = /mob/living/carbon/human/proc/sleeping_carp_help

/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(findtext(streak,WRIST_WRENCH_COMBO))
		streak = ""
		wristWrench(A,D)
		return 1
	if(findtext(streak,BACK_KICK_COMBO))
		streak = ""
		backKick(A,D)
		return 1
	if(findtext(streak,STOMACH_KNEE_COMBO))
		streak = ""
		kneeStomach(A,D)
		return 1
	if(findtext(streak,HEAD_KICK_COMBO))
		streak = ""
		headKick(A,D)
		return 1
	if(findtext(streak,ELBOW_DROP_COMBO))
		streak = ""
		elbowDrop(A,D)
		return 1
	return 0

/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(!D.stat && !D.stunned && !D.weakened)
		A.do_attack_animation(D)
		D.visible_message("[A] grabs [D]'s wrist and wrenches it sideways!", \
						  "[A] grabs your wrist and violently wrenches it to the side!")
		playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
		D.emote("scream")
		D.drop_item()
		D.apply_damage(5, BRUTE, pick("l_arm", "r_arm"))
		D.Stun(3)
		return 1
	return basic_hit(A,D)

/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(A.dir == D.dir && !D.stat && !D.weakened)
		A.do_attack_animation(D)
		D.visible_message("[A] kicks [D] in the back!", \
						  "[A] kicks you in the back, making you stumble and fall!")
		step_to(D,get_step(D,D.dir),1)
		D.Weaken(4)
		playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
		return 1
	return basic_hit(A,D)

/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(!D.stat && !D.weakened)
		A.do_attack_animation(D)
		D.visible_message("[A] knees [D] in the stomach!", \
						  "[A] winds you with a knee in the stomach!")
		D.audible_message("[D] gags!")
		D.losebreath += 3
		D.Stun(2)
		playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
		return 1
	return basic_hit(A,D)

/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(!D.stat && !D.weakened)
		A.do_attack_animation(D)
		D.visible_message("[A] kicks [D] in the head!", \
						  "[A] kicks you in the jaw!")
		D.apply_damage(20, BRUTE, "head")
		D.drop_item()
		playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
		D.Stun(4)
		return 1
	return basic_hit(A,D)

/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D)
	if(D.weakened || D.resting || D.stat)
		A.do_attack_animation(D)
		D.visible_message("[A] elbow drops [D]!", \
						  "[A] piledrives you with their elbow!")
		if(D.stat)
			D.death() //FINISH HIM!
		D.apply_damage(50, BRUTE, "chest")
		playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1)
		return 1
	return basic_hit(A,D)

/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	add_to_streak("G",D)
	if(check_streak(A,D))
		return 1
	D.grabbedby(A,1)
	var/obj/item/weapon/grab/G = A.get_active_hand()
	if(G)
		G.state = GRAB_AGGRESSIVE //Instant aggressive grab

/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	add_to_streak("H",D)
	if(check_streak(A,D))
		return 1
	A.do_attack_animation(D)
	var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams")
	D.visible_message("[A] [atk_verb] [D]!", \
					  "[A] [atk_verb] you!")
	D.apply_damage(rand(10,15), BRUTE)
	playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1)
	if(prob(D.getBruteLoss()) && !D.lying)
		D.visible_message("[D] stumbles and falls!", "The blow sends you to the ground!")
		D.Weaken(4)
	return 1

/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
	add_to_streak("D",D)
	if(check_streak(A,D))
		return 1
	return ..()

/mob/living/carbon/human/proc/sleeping_carp_help()
	set name = "Recall Teachings"
	set desc = "Remember the martial techniques of the Sleeping Carp clan."
	set category = "Sleeping Carp"

	usr << "You retreat inward and recall the teachings of the Sleeping Carp..."

	usr << "Wrist Wrench: Disarm Disarm. Forces opponent to drop item in hand."
	usr << "Back Kick: Harm Grab. Opponent must be facing away. Knocks down."
	usr << "Stomach Knee: Grab Harm. Knocks the wind out of opponent and stuns."
	usr << "Head Kick: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand."
	usr << "Elbow Drop: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition."

//ITEMS

/obj/item/clothing/gloves/boxing
	var/datum/martial_art/boxing/style = new

/obj/item/clothing/gloves/boxing/equipped(mob/user, slot)
	if(!ishuman(user))
		return
	if(slot == slot_gloves)
		var/mob/living/carbon/human/H = user
		style.teach(H,1)
	return

/obj/item/clothing/gloves/boxing/dropped(mob/user)
	if(!ishuman(user))
		return
	var/mob/living/carbon/human/H = user
	if(H.get_item_by_slot(slot_gloves) == src)
		style.remove(H)
	return

/obj/item/weapon/storage/belt/champion/wrestling
	name = "Wrestling Belt"
	var/datum/martial_art/wrestling/style = new

/obj/item/weapon/storage/belt/champion/wrestling/equipped(mob/user, slot)
	if(!ishuman(user))
		return
	if(slot == slot_belt)
		var/mob/living/carbon/human/H = user
		style.teach(H,1)
		user << "You have an urge to flex your muscles and get into a fight. You have the knowledge of a thousand wrestlers before you. You can remember more by using the Recall teaching verb in the wrestling tab."
	return

/obj/item/weapon/storage/belt/champion/wrestling/dropped(mob/user)
	if(!ishuman(user))
		return
	var/mob/living/carbon/human/H = user
	if(H.get_item_by_slot(slot_belt) == src)
		style.remove(H)
		user << "You no longer have an urge to flex your muscles."
	return

/obj/item/weapon/plasma_fist_scroll
	name = "frayed scroll"
	desc = "An aged and frayed scrap of paper written in shifting runes. There are hand-drawn illustrations of pugilism."
	icon = 'icons/obj/wizard.dmi'
	icon_state ="scroll2"
	var/used = 0

/obj/item/weapon/plasma_fist_scroll/attack_self(mob/user)
	if(!ishuman(user))
		return
	if(!used)
		var/mob/living/carbon/human/H = user
		var/datum/martial_art/plasma_fist/F = new/datum/martial_art/plasma_fist(null)
		F.teach(H)
		H << "You have learned the ancient martial art of Plasma Fist."
		used = 1
		desc = "It's completely blank."
		name = "empty scroll"
		icon_state = "blankscroll"

/obj/item/weapon/sleeping_carp_scroll
	name = "mysterious scroll"
	desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art."
	icon = 'icons/obj/wizard.dmi'
	icon_state = "scroll2"

/obj/item/weapon/sleeping_carp_scroll/attack_self(mob/living/carbon/human/user)
	if(!istype(user) || !user)
		return
	user << "You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \
	directed toward you. However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab."
	var/datum/martial_art/the_sleeping_carp/theSleepingCarp = new(null)
	theSleepingCarp.teach(user)
	user.drop_item()
	visible_message("[src] lights up in fire and quickly burns to ash.")
	new /obj/effect/decal/cleanable/ash(get_turf(src))
	qdel(src)

/obj/item/weapon/twohanded/bostaff
	name = "bo staff"
	desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate."
	force = 10
	w_class = 4
	slot_flags = SLOT_BACK
	force_unwielded = 10
	force_wielded = 24
	throwforce = 20
	throw_speed = 2
	attack_verb = list("smashed", "slammed", "whacked", "thwacked")
	icon = 'icons/obj/weapons.dmi'
	icon_state = "bostaff0"
	block_chance = 50

/obj/item/weapon/twohanded/bostaff/update_icon()
	icon_state = "bostaff[wielded]"
	return

/obj/item/weapon/twohanded/bostaff/attack(mob/target, mob/living/user)
	add_fingerprint(user)
	if((CLUMSY in user.disabilities) && prob(50))
		user << "You club yourself over the head with [src]."
		user.Weaken(3)
		if(ishuman(user))
			var/mob/living/carbon/human/H = user
			H.apply_damage(2*force, BRUTE, "head")
		else
			user.take_organ_damage(2*force)
		return
	if(isrobot(target))
		return ..()
	if(!isliving(target))
		return ..()
	var/mob/living/carbon/C = target
	if(C.stat)
		user << "It would be dishonorable to attack a foe while they cannot retaliate."
		return
	switch(user.a_intent)
		if("disarm")
			if(!wielded)
				return ..()
			if(!ishuman(target))
				return ..()
			var/mob/living/carbon/human/H = target
			var/list/fluffmessages = list("[user] clubs [H] with [src]!", \
										  "[user] smacks [H] with the butt of [src]!", \
										  "[user] broadsides [H] with [src]!", \
										  "[user] smashes [H]'s head with [src]!", \
										  "[user] beats [H] with front of [src]!", \
										  "[user] twirls and slams [H] with [src]!")
			H.visible_message("[pick(fluffmessages)]", \
								   "[pick(fluffmessages)]")
			playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, 1, -1)
			H.adjustStaminaLoss(rand(13,20))
			if(prob(10))
				H.visible_message("[H] collapses!", \
									   "Your legs give out!")
				H.Weaken(4)
			if(H.staminaloss && !H.sleeping)
				var/total_health = (H.health - H.staminaloss)
				if(total_health <= config.health_threshold_crit && !H.stat)
					H.visible_message("[user] delivers a heavy hit to [H]'s head, knocking them out cold!", \
										   "[user] knocks you unconscious!")
					H.SetSleeping(30)
					H.adjustBrainLoss(25)
			return
		else
			return ..()
	return ..()

/obj/item/weapon/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance)
	if(wielded)
		return ..()
	return 0


code:
/*
	This datum should be used for handling mineral contents of machines and whatever else is supposed to hold minerals and make use of them.

	Variables:
		amount - raw amount of the mineral this container is holding, calculated by the defined value MINERAL_MATERIAL_AMOUNT=2000.
		max_amount - max raw amount of mineral this container can hold.
		sheet_type - type of the mineral sheet the container handles, used for output.
		owner - object that this container is being used by, used for output.
		MAX_STACK_SIZE - size of a stack of mineral sheets. Constant.
*/

/datum/material_container
	var/total_amount = 0
	var/max_amount
	var/sheet_type
	var/obj/owner
	var/list/materials = list()
	//MAX_STACK_SIZE = 50
	//MINERAL_MATERIAL_AMOUNT = 2000

/datum/material_container/New(obj/O, list/mat_list, max_amt = 0)
	owner = O
	max_amount = max(0, max_amt)

	if(mat_list[MAT_METAL])
		materials[MAT_METAL] = new /datum/material/metal()
	if(mat_list[MAT_GLASS])
		materials[MAT_GLASS] = new /datum/material/glass()
	if(mat_list[MAT_SILVER])
		materials[MAT_SILVER] = new /datum/material/silver()
	if(mat_list[MAT_GOLD])
		materials[MAT_GOLD] = new /datum/material/gold()
	if(mat_list[MAT_DIAMOND])
		materials[MAT_DIAMOND] = new /datum/material/diamond()
	if(mat_list[MAT_URANIUM])
		materials[MAT_URANIUM] = new /datum/material/uranium()
	if(mat_list[MAT_PLASMA])
		materials[MAT_PLASMA] = new /datum/material/plasma()
	if(mat_list[MAT_BANANIUM])
		materials[MAT_BANANIUM] = new /datum/material/bananium()

/datum/material_container/Destroy()
	owner = null
	return ..()

//For inserting an amount of material
/datum/material_container/proc/insert_amount(amt, material_type = null)
	if(amt > 0 && has_space(amt))
		var/total_amount_saved = total_amount
		if(material_type)
			var/datum/material/M = materials[material_type]
			if(M)
				M.amount += amt
				total_amount += amt
		else
			for(var/datum/material/M in materials)
				M.amount += amt
				total_amount += amt
		return (total_amount - total_amount_saved)
	return 0

/datum/material_container/proc/insert_stack(obj/item/stack/S, amt = 0)
	if(amt <= 0)
		return 0
	if(amt > S.amount)
		amt = S.amount

	var/material_amt = get_item_material_amount(S)
	if(!material_amt)
		return 0

	amt = min(amt, round(((max_amount - total_amount) / material_amt)))
	if(!amt)
		return 0

	insert_materials(S,amt)
	S.use(amt)
	return amt

/datum/material_container/proc/insert_item(obj/item/I, multiplier = 1)
	if(!I)
		return 0
	if(istype(I,/obj/item/stack))
		var/obj/item/stack/S = I
		return insert_stack(I, S.amount)

	var/material_amount = get_item_material_amount(I)
	if(!material_amount || !has_space(material_amount))
		return 0

	insert_materials(I, multiplier)
	return material_amount

/datum/material_container/proc/insert_materials(obj/item/I, multiplier = 1) //for internal usage only
	var/datum/material/M
	for(var/MAT in materials)
		M = materials[MAT]
		M.amount += I.materials[MAT] * multiplier
		total_amount += I.materials[MAT] * multiplier

//For consuming material
//mats is a list of types of material to use and the corresponding amounts, example: list(MAT_METAL=100, MAT_GLASS=200)
/datum/material_container/proc/use_amount(list/mats, multiplier=1)
	if(!mats || !mats.len)
		return 0

	var/datum/material/M
	for(var/MAT in materials)
		M = materials[MAT]
		if(M.amount < (mats[MAT] * multiplier))
			return 0

	var/total_amount_save = total_amount
	for(var/MAT in materials)
		M = materials[MAT]
		M.amount -= mats[MAT] * multiplier
		total_amount -= mats[MAT] * multiplier

	return total_amount_save - total_amount

/datum/material_container/proc/use_amount_type(amt, material_type)
	var/datum/material/M
	M = materials[material_type]
	if(M)
		if(M.amount >= amt)
			M.amount -= amt
			total_amount -= amt
			return amt
	return 0

//For spawning mineral sheets; internal use only
/datum/material_container/proc/retrieve(sheet_amt, datum/material/M)
	if(sheet_amt > 0)
		if(M.amount < (sheet_amt * MINERAL_MATERIAL_AMOUNT))
			sheet_amt = round(M.amount / MINERAL_MATERIAL_AMOUNT)
		var/count = 0

		while(sheet_amt > MAX_STACK_SIZE)
			new M.sheet_type(get_turf(owner), MAX_STACK_SIZE)
			count += MAX_STACK_SIZE
			use_amount_type(sheet_amt * MINERAL_MATERIAL_AMOUNT, M.material_type)
			sheet_amt -= MAX_STACK_SIZE

		if(round(M.amount / MINERAL_MATERIAL_AMOUNT))
			new M.sheet_type(get_turf(owner), sheet_amt)
			count += sheet_amt
			use_amount_type(sheet_amt * MINERAL_MATERIAL_AMOUNT, M.material_type)
		return count
	return 0

/datum/material_container/proc/retrieve_sheets(sheet_amt, material_type)
	if(materials[material_type])
		return retrieve(sheet_amt, materials[material_type])
	return 0

/datum/material_container/proc/retrieve_amount(amt, material_type)
	return retrieve_sheets(amount2sheet(amt),material_type)

/datum/material_container/proc/retrieve_all()
	var/result = 0
	var/datum/material/M
	for(var/MAT in materials)
		M = materials[MAT]
		result += retrieve_sheets(amount2sheet(M.amount), MAT)
	return result

/datum/material_container/proc/has_space(amt = 0)
	return (total_amount + amt) <= max_amount

/datum/material_container/proc/has_materials(list/mats, multiplier=1)
	if(!mats || !mats.len)
		return 0

	var/datum/material/M
	for(var/MAT in mats)
		M = materials[MAT]
		if(M.amount < (mats[MAT] * multiplier))
			return 0
	return 1

/datum/material_container/proc/amount2sheet(amt)
	if(amt >= MINERAL_MATERIAL_AMOUNT)
		return round(amt / MINERAL_MATERIAL_AMOUNT)
	return 0

/datum/material_container/proc/sheet2amount(sheet_amt)
	if(sheet_amt > 0)
		return sheet_amt * MINERAL_MATERIAL_AMOUNT
	return 0

/datum/material_container/proc/amount(material_type)
	var/datum/material/M = materials[material_type]
	return M ? M.amount : 0

//returns the amount of material relevant to this container;
//if this container does not support glass, any glass in 'I' will not be taken into account
/datum/material_container/proc/get_item_material_amount(obj/item/I)
	if(!istype(I))
		return 0
	var/material_amount = 0
	for(var/MAT in materials)
		material_amount += I.materials[MAT]
	return material_amount

/datum/material
	var/amount = 0
	var/material_type = null
	var/sheet_type = null

/datum/material/metal

/datum/material/metal/New()
	..()
	material_type = MAT_METAL
	sheet_type = /obj/item/stack/sheet/metal

/datum/material/glass

/datum/material/glass/New()
	..()
	material_type = MAT_GLASS
	sheet_type = /obj/item/stack/sheet/glass

/datum/material/silver

/datum/material/silver/New()
	..()
	material_type = MAT_SILVER
	sheet_type = /obj/item/stack/sheet/mineral/silver

/datum/material/gold

/datum/material/gold/New()
	..()
	material_type = MAT_GOLD
	sheet_type = /obj/item/stack/sheet/mineral/gold

/datum/material/diamond

/datum/material/diamond/New()
	..()
	material_type = MAT_DIAMOND
	sheet_type = /obj/item/stack/sheet/mineral/diamond

/datum/material/uranium

/datum/material/uranium/New()
	..()
	material_type = MAT_URANIUM
	sheet_type = /obj/item/stack/sheet/mineral/uranium

/datum/material/plasma

/datum/material/plasma/New()
	..()
	material_type = MAT_PLASMA
	sheet_type = /obj/item/stack/sheet/mineral/plasma

/datum/material/bananium

/datum/material/bananium/New()
	..()
	material_type = MAT_BANANIUM
	sheet_type = /obj/item/stack/sheet/mineral/bananium