code:
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32

var/const/TOUCH = 1 //splashing
var/const/INGEST = 2 //ingestion
var/const/VAPOR = 3 //foam, spray, blob attack
var/const/PATCH = 4 //patches
var/const/INJECT = 5 //injection

///////////////////////////////////////////////////////////////////////////////////

/datum/reagents
	var/list/datum/reagent/reagent_list = new/list()
	var/total_volume = 0
	var/maximum_volume = 100
	var/atom/my_atom = null
	var/chem_temp = 150
	var/last_tick = 1
	var/addiction_tick = 1
	var/list/datum/reagent/addiction_list = new/list()

/datum/reagents/New(maximum=100)
	maximum_volume = maximum
	SSobj.processing |= src
	//I dislike having these here but map-objects are initialised before world/New() is called. >_>
	if(!chemical_reagents_list)
		//Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id
		var/paths = subtypesof(/datum/reagent)
		chemical_reagents_list = list()
		for(var/path in paths)
			var/datum/reagent/D = new path()
			chemical_reagents_list[D.id] = D
	if(!chemical_reactions_list)
		//Chemical Reactions - Initialises all /datum/chemical_reaction into a list
		// It is filtered into multiple lists within a list.
		// For example:
		// chemical_reaction_list["plasma"] is a list of all reactions relating to plasma

		var/paths = subtypesof(/datum/chemical_reaction)
		chemical_reactions_list = list()

		for(var/path in paths)

			var/datum/chemical_reaction/D = new path()
			var/list/reaction_ids = list()

			if(D.required_reagents && D.required_reagents.len)
				for(var/reaction in D.required_reagents)
					reaction_ids += reaction

			// Create filters based on each reagent id in the required reagents list
			for(var/id in reaction_ids)
				if(!chemical_reactions_list[id])
					chemical_reactions_list[id] = list()
				chemical_reactions_list[id] += D
				break // Don't bother adding ourselves to other reagent ids, it is redundant.

/datum/reagents/Destroy()
	. = ..()
	SSobj.processing.Remove(src)
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		qdel(R)
	reagent_list.Cut()
	reagent_list = null
	if(my_atom && my_atom.reagents == src)
		my_atom.reagents = null

/datum/reagents/proc/remove_any(amount = 1)
	var/total_transfered = 0
	var/current_list_element = 1

	current_list_element = rand(1, reagent_list.len)

	while(total_transfered != amount)
		if(total_transfered >= amount)
			break
		if(total_volume <= 0 || !reagent_list.len)
			break

		if(current_list_element > reagent_list.len)
			current_list_element = 1

		var/datum/reagent/R = reagent_list[current_list_element]
		remove_reagent(R.id, 1)

		current_list_element++
		total_transfered++
		update_total()

	handle_reactions()
	return total_transfered

/datum/reagents/proc/remove_all(amount = 1)
	if(total_volume > 0)
		var/part = amount / total_volume
		for(var/reagent in reagent_list)
			var/datum/reagent/R = reagent
			remove_reagent(R.id, R.volume * part)

		update_total()
		handle_reactions()
		return amount

/datum/reagents/proc/get_master_reagent_name()
	var/name
	var/max_volume = 0
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		if(R.volume > max_volume)
			max_volume = R.volume
			name = R.name

	return name

/datum/reagents/proc/get_master_reagent_id()
	var/id
	var/max_volume = 0
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		if(R.volume > max_volume)
			max_volume = R.volume
			id = R.id

	return id

/datum/reagents/proc/trans_to(obj/target, amount=1, multiplier=1, preserve_data=1)//if preserve_data=0, the reagents data will be lost. Usefull if you use data for some strange stuff and don't want it to be transferred.
	if(!target )
		return
	var/datum/reagents/R
	if(istype(target, /datum/reagents))
		R = target
	else
		if(!target.reagents || src.total_volume<=0)
			return
		R = target.reagents
	amount = min(min(amount, src.total_volume), R.maximum_volume-R.total_volume)
	var/part = amount / src.total_volume
	var/trans_data = null
	for(var/reagent in reagent_list)
		var/datum/reagent/T = reagent
		if(T.id == "blood" && ishuman(target))
			var/mob/living/carbon/human/H = target
			H.inject_blood(my_atom, amount)
			continue
		var/transfer_amount = T.volume * part
		if(preserve_data)
			trans_data = copy_data(T)
		R.add_reagent(T.id, transfer_amount * multiplier, trans_data, chem_temp, no_react = 1) //we only handle reaction after every reagent has been transfered.
		remove_reagent(T.id, transfer_amount)

	update_total()
	R.update_total()
	R.handle_reactions()
	src.handle_reactions()
	return amount

/datum/reagents/proc/copy_to(obj/target, amount=1, multiplier=1, preserve_data=1)
	if(!target)
		return
	if(!target.reagents || src.total_volume<=0)
		return
	var/datum/reagents/R = target.reagents
	amount = min(min(amount, total_volume), R.maximum_volume-R.total_volume)
	var/part = amount / total_volume
	var/trans_data = null
	for(var/reagent in reagent_list)
		var/datum/reagent/T = reagent
		var/copy_amount = T.volume * part
		if(preserve_data)
			trans_data = T.data
		R.add_reagent(T.id, copy_amount * multiplier, trans_data)

	src.update_total()
	R.update_total()
	R.handle_reactions()
	src.handle_reactions()
	return amount

/datum/reagents/proc/trans_id_to(obj/target, reagent, amount=1, preserve_data=1)//Not sure why this proc didn't exist before. It does now! /N
	if (!target)
		return
	if (!target.reagents || src.total_volume<=0 || !src.get_reagent_amount(reagent))
		return

	var/datum/reagents/R = target.reagents
	if(src.get_reagent_amount(reagent)<amount)
		amount = src.get_reagent_amount(reagent)
	amount = min(amount, R.maximum_volume-R.total_volume)
	var/trans_data = null
	for (var/datum/reagent/current_reagent in src.reagent_list)
		if(current_reagent.id == reagent)
			if(preserve_data)
				trans_data = current_reagent.data
			R.add_reagent(current_reagent.id, amount, trans_data, src.chem_temp)
			src.remove_reagent(current_reagent.id, amount, 1)
			break

	src.update_total()
	R.update_total()
	R.handle_reactions()
	//src.handle_reactions() Don't need to handle reactions on the source since you're (presumably isolating and) transferring a specific reagent.
	return amount

/*
				if (!target) return
				var/total_transfered = 0
				var/current_list_element = 1
				var/datum/reagents/R = target.reagents
				var/trans_data = null
				//if(R.total_volume + amount > R.maximum_volume) return 0

				current_list_element = rand(1,reagent_list.len) //Eh, bandaid fix.

				while(total_transfered != amount)
					if(total_transfered >= amount) break //Better safe than sorry.
					if(total_volume <= 0 || !reagent_list.len) break
					if(R.total_volume >= R.maximum_volume) break

					if(current_list_element > reagent_list.len) current_list_element = 1
					var/datum/reagent/current_reagent = reagent_list[current_list_element]
					if(preserve_data)
						trans_data = current_reagent.data
					R.add_reagent(current_reagent.id, (1 * multiplier), trans_data)
					src.remove_reagent(current_reagent.id, 1)

					current_list_element++
					total_transfered++
					src.update_total()
					R.update_total()
				R.handle_reactions()
				handle_reactions()

				return total_transfered
*/

/datum/reagents/proc/metabolize(mob/living/carbon/C, can_overdose = 0)
	if(C)
		chem_temp = C.bodytemperature
		handle_reactions()
	var/need_mob_update = 0
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		if(!R.holder)
			continue
		if(!C)
			C = R.holder.my_atom
		if(C && R)
			if(C.reagent_check(R) != 1)
				if(can_overdose)
					if(R.overdose_threshold)
						if(R.volume >= R.overdose_threshold && !R.overdosed)
							R.overdosed = 1
							need_mob_update += R.overdose_start(C)
					if(R.addiction_threshold)
						if(R.volume >= R.addiction_threshold && !is_type_in_list(R, addiction_list))
							var/datum/reagent/new_reagent = new R.type()
							addiction_list.Add(new_reagent)
					if(R.overdosed)
						need_mob_update += R.overdose_process(C)
					if(is_type_in_list(R,addiction_list))
						for(var/addiction in addiction_list)
							var/datum/reagent/A = addiction
							if(istype(R, A))
								A.addiction_stage = -15 // you're satisfied for a good while.
				need_mob_update += R.on_mob_life(C)

	if(can_overdose)
		if(addiction_tick == 6)
			addiction_tick = 1
			for(var/addiction in addiction_list)
				var/datum/reagent/R = addiction
				if(C && R)
					R.addiction_stage++
					switch(R.addiction_stage)
						if(1 to 10)
							need_mob_update += R.addiction_act_stage1(C)
						if(10 to 20)
							need_mob_update += R.addiction_act_stage2(C)
						if(20 to 30)
							need_mob_update += R.addiction_act_stage3(C)
						if(30 to 40)
							need_mob_update += R.addiction_act_stage4(C)
						if(40 to INFINITY)
							C << "You feel like you've gotten over your need for [R.name]."
							addiction_list.Remove(R)
		addiction_tick++
	if(C && need_mob_update) //some of the metabolized reagents had effects on the mob that requires some updates.
		C.updatehealth()
		C.update_canmove()
		C.update_stamina()
	update_total()

/datum/reagents/process()
	if(my_atom && (my_atom.flags & NOREACT))
		return
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		R.on_tick()
	return

/datum/reagents/proc/conditional_update_move(atom/A, Running = 0)
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		R.on_move (A, Running)
	update_total()

/datum/reagents/proc/conditional_update(atom/A)
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		R.on_update (A)
	update_total()

/datum/reagents/proc/handle_reactions()
	if(my_atom.flags & NOREACT) return //Yup, no reactions here. No siree.

	var/reaction_occured = 0
	do
		reaction_occured = 0
		for(var/reagent in reagent_list)
			var/datum/reagent/R = reagent
			for(var/reaction in chemical_reactions_list[R.id]) // Was a big list but now it should be smaller since we filtered it with our reagent id
				if(!reaction)
					continue

				var/datum/chemical_reaction/C = reaction
				var/total_required_reagents = C.required_reagents.len
				var/total_matching_reagents = 0
				var/total_required_catalysts = C.required_catalysts.len
				var/total_matching_catalysts= 0
				var/matching_container = 0
				var/matching_other = 0
				var/list/multipliers = new/list()
				var/required_temp = C.required_temp

				for(var/B in C.required_reagents)
					if(!has_reagent(B, C.required_reagents[B]))
						break
					total_matching_reagents++
					multipliers += round(get_reagent_amount(B) / C.required_reagents[B])
				for(var/B in C.required_catalysts)
					if(!has_reagent(B, C.required_catalysts[B]))
						break
					total_matching_catalysts++

				if(!C.required_container)
					matching_container = 1

				else
					if(my_atom.type == C.required_container)
						matching_container = 1
				if (isliving(my_atom)) //Makes it so certain chemical reactions don't occur in mobs
					if (C.mob_react)
						return
				if(!C.required_other)
					matching_other = 1

				else if(istype(my_atom, /obj/item/slime_extract))
					var/obj/item/slime_extract/M = my_atom

					if(M.Uses > 0) // added a limit to slime cores -- Muskets requested this
						matching_other = 1

				if(required_temp == 0)
					required_temp = chem_temp

				if(total_matching_reagents == total_required_reagents && total_matching_catalysts == total_required_catalysts && matching_container && matching_other && chem_temp >= required_temp)
					var/multiplier = min(multipliers)
					for(var/B in C.required_reagents)
						remove_reagent(B, (multiplier * C.required_reagents[B]), safety = 1)

					var/created_volume = C.result_amount*multiplier
					if(C.result)
						feedback_add_details("chemical_reaction","[C.result]|[C.result_amount*multiplier]")
						multiplier = max(multiplier, 1) //this shouldnt happen ...
						add_reagent(C.result, C.result_amount*multiplier, null, chem_temp)

					var/list/seen = viewers(4, get_turf(my_atom))

					if(!istype(my_atom, /mob)) // No bubbling mobs
						if(C.mix_sound)
							playsound(get_turf(my_atom), C.mix_sound, 80, 1)
						for(var/mob/M in seen)
							M << "\icon[my_atom] [C.mix_message]"

					if(istype(my_atom, /obj/item/slime_extract))
						var/obj/item/slime_extract/ME2 = my_atom
						ME2.Uses--
						if(ME2.Uses <= 0) // give the notification that the slime core is dead
							for(var/mob/M in seen)
								M << "\icon[my_atom] \The [my_atom]'s power is consumed in the reaction."
								ME2.name = "used slime extract"
								ME2.desc = "This extract has been used up."

					C.on_reaction(src, created_volume)
					reaction_occured = 1
					break

	while(reaction_occured)
	update_total()
	return 0

/datum/reagents/proc/isolate_reagent(reagent)
	for(var/_reagent in reagent_list)
		var/datum/reagent/R = _reagent
		if(R.id != reagent)
			del_reagent(R.id)
			update_total()

/datum/reagents/proc/del_reagent(reagent)
	for(var/_reagent in reagent_list)
		var/datum/reagent/R = _reagent
		if(R.id == reagent)
			if(istype(my_atom, /mob/living))
				var/mob/living/M = my_atom
				R.on_mob_delete(M)
			qdel(R)
			reagent_list -= R
			update_total()
			my_atom.on_reagent_change()
			check_ignoreslow(my_atom)
			check_gofast(my_atom)
			check_goreallyfast(my_atom)
	return 1

/datum/reagents/proc/check_ignoreslow(mob/M)
	if(istype(M, /mob))
		if(M.reagents.has_reagent("morphine")||M.reagents.has_reagent("ephedrine"))
			return 1
		else
			M.status_flags &= ~IGNORESLOWDOWN

/datum/reagents/proc/check_gofast(mob/M)
	if(istype(M, /mob))
		if(M.reagents.has_reagent("unholywater")||M.reagents.has_reagent("nuka_cola")||M.reagents.has_reagent("stimulants"))
			return 1
		else
			M.status_flags &= ~GOTTAGOFAST

/datum/reagents/proc/check_goreallyfast(mob/M)
	if(istype(M, /mob))
		if(M.reagents.has_reagent("methamphetamine"))
			return 1
		else
			M.status_flags &= ~GOTTAGOREALLYFAST

/datum/reagents/proc/update_total()
	total_volume = 0
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		if(R.volume < 0.1)
			del_reagent(R.id)
		else
			total_volume += R.volume

	return 0

/datum/reagents/proc/clear_reagents()
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		del_reagent(R.id)
	return 0

/datum/reagents/proc/reaction(atom/A, method = TOUCH, volume_modifier = 1, show_message = 1)
	if(isliving(A))
		var/touch_protection = 0
		if(method == VAPOR)
			var/mob/living/L = A
			touch_protection = L.get_permeability_protection()
		for(var/reagent in reagent_list)
			var/datum/reagent/R = reagent
			R.reaction_mob(A, method, R.volume * volume_modifier, show_message, touch_protection)
	else if(isturf(A))
		for(var/reagent in reagent_list)
			var/datum/reagent/R = reagent
			R.reaction_turf(A, R.volume * volume_modifier, show_message)
	else if(isobj(A))
		for(var/reagent in reagent_list)
			var/datum/reagent/R = reagent
			R.reaction_obj(A, R.volume * volume_modifier, show_message)

/datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = 300, no_react = 0)
	if(!isnum(amount) || !amount)
		return 1
	update_total()
	if(total_volume + amount > maximum_volume)
		amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen.
	chem_temp = round(((amount * reagtemp) + (total_volume * chem_temp)) / (total_volume + amount)) //equalize with new chems

	for(var/A in reagent_list)

		var/datum/reagent/R = A
		if (R.id == reagent)
			R.volume += amount
			update_total()
			my_atom.on_reagent_change()
			R.on_merge(data)
			if(!no_react)
				handle_reactions()
			return 0

	var/datum/reagent/D = chemical_reagents_list[reagent]
	if(D)

		var/datum/reagent/R = new D.type(data)
		reagent_list += R
		R.holder = src
		R.volume = amount
		if(data)
			R.data = data
			R.on_new(data)

		update_total()
		my_atom.on_reagent_change()
		if(!no_react)
			handle_reactions()
		return 0
	else
		WARNING("[my_atom] attempted to add a reagent called ' [reagent] ' which doesn't exist. ([usr])")

	if(!no_react)
		handle_reactions()

	return 1

/datum/reagents/proc/add_reagent_list(list/list_reagents, list/data=null) // Like add_reagent but you can enter a list. Format it like this: list("toxin" = 10, "beer" = 15)
	for(var/r_id in list_reagents)
		var/amt = list_reagents[r_id]
		add_reagent(r_id, amt, data)

/datum/reagents/proc/remove_reagent(reagent, amount, safety)//Added a safety check for the trans_id_to

	if(!isnum(amount))
		return 1

	for(var/A in reagent_list)
		var/datum/reagent/R = A
		if (R.id == reagent)
			R.volume -= amount
			update_total()
			if(!safety)//So it does not handle reactions when it need not to
				handle_reactions()
			my_atom.on_reagent_change()
			return 0

	return 1

/datum/reagents/proc/has_reagent(reagent, amount = -1)
	for(var/_reagent in reagent_list)
		var/datum/reagent/R = _reagent
		if (R.id == reagent)
			if(!amount)
				return R
			else
				if(R.volume >= amount)
					return R
				else
					return 0

	return 0

/datum/reagents/proc/get_reagent_amount(reagent)
	for(var/_reagent in reagent_list)
		var/datum/reagent/R = _reagent
		if (R.id == reagent)
			return R.volume

	return 0

/datum/reagents/proc/get_reagents()
	var/list/names = list()
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		names += R.name

	return jointext(names, ",")

/datum/reagents/proc/remove_all_type(reagent_type, amount, strict = 0, safety = 1) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included.
	if(!isnum(amount)) return 1

	var/has_removed_reagent = 0

	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		var/matches = 0
		// Switch between how we check the reagent type
		if(strict)
			if(R.type == reagent_type)
				matches = 1
		else
			if(istype(R, reagent_type))
				matches = 1
		// We found a match, proceed to remove the reagent.	Keep looping, we might find other reagents of the same type.
		if(matches)
			// Have our other proc handle removement
			has_removed_reagent = remove_reagent(R.id, amount, safety)

	return has_removed_reagent

//two helper functions to preserve data across reactions (needed for xenoarch)
/datum/reagents/proc/get_data(reagent_id)
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		if(R.id == reagent_id)
			//world << "proffering a data-carrying reagent ([reagent_id])"
			return R.data

/datum/reagents/proc/set_data(reagent_id, new_data)
	for(var/reagent in reagent_list)
		var/datum/reagent/R = reagent
		if(R.id == reagent_id)
			//world << "reagent data set ([reagent_id])"
			R.data = new_data

/datum/reagents/proc/copy_data(datum/reagent/current_reagent)
	if(!current_reagent || !current_reagent.data)
		return null
	if(!istype(current_reagent.data, /list))
		return current_reagent.data

	var/list/trans_data = current_reagent.data.Copy()

	// We do this so that introducing a virus to a blood sample
	// doesn't automagically infect all other blood samples from
	// the same donor.
	//
	// Technically we should probably copy all data lists, but
	// that could possibly eat up a lot of memory needlessly
	// if most data lists are read-only.
	if(trans_data["viruses"])
		var/list/v = trans_data["viruses"]
		trans_data["viruses"] = v.Copy()

	return trans_data

///////////////////////////////////////////////////////////////////////////////////

// Convenience proc to create a reagents holder for an atom
// Max vol is maximum volume of holder
/atom/proc/create_reagents(max_vol)
	if(reagents)
		qdel(reagents)
	reagents = new/datum/reagents(max_vol)
	reagents.my_atom = src


code:
/obj/machinery/chem_dispenser
	name = "chem dispenser"
	desc = "Creates and dispenses chemicals."
	density = 1
	anchored = 1
	icon = 'icons/obj/chemical.dmi'
	icon_state = "dispenser"
	use_power = 1
	idle_power_usage = 40
	interact_offline = 1
	var/recharging_power_usage = 1500  // This thing uses up alot of power (this is still low as shit for creating reagents from thin air)
	var/energy = 100
	var/max_energy = 100
	var/amount = 30
	var/recharged = 0
	var/recharge_delay = 5
	var/image/icon_beaker = null
	var/obj/item/weapon/reagent_containers/beaker = null
	var/list/dispensable_reagents = list(
		"hydrogen",
		"lithium",
		"carbon",
		"nitrogen",
		"oxygen",
		"fluorine",
		"sodium",
		"aluminium",
		"silicon",
		"phosphorus",
		"sulfur",
		"chlorine",
		"potassium",
		"iron",
		"copper",
		"mercury",
		"radium",
		"water",
		"ethanol",
		"sugar",
		"sacid",
		"welding_fuel",
		"silver",
		"iodine",
		"bromine",
		"stable_plasma"
	)

/obj/machinery/chem_dispenser/New()
	..()
	recharge()
	dispensable_reagents = sortList(dispensable_reagents)

/obj/machinery/chem_dispenser/process()

	if(recharged < 0)
		recharge()
		recharged = recharge_delay
	else
		recharged -= 1

/obj/machinery/chem_dispenser/proc/recharge()
	if(stat & (BROKEN|NOPOWER)) return
	var/addenergy = 1
	var/oldenergy = energy
	energy = min(energy + addenergy, max_energy)
	if(energy != oldenergy)
		use_power(2500)

/obj/machinery/chem_dispenser/ex_act(severity, target)
	if(severity < 3)
		..()

/obj/machinery/chem_dispenser/blob_act()
	if(prob(50))
		qdel(src)

/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
											datum/tgui/master_ui = null, datum/ui_state/state = default_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "chem_dispenser", name, 550, 550, master_ui, state)
		ui.open()

/obj/machinery/chem_dispenser/ui_data()
	var/data = list()
	data["amount"] = amount
	data["energy"] = energy
	data["maxEnergy"] = max_energy
	data["isBeakerLoaded"] = beaker ? 1 : 0

	var beakerContents[0]
	var beakerCurrentVolume = 0
	if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
		for(var/datum/reagent/R in beaker.reagents.reagent_list)
			beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
			beakerCurrentVolume += R.volume
	data["beakerContents"] = beakerContents

	if (beaker)
		data["beakerCurrentVolume"] = beakerCurrentVolume
		data["beakerMaxVolume"] = beaker.volume
		data["beakerTransferAmounts"] = beaker.possible_transfer_amounts
	else
		data["beakerCurrentVolume"] = null
		data["beakerMaxVolume"] = null
		data["beakerTransferAmounts"] = null

	var chemicals[0]
	for(var/re in dispensable_reagents)
		var/datum/reagent/temp = chemical_reagents_list[re]
		if(temp)
			chemicals.Add(list(list("title" = temp.name, "id" = temp.id)))
	data["chemicals"] = chemicals
	return data

/obj/machinery/chem_dispenser/ui_act(action, params)
	if(..())
		return
	switch(action)
		if("amount")
			var/target = text2num(params["target"])
			if(target in beaker.possible_transfer_amounts)
				amount = target
				. = TRUE
		if("dispense")
			var/reagent = params["reagent"]
			if(beaker && dispensable_reagents.Find(reagent))
				var/datum/reagents/R = beaker.reagents
				var/free = R.maximum_volume - R.total_volume
				var/actual = min(amount, energy * 10, free)

				R.add_reagent(reagent, actual)
				energy = max(energy - actual / 10, 0)
				. = TRUE
		if("remove")
			var/amount = text2num(params["amount"])
			if(beaker && amount in beaker.possible_transfer_amounts)
				beaker.reagents.remove_all(amount)
				. = TRUE
		if("eject")
			if(beaker)
				beaker.loc = loc
				beaker = null
				overlays.Cut()
				. = TRUE

/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
	if(default_unfasten_wrench(user, I))
		return

	if(isrobot(user))
		return

	var/obj/item/weapon/reagent_containers/B = I // Get a beaker from it?
	if(!istype(B))
		return // Not a beaker?

	if(beaker)
		user << "A beaker is already loaded into the machine!"
		return

	if(!user.drop_item()) // Can't let go?
		return

	beaker = B
	beaker.loc = src
	user << "You add the beaker to the machine."

	if(!icon_beaker)
		icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
	icon_beaker.pixel_x = rand(-10,5)
	overlays += icon_beaker

/obj/machinery/chem_dispenser/constructable
	name = "portable chem dispenser"
	icon = 'icons/obj/chemical.dmi'
	icon_state = "minidispenser"
	energy = 10
	max_energy = 10
	amount = 5
	recharge_delay = 30
	dispensable_reagents = list()
	var/list/dispensable_reagent_tiers = list(
		list(
			"hydrogen",
			"oxygen",
			"silicon",
			"phosphorus",
			"sulfur",
			"carbon",
			"nitrogen",
			"water"
		),
		list(
			"lithium",
			"sugar",
			"sacid",
			"copper",
			"mercury",
			"sodium",
			"iodine",
			"bromine"
		),
		list(
			"ethanol",
			"chlorine",
			"potassium",
			"aluminium",
			"radium",
			"fluorine",
			"iron",
			"welding_fuel",
			"silver",
			"stable_plasma"
		),
		list(
			"oil",
			"ash",
			"acetone",
			"saltpetre",
			"ammonia",
			"diethylamine"
		)
	)

/obj/machinery/chem_dispenser/constructable/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/chem_dispenser(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/capacitor(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	component_parts += new /obj/item/weapon/stock_parts/cell/high(null)
	RefreshParts()

/obj/machinery/chem_dispenser/constructable/RefreshParts()
	var/time = 0
	var/temp_energy = 0
	var/i
	for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
		temp_energy += M.rating
	temp_energy--
	max_energy = temp_energy * 5  //max energy = (bin1.rating + bin2.rating - 1) * 5, 5 on lowest 25 on highest
	for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts)
		time += C.rating
	for(var/obj/item/weapon/stock_parts/cell/P in component_parts)
		time += round(P.maxcharge, 10000) / 10000
	recharge_delay /= time/2         //delay between recharges, double the usual time on lowest 50% less than usual on highest
	for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
		for(i=1, i<=M.rating, i++)
			dispensable_reagents |= dispensable_reagent_tiers[i]
	dispensable_reagents = sortList(dispensable_reagents)

/obj/machinery/chem_dispenser/constructable/attackby(var/obj/item/I, var/mob/user, params)
	..()
	if(default_deconstruction_screwdriver(user, "minidispenser-o", "minidispenser", I))
		return

	if(exchange_parts(user, I))
		return

	if(panel_open)
		if(istype(I, /obj/item/weapon/crowbar))
			if(beaker)
				beaker.loc = loc
				beaker = null
			default_deconstruction_crowbar(I)
			return 1

/obj/machinery/chem_dispenser/drinks
	name = "soda dispenser"
	desc = "Contains a large reservoir of soft drinks."
	anchored = 1
	icon = 'icons/obj/chemical.dmi'
	icon_state = "soda_dispenser"
	amount = 10
	dispensable_reagents = list(
		"water",
		"ice",
		"coffee",
		"cream",
		"tea",
		"icetea",
		"cola",
		"spacemountainwind",
		"dr_gibb",
		"space_up",
		"tonic",
		"sodawater",
		"lemon_lime",
		"sugar",
		"orangejuice",
		"limejuice",
		"tomatojuice",
		"lemonjuice"
	)

/obj/machinery/chem_dispenser/drinks/attackby(obj/item/I, mob/user)
	if(default_unfasten_wrench(user, I))
		return

	if(istype(I, /obj/item/weapon/reagent_containers) && (I.flags & OPENCONTAINER))
		if (beaker)
			return 1
		else
			if(!user.drop_item())
				return 1
			src.beaker =  I
			beaker.loc = src
			update_icon()
			return

/obj/machinery/chem_dispenser/drinks/beer
	name = "booze dispenser"
	desc = "Contains a large reservoir of the good stuff."
	anchored = 1
	icon = 'icons/obj/chemical.dmi'
	icon_state = "booze_dispenser"
	dispensable_reagents = list(
		"beer",
		"kahlua",
		"whiskey",
		"wine",
		"vodka",
		"gin",
		"rum",
		"tequila",
		"vermouth",
		"cognac",
		"ale",
		"absinthe"
	)

/obj/machinery/chem_dispenser/mutagen
	name = "mutagen dispenser"
	desc = "Creates and dispenses mutagen."
	dispensable_reagents = list("mutagen")

license: mit


code:
/obj/machinery/chem_heater
	name = "chemical heater"
	density = 1
	anchored = 1
	icon = 'icons/obj/chemical.dmi'
	icon_state = "mixer0b"
	use_power = 1
	idle_power_usage = 40
	var/obj/item/weapon/reagent_containers/beaker = null
	var/target_temperature = 300
	var/heater_coefficient = 0.10
	var/on = FALSE

/obj/machinery/chem_heater/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/chem_heater(null)
	component_parts += new /obj/item/weapon/stock_parts/micro_laser(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	RefreshParts()

/obj/machinery/chem_heater/RefreshParts()
	heater_coefficient = 0.10
	for(var/obj/item/weapon/stock_parts/micro_laser/M in component_parts)
		heater_coefficient *= M.rating

/obj/machinery/chem_heater/process()
	..()
	if(stat & NOPOWER)
		return
	if(on)
		if(beaker)
			if(beaker.reagents.chem_temp > target_temperature)
				beaker.reagents.chem_temp += min(-1, (target_temperature - beaker.reagents.chem_temp) * heater_coefficient)
			if(beaker.reagents.chem_temp < target_temperature)
				beaker.reagents.chem_temp += max(1, (target_temperature - beaker.reagents.chem_temp) * heater_coefficient)

			beaker.reagents.chem_temp = round(beaker.reagents.chem_temp)
			beaker.reagents.handle_reactions()

/obj/machinery/chem_heater/attackby(obj/item/I, mob/user, params)
	if(isrobot(user))
		return

	if(istype(I, /obj/item/weapon/reagent_containers) && (I.flags & OPENCONTAINER))
		if(beaker)
			user << "A beaker is already loaded into the machine!"
			return

		if(!user.drop_item())
			return
		beaker = I
		I.loc = src
		user << "You add the beaker to the machine."
		icon_state = "mixer1b"

	if(default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I))
		return

	if(exchange_parts(user, I))
		return

	if(panel_open)
		if(istype(I, /obj/item/weapon/crowbar))
			eject_beaker()
			default_deconstruction_crowbar(I)
			return 1

/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
										datum/tgui/master_ui = null, datum/ui_state/state = default_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "chem_heater", name, 275, 400, master_ui, state)
		ui.open()

/obj/machinery/chem_heater/ui_data()
	var/data = list()
	data["targetTemp"] = target_temperature
	data["isActive"] = on
	data["isBeakerLoaded"] = beaker ? 1 : 0

	data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null
	data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
	data["beakerMaxVolume"] = beaker ? beaker.volume : null

	var beakerContents[0]
	if(beaker)
		for(var/datum/reagent/R in beaker.reagents.reagent_list)
			beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
	data["beakerContents"] = beakerContents
	return data

/obj/machinery/chem_heater/ui_act(action, params)
	if(..())
		return
	switch(action)
		if("power")
			on = !on
			. = TRUE
		if("temperature")
			var/target = params["target"]
			var/adjust = text2num(params["adjust"])
			if(target == "input")
				target = input("New target temperature:", name, target_temperature) as num|null
				if(!isnull(target) && !..())
					. = TRUE
			else if(adjust)
				target = target_temperature + adjust
			else if(text2num(target) != null)
				target = text2num(target)
				. = TRUE
			if(.)
				target_temperature = Clamp(target, 0, 1000)
		if("eject")
			on = FALSE
			eject_beaker()
			. = TRUE

/obj/machinery/chem_heater/proc/eject_beaker()
	if(beaker)
		beaker.loc = get_turf(src)
		beaker.reagents.handle_reactions()
		beaker = null
		icon_state = "mixer0b"


code:
/obj/machinery/chem_master
	name = "ChemMaster 3000"
	desc = "Used to bottle chemicals to create pills."
	density = 1
	anchored = 1
	icon = 'icons/obj/chemical.dmi'
	icon_state = "mixer0"
	use_power = 1
	idle_power_usage = 20
	var/obj/item/weapon/reagent_containers/beaker = null
	var/obj/item/weapon/storage/pill_bottle/bottle = null
	var/mode = 0
	var/condi = 0
	var/useramount = 30 // Last used amount

/obj/machinery/chem_master/New()
	create_reagents(100)
	overlays += "waitlight"

/obj/machinery/chem_master/ex_act(severity, target)
	if(severity < 3)
		..()

/obj/machinery/chem_master/blob_act()
	if (prob(50))
		qdel(src)

/obj/machinery/chem_master/power_change()
	if(powered())
		stat &= ~NOPOWER
	else
		spawn(rand(0, 15))
			stat |= NOPOWER

/obj/machinery/chem_master/attackby(obj/item/I, mob/user, params)
	if(default_unfasten_wrench(user, I))
		return

	if(istype(I, /obj/item/weapon/reagent_containers) && (I.flags & OPENCONTAINER))
		if(isrobot(user))
			return
		if(beaker)
			user << "A beaker is already loaded into the machine!"
			return
		if(!user.drop_item())
			return

		beaker = I
		beaker.loc = src
		user << "You add the beaker to the machine."
		src.updateUsrDialog()
		icon_state = "mixer1"

	else if(!condi && istype(I, /obj/item/weapon/storage/pill_bottle))
		if(bottle)
			user << "A pill bottle is already loaded into the machine!"
			return
		if(!user.drop_item())
			return

		bottle = I
		bottle.loc = src
		user << "You add the pill bottle into the dispenser slot."
		src.updateUsrDialog()

	return

/obj/machinery/chem_master/Topic(href, href_list)
	if(..())
		return

	usr.set_machine(src)

	if(href_list["ejectp"])
		if(bottle)
			bottle.loc = src.loc
			bottle = null

	else if(href_list["close"])
		usr << browse(null, "window=chem_master")
		usr.unset_machine()
		return

	else if(href_list["toggle"])
		mode = !mode

	else if(href_list["createbottle"])
		var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
		if(!name)
			return
		var/obj/item/weapon/reagent_containers/P
		if(condi)
			P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc)
		else
			P = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
			P.pixel_x = rand(-7, 7) //random position
			P.pixel_y = rand(-7, 7)
		P.name = trim("[name] bottle")
		reagents.trans_to(P, P.volume)

	if(beaker)

		if(href_list["analyze"])
			if(locate(href_list["reagent"]))
				var/datum/reagent/R = locate(href_list["reagent"])
				if(R)
					var/dat = ""
					dat += "
[condi ? "Condiment" : "Chemical"] information:
"
					dat += "Name: [initial(R.name)]

"
					dat += "State: "
					if(initial(R.reagent_state) == 1)
						dat += "Solid"
					else if(initial(R.reagent_state) == 2)
						dat += "Liquid"
					else if(initial(R.reagent_state) == 3)
						dat += "Gas"
					else
						dat += "Unknown"
					dat += "
"
					dat += "Color: [initial(R.color)]

"
					dat += "Description: [initial(R.description)]

"
					var/const/P = 3 //The number of seconds between life ticks
					var/T = initial(R.metabolization_rate) * (60 / P)
					dat += "Metabolization Rate: [T]u/minute
"
					dat += "Overdose Threshold: [initial(R.overdose_threshold) ? "[initial(R.overdose_threshold)]u" : "none"]
"
					dat += "Addiction Threshold: [initial(R.addiction_threshold) ? "[initial(R.addiction_threshold)]u" : "none"]

"
					dat += "
Back"
					var/datum/browser/popup = new(usr, "chem_master", name)
					popup.set_content(dat)
					popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
					popup.open(1)
					return

		else if(href_list["main"]) // Used to exit the analyze screen.
			attack_hand(usr)
			return

		else if(href_list["add"])
			if(href_list["amount"])
				var/id = href_list["add"]
				var/amount = text2num(href_list["amount"])
				if (amount > 0)
					beaker.reagents.trans_id_to(src, id, amount)

		else if(href_list["addcustom"])
			var/id = href_list["addcustom"]
			var/amt_temp = isgoodnumber(input(usr, "Select the amount to transfer.", "Transfer how much?", useramount) as num|null)
			if(!amt_temp)
				return
			useramount = amt_temp
			src.Topic(null, list("amount" = "[useramount]", "add" = "[id]"))

		else if(href_list["remove"])
			if(href_list["amount"])
				var/id = href_list["remove"]
				var/amount = text2num(href_list["amount"])
				if (amount > 0)
					if(mode)
						reagents.trans_id_to(beaker, id, amount)
					else
						reagents.remove_reagent(id, amount)

		else if(href_list["removecustom"])
			var/id = href_list["removecustom"]
			var/amt_temp = isgoodnumber(input(usr, "Select the amount to transfer.", "Transfer how much?", useramount) as num|null)
			if(!amt_temp)
				return
			useramount = amt_temp
			src.Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))

		else if(href_list["eject"])
			if(beaker)
				beaker.loc = src.loc
				beaker = null
				reagents.clear_reagents()
				icon_state = "mixer0"

		else if(href_list["createpill"]) //Also used for condiment packs.
			if(reagents.total_volume == 0) return
			if(!condi)
				var/amount = 1
				var/vol_each = min(reagents.total_volume, 50)
				if(text2num(href_list["many"]))
					amount = min(max(round(input(usr, "Max 10. Buffer content will be split evenly.", "How many pills?", amount) as num|null), 0), 10)
					if(!amount)
						return
					vol_each = min(reagents.total_volume / amount, 50)
				var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
				if(!name || !reagents.total_volume)
					return
				var/obj/item/weapon/reagent_containers/pill/P

				for(var/i = 0; i < amount; i++)
					if(bottle && bottle.contents.len < bottle.storage_slots)
						P = new/obj/item/weapon/reagent_containers/pill(bottle)
					else
						P = new/obj/item/weapon/reagent_containers/pill(src.loc)
					P.name = trim("[name] pill")
					P.pixel_x = rand(-7, 7) //random position
					P.pixel_y = rand(-7, 7)
					reagents.trans_to(P,vol_each)
			else
				var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)
				if(!name || !reagents.total_volume)
					return
				var/obj/item/weapon/reagent_containers/food/condiment/pack/P = new/obj/item/weapon/reagent_containers/food/condiment/pack(src.loc)

				P.originalname = name
				P.name = trim("[name] pack")
				P.desc = "A small condiment pack. The label says it contains [name]."
				reagents.trans_to(P,10)

		else if(href_list["createpatch"])
			if(reagents.total_volume == 0) return
			var/amount = 1
			var/vol_each = min(reagents.total_volume, 50)
			if(text2num(href_list["many"]))
				amount = min(max(round(input(usr, "Max 10. Buffer content will be split evenly.", "How many patches?", amount) as num|null), 0), 10)
				if(!amount)
					return
				vol_each = min(reagents.total_volume / amount, 50)
			var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
			if(!name || !reagents.total_volume)
				return
			var/obj/item/weapon/reagent_containers/pill/P

			for(var/i = 0; i < amount; i++)
				P = new/obj/item/weapon/reagent_containers/pill/patch(src.loc)
				P.name = trim("[name] patch")
				P.pixel_x = rand(-7, 7) //random position
				P.pixel_y = rand(-7, 7)
				reagents.trans_to(P,vol_each)

	src.updateUsrDialog()
	return

/obj/machinery/chem_master/attack_hand(mob/user)
	if(stat & BROKEN)
		return

	user.set_machine(src)
	var/dat = ""
	if(beaker)
		dat += "Beaker \[[beaker.reagents.total_volume]/[beaker.volume]\] Eject and Clear Buffer
"
	else
		dat = "Please insert beaker.
"

	dat += "
Add to buffer:
"
	if(beaker)
		if(beaker.reagents.total_volume)
			for(var/datum/reagent/G in beaker.reagents.reagent_list)
				dat += "
[G.name], [G.volume] Units - "
				dat += "Analyze "
				dat += "1 "
				dat += "5 "
				dat += "10 "
				dat += "All "
				dat += "Custom"
		else
			dat += "
Beaker is empty."
	else
		dat += "
No beaker."

	dat += "

Transfer to [(!mode ? "disposal" : "beaker")]:
"
	if(reagents.total_volume)
		for(var/datum/reagent/N in reagents.reagent_list)
			dat += "
[N.name], [N.volume] Units - "
			dat += "Analyze "
			dat += "1 "
			dat += "5 "
			dat += "10 "
			dat += "All "
			dat += "Custom"
	else
		dat += "
Buffer is empty."
	dat += "

"

	if(!condi)
		if(bottle)
			dat += "Pill Bottle \[[bottle.contents.len]/[bottle.storage_slots]\] Eject"
		else
			dat += "No pill bottle inserted."
	else
		dat += "
"

	dat += "
"
	if(!condi)
		if(beaker && reagents.total_volume)
			dat += "
Create pill (50 units max)"
			dat += "
Create multiple pills
"
			dat += "
Create patch (50 units max)"
			dat += "
Create multiple patches
"
		else
			dat += "
Create pill (50 units max)"
			dat += "
Create multiple pills
"
			dat += "
Create patch (50 units max)"
			dat += "
Create multiple patches
"
	else
		if(beaker && reagents.total_volume)
			dat += "
Create pack (10 units max)
"
		else
			dat += "
Create pack (10 units max)
"
	dat += "
Create bottle ([condi ? "50" : "30"] units max)"
	dat += "
"
	dat += "
Close"
	var/datum/browser/popup = new(user, "chem_master", name, 470, 500)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open(1)
	return

/obj/machinery/chem_master/proc/isgoodnumber(num)
	if(isnum(num))
		if(num > 200)
			num = 200
		else if(num < 0)
			num = 0
		else
			num = round(num)
		return num
	else
		return 0

/obj/machinery/chem_master/condimaster
	name = "CondiMaster 3000"
	desc = "Used to create condiments and other cooking supplies."
	condi = 1

/obj/machinery/chem_master/constructable
	name = "ChemMaster 2999"
	desc = "Used to seperate chemicals and distribute them in a variety of forms."

/obj/machinery/chem_master/constructable/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/chem_master(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(null)
	component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(null)

/obj/machinery/chem_master/constructable/attackby(obj/item/B, mob/user, params)

	if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", B))
		if(beaker)
			beaker.loc = src.loc
			beaker = null
			reagents.clear_reagents()
		if(bottle)
			bottle.loc = src.loc
			bottle = null
		return

	if(exchange_parts(user, B))
		return

	if(panel_open)
		if(istype(B, /obj/item/weapon/crowbar))
			default_deconstruction_crowbar(B)
			return 1
		else
			user << "You can't use the [src.name] while it's panel is opened!"
			return 1

	if(istype(B, /obj/item/weapon/reagent_containers) && (B.flags & OPENCONTAINER))
		if(beaker)
			user << "A beaker is already loaded into the machine!"
			return
		if(!user.drop_item())
			return

		beaker = B
		beaker.loc = src
		user << "You add the beaker to the machine."
		src.updateUsrDialog()
		icon_state = "mixer1"

	else if(!condi && istype(B, /obj/item/weapon/storage/pill_bottle))
		if(bottle)
			user << "A pill bottle is already loaded into the machine!"
			return
		if(!user.drop_item())
			return

		src.bottle = B
		B.loc = src
		user << "You add the pill bottle into the dispenser slot."
		src.updateUsrDialog()

	return


code:
/obj/machinery/chem_dispenser/constructable/synth
	name = "Advanced chem synthesizer"
	desc = "Synthesizes advanced chemicals."
	icon = 'icons/obj/chemical.dmi'
	icon_state = "synth"
	energy = 50
	max_energy = 50
	amount = 10
	//beaker = null
	recharge_delay = 5  //Time it game ticks between recharges
	//var/image/icon_beaker = null //cached overlay, might not be needed here.
	list/dispensable_reagents = list() //starts with no known chems

/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
											datum/tgui/master_ui = null, datum/ui_state/state = default_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "chem_dispenser", name, 550, 550, master_ui, state)
		ui.open()

/obj/machinery/chem_dispenser/ui_data()
	var/data = list()
	data["amount"] = amount
	data["energy"] = energy
	data["maxEnergy"] = max_energy
	data["isBeakerLoaded"] = beaker ? 1 : 0

	var beakerContents[0]
	var beakerCurrentVolume = 0
	if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
		for(var/datum/reagent/R in beaker.reagents.reagent_list)
			beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
			beakerCurrentVolume += R.volume
	data["beakerContents"] = beakerContents

	if (beaker)
		data["beakerCurrentVolume"] = beakerCurrentVolume
		data["beakerMaxVolume"] = beaker.volume
		data["beakerTransferAmounts"] = beaker.possible_transfer_amounts
	else
		data["beakerCurrentVolume"] = null
		data["beakerMaxVolume"] = null
		data["beakerTransferAmounts"] = null

	var chemicals[0]
	for(var/re in dispensable_reagents)
		var/datum/reagent/temp = chemical_reagents_list[re]
		if(temp)
			chemicals.Add(list(list("title" = temp.name, "id" = temp.id)))
	data["chemicals"] = chemicals
	return data

/obj/machinery/chem_dispenser/ui_act(action, params)
	if(..())
		return
	switch(action)
		if("amount")
			var/target = text2num(params["target"])
			if(target in beaker.possible_transfer_amounts)
				amount = target
				. = TRUE
		if("dispense")
			var/reagent = params["reagent"]
			if(beaker && dispensable_reagents.Find(reagent))
				var/datum/reagents/R = beaker.reagents
				var/free = R.maximum_volume - R.total_volume
				var/actual = min(amount, energy * 10, free)

				R.add_reagent(reagent, actual)
				energy = max(energy - actual / 10, 0)
				. = TRUE
		if("remove")
			var/amount = text2num(params["amount"])
			if(beaker && amount in beaker.possible_transfer_amounts)
				beaker.reagents.remove_all(amount)
				. = TRUE
		if("eject")
			if(beaker)
				beaker.loc = loc
				beaker = null
				overlays.Cut()
				. = TRUE

/obj/machinery/chem_dispenser/constructable/synth/RefreshParts()
	var/time = 0
	var/temp_energy = 0

	for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
		temp_energy += M.rating
	temp_energy--
	max_energy = temp_energy * 20  //max energy = (bin1.rating + bin2.rating - 1) * 5, 20 on lowest 100 on highest
	for(var/obj/item/weapon/stock_parts/capacitor/C in component_parts)
		time += C.rating
	for(var/obj/item/weapon/stock_parts/cell/P in component_parts)
		time += round(P.maxcharge, 10000) / 10000
	recharge_delay /= time/2         //delay between recharges, double the usual time on lowest 50% less than usual on highest

/obj/machinery/chem_dispenser/constructable/synth/Topic(href, href_list)
	if(stat & (BROKEN))
		return 0 // don't update UIs attached to this object
	if(href_list["scanBeaker"])
		if(beaker)
			var/obj/item/weapon/reagent_containers/glass/B = beaker
			for(var/datum/reagent/R in B.reagents.reagent_list)
				if(R.can_synth)
					if(R.can_synth == 1 || (R.can_synth == 2 && emagged))
						add_known_reagent(R.id)
						usr << "Reagent analyzed, identified as [R.name] and added to database."
					else
						usr << "Illegal Reagent detected. NT safety regulations forbid replication of [R.name]."
				else
					usr << "Unable to scan reagent."
		return 1
	..()
	return 1

/obj/machinery/chem_dispenser/constructable/synth/proc/add_known_reagent(r_id)
	if(!(r_id in dispensable_reagents))
		dispensable_reagents += r_id
		return 1
	return 0

/obj/machinery/chem_dispenser/constructable/synth/emag_act(mob/user as mob)
	if(!emagged)
		playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
		emagged = 1
		user << " You you disable the safety regulation unit."


code:
#define TAB_ANALYSIS 1
#define TAB_EXPERIMENT 2
#define TAB_DATABASE 3

/obj/machinery/computer/pandemic
	name = "PanD.E.M.I.C 2200"
	desc = "Used to work with viruses."
	density = 1
	anchored = 1
	icon = 'icons/obj/chemical.dmi'
	icon_state = "mixer0"
	circuit = /obj/item/weapon/circuitboard/pandemic
	use_power = 1
	idle_power_usage = 20
	var/virusfood_ammount = 0
	var/mutagen_ammount = 0
	var/plasma_ammount = 0
	var/synaptizine_ammount = 0
	var/new_diseases = list()
	var/new_symptoms = list()
	var/new_cures = list()
	var/tab_open = TAB_ANALYSIS //the magic of defines!
	var/temp_html = ""
	var/wait = null
	var/obj/item/weapon/reagent_containers/glass/beaker = null

/obj/machinery/computer/pandemic/New()
	..()
	update_icon()

/obj/machinery/computer/pandemic/set_broken()
	icon_state = (src.beaker?"mixer1_b":"mixer0_b")
	overlays.Cut()
	stat |= BROKEN

/obj/machinery/computer/pandemic/proc/GetVirusByIndex(var/index)
	if(beaker && beaker.reagents)
		if(beaker.reagents.reagent_list.len)
			var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
			if(BL)
				if(BL.data && BL.data["viruses"])
					var/list/viruses = BL.data["viruses"]
					return viruses[index]
	return null

/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(var/index)
	if(beaker && beaker.reagents)
		if(beaker.reagents.reagent_list.len)
			var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
			if(BL)
				if(BL.data && BL.data["resistances"])
					var/list/resistances = BL.data["resistances"]
					return resistances[index]
	return null

/obj/machinery/computer/pandemic/proc/GetVirusTypeByIndex(var/index)
	var/datum/disease/D = GetVirusByIndex(index)
	if(D)
		return D.GetDiseaseID()
	return null

/obj/machinery/computer/pandemic/proc/replicator_cooldown(var/waittime)
	wait = 1
	update_icon()
	spawn(waittime)
		src.wait = null
		update_icon()
		playsound(src.loc, 'sound/machines/ping.ogg', 30, 1)

/obj/machinery/computer/pandemic/update_icon()
	if(stat & BROKEN)
		icon_state = (src.beaker?"mixer1_b":"mixer0_b")
		return

	icon_state = "mixer[(beaker)?"1":"0"][(powered()) ? "" : "_nopower"]"

	if(wait)
		overlays.Cut()
	else
		overlays += "waitlight"

/obj/machinery/computer/pandemic/Topic(href, href_list)
	if(..())
		return

	usr.set_machine(src)
	if(!beaker) return

	if (href_list["symptom"])
		if(beaker && beaker.reagents)
			if(beaker.reagents.reagent_list.len)
				var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
				if(BL)
					if(BL.data && BL.data["viruses"])
						var/list/viruses = BL.data["viruses"]
						for(var/datum/disease/advance/D in viruses)
							D.AddSymptom(new_symptoms[text2num(href_list["symptom"])])
		src.updateUsrDialog()
		return

	if (href_list["cure"])
		if(!src.wait)
			var/obj/item/weapon/reagent_containers/glass/bottle/B = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
			if(B)
				B.pixel_x = rand(-3, 3)
				B.pixel_y = rand(-3, 3)
				var/vaccine_type = new_cures[text2num(href_list["cure"])]
				if(vaccine_type)
					if(!ispath(vaccine_type))
						if(archive_diseases[vaccine_type])
							var/datum/disease/D = archive_diseases[vaccine_type]
							B.name = "[D.name] vaccine bottle"
							B.reagents.add_reagent("vaccine", 15, list(vaccine_type))
							replicator_cooldown(200)
					else
						var/datum/disease/D = vaccine_type
						B.name = "[D.name] vaccine bottle"
						B.reagents.add_reagent("vaccine", 15, list(vaccine_type))
						replicator_cooldown(200)
		else
			src.temp_html = "The replicator is not ready yet."
		src.updateUsrDialog()
		return

	else if (href_list["virus"])
		if(!wait)
			var/datum/disease/D = new_diseases[text2num(href_list["virus"])]
			if(!D)
				return
			var/name = stripped_input(usr,"Name:","Name the culture",D.name,MAX_NAME_LEN)
			if(name == null || wait)
				return
			var/obj/item/weapon/reagent_containers/glass/bottle/B = new/obj/item/weapon/reagent_containers/glass/bottle(src.loc)
			B.icon_state = "bottle3"
			B.pixel_x = rand(-3, 3)
			B.pixel_y = rand(-3, 3)
			replicator_cooldown(50)
			var/list/data = list("viruses"=list(D))
			B.name = "[name] culture bottle"
			B.desc = "A small bottle. Contains [D.agent] culture in synthblood medium."
			B.reagents.add_reagent("blood",20,data)
			src.updateUsrDialog()
		else
			src.temp_html = "The replicator is not ready yet."
		src.updateUsrDialog()
		return

	else if(href_list["name_disease"])
		var/new_name = stripped_input(usr, "Name the Disease", "New Name", "", MAX_NAME_LEN)
		if(!new_name)
			return
		if(..())
			return
		var/id = GetVirusTypeByIndex(text2num(href_list["name_disease"]))
		if(archive_diseases[id])
			var/datum/disease/advance/A = archive_diseases[id]
			A.AssignName(new_name)
			for(var/datum/disease/advance/AD in SSdisease.processing)
				AD.Refresh()
		src.updateUsrDialog()

	else if (href_list["eject"])
		if(beaker)
			var/obj/item/weapon/reagent_containers/glass/B = beaker
			B.loc = loc
			beaker = null
			icon_state = "mixer0"
			src.updateUsrDialog()
			return

	else if (href_list["tab_open"])
		tab_open = text2num(href_list["tab_open"]) //fucking text
		src.updateUsrDialog()
		return

	else if(href_list["chem_choice"])
		switch(href_list["chem_choice"])
			if("virusfood")
				if(virusfood_ammount>0)
					beaker.reagents.add_reagent("virusfood",max(beaker.reagents.maximum_volume-beaker.reagents.total_volume,1))
					virusfood_ammount -= 1
					usr << "Virus Food administered."
				else
					usr << "Not enough Virus Food stored!"
			if("mutagen")
				if(mutagen_ammount>0)
					beaker.reagents.add_reagent("mutagen",max(beaker.reagents.maximum_volume-beaker.reagents.total_volume,1))
					mutagen_ammount -= 1
					usr << "Unstable Mutagen administered."
				else
					usr << "Not enough Unstable Mutagen stored!"
			if("plasma")
				if(plasma_ammount>0)
					beaker.reagents.add_reagent("plasma",max(beaker.reagents.maximum_volume-beaker.reagents.total_volume,1))
					plasma_ammount -= 1 //no idea why plasma_ammount-- doesn't work here.
					usr << "Plasma administered."
				else
					usr << "Not enough Plasma stored!"
			if("synaptizine")
				if(synaptizine_ammount>0)
					beaker.reagents.add_reagent("synaptizine",max(beaker.reagents.maximum_volume-beaker.reagents.total_volume,1))
					synaptizine_ammount -= 1
					usr << "Synaptizine administered."
				else
					usr << "Not enough Synaptizine!"
			if("reset")
				beaker.reagents.clear_reagents()
				var/datum/disease/advance/AD = new /datum/disease/advance/inert
				var/list/data = list("viruses"=list(AD))
				beaker.reagents.add_reagent("blood",20,data)
				usr << "Viral strain reset!."
		src.updateUsrDialog()
		return

	else if(href_list["update_virus"])
		if(beaker && beaker.reagents)
			if(beaker.reagents.reagent_list.len)
				var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
				if(BL)
					if(BL.data && BL.data["viruses"])
						var/list/viruses = BL.data["viruses"]
						for(var/datum/disease/D in viruses)
							var/d_test = 1
							for(var/datum/disease/DT in new_diseases) //we scan for the desease itself to add to the list
								if(D.IsSame(DT))
									d_test = 0
							if(d_test)
								new_diseases += D
								usr << "New disease added to the database!"
	else if(href_list["update_symptom"])
		if(beaker && beaker.reagents)
			if(beaker.reagents.reagent_list.len)
				var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
				if(BL)
					if(BL.data && BL.data["viruses"])
						var/list/viruses = BL.data["viruses"]
						for(var/datum/disease/D in viruses)
							if(istype(D,/datum/disease/advance)) //advanced deseases, we scan for symptoms
								var/datum/disease/advance/AD = D //inheritance failed me today
								for(var/datum/symptom/S in AD.symptoms)
									var/s_test = 1
									for(var/datum/symptom/ST in new_symptoms ) //this is awfull, I know.
										if(S.name == ST.name) //I really hoped there was another way of doing this.
											s_test = 0
									if(s_test)
										new_symptoms += S
										usr << "New symptom added to the database!"
	else if(href_list["update_cure"])
		if(beaker && beaker.reagents)
			if(beaker.reagents.reagent_list.len)
				var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list
				if(BL)
					if(BL.data && BL.data["resistances"])
						var/v_test = 1
						for(var/resistance in BL.data["resistances"])
							for(var/res in new_cures)
								if(resistance == res)
									v_test = 0
							if(v_test)
								new_cures += list(resistance)
								if(!istype(resistance, /datum/disease))
									new_cures[resistance] = resistance
								usr << "New vaccine added to the database!"
					usr << "No virus found!"
				else
					usr << "No blood found!"
			else
				usr << "Beaker is empty!"
		else
			usr << "No beaker found!"
		src.updateUsrDialog()
		return
	else
		usr << browse(null, "window=pandemic")
		src.updateUsrDialog()
		return

	src.add_fingerprint(usr)
	return

/obj/machinery/computer/pandemic/attack_hand(mob/user as mob)
	if(..())
		return
	user.set_machine(src)
	var/dat = ""
	dat += "Analysis"
	dat += "Experiment"
	dat += "Database

"

	switch(tab_open)
		if(TAB_ANALYSIS)
			if(!beaker)
				dat += "No beaker inserted.
"

			else
				var/datum/reagents/R = beaker.reagents
				var/datum/reagent/blood/Blood = null
				for(var/datum/reagent/blood/B in R.reagent_list)
					if(B)
						Blood = B
						break
				if(!R.total_volume||!R.reagent_list.len)
					dat += "The beaker is empty
"
				else if(!Blood)
					dat += "No blood sample found in beaker."
				else if(!Blood.data)
					dat += "No blood data found in beaker."
				else
					if(Blood.data["viruses"])
						var/list/vir = Blood.data["viruses"]
						if(vir.len)
							var/i = 0
							for(var/datum/disease/D in Blood.data["viruses"])
								i++
								if(!(D.visibility_flags & HIDDEN_PANDEMIC))

									if(istype(D, /datum/disease/advance))

										var/datum/disease/advance/A = D
										D = archive_diseases[A.GetDiseaseID()]
										if(D && D.name == "Unknown")
											dat += "Name Disease
"

									if(!D)
										CRASH("We weren't able to get the advance disease from the archive.")

									dat += "Disease Agent: [D?"[D.agent]":"none"]
"
									dat += "Common name: [(D.name||"none")]
"
									dat += "Description:  [(D.desc||"none")]
"
									dat += "Spread: [(D.spread_text||"none")]

"
									dat += "Possible cure: [(D.cure_text||"none")]
"

									if(istype(D, /datum/disease/advance))
										var/datum/disease/advance/A = D
										dat += "Symptoms: "
										var/english_symptoms = list()
										for(var/datum/symptom/S in A.symptoms)
											english_symptoms += S.name
										dat += english_list(english_symptoms)

								else
									dat += "No detectable virus in the sample."
					else
						dat += "No detectable virus in the sample."
					dat += "

Contains antibodies to: "
					if(Blood.data["resistances"])
						var/list/res = Blood.data["resistances"]
						if(res.len)
							dat += "
"
							for(var/type in Blood.data["resistances"])
								var/disease_name = "Unknown"
								if(!ispath(type))
									var/datum/disease/advance/A = archive_diseases[type]
									if(A)
										disease_name = A.name
								else
									var/datum/disease/D = new type(0, null)
									disease_name = D.name
								dat += "
[disease_name]
"
							dat += "

"
						else
							dat += "nothing
"
					else
						dat += "nothing
"
		if(TAB_EXPERIMENT)
			dat += "Available Chems:
"
			dat += "Virus Food: [virusfood_ammount].
"
			dat += "Unstable Mutage: [mutagen_ammount].
"
			dat += "Plasma: [plasma_ammount].
"
			dat += "Synaptizine: [synaptizine_ammount].

"

			if(!beaker)
				dat += "No beaker inserted.
"
			else
				var/datum/reagents/R = beaker.reagents
				var/datum/reagent/blood/Blood = null
				for(var/datum/reagent/blood/B in R.reagent_list)
					if(B)
						Blood = B
						break
				if(!R.total_volume||!R.reagent_list.len)
					dat += "The beaker is empty
"
				else if(!Blood)
					dat += "No blood sample found in beaker."
				else if(!Blood.data)
					dat += "No blood data found in beaker."
				else
					if(Blood.data["viruses"])
						var/list/vir = Blood.data["viruses"]
						if(vir.len)
							for(var/datum/disease/D in Blood.data["viruses"])
								if(!(D.visibility_flags & HIDDEN_PANDEMIC))
									if(!D)
										CRASH("We weren't able to get the advance disease from the archive.")
									if(istype(D, /datum/disease/advance))
										var/datum/disease/advance/A = D
										dat += "Symptoms: "
										var/english_symptoms = list()
										dat += "
"
										for(var/datum/symptom/S in A.symptoms)
											english_symptoms += S.name
										dat += english_list(english_symptoms)+"
"
										dat += "
"

								else
									dat += "No detectable virus in the sample."
				dat += "

"
				dat += "Inject Sample with:
"
				dat += "Virus Food
"
				dat += "Unstable Mutagen
"
				dat += "Plasma
"
				dat += "Synaptizine
"
				dat += "Reset Virus
"

		if(TAB_DATABASE)
			dat += "Database:

"
			//describe database here
			var/loop = 0
			dat += "
Diseases:"
			dat += "Update

"
			for(var/datum/disease/type in new_diseases)
				loop++
				dat += "[type.name] "
				dat += "
- Make

"
			loop = 0
			dat += "
Symptoms:"
			dat += "Update

"
			for(var/datum/symptom/type in new_symptoms)
				loop++
				dat += "[type.name] "
				dat += "
- Mutate

"
			loop = 0
			dat += "
Vaccines:"
			dat += "Update

"
			for(var/type in new_cures)
				loop++
				if(!ispath(type))
					var/datum/disease/DD = archive_diseases[type]
					dat += "[DD.name] "
				else
					var/datum/disease/gn = new type(0, null)
					dat += "[gn.name] "
				dat += "
 - Make

"

	dat += "

Eject beaker"

	var/datum/browser/popup = new(user, "pandemic", "PanD.E.M.I.C 2200")
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open(1)
	return

/obj/machinery/computer/pandemic/attackby(var/obj/I as obj, var/mob/user as mob, params)
	if(istype(I, /obj/item/weapon/reagent_containers/glass))
		if(stat & (NOPOWER|BROKEN)) return

		for(var/datum/reagent/R in I.reagents.reagent_list)
			if(R.id == "virusfood")
				virusfood_ammount += R.volume
				I.reagents.remove_reagent("virusfood",R.volume)
				user << "You add the Virus Food into the machine!"
				src.updateUsrDialog()
				return
			if(R.id == "mutagen")
				mutagen_ammount += R.volume
				I.reagents.remove_reagent("mutagen",R.volume)
				user << "You add the Unstable Mutagen into the machine!"
				src.updateUsrDialog()
				return
			if(R.id == "plasma")
				plasma_ammount += R.volume
				I.reagents.remove_reagent("plasma",R.volume)
				user << "You add the Plasma into the machine!"
				src.updateUsrDialog()
				return
			if(R.id == "synaptizine")
				synaptizine_ammount += R.volume
				I.reagents.remove_reagent("synaptizine",R.volume)
				user << "You add the Synaptizine into the machine!"
				src.updateUsrDialog()
				return

		if(src.beaker)
			user << "A beaker is already loaded into the machine."
			return
		src.beaker =  I
		user.drop_item()
		I.loc = src
		user << "You add the beaker to the machine!"
		src.updateUsrDialog()
		icon_state = "mixer1"

	else if(istype(I, /obj/item/weapon/screwdriver))
		if(src.beaker)
			beaker.loc = get_turf(src)
		..()
		return
	else
		..()
	return

#undef TAB_ANALYSIS
#undef TAB_EXPERIMENT
#undef TAB_DATABASE


code:
/obj/machinery/reagentgrinder
		name = "All-In-One Grinder"
		desc = "Used to grind things up into raw materials."
		icon = 'icons/obj/kitchen.dmi'
		icon_state = "juicer1"
		layer = 2.9
		anchored = 1
		use_power = 1
		idle_power_usage = 5
		active_power_usage = 100
		pass_flags = PASSTABLE
		var/operating = 0
		var/obj/item/weapon/reagent_containers/beaker = null
		var/limit = 10
		var/list/blend_items = list (

				//Sheets
				/obj/item/stack/sheet/mineral/plasma = list("plasma" = 20),
				/obj/item/stack/sheet/metal = list("iron" = 20),
				/obj/item/stack/sheet/plasteel = list("iron" = 20, "plasma" = 20),
				/obj/item/stack/sheet/mineral/wood = list("carbon" = 20),
				/obj/item/stack/sheet/glass = list("silicon" = 20),
				/obj/item/stack/sheet/rglass = list("silicon" = 20, "iron" = 20),
				/obj/item/stack/sheet/mineral/uranium = list("uranium" = 20),
				/obj/item/stack/sheet/mineral/bananium = list("banana" = 20),
				/obj/item/stack/sheet/mineral/silver = list("silver" = 20),
				/obj/item/stack/sheet/mineral/gold = list("gold" = 20),
				/obj/item/weapon/grown/nettle/basic = list("sacid" = 0),
				/obj/item/weapon/grown/nettle/death = list("facid" = 0),
				/obj/item/weapon/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0),

				//Crayons (for overriding colours)
				/obj/item/toy/crayon/red = list("redcrayonpowder" = 10),
				/obj/item/toy/crayon/orange = list("orangecrayonpowder" = 10),
				/obj/item/toy/crayon/yellow = list("yellowcrayonpowder" = 10),
				/obj/item/toy/crayon/green = list("greencrayonpowder" = 10),
				/obj/item/toy/crayon/blue = list("bluecrayonpowder" = 10),
				/obj/item/toy/crayon/purple = list("purplecrayonpowder" = 10),
				/obj/item/toy/crayon/mime = list("invisiblecrayonpowder" = 50),

				//Blender Stuff
				/obj/item/weapon/reagent_containers/food/snacks/grown/soybeans = list("soymilk" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("ketchup" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/wheat = list("flour" = -5),
				/obj/item/weapon/reagent_containers/food/snacks/grown/oat = list("flour" = -5),
				/obj/item/weapon/reagent_containers/food/snacks/grown/cherries = list("cherryjelly" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/bluecherries = list("bluecherryjelly" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/egg = list("eggyolk" = -5),

				//Grinder stuff, but only if dry
				/obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0),

				//All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.!
				/obj/item/weapon/reagent_containers/pill = list(),
				/obj/item/weapon/reagent_containers/food = list(),
				/obj/item/weapon/reagent_containers/honeycomb = list()
		)

		var/list/juice_items = list (

				//Juicer Stuff
				/obj/item/weapon/reagent_containers/food/snacks/grown/corn = list("corn_starch" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("tomatojuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/carrot = list("carrotjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/berries = list("berryjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/banana = list("banana" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/potato = list("potato" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon = list("lemonjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange = list("orangejuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime = list("limejuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin = list("blumpkinjuice" = 0),
		)

		var/list/dried_items = list(
				//Grinder stuff, but only if dry,
				/obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0),
				/obj/item/weapon/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0)
		)

		var/list/holdingitems = list()

/obj/machinery/reagentgrinder/New()
		..()
		beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
		return

/obj/machinery/reagentgrinder/update_icon()
		icon_state = "juicer"+num2text(!isnull(beaker))
		return

/obj/machinery/reagentgrinder/attackby(obj/item/I, mob/user, params)
		if(default_unfasten_wrench(user, I))
				return

		if (istype(I, /obj/item/weapon/reagent_containers) && (I.flags & OPENCONTAINER) )
				if (beaker)
						return 1
				else
						if(!user.drop_item())
								return 1
						beaker =  I
						beaker.loc = src
						update_icon()
						src.updateUsrDialog()
						return 0

		if(is_type_in_list(I, dried_items))
				if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/grown))
						var/obj/item/weapon/reagent_containers/food/snacks/grown/G = I
						if(!G.dry)
								user << "You must dry that first!"
								return 1

		if(holdingitems && holdingitems.len >= limit)
				usr << "The machine cannot hold anymore items."
				return 1

		//Fill machine with a bag!
		if(istype(I, /obj/item/weapon/storage/bag))
				var/obj/item/weapon/storage/bag/B = I
				for (var/obj/item/weapon/reagent_containers/food/snacks/grown/G in B.contents)
						B.remove_from_storage(G, src)
						holdingitems += G
						if(holdingitems && holdingitems.len >= limit) //Sanity checking so the blender doesn't overfill
								user << "You fill the All-In-One grinder to the brim."
								break

				if(!I.contents.len)
						user << "You empty the plant bag into the All-In-One grinder."

				src.updateUsrDialog()
				return 0

		if (!is_type_in_list(I, blend_items) && !is_type_in_list(I, juice_items))
				user << "Cannot refine into a reagent!"
				return 1

		user.unEquip(I)
		I.loc = src
		holdingitems += I
		src.updateUsrDialog()
		return 0

/obj/machinery/reagentgrinder/attack_paw(mob/user)
		return src.attack_hand(user)

/obj/machinery/reagentgrinder/attack_ai(mob/user)
		return 0

/obj/machinery/reagentgrinder/attack_hand(mob/user)
		user.set_machine(src)
		interact(user)

/obj/machinery/reagentgrinder/interact(mob/user) // The microwave Menu
		var/is_chamber_empty = 0
		var/is_beaker_ready = 0
		var/processing_chamber = ""
		var/beaker_contents = ""
		var/dat = ""

		if(!operating)
				for (var/obj/item/O in holdingitems)
						processing_chamber += "\A [O.name]
"

				if (!processing_chamber)
						is_chamber_empty = 1
						processing_chamber = "Nothing."
				if (!beaker)
						beaker_contents = "No beaker attached.
"
				else
						is_beaker_ready = 1
						beaker_contents = "The beaker contains:
"
						var/anything = 0
						for(var/datum/reagent/R in beaker.reagents.reagent_list)
								anything = 1
								beaker_contents += "[R.volume] - [R.name]
"
						if(!anything)
								beaker_contents += "Nothing
"

				dat = {"
		Processing chamber contains:

		[processing_chamber]

		[beaker_contents]

		"}
				if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
						dat += "Grind the reagents
"
						dat += "Juice the reagents

"
				if(holdingitems && holdingitems.len > 0)
						dat += "Eject the reagents
"
				if (beaker)
						dat += "Detach the beaker
"
		else
				dat += "Please wait..."

		var/datum/browser/popup = new(user, "reagentgrinder", "All-In-One Grinder")
		popup.set_content(dat)
		popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
		popup.open(1)
		return

/obj/machinery/reagentgrinder/Topic(href, href_list)
	if(..())
		return
	usr.set_machine(src)
	if(operating)
		updateUsrDialog()
		return
	switch(href_list["action"])
		if ("grind")
			grind()
		if("juice")
			juice()
		if("eject")
			eject()
		if ("detach")
			detach()

/obj/machinery/reagentgrinder/proc/detach()

		if (usr.stat != 0)
				return
		if (!beaker)
				return
		beaker.loc = src.loc
		beaker = null
		update_icon()
		updateUsrDialog()

/obj/machinery/reagentgrinder/proc/eject()

		if (usr.stat != 0)
				return
		if (holdingitems && holdingitems.len == 0)
				return

		for(var/obj/item/O in holdingitems)
				O.loc = src.loc
				holdingitems -= O
		holdingitems = list()
		updateUsrDialog()

/obj/machinery/reagentgrinder/proc/is_allowed(obj/item/weapon/reagent_containers/O)
		for (var/i in blend_items)
				if(istype(O, i))
						return 1
		return 0

/obj/machinery/reagentgrinder/proc/get_allowed_by_id(obj/item/O)
		for (var/i in blend_items)
				if (istype(O, i))
						return blend_items[i]

/obj/machinery/reagentgrinder/proc/get_allowed_snack_by_id(obj/item/weapon/reagent_containers/food/snacks/O)
		for(var/i in blend_items)
				if(istype(O, i))
						return blend_items[i]

/obj/machinery/reagentgrinder/proc/get_allowed_juice_by_id(obj/item/weapon/reagent_containers/food/snacks/O)
		for(var/i in juice_items)
				if(istype(O, i))
						return juice_items[i]

/obj/machinery/reagentgrinder/proc/get_grownweapon_amount(obj/item/weapon/grown/O)
		if (!istype(O))
				return 5
		else if (O.seed.potency == -1)
				return 5
		else
				return round(O.seed.potency)

/obj/machinery/reagentgrinder/proc/get_juice_amount(obj/item/weapon/reagent_containers/food/snacks/grown/O)
		if (!istype(O))
				return 5
		else if (O.seed.potency == -1)
				return 5
		else
				return round(5*sqrt(O.seed.potency))

/obj/machinery/reagentgrinder/proc/remove_object(obj/item/O)
		holdingitems -= O
		qdel(O)

/obj/machinery/reagentgrinder/proc/juice()
		power_change()
		if(stat & (NOPOWER|BROKEN))
				return
		if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
				return
		playsound(src.loc, 'sound/machines/juicer.ogg', 20, 1)
		var/offset = prob(50) ? -2 : 2
		animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
		operating = 1
		updateUsrDialog()
		spawn(50)
				pixel_x = initial(pixel_x) //return to its spot after shaking
				operating = 0
				updateUsrDialog()

		//Snacks
		for (var/obj/item/weapon/reagent_containers/food/snacks/O in holdingitems)
				if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
						break

				var/allowed = get_allowed_juice_by_id(O)
				if(isnull(allowed))
						break

				for (var/r_id in allowed)

						var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
						var/amount = get_juice_amount(O)

						beaker.reagents.add_reagent(r_id, min(amount, space))

						if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
								break

				remove_object(O)

/obj/machinery/reagentgrinder/proc/grind()

		power_change()
		if(stat & (NOPOWER|BROKEN))
				return
		if (!beaker || (beaker && beaker.reagents.total_volume >= beaker.reagents.maximum_volume))
				return
		playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
		var/offset = prob(50) ? -2 : 2
		animate(src, pixel_x = pixel_x + offset, time = 0.2, loop = 250) //start shaking
		operating = 1
		updateUsrDialog()
		spawn(60)
				pixel_x = initial(pixel_x) //return to its spot after shaking
				operating = 0
				updateUsrDialog()

		//Snacks and Plants
		for (var/obj/item/weapon/reagent_containers/food/snacks/O in holdingitems)
				if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
						break

				var/allowed = get_allowed_snack_by_id(O)
				if(isnull(allowed))
						break

				for (var/r_id in allowed)

						var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
						var/amount = allowed[r_id]
						if(amount <= 0)
								if(amount == 0)
										if (O.reagents != null && O.reagents.has_reagent("nutriment"))
												beaker.reagents.add_reagent(r_id, min(O.reagents.get_reagent_amount("nutriment"), space))
												O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))
								else
										if (O.reagents != null && O.reagents.has_reagent("nutriment"))
												beaker.reagents.add_reagent(r_id, min(round(O.reagents.get_reagent_amount("nutriment")*abs(amount)), space))
												O.reagents.remove_reagent("nutriment", min(O.reagents.get_reagent_amount("nutriment"), space))

						else
								O.reagents.trans_id_to(beaker, r_id, min(amount, space))

						if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
								break

				if(O.reagents.reagent_list.len == 0)
						remove_object(O)

		//Sheets
		for (var/obj/item/stack/sheet/O in holdingitems)
				var/allowed = get_allowed_by_id(O)
				if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
						break
				for(var/i = 1; i <= round(O.amount, 1); i++)
						for (var/r_id in allowed)
								var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
								var/amount = allowed[r_id]
								beaker.reagents.add_reagent(r_id,min(amount, space))
								if (space < amount)
										break
						if (i == round(O.amount, 1))
								remove_object(O)
								break
		//Plants
		for (var/obj/item/weapon/grown/O in holdingitems)
				if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
						break
				var/allowed = get_allowed_by_id(O)
				for (var/r_id in allowed)
						var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
						var/amount = allowed[r_id]
						if (amount == 0)
								if (O.reagents != null && O.reagents.has_reagent(r_id))
										beaker.reagents.add_reagent(r_id,min(O.reagents.get_reagent_amount(r_id), space))
						else
								beaker.reagents.add_reagent(r_id,min(amount, space))

						if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
								break
				remove_object(O)

		//Crayons
		//With some input from aranclanos, now 30% less shoddily copypasta
		for (var/obj/item/toy/crayon/O in holdingitems)
				if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
						break
				var/allowed = get_allowed_by_id(O)
				for (var/r_id in allowed)
						var/space = beaker.reagents.maximum_volume - beaker.reagents.total_volume
						var/amount = allowed[r_id]
						beaker.reagents.add_reagent(r_id,min(amount, space))
						if (space < amount)
								break
						remove_object(O)

		//Everything else - Transfers reagents from it into beaker
		for (var/obj/item/weapon/reagent_containers/O in holdingitems)
				if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
						break
				var/amount = O.reagents.total_volume
				O.reagents.trans_to(beaker, amount)
				if(!O.reagents.total_volume)
						remove_object(O)


code:
/*
NOTE: IF YOU UPDATE THE REAGENT-SYSTEM, ALSO UPDATE THIS README.

Structure: ///////////////////          //////////////////////////
		   // Mob or object // -------> // Reagents var (datum) // 	    Is a reference to the datum that holds the reagents.
		   ///////////////////          //////////////////////////
		   			|				    			 |
    The object that holds everything.   			 V
		   							      reagent_list var (list)   	A List of datums, each datum is a reagent.

		   							      |          |          |
		   							      V          V          V

		   							         reagents (datums)	    	Reagents. I.e. Water , cryoxadone or mercury.

Random important notes:

	An objects on_reagent_change will be called every time the objects reagents change.
	Useful if you want to update the objects icon etc.

About the Holder:

	The holder (reagents datum) is the datum that holds a list of all reagents
	currently in the object.It also has all the procs needed to manipulate reagents

		remove_any(var/amount)
			This proc removes reagents from the holder until the passed amount
			is matched. It'll try to remove some of ALL reagents contained.

		remove_all(var/amount)
			This proc removes reagents from the holder equally.

		trans_to(var/obj/target, var/amount)
			This proc equally transfers the contents of the holder to another
			objects holder. You need to pass it the object (not the holder) you want
			to transfer to and the amount you want to transfer. Its return value is the
			actual amount transfered (if one of the objects is full/empty)

		trans_id_to(var/obj/target, var/reagent, var/amount)
			Same as above but only for a specific reagent in the reagent list.
			If the specified amount is greater than what is available, it will use
			the amount of the reagent that is available. If no reagent exists, returns null.

		metabolize(var/mob/M)
			This proc is called by the mobs life proc. It simply calls on_mob_life for
			all contained reagents. You shouldnt have to use this one directly.

		handle_reactions()
			This proc check all recipes and, on a match, uses them.
			It will also call the recipe's on_reaction proc (for explosions or w/e).
			Currently, this proc is automatically called by trans_to.

		isolate_reagent(var/reagent)
			Pass it a reagent id and it will remove all reagents but that one.
			It's that simple.

		del_reagent(var/reagent)
			Completely remove the reagent with the matching id.

		reaction_fire(exposed_temp)
			Simply calls the reaction_fire procs of all contained reagents.

		update_total()
			This one simply updates the total volume of the holder.
			(the volume of all reagents added together)

		clear_reagents()
			This proc removes ALL reagents from the holder.

		reaction(var/atom/A, var/method=TOUCH, var/volume_modifier=0)
			This proc calls the appropriate reaction procs of the reagents.
			I.e. if A is an object, it will call the reagents reaction_obj
			proc. The method var is used for reaction on mobs. It simply tells
			us if the mob TOUCHed the reagent, if it INGESTed the reagent, if the reagent
			was VAPORIZEd on them, if the reagent was INJECTed,	or transfered via a PATCH to them.
			Since the volume can be checked in a reagents proc, you might want to
			use the volume_modifier var to modifiy the passed value without actually
			changing the volume of the reagents.
			If you're not sure if you need to use this the answer is very most likely 'No'.
			You'll want to use this proc whenever an atom first comes in
			contact with the reagents of a holder. (in the 'splash' part of a beaker i.e.)
			More on the reaction in the reagent part of this readme.

		add_reagent(var/reagent, var/amount, var/data)
			Attempts to add X of the matching reagent to the holder.
			You wont use this much. Mostly in new procs for pre-filled
			objects.

		remove_reagent(var/reagent, var/amount)
			The exact opposite of the add_reagent proc.

		has_reagent(var/reagent, var/amount)
			Returns 1 if the holder contains this reagent.
			Or 0 if not.
			If you pass it an amount it will additionally check
			if the amount is matched. This is optional.

		get_reagent_amount(var/reagent)
			Returns the amount of the matching reagent inside the
			holder. Returns 0 if the reagent is missing.

		Important variables:

			total_volume
				This variable contains the total volume of all reagents in this holder.

			reagent_list
				This is a list of all contained reagents. More specifically, references
				to the reagent datums.

			maximum_volume
				This is the maximum volume of the holder.

			my_atom
				This is the atom the holder is 'in'. Useful if you need to find the location.
				(i.e. for explosions)

About Reagents:

	Reagents are all the things you can mix and fille in bottles etc. This can be anything from
	rejuvs over water to ... iron. Each reagent also has a few procs - i'll explain those below.

		reaction_mob(var/mob/M, var/method=TOUCH)
			This is called by the holder's reation proc.
			This version is only called when the reagent
			reacts with a mob. The method var can be either
			TOUCH or INGEST. You'll want to put stuff like
			acid-facemelting in here.

		reaction_obj(var/obj/O)
			This is called by the holder's reation proc.
			This version is called when the reagents reacts
			with an object. You'll want to put stuff like
			object melting in here ... or something. i dunno.

		reaction_turf(var/turf/T)
			This is called by the holder's reation proc.
			This version is called when the reagents reacts
			with a turf. You'll want to put stuff like extra
			slippery floors for lube or something in here.

		on_mob_life(var/mob/M)
			This proc is called everytime the mobs life proc executes.
			This is the place where you put damage for toxins ,
			drowsyness for sleep toxins etc etc.
			You'll want to call the parents proc by using ..() .
			If you dont, the chemical will stay in the mob forever -
			unless you write your own piece of code to slowly remove it.
			(Should be pretty easy, 1 line of code)

	Important variables:

		holder
			This variable contains a reference to the holder the chemical is 'in'

		volume
			This is the volume of the reagent.

		id
			The id of the reagent

		name
			The name of the reagent.

		data
			This var can be used for whatever the fuck you want. I used it for the sleep
			toxins to make them work slowly instead of instantly. You could also use this
			for DNA in a blood reagent or ... well whatever you want.

		color
			This is a hexadecimal color that represents the reagent outside of containers,
			you define it as "#RRGGBB", or, red green blue. You can also define it using the
			rgb() proc, which returns a hexadecimal value too. The color is black by default.

			A good website for color calculations: http://www.psyclops.com/tools/rgb/

About Recipes:

	Recipes are simple datums that contain a list of required reagents and a result.
	They also have a proc that is called when the recipe is matched.

		on_reaction(var/datum/reagents/holder, var/created_volume)
			This proc is called when the recipe is matched.
			You'll want to add explosions etc here.
			To find the location you'll have to do something
			like get_turf(holder.my_atom)

		name & id
			Should be pretty obvious.

		result
			This var contains the id of the resulting reagent.

		required_reagents
			This is a list of ids of the required reagents.
			Each id also needs an associated value that gives us the minimum required amount
			of that reagent. The handle_reaction proc can detect mutiples of the same recipes
			so for most cases you want to set the required amount to 1.

		required_catalysts
			This is a list of the ids of the required catalysts.
			Functionally similar to required_reagents, it is a list of reagents that are required
			for the reaction. However, unlike required_reagents, catalysts are NOT consumed.
			They mearly have to be present in the container.

		result_amount
			This is the amount of the resulting reagent this recipe will produce.
			I recommend you set this to the total volume of all required reagent.

		required_container
			The container the recipe has to take place in in order to happen. Leave this blank/null
			if you want the reaction to happen anywhere.

		required_other
			Basically like a reagent's data variable. You can set extra requirements for a
			reaction with this.

		required_temp
			This is the required temperature.

About the Tools:

	By default, all atom have a reagents var - but its empty. if you want to use an object for the chem.
	system you'll need to add something like this in its new proc:

		var/datum/reagents/R = new/datum/reagents(100) <<<<< create a new datum , 100 is the maximum_volume of the new holder datum.
		reagents = R <<<<< assign the new datum to the objects reagents var
		R.my_atom = src <<<<< set the holders my_atom to src so that we know where we are.

		This can also be done by calling a convenience proc:
		atom/proc/create_reagents(var/max_volume)

	Other important stuff:

		amount_per_transfer_from_this var
			This var is mostly used by beakers and bottles.
			It simply tells us how much to transfer when
			'pouring' our reagents into something else.

		atom/proc/is_open_container()
			Checks atom/var/flags & OPENCONTAINER.
			If this returns 1 , you can use syringes, beakers etc
			to manipulate the contents of this object.
			If it's 0, you'll need to write your own custom reagent
			transfer code since you will not be able to use the standard
			tools to manipulate it.

*/

/*  GOON CHEMS README:

	Credit goes to Cogwerks, and all the other goonstation coders
	for the original idea and implementation of this over at goonstation.

	THE REQUESTED DON'T PORT LIST: IF YOU PORT THESE THE GOONS WILL MURDER US IN OUR SLEEP SO PLEASE DON'T KTHX - Iamgoofball
	Any of the Secret Chems
	Goon in-joke chems (Eg. Cat Drugs, Hairgrownium)
	Liquid Electricity
	Rajajajah

*/


code:
#define SOLID 1
#define LIQUID 2
#define GAS 3

#define REM REAGENTS_EFFECT_MULTIPLIER

//Various reagents
//Toxin & acid reagents
//Hydroponics stuff

/datum/reagent
	var/name = "Reagent"
	var/id = "reagent"
	var/description = ""
	var/datum/reagents/holder = null
	var/reagent_state = LIQUID
	var/list/data
	var/current_cycle = 0
	var/volume = 0
	var/color = "#000000" // rgb: 0, 0, 0
	var/metabolization_rate = REAGENTS_METABOLISM //how fast the reagent is metabolized by the mob
	var/overrides_metab = 0
	var/overdose_threshold = 0
	var/addiction_threshold = 0
	var/addiction_stage = 0
	var/overdosed = 0 // You fucked up and this is now triggering its overdose effects, purge that shit quick.
	var/synth_cost = 1
	var/can_synth = 1

/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
	. = ..()
	holder = null

/datum/reagent/proc/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1, touch_protection = 0)
	if(!istype(M))
		return 0
	if(method == VAPOR) //smoke, foam, spray
		if(M.reagents)
			var/modifier = Clamp((1 - touch_protection), 0, 1)
			var/amount = round(reac_volume*modifier, 0.1)
			if(amount >= 0.5)
				M.reagents.add_reagent(id, amount)
	return 1

/datum/reagent/proc/reaction_obj(obj/O, volume)
	return

/datum/reagent/proc/reaction_turf(turf/T, volume)
	return

/datum/reagent/proc/on_mob_life(mob/living/M)
	current_cycle++
	holder.remove_reagent(src.id, metabolization_rate * M.metabolism_efficiency) //By default it slowly disappears.
	return

// Called when this reagent is removed while inside a mob
/datum/reagent/proc/on_mob_delete(mob/M)
	return

/datum/reagent/proc/on_move(mob/M)
	return

// Called after add_reagents creates a new reagent.
/datum/reagent/proc/on_new(data)
	return

// Called when two reagents of the same are mixing.
/datum/reagent/proc/on_merge(data)
	return

/datum/reagent/proc/on_update(atom/A)
	return

// Called every time reagent containers process.
/datum/reagent/proc/on_tick(data)
	return

// Called when the reagent container is hit by an explosion
/datum/reagent/proc/on_ex_act(severity)
	return

// Called if the reagent has passed the overdose threshold and is set to be triggering overdose effects
/datum/reagent/proc/overdose_process(mob/living/M)
	return

/datum/reagent/proc/overdose_start(mob/living/M)
	M << "You feel like you took too much of [name]!"
	return

/datum/reagent/proc/addiction_act_stage1(mob/living/M)
	if(prob(30))
		M << "You feel like some [name] right about now."
	return

/datum/reagent/proc/addiction_act_stage2(mob/living/M)
	if(prob(30))
		M << "You feel like you need [name]. You just can't get enough."
	return

/datum/reagent/proc/addiction_act_stage3(mob/living/M)
	if(prob(30))
		M << "You have an intense craving for [name]."
	return

/datum/reagent/proc/addiction_act_stage4(mob/living/M)
	if(prob(30))
		M << "You're not feeling good at all! You really need some [name]."
	return

/proc/pretty_string_from_reagent_list(var/list/reagent_list)
	//Convert reagent list to a printable string for logging etc
	var/result = "| "
	for (var/datum/reagent/R in reagent_list)
		result += "[R.name], [R.volume] | "

	return result