code:
/* Library Machines
 *
 * Contains:
 *		Borrowbook datum
 *		Library Public Computer
 *		Cachedbook datum
 *		Library Computer
 *		Library Scanner
 *		Book Binder
 */

/*
 * Library Public Computer
 */
/obj/machinery/computer/libraryconsole
	name = "library visitor console"
	icon = 'icons/obj/computer.dmi'
	icon_state = "library"
	circuit = /obj/item/weapon/circuitboard/libraryconsole
	var/screenstate = 0
	var/title
	var/category = "Any"
	var/author
	var/SQLquery

/obj/machinery/computer/libraryconsole/attack_hand(var/mob/user as mob)
	if(..())
		return
	interact(user)

/obj/machinery/computer/libraryconsole/interact(mob/user)
	user.set_machine(src)
	var/dat = "" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
	switch(screenstate)
		if(0)
			dat += "
Search Settings

"
			dat += "Filter by Title: [title]
"
			dat += "Filter by Category: [category]
"
			dat += "Filter by Author: [author]
"
			dat += "\[Start Search\]
"
		if(1)
			establish_db_connection()
			if(!dbcon.IsConnected())
				dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
"
			else if(!SQLquery)
				dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
"
			else
				dat += "
"
				dat += "
AUTHOR	TITLE	CATEGORY	SS13BN
"

				var/DBQuery/query = dbcon.NewQuery(SQLquery)
				query.Execute()

				while(query.NextRow())
					var/author = query.item[1]
					var/title = query.item[2]
					var/category = query.item[3]
					var/id = query.item[4]
					dat += "
[author]	[title]	[category]	[id]
"
				dat += "

"
			dat += "\[Go Back\]
"
	var/datum/browser/popup = new(user, "publiclibrary", name, 600, 400)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()

/obj/machinery/computer/libraryconsole/Topic(href, href_list)
	. = ..()
	if(..())
		usr << browse(null, "window=publiclibrary")
		onclose(usr, "publiclibrary")
		return

	if(href_list["settitle"])
		var/newtitle = input("Enter a title to search for:") as text|null
		if(newtitle)
			title = sanitize(newtitle)
		else
			title = null
		title = sanitizeSQL(title)
	if(href_list["setcategory"])
		var/newcategory = input("Choose a category to search for:") in list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")
		if(newcategory)
			category = sanitize(newcategory)
		else
			category = "Any"
		category = sanitizeSQL(category)
	if(href_list["setauthor"])
		var/newauthor = input("Enter an author to search for:") as text|null
		if(newauthor)
			author = sanitize(newauthor)
		else
			author = null
		author = sanitizeSQL(author)
	if(href_list["search"])
		SQLquery = "SELECT author, title, category, id FROM [format_table_name("library")] WHERE isnull(deleted) AND "
		if(category == "Any")
			SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'"
		else
			SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'"
		screenstate = 1

	if(href_list["back"])
		screenstate = 0

	src.add_fingerprint(usr)
	src.updateUsrDialog()
	return

/*
 * Borrowbook datum
 */
datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long.
	var/bookname
	var/mobname
	var/getdate
	var/duedate

/*
 * Cachedbook datum
 */
datum/cachedbook // Datum used to cache the SQL DB books locally in order to achieve a performance gain.
	var/id
	var/title
	var/author
	var/category

var/global/list/datum/cachedbook/cachedbooks // List of our cached book datums

/proc/load_library_db_to_cache()
	if(cachedbooks)
		return
	establish_db_connection()
	if(!dbcon.IsConnected())
		return
	cachedbooks = list()
	var/DBQuery/query = dbcon.NewQuery("SELECT id, author, title, category FROM [format_table_name("library")] WHERE isnull(deleted)")
	query.Execute()

	while(query.NextRow())
		var/datum/cachedbook/newbook = new()
		newbook.id = query.item[1]
		newbook.author = query.item[2]
		newbook.title = query.item[3]
		newbook.category = query.item[4]
		cachedbooks += newbook

/*
 * Library Computer
 * After 860 days, it's finally a buildable computer.
 */
// TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such
// It is August 22nd, 2012... This TODO has already been here for months.. I wonder how long it'll last before someone does something about it.
// It's December 25th, 2014, and this is STILL here, and it's STILL relevant. Kill me
/obj/machinery/computer/libraryconsole/bookmanagement
	name = "book inventory management console"
	var/arcanecheckout = 0
	screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book
	var/buffer_book
	var/buffer_mob
	var/upload_category = "Fiction"
	var/list/checkouts = list()
	var/list/inventory = list()
	var/checkoutperiod = 5 // In minutes
	var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive
	var/libcomp_menu
	var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl

/obj/machinery/computer/libraryconsole/bookmanagement/proc/build_library_menu()
	if(libcomp_menu)
		return
	load_library_db_to_cache()
	if(!cachedbooks)
		return
	libcomp_menu = ""
	for(var/datum/cachedbook/C in cachedbooks)
		libcomp_menu += "
[C.author]	[C.title]	[C.category]	\[Order\]
\n"

/obj/machinery/computer/libraryconsole/bookmanagement/New()
	..()
	if(circuit)
		circuit.name = "circuit board (Book Inventory Management Console)"
		circuit.build_path = /obj/machinery/computer/libraryconsole/bookmanagement

/obj/machinery/computer/libraryconsole/bookmanagement/interact(mob/user)
	user.set_machine(src)
	var/dat = "" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
	switch(screenstate)
		if(0)
			// Main Menu
			dat += "1. View General Inventory
"
			dat += "2. View Checked Out Inventory
"
			dat += "3. Check out a Book
"
			dat += "4. Connect to External Archive
"
			dat += "5. Upload New Title to Archive
"
			dat += "6. Print a Bible
"
			if(src.emagged)
				dat += "7. Access the Forbidden Lore Vault
"
			if(src.arcanecheckout)
				new /obj/item/weapon/tome(src.loc)
				user << "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it."
				user.visible_message("[user] stares at the blank screen for a few moments, his expression frozen in fear. When he finally awakens from it, he looks a lot older.", 2)
				src.arcanecheckout = 0
		if(1)
			// Inventory
			dat += "
Inventory

"
			for(var/obj/item/weapon/book/b in inventory)
				dat += "[b.name] (Delete)
"
			dat += "(Return to main menu)
"
		if(2)
			// Checked Out
			dat += "
Checked Out Books

"
			for(var/datum/borrowbook/b in checkouts)
				var/timetaken = world.time - b.getdate
				//timetaken *= 10
				timetaken /= 600
				timetaken = round(timetaken)
				var/timedue = b.duedate - world.time
				//timedue *= 10
				timedue /= 600
				if(timedue <= 0)
					timedue = "(OVERDUE) [timedue]"
				else
					timedue = round(timedue)
				dat += "\"[b.bookname]\", Checked out to: [b.mobname]
--- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
"
				dat += "(Check In)

"
			dat += "(Return to main menu)
"
		if(3)
			// Check Out a Book
			dat += "
Check Out a Book

"
			dat += "Book: [src.buffer_book] "
			dat += "\[Edit\]
"
			dat += "Recipient: [src.buffer_mob] "
			dat += "\[Edit\]
"
			dat += "Checkout Date : [world.time/600]
"
			dat += "Due Date: [(world.time + checkoutperiod)/600]
"
			dat += "(Checkout Period: [checkoutperiod] minutes) (+/-)"
			dat += "(Commit Entry)
"
			dat += "(Return to main menu)
"
		if(4)
			dat += "
External Archive
"
			build_library_menu()

			if(!cachedbooks)
				dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance."
			else
				dat += "(Order book by SS13BN)

"
				dat += "
"
				dat += "
AUTHOR	TITLE	CATEGORY
"
				dat += libcomp_menu
				dat += "
"
			dat += "
(Return to main menu)
"
		if(5)
			dat += "
Upload a New Title
"
			if(!scanner)
				for(var/obj/machinery/libraryscanner/S in range(9))
					scanner = S
					break
			if(!scanner)
				dat += "No scanner found within wireless network range.
"
			else if(!scanner.cache)
				dat += "No data found in scanner memory.
"
			else
				dat += "<TT>Data marked for upload...</TT>
"
				dat += "<TT>Title: </TT>[scanner.cache.name]
"
				if(!scanner.cache.author)
					scanner.cache.author = "Anonymous"
				dat += "<TT>Author: </TT>[scanner.cache.author]
"
				dat += "<TT>Category: </TT>[upload_category]
"
				dat += "\[Upload\]
"
			dat += "(Return to main menu)
"
		if(7)
			dat += "
Accessing Forbidden Lore Vault v 1.3
"
			dat += "Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.
"
			dat += "Yes.
"
			dat += "No.
"

	var/datum/browser/popup = new(user, "library", name, 600, 400)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()

/obj/machinery/computer/libraryconsole/bookmanagement/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
	if(istype(W, /obj/item/weapon/barcodescanner))
		var/obj/item/weapon/barcodescanner/scanner = W
		scanner.computer = src
		user << "[scanner]'s associated machine has been set to [src]."
		audible_message("[src] lets out a low, short blip.")
	else
		..()

/obj/machinery/computer/libraryconsole/bookmanagement/emag_act(mob/user as mob)
	if(density && !emagged)
		emagged = 1

/obj/machinery/computer/libraryconsole/bookmanagement/Topic(href, href_list)
	if(..())
		usr << browse(null, "window=library")
		onclose(usr, "library")
		return

	if(href_list["switchscreen"])
		switch(href_list["switchscreen"])
			if("0")
				screenstate = 0
			if("1")
				screenstate = 1
			if("2")
				screenstate = 2
			if("3")
				screenstate = 3
			if("4")
				screenstate = 4
			if("5")
				screenstate = 5
			if("6")
				if(!bibledelay)

					var/obj/item/weapon/storage/book/bible/B = new /obj/item/weapon/storage/book/bible(src.loc)
					if(ticker && ( ticker.Bible_icon_state && ticker.Bible_item_state) )
						B.icon_state = ticker.Bible_icon_state
						B.item_state = ticker.Bible_item_state
						B.name = ticker.Bible_name
						B.deity_name = ticker.Bible_deity_name

					bibledelay = 1
					spawn(60)
						bibledelay = 0

				else
					say("Bible printer currently unavailable, please wait a moment.")

			if("7")
				screenstate = 7
	if(href_list["arccheckout"])
		if(src.emagged)
			src.arcanecheckout = 1
		src.screenstate = 0
	if(href_list["increasetime"])
		checkoutperiod += 1
	if(href_list["decreasetime"])
		checkoutperiod -= 1
		if(checkoutperiod < 1)
			checkoutperiod = 1
	if(href_list["editbook"])
		buffer_book = copytext(sanitize(input("Enter the book's title:") as text|null),1,MAX_MESSAGE_LEN)
	if(href_list["editmob"])
		buffer_mob = copytext(sanitize(input("Enter the recipient's name:") as text|null),1,MAX_NAME_LEN)
	if(href_list["checkout"])
		var/datum/borrowbook/b = new /datum/borrowbook
		b.bookname = sanitize(buffer_book)
		b.mobname = sanitize(buffer_mob)
		b.getdate = world.time
		b.duedate = world.time + (checkoutperiod * 600)
		checkouts.Add(b)
	if(href_list["checkin"])
		var/datum/borrowbook/b = locate(href_list["checkin"])
		checkouts.Remove(b)
	if(href_list["delbook"])
		var/obj/item/weapon/book/b = locate(href_list["delbook"])
		inventory.Remove(b)
	if(href_list["setauthor"])
		var/newauthor = copytext(sanitize(input("Enter the author's name: ") as text|null),1,MAX_MESSAGE_LEN)
		if(newauthor)
			scanner.cache.author = newauthor
	if(href_list["setcategory"])
		var/newcategory = input("Choose a category: ") in list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion")
		if(newcategory)
			upload_category = newcategory
	if(href_list["upload"])
		if(scanner)
			if(scanner.cache)
				var/choice = input("Are you certain you wish to upload this title to the Archive?") in list("Confirm", "Abort")
				if(choice == "Confirm")
					establish_db_connection()
					if(!dbcon.IsConnected())
						alert("Connection to Archive has been severed. Aborting.")
					else

						var/sqltitle = sanitizeSQL(scanner.cache.name)
						var/sqlauthor = sanitizeSQL(scanner.cache.author)
						var/sqlcontent = sanitizeSQL(scanner.cache.dat)
						var/sqlcategory = sanitizeSQL(upload_category)
						var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, datetime) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[usr.ckey]', Now())")
						if(!query.Execute())
							usr << query.ErrorMsg()
						else
							log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs")
							alert("Upload Complete. Uploaded title will be unavailable for printing for a short period")

	if(href_list["targetid"])
		var/sqlid = sanitizeSQL(href_list["targetid"])
		establish_db_connection()
		if(!dbcon.IsConnected())
			alert("Connection to Archive has been severed. Aborting.")
		if(bibledelay)
			say("Printer unavailable. Please allow a short time before attempting to print.")
		else
			bibledelay = 1
			spawn(60)
				bibledelay = 0
			var/DBQuery/query = dbcon.NewQuery("SELECT * FROM [format_table_name("library")] WHERE id=[sqlid] AND isnull(deleted)")
			query.Execute()

			while(query.NextRow())
				var/author = query.item[2]
				var/title = query.item[3]
				var/content = query.item[4]
				var/obj/item/weapon/book/B = new(src.loc)
				B.name = "Book: [title]"
				B.title = title
				B.author = author
				B.dat = content
				B.icon_state = "book[rand(1,7)]"
				src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?")
				break
	if(href_list["orderbyid"])
		var/orderid = input("Enter your order:") as num|null
		if(orderid)
			if(isnum(orderid))
				var/nhref = "src=\ref[src];targetid=[orderid]"
				spawn() src.Topic(nhref, params2list(nhref), src)
	src.add_fingerprint(usr)
	src.updateUsrDialog()
	return

/obj/machinery/computer/libraryconsole/bookmanagement/say_quote(text)
	return "flashes, \"[text]\""

/*
 * Library Scanner
 */
/obj/machinery/libraryscanner
	name = "scanner control interface"
	icon = 'icons/obj/library.dmi'
	icon_state = "bigscanner"
	anchored = 1
	density = 1
	var/obj/item/weapon/book/cache		// Last scanned book

/obj/machinery/libraryscanner/attackby(var/obj/O as obj, var/mob/user as mob, params)
	if(istype(O, /obj/item/weapon/book))
		user.drop_item()
		O.loc = src

/obj/machinery/libraryscanner/attack_hand(var/mob/user as mob)
	usr.set_machine(src)
	var/dat = "" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
	if(cache)
		dat += "Data stored in memory.
"
	else
		dat += "No data stored in memory.
"
	dat += "\[Scan\]"
	if(cache)
		dat += "       \[Clear Memory\]

\[Remove Book\]"
	else
		dat += "
"
	//user << browse(dat, "window=scanner")
	//onclose(user, "scanner")
	var/datum/browser/popup = new(user, "scanner", name, 600, 400)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()

/obj/machinery/libraryscanner/Topic(href, href_list)
	if(..())
		usr << browse(null, "window=scanner")
		onclose(usr, "scanner")
		return

	if(href_list["scan"])
		for(var/obj/item/weapon/book/B in contents)
			cache = B
			break
	if(href_list["clear"])
		cache = null
	if(href_list["eject"])
		for(var/obj/item/weapon/book/B in contents)
			B.loc = src.loc
	src.add_fingerprint(usr)
	src.updateUsrDialog()
	return

/*
 * Book binder
 */
/obj/machinery/bookbinder
	name = "book binder"
	icon = 'icons/obj/library.dmi'
	icon_state = "binder"
	anchored = 1
	density = 1
	var/busy = 0

/obj/machinery/bookbinder/attackby(var/obj/O as obj, var/mob/user as mob, params)
	if(istype(O, /obj/item/weapon/paper))
		if(busy)
			user << "The book binder is busy. Please wait for completion of previous operation."
			return
		user.drop_item()
		O.loc = src
		user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].")
		src.visible_message("[src] begins to hum as it warms up its printing drums.")
		busy = 1
		sleep(rand(200,400))
		busy = 0
		src.visible_message("[src] whirs as it prints and binds a new book.")
		var/obj/item/weapon/book/b = new(src.loc)
		b.dat = O:info
		b.name = "Print Job #" + "[rand(100, 999)]"
		b.icon_state = "book[rand(1,7)]"
		qdel(O)
	else
		..()


code:
//*******************************
//
//	Library SQL Configuration
//
//*******************************

// Deprecated! See global.dm for new SQL config vars
/*
#define SQL_ADDRESS ""
#define SQL_DB ""
#define SQL_PORT "3306"
#define SQL_LOGIN ""
#define SQL_PASS ""
*/

//*******************************
// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB )

/*
   The Library
   ------------
   A place for the crew to go, relax, and enjoy a good book.
   Aspiring authors can even self publish and, if they're lucky
   convince the on-staff Librarian to submit it to the Archives
   to be chronicled in history forever - some say even persisting
   through alternate dimensions.

   Written by TLE for /tg/station 13
   Feel free to use this as you like. Some credit would be cool.
   Check us out at http://nanotrasen.com/ if you're so inclined.
*/

// CONTAINS:

// Objects:
//  - bookcase
//  - book
//  - barcode scanner
// Machinery:
//  - library computer
//  - visitor's computer
//  - book binder
//  - book scanner
// Datum:
//	- borrowbook

// Ideas for the future
// ---------------------
// 	- Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in)
//  -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents.
//	- Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game.
//  -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?).
//	   The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table.
//	- When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around.
//  - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside.
//  - Make books/book cases burn when exposed to flame.
//  - Make book binder hackable.
//  - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource.

license: mit


code:
/*
BS12 object based lighting system
*/

/*
Changes from TG DAL:
  -	Lighting is done using objects instead of subareas.
  - Animated transitions. (newer TG DAL has this)
  - Full colours with mixing.
  - Support for lights on shuttles.

  - Code:
	  - Instead of one flat luminosity var, light is represented by 3 atom vars:
		  - light_range; range in tiles of the light, used for calculating falloff,
		  - light_power; multiplier for the brightness of lights,
		  - light_color; hex string representing the RGB colour of the light.
	  - SetLuminosity() is now set_light() and takes the three variables above.
		  - Variables can be left as null to not update them.
	  - SetOpacity() is now set_opacity().
	  - Areas have luminosity set to 1 permanently, no hard-lighting.
	  - Objects inside other objects can have lights and they properly affect the turf. (flashlights)
	  - area/master and area/list/related have been eviscerated since subareas aren't needed.
*/

/*
Relevant vars/procs:

global: (uh, I placed the only one in lighting_system.dm)
  - var/list/all_lighting_overlays; Just a list of ALL of the lighting overlays.

atom: (lighting_atom.dm)
  - var/light_range; range in tiles of the light, used for calculating falloff
  - var/light_power; multiplier for the brightness of lights
  - var/light_color; hex string representing the RGB colour of the light

  - var/datum/light_source/light; light source datum for this atom, only present if light_range && light_power
  - var/list/light_sources; light sources in contents that are shining through this object, including this object

  - proc/set_light(l_range, l_power, l_color):
	  - Sets light_range/power/color to non-null args and calls update_light()
  - proc/set_opacity(new_opacity):
	  - Sets opacity to new_opacity.
	  - If opacity has changed, call turf.reconsider_lights() to fix light occlusion
  - proc/update_light():
	  - Updates the light var on this atom, deleting or creating as needed and calling .update()

turf: (lighting_turf.dm)
  - var/list/affecting_lights; list of light sources that are shining onto this turf
  - var/list/lighting_overlays; list of lighting overlays in the turf. (only used if higher resolutions
  - var/lighting_overlay; ref to the lighting overlay (only used if resolution is 1)

  - proc/reconsider_lights():
	  - Force all light sources shining onto this turf to update

  - proc/lighting_clear_overlays():
	  - Delete (manual GC) all light overlays on this turf, used when changing turf to space
  - proc/lighting_build_overlays():
	  - Create lighting overlays for this turf
  - proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
  	  - Returns a decimal according to the amount of lums on a turf's overlay (also averages them)
  	  - With default arguments (based on the fact that 0 = pitch black and 1 = full bright), it will return .5 for a 50% lit tile.

atom/movable/lighting_overlay: (lighting_overlay.dm)
  - var/lum_r, var/lum_g, var/lum_b; lumcounts of each colour
  - var/needs_update; set on update_lumcount, checked by lighting process

  - var/xoffset, var/yoffset; (only present when using sub-tile overlays) fractional offset of this overlay in the tile

  - proc/update_lumcount(delta_r, delta_g, delta_b):
      - Change the lumcount vars and queue the overlay for update
  - proc/update_overlay()
	  - Called by the lighting process to update the color of the overlay
*/


code:
/datum/light_source
	var/atom/top_atom
	var/atom/source_atom

	var/turf/source_turf
	var/light_power
	var/light_range
	var/light_color // string, decomposed by parse_light_color()

	var/lum_r
	var/lum_g
	var/lum_b

	var/list/effect_r
	var/list/effect_g
	var/list/effect_b
	var/list/effect_turf

	var/applied

	var/needs_update
	var/destroyed
	var/force_update

/datum/light_source/New(atom/owner, atom/top)
	source_atom = owner
	if(!source_atom.light_sources) source_atom.light_sources = list()
	source_atom.light_sources += src
	top_atom = top
	if(top_atom != source_atom)
		if(!top.light_sources) top.light_sources = list()
		top_atom.light_sources += src
	if(!needs_update)
		lighting_update_lights += src
		needs_update = 1

	source_turf = top_atom
	light_power = source_atom.light_power
	light_range = source_atom.light_range
	light_color = source_atom.light_color

	parse_light_color()

	effect_r = list()
	effect_g = list()
	effect_b = list()
	effect_turf = list()

	update()

	return ..()

/datum/light_source/proc/destroy()
	destroyed = 1
	force_update()
	if(source_atom && source_atom.light_sources) source_atom.light_sources -= src
	if(top_atom && top_atom.light_sources) top_atom.light_sources -= src

/datum/light_source/proc/update(atom/new_top_atom)
	if(new_top_atom && new_top_atom != top_atom)
		if(top_atom != source_atom) top_atom.light_sources -= src
		top_atom = new_top_atom
		if(top_atom != source_atom)
			if(!top_atom.light_sources) top_atom.light_sources = list()
			top_atom.light_sources += src
	if(!needs_update)
		lighting_update_lights += src
		needs_update = 1

/datum/light_source/proc/force_update()
	force_update = 1
	if(!needs_update)
		lighting_update_lights += src
		needs_update = 1

/datum/light_source/proc/check()
	if(!source_atom || !light_range || !light_power)
		destroy()
		return 1

	if(!top_atom)
		top_atom = source_atom
		. = 1

	if(needs_update)
		. = 1

	if(istype(top_atom, /turf))
		if(source_turf != top_atom)
			source_turf = top_atom
			. = 1
	else if(top_atom.loc != source_turf)
		source_turf = top_atom.loc
		. = 1

	if(source_atom.light_power != light_power)
		light_power = source_atom.light_power
		. = 1

	if(source_atom.light_range != light_range)
		light_range = source_atom.light_range
		. = 1

	if(source_atom.light_color != light_color)
		light_color = source_atom.light_color
		parse_light_color()
		. = 1

	if(light_range && light_power && !applied)
		. = 1

/datum/light_source/proc/parse_light_color()
	if(light_color)
		lum_r = GetRedPart(light_color) / 255
		lum_g = GetGreenPart(light_color) / 255
		lum_b = GetBluePart(light_color) / 255
	else
		lum_r = 1
		lum_g = 1
		lum_b = 1

/datum/light_source/proc/falloff(atom/movable/lighting_overlay/O)
  #if LIGHTING_FALLOFF == 1 // circular
   #if LIGHTING_RESOLUTION == 1
	. = (O.x - source_turf.x)**2 + (O.y - source_turf.y)**2 + LIGHTING_HEIGHT
   #else
	. = (O.x - source_turf.x + O.xoffset)**2 + (O.y - source_turf.y + O.yoffset)**2 + LIGHTING_HEIGHT
   #endif

   #if LIGHTING_LAMBERTIAN == 1
	if(!light_range || !sqrt(. + 1))
		return 1
	. = CLAMP01((1 - CLAMP01(sqrt(.) / light_range)) * (1 / (sqrt(. + 1))))
   #else
	. = 1 - CLAMP01(sqrt(.) / light_range)
   #endif

  #elif LIGHTING_FALLOFF == 2 // square
   #if LIGHTING_RESOLUTION == 1
	. = abs(O.x - source_turf.x) + abs(O.y - source_turf.y) + LIGHTING_HEIGHT
   #else
	. = abs(O.x - source_turf.x + O.xoffset) + abs(O.y - source_turf.y + O.yoffset) + LIGHTING_HEIGHT
   #endif

   #if LIGHTING_LAMBERTIAN == 1
	. = CLAMP01((1 - CLAMP01(. / light_range)) * (1 / (sqrt(.)**2 + )))
   #else
	. = 1 - CLAMP01(. / light_range)
   #endif
  #endif

/datum/light_source/proc/apply_lum()
	applied = 1
	if(istype(source_turf))
		#if LIGHTING_RESOLUTION == 1
		for(var/turf/T in dview(light_range, source_turf, INVISIBILITY_LIGHTING))
			if(T.dynamic_lighting && T.lighting_overlay)
				var/strength = light_power * falloff(T.lighting_overlay)

				effect_r[T.lighting_overlay] = lum_r * strength
				effect_g[T.lighting_overlay] = lum_g * strength
				effect_b[T.lighting_overlay] = lum_b * strength

				T.lighting_overlay.update_lumcount(lum_r * strength, lum_g * strength, lum_b * strength)

			if(!T.affecting_lights)
				T.affecting_lights = list()

			T.affecting_lights += src
			effect_turf += T

		#else
		for(var/turf/T in dview(light_range, source_turf, INVISIBILITY_LIGHTING))
			for(var/atom/movable/lighting_overlay/L in T.lighting_overlays)
				var/strength = light_power * falloff(L)

				effect_r[L] = lum_r * strength
				effect_g[L] = lum_g * strength
				effect_b[L] = lum_b * strength

				L.update_lumcount(lum_r * strength, lum_g * strength, lum_b * strength)

			if(!T.affecting_lights)
				T.affecting_lights = list()

			T.affecting_lights += src
			effect_turf += T
		#endif

/datum/light_source/proc/remove_lum()
	applied = 0

	for(var/turf/T in effect_turf)
		if(T.affecting_lights)
			T.affecting_lights -= src

		#if LIGHTING_RESOLUTION == 1
		if(T.dynamic_lighting && T.lighting_overlay)
			T.lighting_overlay.update_lumcount(-effect_r[T.lighting_overlay], -effect_g[T.lighting_overlay], -effect_b[T.lighting_overlay])
		#else
		for(var/atom/movable/lighting_overlay/L in T.lighting_overlays)
			L.lighting_overlay.update_lumcount(-effect_r[L], -effect_g[L], -effect_b[L])
		#endif

	effect_r.Cut()
	effect_g.Cut()
	effect_b.Cut()
	effect_turf.Cut()


code:
/atom
	var/light_power = 1 // intensity of the light
	var/light_range = 0 // range in tiles of the light
	var/light_color		// RGB string representing the colour of the light

	var/datum/light_source/light
	var/list/light_sources

/atom/proc/set_light(l_range, l_power, l_color)
	if(l_power != null) light_power = l_power
	if(l_range != null) light_range = l_range
	if(l_color != null) light_color = l_color

	update_light()

/atom/proc/update_light()
	if(!light_power || !light_range)
		if(light)
			light.destroy()
			light = null
	else
		if(!istype(loc, /atom/movable))
			. = src
		else
			. = loc

		if(light)
			light.update(.)
		else
			light = new /datum/light_source(src, .)

/atom/proc/update_all_lights()
	if(src.light_sources)
		for(var/datum/light_source/L in src.light_sources)
			L.update(src)

/atom/New()
	. = ..()
	if(light_power && light_range)
		update_light()

/atom/Destroy()
	if(light)
		light.destroy()
		light = null
	return ..()

/atom/movable/Destroy()
	var/turf/T = loc
	if(opacity && istype(T))
		T.reconsider_lights()
	return ..()

/atom/proc/set_opacity(new_opacity)
	var/old_opacity = opacity
	opacity = new_opacity
	var/turf/T = loc
	if(old_opacity != new_opacity && istype(T))
		T.reconsider_lights()

/obj/item/equipped()
	. = ..()
	update_light(src.loc)

/obj/item/pickup()
	. = ..()
	update_light(src.loc)

/obj/item/dropped()
	. = ..()
	update_light(src.loc)

/atom/Entered(atom/movable/Obj, atom/OldLoc) //Implemented here because forceMove() doesn't call Move()
	if(Obj && OldLoc != src)
		Obj.update_all_lights()
	..()


code:
/atom/movable/lighting_overlay
	name = ""
	mouse_opacity = 0
	anchored = 1

	icon = LIGHTING_ICON
	layer = LIGHTING_LAYER
	invisibility = INVISIBILITY_LIGHTING
	blend_mode = BLEND_MULTIPLY
	color = "#000000"

	var/lum_r
	var/lum_g
	var/lum_b

	#if LIGHTING_RESOLUTION != 1
	var/xoffset
	var/yoffset
	#endif

	var/needs_update

/atom/movable/lighting_overlay/New()
	. = ..()
	verbs.Cut()

/atom/movable/lighting_overlay/Destroy()
	return QDEL_HINT_PUTINPOOL

/atom/movable/lighting_overlay/proc/update_lumcount(delta_r, delta_g, delta_b)
	if(!delta_r && !delta_g && !delta_b) //Nothing is being changed all together.
		return

	var/should_update = 0
	if(!needs_update) //If this isn't true, we're already updating anyways.
		if(max(lum_r, lum_g, lum_b) < 1) //Any change that could happen WILL change appearance.
			should_update = 1

		else if(max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b) < 1) //The change would bring us under 1 max lum, again, guaranteed to change appearance.
			should_update = 1

		else //We need to make sure that the colour ratios won't change in this code block.
			var/mx1 = max(lum_r, lum_g, lum_b)
			var/mx2 = max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b)

			if(lum_r / mx1 != (lum_r + delta_r) / mx2 || lum_g / mx1 != (lum_g + delta_g) / mx2 || lum_b / mx1 != (lum_b + delta_b) / mx2) //Stuff would change.
				should_update = 1

	lum_r += delta_r
	lum_g += delta_g
	lum_b += delta_b

	if(!needs_update && should_update)
		needs_update = 1
		lighting_update_overlays += src

/atom/movable/lighting_overlay/proc/update_overlay()
	var/mx = max(lum_r, lum_g, lum_b)
	. = 1 // factor
	if(mx > 1)
		. = 1/mx
	#if LIGHTING_TRANSITIONS == 1
	animate(src,
		color = rgb(lum_r * 255 * ., lum_g * 255 * ., lum_b * 255 * .),
		LIGHTING_TRANSITION_SPEED
	)
	#else
	color = rgb(lum_r * 255 * ., lum_g * 255 * ., lum_b * 255 * .)
	#endif

	var/turf/T = loc

	if(istype(T)) //Incase we're not on a turf, pool ourselves, something happened.
		if(color != "#000000")
			T.luminosity = 1
		else  //No light, set the turf's luminosity to 0 to remove it from view()
			#if LIGHTING_TRANSITIONS == 1
			spawn(LIGHTING_TRANSITION_SPEED)
				T.luminosity = 0
			#else
			T.luminosity = 0
			#endif

		universe.OnTurfTick(T)
	else
		warning("A lighting overlay realised it's loc was NOT a turf (actual loc: [loc], [loc.type]) in update_overlay() and got pooled!")
		qdel(src)

/atom/movable/lighting_overlay/ResetVars()
//	testing("Lighting_overlays: resetvars called")
	loc = null

	lum_r = 0
	lum_g = 0
	lum_b = 0

	color = "#000000"

	#if LIGHTING_RESOLUTION != 1
	xoffset = null
	yoffset = null
	#endif

	needs_update = null

/atom/movable/lighting_overlay/Destroy()
	all_lighting_overlays -= src
	lighting_update_overlays -= src

	var/turf/T = loc
	if(istype(T))
		#if LIGHTING_RESOLUTION == 1
		T.lighting_overlay = null
		#else
		T.lighting_overlays -= src
		#endif
		for(var/datum/light_source/D in T.affecting_lights) //Remove references to us on the light sources affecting us.
			D.effect_r -= src
			D.effect_g -= src
			D.effect_b -= src

/atom/movable/lighting_overlay/SinguloCanEat()
	return 0
/atom/movable/lighting_overlay/singularity_act()
	return 0

/atom/movable/lighting_overlay/narsie_act()
	return 0

/atom/movable/lighting_overlay/singularity_pull()
	return 0
/atom/movable/lighting_overlay/ex_act(severity)
	return 0


code:
/var/list/lighting_update_lights = list()
/var/list/lighting_update_overlays = list()
/var/list/all_lighting_overlays = list()

/area/var/lighting_use_dynamic = 1

// duplicates lots of code, but this proc needs to be as fast as possible.
/proc/create_lighting_overlays(zlevel = 0)
	var/state = "light[LIGHTING_RESOLUTION]"
	var/area/A
	if(zlevel == 0) // populate all zlevels
		for(var/turf/T in turfs)
			if(T.dynamic_lighting)
				A = T.loc
				if(A.lighting_use_dynamic)
					#if LIGHTING_RESOLUTION == 1
					var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
					O.icon_state = state
					all_lighting_overlays |= O
					T.lighting_overlay = O
					#else
					for(var/i = 0; i < LIGHTING_RESOLUTION; i++)
						for(var/j = 0; j < LIGHTING_RESOLUTION; j++)
							var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
							O.pixel_x = i * (32 / LIGHTING_RESOLUTION)
							O.pixel_y = j * (32 / LIGHTING_RESOLUTION)
							O.xoffset = (((2*i + 1) / (LIGHTING_RESOLUTION * 2)) - 0.5)
							O.yoffset = (((2*j + 1) / (LIGHTING_RESOLUTION * 2)) - 0.5)
							O.icon_state = state
							all_lighting_overlays |= O
							T.lighting_overlays |= O
					#endif
	else
		for(var/x = 1; x <= world.maxx; x++)
			for(var/y = 1; y <= world.maxy; y++)
				var/turf/T = locate(x, y, zlevel)
				if(T.dynamic_lighting)
					A = T.loc
					if(A.lighting_use_dynamic)
						#if LIGHTING_RESOLUTION == 1
						var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
						O.icon_state = state
						all_lighting_overlays |= O
						T.lighting_overlay = O
						#else
						for(var/i = 0; i < LIGHTING_RESOLUTION; i++)
							for(var/j = 0; j < LIGHTING_RESOLUTION; j++)
								var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
								O.pixel_x = i * (32 / LIGHTING_RESOLUTION)
								O.pixel_y = j * (32 / LIGHTING_RESOLUTION)
								O.xoffset = (((2*i + 1) / (LIGHTING_RESOLUTION * 2)) - 0.5)
								O.yoffset = (((2*j + 1) / (LIGHTING_RESOLUTION * 2)) - 0.5)
								O.icon_state = state
								all_lighting_overlays |= O
								T.lighting_overlays |= O
						#endif


code:
/turf
	var/list/affecting_lights
	#if LIGHTING_RESOLUTION == 1
	var/atom/movable/lighting_overlay/lighting_overlay
	#else
	var/list/lighting_overlays[LIGHTING_RESOLUTION ** 2]
	#endif

/turf/proc/reconsider_lights()
	for(var/datum/light_source/L in affecting_lights)
		L.force_update()

/turf/proc/lighting_clear_overlays()
//	testing("Clearing lighting overlays on \the [src]")
	#if LIGHTING_RESOLUTION == 1
	if(lighting_overlay)
		qdel(lighting_overlay)
	#else
	for(var/atom/movable/lighting_overlay/L in lighting_overlays)
		qdel(L)
	#endif

/turf/proc/lighting_build_overlays()
	#if LIGHTING_RESOLUTION == 1
	if(lighting_overlay)
	#else
	if(lighting_overlays.len)
	#endif
		return

	var/state = "light[LIGHTING_RESOLUTION]"
	var/area/A = loc
	if(A.lighting_use_dynamic)
		#if LIGHTING_RESOLUTION == 1
		var/atom/movable/lighting_overlay/O = new(src)
		O.icon_state = state
		lighting_overlay = O
		all_lighting_overlays |= O
		#else
		for(var/i = 0; i < LIGHTING_RESOLUTION; i++)
			for(var/j = 0; j < LIGHTING_RESOLUTION; j++)
				var/atom/movable/lighting_overlay/O = new(src)
				O.pixel_x = i * (32 / LIGHTING_RESOLUTION)
				O.pixel_y = j * (32 / LIGHTING_RESOLUTION)
				O.xoffset = (((2*i + 1) / (LIGHTING_RESOLUTION * 2)) - 0.5)
				O.yoffset = (((2*j + 1) / (LIGHTING_RESOLUTION * 2)) - 0.5)
				O.icon_state = state
				lighting_overlays |= O
				all_lighting_overlays |= O
		#endif

/turf/proc/lighting_refresh_overlays()
	lighting_clear_overlays()
	lighting_build_overlays()

/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
	if(!dynamic_lighting) //We're not dynamic, whatever, return 50% lighting.
		return 0.5

	var/totallums = 0
	#if LIGHTING_RESOLUTION == 1

	if(!lighting_overlay) //U WOT
		return

	totallums = (lighting_overlay.lum_r + lighting_overlay.lum_b + lighting_overlay.lum_g) / 3

	#else

	if(!lighting_overlays.len) //Ya wot.
		return

	for(var/atom/movable/lighting_overlay/L in lighting_overlays)
		if(!L) //u fukken wot m8
			return
		totallums += (L.lum_r + L.lum_g + L.lum_b) / 3

	totallums /= lighting_overlays.len //Get the average, used for higher resolutions of lighting.

	#endif

	totallums = (totallums - minlum) / (maxlum - minlum)

	return Clamp(totallums, 0, 1)

//Proc I made to dick around with update lumcount
/turf/proc/update_lumcount(delta_r, delta_g, delta_b)
	#if LIGHTING_RESOLUTION == 1
	lighting_overlay.update_lumcount(delta_r, delta_g, delta_b)
	#else
	for(var/atom/movable/lighting_overlay/L in lighting_overlays)
		L.update_lumcount(delta_r, delta_g, delta_b)
	#endif

//Used for shuttles
/turf/proc/update_overlay()
	#if LIGHTING_RESOLUTION == 1
	if(lighting_overlay)
		lighting_overlay.update_overlay()
	#else
	for(var/atom/movable/lighting_overlay/L in lighting_overlays)
		L.update_overlay()
	#endif

/turf/Entered(atom/movable/Obj, atom/OldLoc)
	if(Obj && Obj.opacity)
		reconsider_lights()
	if(Obj && OldLoc != src)
		Obj.update_all_lights()
	..()

/turf/Exited(atom/movable/Obj, atom/newloc)
	..()
	if(Obj && Obj.opacity)
		reconsider_lights()


code:
#undef LIGHTING_INTERVAL

#undef LIGHTING_FALLOFF
#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
#undef LIGHTING_TRANSITIONS

#undef LIGHTING_RESOLUTION
//#undef LIGHTING_LAYER
#undef LIGHTING_ICON


code:
/obj/item/clothing/suit/armor/makeshift
	name = "makeshift armor"
	desc = "A hazard vest with metal plate taped on it. It offers some protection, but not as much as the real deal."
	icon_state = "metalarmor"
	item_state = "metalarmor"
	w_class = 3
	blood_overlay_type = "armor"
	armor = list(melee = 30, bullet = 10, laser = 0, energy = 0, bomb = 5, bio = 0, rad = 0)

/obj/item/clothing/suit/hazardvest/attackby(obj/item/W as obj, mob/user as mob, params)
	..()
	if(istype(W, /obj/item/stack/sheet/metal))
		var/obj/item/stack/sheet/metal/M = W
		if(M.use(6))
			var/obj/item/clothing/suit/armor/makeshift/new_item = new(user.loc)
			user << "You use [W] to turn [src] into [new_item]."
			qdel(src)
			user.put_in_hands(new_item)
		return


code:
/obj/item/weapon/shield/riot/trayshield
	name = "tray shield"
	desc = "A makeshift shield that won't last for long."
	icon = 'icons/obj/weapons.dmi'
	icon_state = "trayshield"
	slot_flags = null //SLOT_BACK
	force = 5
	throwforce = 5
	throw_speed = 2
	throw_range = 3
	w_class = 4
	origin_tech = "materials=2"
	attack_verb = list("shoved", "bashed")

/obj/item/weapon/shield/riot/trayshield/IsShield()
	if(prob(30))
		if(ishuman(loc))
			var/mob/living/carbon/human/H = loc
			visible_message("[H]'s shield breaks!", "Your shield breaks!")
			H.unEquip(src, 1)
		qdel(src)
		return 0
	else
		return 1

/obj/item/weapon/storage/bag/tray/attackby(obj/item/W as obj, mob/user as mob, params)
	..()
	if(istype(W, /obj/item/stack/cable_coil))
		var/obj/item/stack/cable_coil/M = W
		if(M.use(15))
			var/obj/item/weapon/shield/riot/trayshield/new_item = new(user.loc)
			user << "You use [W] to turn [src] into [new_item]."
			qdel(src)
			user.put_in_hands(new_item)
		return


code:
//BAIT: Stops a mining creature from following you, distracting it if it hits
/obj/item/weapon/mining_bait
	name = "chunk of rotten meat"
	desc ="A chunk of raw meat used to distract some hostile creatures to allow for an escape. Only works on goliaths and basilisks. It's possible that this is a piece of an unfortunate miner's flesh."
	icon = 'icons/obj/mining.dmi'
	icon_state = "bug_bait"
	w_class = 2
	throw_range = 9
	throw_speed = 5

/obj/item/weapon/mining_bait/throw_impact(atom/hitAtom)
	..()
	src.visible_message("[src] breaks apart into chunks of meat!")
	if(ismob(hitAtom))
		var/mob/living/simple_animal/hostile/asteroid/M = hitAtom
		if(!istype(M) || !M)
			return
		if(istype(M, /mob/living/simple_animal/hostile/asteroid/hivelord) || istype(M, /mob/living/simple_animal/hostile/asteroid/goldgrub))
			M.visible_message("[M] seems uninterested.", \
					  		  "Your attention is drawn to something for a moment, but it fades...")
		else
			M.visible_message("[M] stops and begins chomping at the remains of [src].", \
					  "Free food? Truth be told, you're pretty hungry. Can't let this go to waste...")
			M.LoseAggro()
			M.notransform = 1
			qdel(src)
			sleep(100) //10 seconds
			M.visible_message("[M] finishes eating the meat scraps.", \
					  		  "That wasn't very filling. Now what were you doing again?")
			M.notransform = 0
	if(src)
		qdel(src)


code:
//CAPTURE SPHERE: Captures a monster for later use. Gotta catch 'em all.
/obj/item/device/capture_sphere
	name = "capture sphere"
	desc = "An odd device that can be used to entrap a creature for later release."
	w_class = 2
	icon = 'icons/obj/mining.dmi'
	icon_state = "capture_sphere"
	item_state = "electronic"
	throw_speed = 3
	throw_range = 7
	slot_flags = SLOT_BELT
	var/mob/living/simple_animal/capturedMob = null
	var/capturing = 0
	var/newlyCaught = 0

/obj/item/device/capture_sphere/attack_hand(mob/user)
	if(capturing)
		return 0
	if(newlyCaught)
		capturedMob.name = stripped_input(usr, "Give a name to the newly-caught monster?", "Monster Capture", "")
		newlyCaught = 0
		name = "[initial(name)] ([capturedMob.name])"
	..()

/obj/item/device/capture_sphere/attack_self(mob/user)
	if(capturedMob && capturedMob.loc != src)
		if(!in_range(user, capturedMob))
			return
		capturedMob.visible_message("[capturedMob] is sucked into [src]!")
		capturedMob.loc = src
		user.say("[pick("That's enough", "Come back", "Retreat")], [capturedMob.name]!")
		capturedMob.revive() //todo: replace
	..()

/obj/item/device/capture_sphere/throw_impact(atom/hitAtom)
	..()
	if(!capturedMob)
		if(ismob(hitAtom))
			Capture(hitAtom)
		return
	if(capturedMob)
		Release(usr)

/obj/item/device/capture_sphere/proc/Capture(var/mob/living/simple_animal/M)
	if(M.client || M.key || !istype(M) || !M || capturedMob || capturing)
		return 0
	capturing = 1
	src.visible_message("[M] is sucked into [src]!")
	anchored = 1
	M.loc = src
	for(var/i = 0; i < 3; i++)
		sleep(10)
		src.visible_message("[src] [pick("jiggles", "wiggles", "spins", "rolls", "bounces")]...")
		playsound(get_turf(src), 'sound/effects/stealthoff.ogg', 50, 1, 5)
		if(prob(M.health))
			playsound(get_turf(src), 'sound/effects/bang.ogg', 50, 1, 5)
			src.visible_message("[M] broke free!")
			M.loc = get_turf(src)
			anchored = 0
			capturing = 0
			return 0
	capturedMob = M
	src.visible_message("[M] was caught!")
	capturing = 0
	anchored = 0
	newlyCaught = 1
	capturedMob.faction = list("neutral")
	name = "[initial(name)] ([capturedMob.name])"
	return

/obj/item/device/capture_sphere/proc/Release(var/mob/user)
	if(!capturedMob)
		return
	if(capturedMob && capturedMob.loc != src)
		return
	user.say("[pick("Go", "Get 'em")], [capturedMob.name]!")
	capturedMob.loc = get_turf(src)
	capturedMob.say("[capturedMob.name]!")

/*
HELP ME EI- ZAERS

//known issues
//captured mobs hp resets when released
//mobs previously captured can been stolen by other trainers, every ball is a snag ball
//captured mobs do not stay in the ball upon being captured and instead pop out immediately
//captured mobs are neutral, and will not attack anyone unless provoked

		if(malfunctioning)
			var/mob/living/simple_animal/hostile/H = M
			H.faction |= list("capturesphere", "\ref[user]") // needs var/mob/user defined
			H.robust_searching = 1
			H.friends += user
			H.attack_same = 1
			log_game("[user] has captured hostile mob [M] with a malfunctioning capture sphere!")
			return

/obj/item/device/capture_sphere/proc/Healing
	health ++ 10
	spawn(50)

/obj/item/device/capture_sphere/emp_act()
    if(!malfunctioning)
        malfunctioning = 1

/obj/item/device/capture_sphere/examine(mob/user)
    ..()
    if(malfunctioning)
        user << "The display on [src] seems to be flickering."
*/


code:
/**********************Ore Redemption Unit**************************/
//Turns all the various mining machines into a single unit to speed up mining and establish a point system

/obj/machinery/mineral/ore_redemption
	name = "ore redemption machine"
	desc = "A machine that accepts ore and instantly transforms it into workable material sheets. Points for ore are generated based on type and can be redeemed at a mining equipment vendor."
	icon = 'icons/obj/machines/mining_machines.dmi'
	icon_state = "ore_redemption"
	density = 1
	anchored = 1.0
	input_dir = NORTH
	output_dir = SOUTH
	req_access = list(access_mineral_storeroom)
	var/stk_types = list()
	var/stk_amt   = list()
	var/stack_list[0] //Key: Type.  Value: Instance of type.
	var/obj/item/weapon/card/id/inserted_id
	var/points = 0
	var/ore_pickup_rate = 15
	var/sheet_per_ore = 1
	var/point_upgrade = 1
	var/list/ore_values = list(("sand" = 1), ("iron" = 1), ("gold" = 20), ("silver" = 20), ("uranium" = 20), ("bananium" = 30), ("diamond" = 40), ("plasma" = 40))

/obj/machinery/mineral/ore_redemption/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/ore_redemption(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/micro_laser(null)
	component_parts += new /obj/item/device/assembly/igniter(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	RefreshParts()

/obj/machinery/mineral/ore_redemption/RefreshParts()
	var/ore_pickup_rate_temp = 15
	var/point_upgrade_temp = 1
	var/sheet_per_ore_temp = 1
	for(var/obj/item/weapon/stock_parts/matter_bin/B in component_parts)
		sheet_per_ore_temp = B.rating
	for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
		ore_pickup_rate_temp = 15 * M.rating
	for(var/obj/item/weapon/stock_parts/micro_laser/L in component_parts)
		point_upgrade_temp = L.rating
	ore_pickup_rate = ore_pickup_rate_temp
	point_upgrade = point_upgrade_temp
	sheet_per_ore = sheet_per_ore_temp

/obj/machinery/mineral/ore_redemption/proc/process_sheet(obj/item/weapon/ore/O)
	var/obj/item/stack/sheet/processed_sheet = SmeltMineral(O)
	if(processed_sheet)
		if(!(processed_sheet in stack_list)) //It's the first of this sheet added
			var/obj/item/stack/sheet/s = new processed_sheet(src,0)
			s.amount = 0
			stack_list[processed_sheet] = s
			if(s.name != "glass" && s.name != "metal")		//we can get these from cargo anyway
				var/msg = "[capitalize(s.name)] sheets are now available in the Cargo Bay."
				for(var/obj/machinery/requests_console/D in allConsoles)
					if(D.department == "Science" || D.department == "Robotics" || D.department == "Research Director's Desk" || (D.department == "Chemistry" && (s.name == "uranium" || s.name == "solid plasma")))
						D.createmessage("Ore Redemption Machine", "New minerals available!", msg, 1, 0)
		var/obj/item/stack/sheet/storage = stack_list[processed_sheet]
		storage.amount += sheet_per_ore //Stack the sheets
		O.loc = null //Let the old sheet...
		qdel(O) //... garbage collect

/obj/machinery/mineral/ore_redemption/process()
	if(!panel_open) //If the machine is partially dissassembled, it should not process minerals
		var/turf/T = get_turf(get_step(src, input_dir))
		var/i
		if(T)
			if(locate(/obj/item/weapon/ore) in T)
				for (i = 0; i < ore_pickup_rate; i++)
					var/obj/item/weapon/ore/O = locate() in T
					if(O)
						process_sheet(O)
					else
						break
			else
				var/obj/structure/ore_box/B = locate() in T
				if(B)
					for (i = 0; i < ore_pickup_rate; i++)
						var/obj/item/weapon/ore/O = locate() in B.contents
						if(O)
							process_sheet(O)
						else
							break

/obj/machinery/mineral/ore_redemption/attackby(var/obj/item/weapon/W, var/mob/user, params)
	if(istype(W,/obj/item/weapon/card/id))
		var/obj/item/weapon/card/id/I = usr.get_active_hand()
		if(istype(I) && !istype(inserted_id))
			usr.drop_item()
			I.loc = src
			inserted_id = I
			interact(user)
		return
	if(exchange_parts(user, W))
		return

	if(default_pry_open(W))
		return

	if(default_unfasten_wrench(user, W))
		return
	if(default_deconstruction_screwdriver(user, "ore_redemption-open", "ore_redemption", W))
		updateUsrDialog()
		return
	if(panel_open)
		if(istype(W, /obj/item/weapon/crowbar))
			empty_content()
			default_deconstruction_crowbar(W)
		return 1
	..()

/obj/machinery/mineral/ore_redemption/proc/SmeltMineral(var/obj/item/weapon/ore/O)
	if(O.refined_type)
		var/obj/item/stack/sheet/M = O.refined_type
		points += O.points * point_upgrade
		return M
	qdel(O)//No refined type? Purge it.
	return

/obj/machinery/mineral/ore_redemption/attack_hand(user as mob)
	if(..())
		return
	interact(user)

/obj/machinery/mineral/ore_redemption/interact(mob/user)
	var/obj/item/stack/sheet/s
	var/dat

	dat += text("This machine only accepts ore. Gibtonite and Slag are not accepted.

")
	dat += text("Current unclaimed points: [points]
")

	if(istype(inserted_id))
		dat += text("You have [inserted_id.mining_points] mining points collected. Eject ID.
")
		dat += text("Claim points.
")
	else
		dat += text("No ID inserted.  Insert ID.
")

	for(var/O in stack_list)
		s = stack_list[O]
		if(s.amount > 0)
			if(O == stack_list[1])
				dat += "
"		//just looks nicer
			dat += text("[capitalize(s.name)]: [s.amount] Release
")

	if((/obj/item/stack/sheet/metal in stack_list) && (/obj/item/stack/sheet/mineral/plasma in stack_list))
		var/obj/item/stack/sheet/metalstack = stack_list[/obj/item/stack/sheet/metal]
		var/obj/item/stack/sheet/plasmastack = stack_list[/obj/item/stack/sheet/mineral/plasma]
		if(min(metalstack.amount, plasmastack.amount))
			dat += text("Plasteel Alloy (Metal + Plasma): Smelt
")

	dat += text("

Mineral Value List:
[get_ore_values()]
")

	var/datum/browser/popup = new(user, "console_stacking_machine", "Ore Redemption Machine", 400, 500)
	popup.set_content(dat)
	popup.open()
	return

/obj/machinery/mineral/ore_redemption/proc/get_ore_values()
	var/dat = "
"
	for(var/ore in ore_values)
		var/value = ore_values[ore]
		dat += "
[capitalize(ore)]	[value]
"
	dat += "
"
	return dat

/obj/machinery/mineral/ore_redemption/Topic(href, href_list)
	if(..())
		return
	if(href_list["choice"])
		if(istype(inserted_id))
			if(href_list["choice"] == "eject")
				inserted_id.loc = loc
				inserted_id.verb_pickup()
				inserted_id = null
			if(href_list["choice"] == "claim")
				if(access_mining_station in inserted_id.access)
					inserted_id.mining_points += points
					points = 0
				else
					usr << "Required access not found."
		else if(href_list["choice"] == "insert")
			var/obj/item/weapon/card/id/I = usr.get_active_hand()
			if(istype(I))
				usr.drop_item()
				I.loc = src
				inserted_id = I
			else usr << "No valid ID."
	if(href_list["release"])
		if(check_access(inserted_id) || usr.has_unlimited_silicon_privilege)
			if(!(text2path(href_list["release"]) in stack_list)) return
			var/obj/item/stack/sheet/inp = stack_list[text2path(href_list["release"])]
			var/obj/item/stack/sheet/out = new inp.type()
			var/desired = input("How much?", "How much to eject?", 1) as num
			out.amount = min(desired,50,inp.amount)
			if(out.amount >= 1)
				inp.amount -= out.amount
				unload_mineral(out)
		else
			usr << "Required access not found."
	if(href_list["plasteel"])
		if(check_access(inserted_id) || usr.has_unlimited_silicon_privilege)
			if(!(/obj/item/stack/sheet/metal in stack_list)) return
			if(!(/obj/item/stack/sheet/mineral/plasma in stack_list)) return
			var/obj/item/stack/sheet/metalstack = stack_list[/obj/item/stack/sheet/metal]
			var/obj/item/stack/sheet/plasmastack = stack_list[/obj/item/stack/sheet/mineral/plasma]

			var/desired = input("How much?", "How much would you like to smelt?", 1) as num
			var/obj/item/stack/sheet/plasteel/plasteelout = new
			plasteelout.amount = min(desired,50,metalstack.amount,plasmastack.amount)
			if(plasteelout.amount >= 1)
				metalstack.amount -= plasteelout.amount
				plasmastack.amount -= plasteelout.amount
				unload_mineral(plasteelout)
		else
			usr << "Required access not found."
	updateUsrDialog()
	return

/obj/machinery/mineral/ore_redemption/ex_act(severity, target)
	var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
	s.set_up(5, 1, src)
	s.start()
	if(severity == 1)
		if(prob(50))
			empty_content()
			qdel(src)
	else if(severity == 2)
		if(prob(25))
			empty_content()
			qdel(src)

//empty the redemption machine by stacks of at most max_amount (50 at this time) size
/obj/machinery/mineral/ore_redemption/proc/empty_content()
	var/obj/item/stack/sheet/s

	for(var/O in stack_list)
		s = stack_list[O]
		while(s.amount > s.max_amount)
			new s.type(loc,s.max_amount)
			s.use(s.max_amount)
		s.loc = loc
		s.layer = initial(s.layer)

/**********************Mining Equipment Vendor**************************/

/obj/machinery/mineral/equipment_vendor
	name = "mining equipment vendor"
	desc = "An equipment vendor for miners, points collected at an ore redemption machine can be spent here."
	icon = 'icons/obj/machines/mining_machines.dmi'
	icon_state = "mining"
	density = 1
	anchored = 1.0
	var/obj/item/weapon/card/id/inserted_id
	var/list/prize_list = list(
		new /datum/data/mining_equipment("Stimpack",			/obj/item/weapon/reagent_containers/hypospray/medipen/stimpack,	    50),
		new /datum/data/mining_equipment("Stimpack Bundle",		/obj/item/weapon/storage/box/medipens/utility,	 				   200),
		new /datum/data/mining_equipment("Whiskey",             /obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey,    100),
		new /datum/data/mining_equipment("Cigar",               /obj/item/clothing/mask/cigarette/cigar/havana,                    150),
		new /datum/data/mining_equipment("Soap",                /obj/item/weapon/soap/nanotrasen, 						           200),
		new /datum/data/mining_equipment("Mining Charge", 		/obj/item/device/mining_charge,									    75),
		new /datum/data/mining_equipment("Mining Charge Kit",	/obj/item/weapon/storage/box/mining_charges,						   300),
		new /datum/data/mining_equipment("Monster Bait",		/obj/item/weapon/mining_bait,									   100),
		new /datum/data/mining_equipment("Jaunter",             /obj/item/device/wormhole_jaunter,                                 250),
		new /datum/data/mining_equipment("Laser Pointer",       /obj/item/device/laser_pointer, 				                   300),
		new /datum/data/mining_equipment("Alien Toy",           /obj/item/clothing/mask/facehugger/toy, 		                   300),
		new /datum/data/mining_equipment("Advanced Scanner",	/obj/item/device/t_scanner/adv_mining_scanner,                     400),
		new /datum/data/mining_equipment("Mining Drone",        /mob/living/simple_animal/hostile/mining_drone,                    500),
		new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/weapon/gun/energy/kinetic_accelerator,               	   750),
		new /datum/data/mining_equipment("Resonator",           /obj/item/weapon/resonator,                                    	   800),
		new /datum/data/mining_equipment("Lazarus Injector",    /obj/item/weapon/lazarus_injector,                                1000),
		new /datum/data/mining_equipment("Hivelord Fridge",		/obj/item/device/hivelord_fridge,								  1100),
		new /datum/data/mining_equipment("Diamond Pickaxe",		/obj/item/weapon/pickaxe/diamond,				                  1200),
		new /datum/data/mining_equipment("Jetpack",             /obj/item/weapon/tank/jetpack/carbondioxide/mining,               1500),
		new /datum/data/mining_equipment("Space Cash",    		/obj/item/weapon/spacecash/c1000,                    			  2000),
//		new /datum/data/mining_equipment("Capture Sphere", 		/obj/item/device/capture_sphere,									   500),
		new /datum/data/mining_equipment("Point Transfer Card", /obj/item/weapon/card/mining_point_card,               			   500),
		)

/datum/data/mining_equipment/
	var/equipment_name = "generic"
	var/equipment_path = null
	var/cost = 0

/datum/data/mining_equipment/New(name, path, cost)
	src.equipment_name = name
	src.equipment_path = path
	src.cost = cost

/obj/machinery/mineral/equipment_vendor/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/mining_equipment_vendor(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/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	RefreshParts()

/obj/machinery/mineral/equipment_vendor/attack_hand(user as mob)
	if(..())
		return
	interact(user)

/obj/machinery/mineral/equipment_vendor/interact(mob/user)
	var/dat
	dat +="
"
	if(istype(inserted_id))
		dat += "You have [inserted_id.mining_points] mining points collected. Eject ID.
"
	else
		dat += "No ID inserted.  Insert ID.
"
	dat += "
"
	dat += "
Equipment point cost list:

"
	for(var/datum/data/mining_equipment/prize in prize_list)
		dat += "
[prize.equipment_name]	[prize.cost]	Purchase
"
	dat += "
"

	var/datum/browser/popup = new(user, "miningvendor", "Mining Equipment Vendor", 400, 350)
	popup.set_content(dat)
	popup.open()
	return

/obj/machinery/mineral/equipment_vendor/Topic(href, href_list)
	if(..())
		return
	if(href_list["choice"])
		if(istype(inserted_id))
			if(href_list["choice"] == "eject")
				inserted_id.loc = loc
				inserted_id.verb_pickup()
				inserted_id = null
		else if(href_list["choice"] == "insert")
			var/obj/item/weapon/card/id/I = usr.get_active_hand()
			if(istype(I))
				usr.drop_item()
				I.loc = src
				inserted_id = I
			else usr << "No valid ID."
	if(href_list["purchase"])
		if(istype(inserted_id))
			var/datum/data/mining_equipment/prize = locate(href_list["purchase"])
			if (!prize || !(prize in prize_list))
				return
			if(prize.cost > inserted_id.mining_points)
			else
				inserted_id.mining_points -= prize.cost
				new prize.equipment_path(src.loc)
	updateUsrDialog()
	return

/obj/machinery/mineral/equipment_vendor/attackby(obj/item/I as obj, mob/user as mob, params)
	if(istype(I, /obj/item/weapon/mining_voucher))
		RedeemVoucher(I, user)
		return
	if(istype(I,/obj/item/weapon/card/id))
		var/obj/item/weapon/card/id/C = usr.get_active_hand()
		if(istype(C) && !istype(inserted_id))
			usr.drop_item()
			C.loc = src
			inserted_id = C
			interact(user)
		return
	if(default_deconstruction_screwdriver(user, "mining-open", "mining", I))
		updateUsrDialog()
		return
	if(panel_open)
		if(istype(I, /obj/item/weapon/crowbar))
			default_deconstruction_crowbar(I)
		return 1
	..()

/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/weapon/mining_voucher/voucher, mob/redeemer)
	var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in list("Mining Drill", "Kinetic Accelerator", "Resonator", "Mining Drone", "Advanced Scanner")
	if(!selection || !Adjacent(redeemer) || voucher.gc_destroyed || voucher.loc != redeemer)
		return
	switch(selection)
		if("Mining Drill")
			new /obj/item/weapon/pickaxe/drill(src.loc)
			new /obj/item/weapon/stock_parts/cell/high(src.loc)
		if("Kinetic Accelerator")
			new /obj/item/weapon/gun/energy/kinetic_accelerator(src.loc)
			new /obj/item/weapon/screwdriver(src.loc)
		if("Resonator")
			new /obj/item/weapon/resonator(src.loc)
			new /obj/item/weapon/screwdriver(src.loc)
		if("Mining Drone")
			new /mob/living/simple_animal/hostile/mining_drone(src.loc)
			new /obj/item/weapon/weldingtool/hugetank(src.loc)
		if("Advanced Scanner")
			new /obj/item/device/t_scanner/adv_mining_scanner(src.loc)
	qdel(voucher)

/obj/machinery/mineral/equipment_vendor/ex_act(severity, target)
	var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
	s.set_up(5, 1, src)
	s.start()
	if(prob(50 / severity) && severity < 3)
		qdel(src)

/**********************Mining Equipment Vendor Items**************************/

/**********************Mining Equipment Voucher**********************/

/obj/item/weapon/mining_voucher
	name = "mining voucher"
	desc = "A token to redeem a piece of equipment. Use it on a mining equipment vendor."
	icon = 'icons/obj/items.dmi'
	icon_state = "mining_voucher"
	w_class = 1

/**********************Mining Point Card**********************/

/obj/item/weapon/card/mining_point_card
	name = "mining points card"
	desc = "A small card preloaded with mining points. Swipe your ID card over it to transfer the points, then discard."
	icon_state = "data"
	var/points = 500

/obj/item/weapon/card/mining_point_card/attackby(obj/item/I as obj, mob/user as mob, params)
	if(istype(I, /obj/item/weapon/card/id))
		if(points)
			var/obj/item/weapon/card/id/C = I
			C.mining_points += points
			user << "You transfer [points] points to [C]."
			points = 0
		else
			user << "There's no points left on [src]."
	..()

/obj/item/weapon/card/mining_point_card/examine(mob/user)
	..()
	user << "There's [points] point\s on the card."

/**********************Jaunter**********************/

/obj/item/device/wormhole_jaunter
	name = "wormhole jaunter"
	desc = "A single use device harnessing outdated wormhole technology, Nanotrasen has since turned its eyes to blue space for more accurate teleportation. The wormholes it creates are unpleasant to travel through, to say the least."
	icon = 'icons/obj/items.dmi'
	icon_state = "Jaunter"
	item_state = "electronic"
	throwforce = 0
	w_class = 2.0
	throw_speed = 3
	throw_range = 5
	origin_tech = "bluespace=2"

/obj/item/device/wormhole_jaunter/attack_self(mob/user as mob)
	var/turf/device_turf = get_turf(user)
	if(!device_turf||device_turf.z==2||device_turf.z>=7)
		user << "You're having difficulties getting the [src.name] to work."
		return
	else
		user.visible_message("[user.name] activates the [src.name]!")
		var/list/L = list()
		for(var/obj/item/device/radio/beacon/B in world)
			var/turf/T = get_turf(B)
			if(T.z == ZLEVEL_STATION)
				L += B
		if(!L.len)
			user << "The [src.name] failed to create a wormhole."
			return
		var/chosen_beacon = pick(L)
		var/obj/effect/portal/wormhole/jaunt_tunnel/J = new /obj/effect/portal/wormhole/jaunt_tunnel(get_turf(src), chosen_beacon, lifespan=100)
		J.target = chosen_beacon
		try_move_adjacent(J)
		playsound(src,'sound/effects/sparks4.ogg',50,1)
		qdel(src)

/obj/effect/portal/wormhole/jaunt_tunnel
	name = "jaunt tunnel"
	icon = 'icons/effects/effects.dmi'
	icon_state = "bhole3"
	desc = "A stable hole in the universe made by a wormhole jaunter. Turbulent doesn't even begin to describe how rough passage through one of these is, but at least it will always get you somewhere near a beacon."

/obj/effect/portal/wormhole/jaunt_tunnel/teleport(atom/movable/M)
	if(istype(M, /obj/effect))
		return
	if(istype(M, /atom/movable))
		if(do_teleport(M, target, 6))
			if(isliving(M))
				var/mob/living/L = M
				L.Weaken(3)
				if(ishuman(L))
					shake_camera(L, 20, 1)
					spawn(20)
						if(L)
							L.visible_message("[L.name] vomits from travelling through the [src.name]!", "You throw up from travelling through the [src.name]!")
							L.nutrition -= 20
							L.adjustToxLoss(-3)
							var/turf/T = get_turf(L)
							T.add_vomit_floor(L)
							playsound(L, 'sound/effects/splat.ogg', 50, 1)

/**********************Resonator**********************/

/obj/item/weapon/resonator
	name = "resonator"
	icon = 'icons/obj/items.dmi'
	icon_state = "resonator"
	item_state = "resonator"
	desc = "A handheld device that creates small fields of energy that resonate until they detonate, crushing rock. It can also be activated without a target to create a field at the user's location, to act as a delayed time trap. It's more effective in a vacuum."
	w_class = 3
	force = 8
	throwforce = 10
	var/cooldown = 0
	var/fieldsactive = 0
	var/burst_time = 50
	var/fieldlimit = 3

/obj/item/weapon/resonator/proc/CreateResonance(var/target, var/creator)
	if(fieldsactive < fieldlimit)
		playsound(src,'sound/weapons/resonator_fire.ogg',50,1)
		new /obj/effect/resonance(get_turf(target), creator, burst_time)
		fieldsactive++
		spawn(50)
			fieldsactive--

/obj/item/weapon/resonator/attack_self(mob/user as mob)
	CreateResonance(src, user)
	..()

/obj/item/weapon/resonator/attackby(obj/item/weapon/W as obj, mob/user as mob)
	if(istype(W, /obj/item/weapon/screwdriver))
		if(burst_time == 50)
			burst_time = 30
			user << "You set the resonator's fields to detonate after 3 seconds."
		else
			burst_time = 50
			user << "You set the resonator's fields to detonate after 5 seconds."

	else if(istype(W, /obj/item/stack))
		var/obj/item/stack/S = W

		if(istype(S, /obj/item/stack/sheet/mineral/diamond))
			if(fieldlimit < 8)
				fieldlimit++
				user << "You upgrade [src]'s field generator with diamonds."
				S.use(1)
			else
				user << "The [src.name]'s field generator is at the limit."

/obj/item/weapon/resonator/afterattack(atom/target, mob/user, proximity_flag)
	if(proximity_flag)
		if(!check_allowed_items(target, 1)) return
		CreateResonance(target, user)

/obj/effect/resonance
	name = "resonance field"
	desc = "A resonating field that significantly damages anything inside of it when the field eventually ruptures."
	icon = 'icons/effects/effects.dmi'
	icon_state = "shield1"
	layer = 4.1
	mouse_opacity = 0
	var/resonance_damage = 20

/obj/effect/resonance/New(loc, var/creator = null, var/timetoburst)
	var/turf/proj_turf = get_turf(src)
	if(!istype(proj_turf))
		return
	if(istype(proj_turf, /turf/simulated/mineral))
		var/turf/simulated/mineral/M = proj_turf
		spawn(timetoburst)
			playsound(src,'sound/weapons/resonator_blast.ogg',50,1)
			M.gets_drilled(creator)
			qdel(src)
	else
		var/datum/gas_mixture/environment = proj_turf.return_air()
		var/pressure = environment.return_pressure()
		if(pressure < 50)
			name = "strong resonance field"
			resonance_damage = 50
		spawn(timetoburst)
			playsound(src,'sound/weapons/resonator_blast.ogg',50,1)
			if(creator)
				for(var/mob/living/L in src.loc)
					add_logs(creator, L, "used a resonator field on", object="resonator")
					L << "The [src.name] ruptured with you in it!"
					L.adjustBruteLoss(resonance_damage)
			else
				for(var/mob/living/L in src.loc)
					L << "The [src.name] ruptured with you in it!"
					L.adjustBruteLoss(resonance_damage)
			qdel(src)

/**********************Facehugger toy**********************/

/obj/item/clothing/mask/facehugger/toy
	desc = "A toy often used to play pranks on other miners by putting it in their beds. It takes a bit to recharge after latching onto something."
	throwforce = 0
	real = 0
	sterile = 1
	tint = 3 //Makes it feel more authentic when it latches on

/obj/item/clothing/mask/facehugger/toy/Die()
	return

/**********************Mining drone**********************/

/mob/living/simple_animal/hostile/mining_drone/
	name = "nanotrasen minebot"
	desc = "The instructions printed on the side read: This is a small robot used to support miners, can be set to search and collect loose ore, or to help fend off wildlife. A mining scanner can instruct it to drop loose ore. Field repairs can be done with a welder."
	icon = 'icons/obj/aibots.dmi'
	icon_state = "mining_drone"
	icon_living = "mining_drone"
	status_flags = CANSTUN|CANWEAKEN|CANPUSH
	stop_automated_movement_when_pulled = 1
	mouse_opacity = 1
	faction = list("neutral")
	a_intent = "harm"
	min_oxy = 0
	max_oxy = 0
	min_tox = 0
	max_tox = 0
	min_co2 = 0
	max_co2 = 0
	min_n2 = 0
	max_n2 = 0
	minbodytemp = 0
	wander = 0
	idle_vision_range = 5
	move_to_delay = 10
	retreat_distance = 1
	minimum_distance = 2
	health = 125
	maxHealth = 125
	melee_damage_lower = 15
	melee_damage_upper = 15
	environment_smash = 0
	attacktext = "drills"
	attack_sound = 'sound/weapons/circsawhit.ogg'
	ranged = 1
	ranged_message = "shoots"
	ranged_cooldown_cap = 3
	projectiletype = /obj/item/projectile/kinetic
	projectilesound = 'sound/weapons/Gunshot4.ogg'
	wanted_objects = list(/obj/item/weapon/ore/diamond, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/silver,
						  /obj/item/weapon/ore/plasma,  /obj/item/weapon/ore/uranium,    /obj/item/weapon/ore/iron,
						  /obj/item/weapon/ore/bananium)

/mob/living/simple_animal/hostile/mining_drone/attackby(obj/item/I as obj, mob/user as mob, params)
	if(istype(I, /obj/item/weapon/weldingtool))
		var/obj/item/weapon/weldingtool/W = I
		if(W.welding && !stat)
			if(stance != HOSTILE_STANCE_IDLE)
				user << "[src] is moving around too much to repair!"
				return
			if(maxHealth == health)
				user << "[src] is at full integrity."
			else
				health += 10
				user << "You repair some of the armor on [src]."
			return
	if(istype(I, /obj/item/device/mining_scanner) || istype(I, /obj/item/device/t_scanner/adv_mining_scanner))
		user << "You instruct [src] to drop any collected ore."
		DropOre()
		return
	..()

/mob/living/simple_animal/hostile/mining_drone/Die()
	..()
	visible_message("[src] is destroyed!")
	new /obj/effect/decal/cleanable/robot_debris(src.loc)
	DropOre()
	qdel(src)
	return

/mob/living/simple_animal/hostile/mining_drone/New()
	..()
	SetCollectBehavior()

/mob/living/simple_animal/hostile/mining_drone/attack_hand(mob/living/carbon/human/M)
	if(M.a_intent == "help")
		switch(search_objects)
			if(0)
				SetCollectBehavior()
				M << "[src] has been set to search and store loose ore."
			if(2)
				SetOffenseBehavior()
				M << "[src] has been set to attack hostile wildlife."
		return
	..()

/mob/living/simple_animal/hostile/mining_drone/proc/SetCollectBehavior()
	idle_vision_range = 9
	search_objects = 2
	wander = 1
	ranged = 0
	minimum_distance = 1
	retreat_distance = null
	icon_state = "mining_drone"

/mob/living/simple_animal/hostile/mining_drone/proc/SetOffenseBehavior()
	idle_vision_range = 7
	search_objects = 0
	wander = 0
	ranged = 1
	retreat_distance = 1
	minimum_distance = 2
	icon_state = "mining_drone_offense"

/mob/living/simple_animal/hostile/mining_drone/AttackingTarget()
	if(istype(target, /obj/item/weapon/ore))
		CollectOre()
		return
	..()

/mob/living/simple_animal/hostile/mining_drone/proc/CollectOre()
	var/obj/item/weapon/ore/O
	for(O in src.loc)
		O.loc = src
	for(var/dir in alldirs)
		var/turf/T = get_step(src,dir)
		for(O in T)
			O.loc = src
	return

/mob/living/simple_animal/hostile/mining_drone/proc/DropOre()
	if(!contents.len)
		return
	for(var/obj/item/weapon/ore/O in contents)
		contents -= O
		O.loc = src.loc
	return

/mob/living/simple_animal/hostile/mining_drone/adjustBruteLoss()
	if(search_objects)
		SetOffenseBehavior()
	..()

/**********************Lazarus Injector**********************/

/obj/item/weapon/lazarus_injector
	name = "lazarus injector"
	desc = "An injector with a cocktail of nanomachines and chemicals, this device can seemingly raise animals from the dead, making them become friendly to the user. Unfortunately, the process is useless on higher forms of life and incredibly costly, so these were hidden in storage until an executive thought they'd be great motivation for some of their employees."
	icon = 'icons/obj/syringe.dmi'
	icon_state = "lazarus_hypo"
	item_state = "hypo"
	throwforce = 0
	w_class = 2.0
	throw_speed = 3
	throw_range = 5
	var/loaded = 1
	var/malfunctioning = 0

/obj/item/weapon/lazarus_injector/afterattack(atom/target, mob/user, proximity_flag)
	if(!loaded)
		return
	if(istype(target, /mob/living) && proximity_flag)
		if(istype(target, /mob/living/simple_animal))
			var/mob/living/simple_animal/M = target
			if(M.stat == DEAD)
				M.faction = list("neutral")
				M.revive()
				if(istype(target, /mob/living/simple_animal/hostile))
					var/mob/living/simple_animal/hostile/H = M
					if(malfunctioning)
						H.faction |= list("lazarus", "\ref[user]")
						H.robust_searching = 1
						H.friends += user
						H.attack_same = 1
						log_game("[user] has revived hostile mob [target] with a malfunctioning lazarus injector")
					else
						H.attack_same = 0
				loaded = 0
				user.visible_message("[user] injects [M] with [src], reviving it.")
				playsound(src,'sound/effects/refill.ogg',50,1)
				icon_state = "lazarus_empty"
				return
			else
				user << "[src] is only effective on the dead."
				return
		else
			user << "[src] is only effective on lesser beings."
			return

/obj/item/weapon/lazarus_injector/emp_act()
	if(!malfunctioning)
		malfunctioning = 1

/obj/item/weapon/lazarus_injector/examine(mob/user)
	..()
	if(!loaded)
		user << "[src] is empty."
	if(malfunctioning)
		user << "The display on [src] seems to be flickering."

/**********************Mining Scanners**********************/

/obj/item/device/mining_scanner
	desc = "A scanner that checks surrounding rock for useful minerals; it can also be used to stop gibtonite detonations. Requires you to wear mesons to function properly."
	name = "mining scanner"
	icon_state = "mining1"
	item_state = "analyzer"
	w_class = 2.0
	flags = CONDUCT
	slot_flags = SLOT_BELT
	var/cooldown = 0

/obj/item/device/mining_scanner/attack_self(mob/user)
	if(!user.client)
		return
	if(!cooldown)
		cooldown = 1
		spawn(40)
			cooldown = 0
		var/client/C = user.client
		var/list/L = list()
		var/turf/simulated/mineral/M
		for(M in range(7, user))
			if(M.scan_state)
				L += M
		if(!L.len)
			user << "[src] reports that nothing was detected nearby."
			return
		else
			for(M in L)
				var/turf/T = get_turf(M)
				var/image/I = image('icons/turf/walls.dmi', loc = T, icon_state = M.scan_state, layer = 18)
				C.images += I
				spawn(30)
					if(C)
						C.images -= I

//Debug item to identify all ore spread quickly
/obj/item/device/mining_scanner/admin

/obj/item/device/mining_scanner/admin/attack_self(mob/user)
	for(var/turf/simulated/mineral/M in world)
		if(M.scan_state)
			M.icon_state = M.scan_state
	qdel(src)

/obj/item/device/t_scanner/adv_mining_scanner
	desc = "A scanner that automatically checks surrounding rock for useful minerals; it can also be used to stop gibtonite detonations. Requires you to wear mesons to function properly."
	name = "advanced mining scanner"
	icon_state = "mining0"
	item_state = "analyzer"
	w_class = 2.0
	flags = CONDUCT
	slot_flags = SLOT_BELT
	var/cooldown = 0

/obj/item/device/t_scanner/adv_mining_scanner/scan()
	if(!cooldown)
		cooldown = 1
		spawn(60)
			cooldown = 0
		var/turf/t = get_turf(src)
		var/list/mobs = recursive_mob_check(t, 1,0,0)
		if(!mobs.len)
			return
		var/list/L = list()
		var/turf/simulated/mineral/M
		for(M in range(7, t))
			if(M.scan_state)
				L += M
		if(L.len)
			for(var/mob/user in mobs)
				if(user.client)
					var/client/C = user.client
					for(M in L)
						var/turf/T = get_turf(M)
						var/image/I = image('icons/turf/walls.dmi', loc = T, icon_state = M.scan_state, layer = 18)
						C.images += I
						spawn(30)
							if(C)
								C.images -= I

/**********************Xeno Warning Sign**********************/
/obj/structure/sign/xeno_warning_mining
	name = "DANGEROUS ALIEN LIFE"
	desc = "A sign that warns would be travellers of hostile alien life in the vicinity."
	icon = 'icons/obj/mining.dmi'
	icon_state = "xeno_warning"

/**********************Mining Jetpack**********************/
/obj/item/weapon/tank/jetpack/carbondioxide/mining
	name = "mining jetpack"
	icon_state = "jetpack-mining"
	item_state = "jetpack-mining"
	desc = "A tank of compressed carbon dioxide for miners to use as propulsion in local space. The compact size allows for easy storage at the cost of capacity."
	volume = 40
	throw_range = 7
	w_class = 3 //same as syndie harness


code:
//PORTABLE REFRIGERATOR: Allows storage of hivelord cores to prevent them from decaying.
/obj/item/device/hivelord_fridge
	name = "portable refrigerator"
	desc = "A heavy cube used to store hivelord cores for later use. Will indefinitely prevent them from becoming inert."
	w_class = 3
	icon = 'icons/obj/mining.dmi'
	icon_state = "fridge0"
	item_state = "electronic"
	throw_speed = 1
	throw_range = 2
	slot_flags = SLOT_BELT
	var/obj/item/asteroid/hivelord_core/stored_core = null
	var/emagged = 0

/obj/item/device/hivelord_fridge/attack_self(mob/user)
	if(!stored_core)
		return
	user.visible_message("[user] removes [stored_core] from [src].", \
						 "\icon[src]You pop open [src] and remove [stored_core].")
	if(!user.put_in_hands(stored_core))
		stored_core.loc = get_turf(user)
	stored_core.preserved = 0
	stored_core = null
	icon_state = "fridge0"
	..()

/obj/item/device/hivelord_fridge/examine(mob/user)
	..()
	if(stored_core)
		user << "It has\icon [stored_core][stored_core] loaded."
	else
		user << "Nothing is loaded."

/obj/item/device/hivelord_fridge/attackby(obj/item/weapon/W, mob/user)
	if(istype(W, /obj/item/asteroid/hivelord_core) && !stored_core)
		var/obj/item/asteroid/hivelord_core/H = W
		user.visible_message("[user] places [H] into [src].", \
							 "\icon[src]You place [H] into [src] and close it with a hiss of cold air.")
		user.drop_item()
		H.loc = src
		icon_state = "fridge1[emagged]"
		playsound(src, 'sound/machines/hiss.ogg', 50, 1, 5)
		H.preserved = 1
		stored_core = H
		return
	..()


code:
/turf/simulated/mineral/random/labormineral
	mineralSpawnChanceList = list("Uranium" = 1, "Iron" = 100, "Diamond" = 1, "Gold" = 1, "Silver" = 1, "Plasma" = 1/*, "Adamantine" =5, "Cave" = 1 */) //Don't suffocate the prisoners with caves
	icon_state = "rock_labor"

/turf/simulated/mineral/random/labormineral/New()
	icon_state = "rock"
	..()


code:
/obj/machinery/computer/shuttle/labor
	name = "labor shuttle console"
	desc = "Used to call and send the labor camp shuttle."
	icon = 'icons/obj/computer.dmi'
	icon_state = "shuttle"
	circuit = /obj/item/weapon/circuitboard/labor_shuttle
	shuttleId = "laborcamp"
	possible_destinations = "laborcamp_home;laborcamp_away"
	req_access = list(access_brig)

/obj/machinery/computer/shuttle/labor/one_way
	name = "prisoner shuttle console"
	desc = "A one-way shuttle console, used to summon the shuttle to the labor camp."
	possible_destinations = "laborcamp_away"
	circuit = /obj/item/weapon/circuitboard/labor_shuttle/one_way
	req_access = list( )

/obj/machinery/computer/shuttle/labor/one_way/Topic(href, href_list)
	if(href_list["move"])
		var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp")
		if(!M)
			usr << "Cannot locate shuttle."
			return 0
		var/obj/docking_port/stationary/S = M.get_docked()
		if(S && S.name == "laborcamp_away")
			usr << "Shuttle is already at the outpost."
			return 0
	..()


code:
/**********************Prisoners' Console**************************/

/obj/machinery/mineral/labor_claim_console
	name = "point claim console"
	desc = "A stacking console with an electromagnetic writer, used to track ore mined by prisoners."
	icon = 'icons/obj/machines/mining_machines.dmi'
	icon_state = "console"
	density = 0
	anchored = 1
	var/obj/machinery/mineral/stacking_machine/laborstacker/machine = null
	var/machinedir = SOUTH
	var/obj/item/weapon/card/id/prisoner/inserted_id
	var/obj/machinery/door/airlock/release_door
	var/door_tag = "prisonshuttle"
	var/obj/item/device/radio/Radio //needed to send messages to sec radio

/obj/machinery/mineral/labor_claim_console/New()
	..()
	Radio = new/obj/item/device/radio(src)
	Radio.listening = 0
	spawn(7)
		src.machine = locate(/obj/machinery/mineral/stacking_machine, get_step(src, machinedir))
		var/t
		for(var/obj/machinery/door/airlock/d in range(5,src))
			t = d.id_tag
			if(t == src.door_tag)
				src.release_door = d
		if (machine && release_door)
			machine.CONSOLE = src
		else
			qdel(src)

/obj/machinery/mineral/labor_claim_console/proc/check_auth()
	if(emagged) return 1 //Shuttle is emagged, let any ol' person through
	return (istype(inserted_id) && inserted_id.points >= inserted_id.goal) //Otherwise, only let them out if the prisoner's reached his quota.

/obj/machinery/mineral/labor_claim_console/attack_hand(user as mob)
	var/dat
	dat += text("Point Claim Console

")
	if(emagged) //Shit's broken
		dat += text("QU&#t0A In%aL*D
")
	else if(istype(inserted_id)) //There's an ID in there.
		dat += text("ID: [inserted_id.registered_name] Eject ID.
")
		dat += text("Points Collected:[inserted_id.points]
")
		dat += text("Point Quota: [inserted_id.goal] - Reach your quota to earn your release
")
		dat += text("Unclaimed Collection Points: [machine.points].  Claim points.
")
	else	//No ID in sight.  Complain about it.
		dat += text("No ID inserted.  Insert ID.
")
	if(check_auth())
		dat += text("Proceed to Station.
")
		dat += text("Open release door.
")
	if(machine)
		dat += text("
Mineral Value List:
[machine.get_ore_values()]")

	user << browse("[dat]", "window=console_stacking_machine")

/obj/machinery/mineral/labor_claim_console/attackby(obj/item/I as obj, mob/user as mob, params)
	if(istype(I, /obj/item/weapon/card/id))
		return attack_hand(user)
	..()

/obj/machinery/mineral/labor_claim_console/emag_act(mob/user as mob)
	if(!emagged)
		emagged = 1
		user << "PZZTTPFFFT"

/obj/machinery/mineral/labor_claim_console/Topic(href, href_list)
	usr.set_machine(src)
	src.add_fingerprint(usr)
	if(href_list["choice"])
		if(istype(inserted_id)) //Sanity check against href spoofs
			if(href_list["choice"] == "eject")
				inserted_id.loc = loc
				inserted_id.verb_pickup()
				inserted_id = null
			if(href_list["choice"] == "claim")
				inserted_id.points += machine.points
				machine.points = 0
				src << "Points transferred."
		else if(href_list["choice"] == "insert")
			var/obj/item/weapon/card/id/prisoner/I = usr.get_active_hand()
			if(istype(I))
				usr.drop_item()
				I.loc = src
				inserted_id = I
			else usr << "Invalid ID."
		if(check_auth()) //Sanity check against hef spoofs
			if(href_list["choice"] == "station")
				if(!alone_in_area(get_area(src), usr))
					usr << "Prisoners are only allowed to be released while alone."
				else
					switch(SSshuttle.moveShuttle("laborcamp","laborcamp_home"))
						if(1)
							usr << "Shuttle not found"
						if(2)
							usr << "Shuttle already at station"
						if(3)
							usr << "No permission to dock could be granted."
						else
							Radio.set_frequency(SEC_FREQ)
							Radio.talk_into(src, "[inserted_id.registered_name] has returned to the station. Minerals and Prisoner ID card ready for retrieval.", SEC_FREQ)
							usr << "Shuttle recieved message and will be sent shortly."

			if(href_list["choice"] == "release")
				if(alone_in_area(get_area(loc), usr))
					var/obj/docking_port/stationary/S = SSshuttle.getDock("laborcamp_home")
					if(S && S.get_docked())
						if(release_door && release_door.density)
							release_door.open()
					else
						usr << "Prisoners can only be released while docked with the station."
				else
					usr << "Prisoners are only allowed to be released while alone."

		src.updateUsrDialog()
	return

/**********************Prisoner Collection Unit**************************/

/obj/machinery/mineral/stacking_machine/laborstacker
	var/points = 0 //The unclaimed value of ore stacked.  Value for each ore loosely relative to its rarity.
	var/list/ore_values = list(("glass" = 1), ("metal" = 2), ("solid plasma" = 20), ("plasteel" = 23), ("reinforced glass" = 4), ("gold" = 20), ("silver" = 20), ("uranium" = 20), ("diamond" = 25), ("bananium" = 50))

/obj/machinery/mineral/stacking_machine/laborstacker/proc/get_ore_values()
	var/dat = "
"
	for(var/ore in ore_values)
		var/value = ore_values[ore]
		dat += "
[capitalize(ore)]	[value]
"
	dat += "
"
	return dat

/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp)
	if(istype(inp))
		var/n = inp.name
		var/a = inp.amount
		if(n in ore_values)
			points += ore_values[n] * a
	..()

/**********************Point Lookup Console**************************/
/obj/machinery/mineral/labor_points_checker
	name = "points checking console"
	desc = "A console used by prisoners to check the progress on their quotas. Simply swipe a prisoner ID."
	icon = 'icons/obj/machines/mining_machines.dmi'
	icon_state = "console"
	density = 0
	anchored = 1

/obj/machinery/mineral/labor_points_checker/attack_hand(mob/user)
	user.examinate(src)

/obj/machinery/mineral/labor_points_checker/attackby(obj/item/I as obj, mob/user as mob, params)
	if(istype(I, /obj/item/weapon/card/id))
		if(istype(I, /obj/item/weapon/card/id/prisoner))
			var/obj/item/weapon/card/id/prisoner/prisoner_id = I
			user << "ID: [prisoner_id.registered_name]"
			user << "Points Collected:[prisoner_id.points]"
			user << "Point Quota: [prisoner_id.goal]"
			user << "Collect points by bringing smelted minerals to the Labor Shuttle stacking machine. Reach your quota to earn your release."
		else
			user << "Error: Invalid ID"
		return
	..()


code:
/**********************Input and output plates**************************/

/obj/machinery/mineral/input
	icon = 'icons/mob/screen_gen.dmi'
	icon_state = "x2"
	name = "Input area"
	density = 0
	anchored = 1.0
	New()
		icon_state = "blank"

/obj/machinery/mineral/output
	icon = 'icons/mob/screen_gen.dmi'
	icon_state = "x"
	name = "Output area"
	density = 0
	anchored = 1.0
	New()
		icon_state = "blank"

/obj/machinery/mineral
	var/input_dir = NORTH
	var/output_dir = SOUTH

/obj/machinery/mineral/proc/unload_mineral(var/atom/movable/S)
	S.loc = loc
	var/turf/T = get_step(src,output_dir)
	if(T)
		S.loc = T


code:
/**********************Mineral processing unit console**************************/

/obj/machinery/mineral/processing_unit_console
	name = "production machine console"
	icon = 'icons/obj/machines/mining_machines.dmi'
	icon_state = "console"
	density = 1
	anchored = 1
	var/obj/machinery/mineral/processing_unit/machine = null
	var/machinedir = EAST

/obj/machinery/mineral/processing_unit_console/New()
	..()
	spawn(7)
		src.machine = locate(/obj/machinery/mineral/processing_unit, get_step(src, machinedir))
		if (machine)
			machine.CONSOLE = src
		else
			qdel(src)

/obj/machinery/mineral/processing_unit_console/attack_hand(user as mob)

	var/dat = "Smelter control console

"
	//iron
	if(machine.ore_iron || machine.ore_glass || machine.ore_plasma || machine.ore_uranium || machine.ore_gold || machine.ore_silver || machine.ore_diamond || machine.ore_clown || machine.ore_adamantine)
		if(machine.ore_iron)
			if (machine.selected_iron==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Iron: [machine.ore_iron]
")
		else
			machine.selected_iron = 0

		//sand - glass
		if(machine.ore_glass)
			if (machine.selected_glass==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Sand: [machine.ore_glass]
")
		else
			machine.selected_glass = 0

		//plasma
		if(machine.ore_plasma)
			if (machine.selected_plasma==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Plasma: [machine.ore_plasma]
")
		else
			machine.selected_plasma = 0

		//uranium
		if(machine.ore_uranium)
			if (machine.selected_uranium==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Uranium: [machine.ore_uranium]
")
		else
			machine.selected_uranium = 0

		//gold
		if(machine.ore_gold)
			if (machine.selected_gold==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Gold: [machine.ore_gold]
")
		else
			machine.selected_gold = 0

		//silver
		if(machine.ore_silver)
			if (machine.selected_silver==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Silver: [machine.ore_silver]
")
		else
			machine.selected_silver = 0

		//diamond
		if(machine.ore_diamond)
			if (machine.selected_diamond==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Diamond: [machine.ore_diamond]
")
		else
			machine.selected_diamond = 0

		//bananium
		if(machine.ore_clown)
			if (machine.selected_clown==1)
				dat += text("Smelting ")
			else
				dat += text("Not smelting ")
			dat += text("Bananium: [machine.ore_clown]
")
		else
			machine.selected_clown = 0

		//On or off
		dat += text("Machine is currently ")
		if (machine.on==1)
			dat += text("On ")
		else
			dat += text("Off ")
	else
		dat+="---No Materials Loaded---"

	user << browse("[dat]", "window=console_processing_unit")

/obj/machinery/mineral/processing_unit_console/Topic(href, href_list)
	if(..())
		return
	usr.set_machine(src)
	src.add_fingerprint(usr)
	if(href_list["sel_iron"])
		if (href_list["sel_iron"] == "yes")
			machine.selected_iron = 1
		else
			machine.selected_iron = 0
	if(href_list["sel_glass"])
		if (href_list["sel_glass"] == "yes")
			machine.selected_glass = 1
		else
			machine.selected_glass = 0
	if(href_list["sel_plasma"])
		if (href_list["sel_plasma"] == "yes")
			machine.selected_plasma = 1
		else
			machine.selected_plasma = 0
	if(href_list["sel_uranium"])
		if (href_list["sel_uranium"] == "yes")
			machine.selected_uranium = 1
		else
			machine.selected_uranium = 0
	if(href_list["sel_gold"])
		if (href_list["sel_gold"] == "yes")
			machine.selected_gold = 1
		else
			machine.selected_gold = 0
	if(href_list["sel_silver"])
		if (href_list["sel_silver"] == "yes")
			machine.selected_silver = 1
		else
			machine.selected_silver = 0
	if(href_list["sel_diamond"])
		if (href_list["sel_diamond"] == "yes")
			machine.selected_diamond = 1
		else
			machine.selected_diamond = 0
	if(href_list["sel_clown"])
		if (href_list["sel_clown"] == "yes")
			machine.selected_clown = 1
		else
			machine.selected_clown = 0
	if(href_list["set_on"])
		if (href_list["set_on"] == "on")
			machine.on = 1
		else
			machine.on = 0
	src.updateUsrDialog()
	return

/**********************Mineral processing unit**************************/

/obj/machinery/mineral/processing_unit
	name = "furnace"
	icon = 'icons/obj/machines/mining_machines.dmi'
	icon_state = "furnace"
	density = 1
	anchored = 1
	var/obj/machinery/mineral/CONSOLE = null
	var/ore_gold = 0;
	var/ore_silver = 0;
	var/ore_diamond = 0;
	var/ore_glass = 0;
	var/ore_plasma = 0;
	var/ore_uranium = 0;
	var/ore_iron = 0;
	var/ore_clown = 0;
	var/ore_adamantine = 0;
	var/selected_gold = 0
	var/selected_silver = 0
	var/selected_diamond = 0
	var/selected_glass = 0
	var/selected_plasma = 0
	var/selected_uranium = 0
	var/selected_iron = 0
	var/selected_clown = 0
	var/on = 0 //0 = off, 1 =... oh you know!

/obj/machinery/mineral/processing_unit/process()
	var/i
	for (i = 0; i < 10; i++)
		if (on)
			if (selected_glass == 1 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0)
				if (ore_glass > 0)
					ore_glass--;
					generate_mineral(/obj/item/stack/sheet/glass)
				else
					on = 0
				continue
			if (selected_glass == 1 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 1 && selected_clown == 0)
				if (ore_glass > 0 && ore_iron > 0)
					ore_glass--;
					ore_iron--;
					generate_mineral(/obj/item/stack/sheet/rglass)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 1 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0)
				if (ore_gold > 0)
					ore_gold--;
					generate_mineral(/obj/item/stack/sheet/mineral/gold)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 1 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0)
				if (ore_silver > 0)
					ore_silver--;
					generate_mineral(/obj/item/stack/sheet/mineral/silver)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 1 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0)
				if (ore_diamond > 0)
					ore_diamond--;
					generate_mineral(/obj/item/stack/sheet/mineral/diamond)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 1 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0)
				if (ore_plasma > 0)
					ore_plasma--;
					generate_mineral(/obj/item/stack/sheet/mineral/plasma)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 1 && selected_iron == 0 && selected_clown == 0)
				if (ore_uranium > 0)
					ore_uranium--;
					generate_mineral(/obj/item/stack/sheet/mineral/uranium)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 1 && selected_clown == 0)
				if (ore_iron > 0)
					ore_iron--;
					generate_mineral(/obj/item/stack/sheet/metal)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 1 && selected_uranium == 0 && selected_iron == 1 && selected_clown == 0)
				if (ore_iron > 0 && ore_plasma > 0)
					ore_iron--;
					ore_plasma--;
					generate_mineral(/obj/item/stack/sheet/plasteel)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 0 && selected_plasma == 0 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 1)
				if (ore_clown > 0)
					ore_clown--;
					generate_mineral(/obj/item/stack/sheet/mineral/bananium)
				else
					on = 0
				continue
			//THESE TWO ARE CODED FOR URIST TO USE WHEN HE GETS AROUND TO IT.
			//They were coded on 18 Feb 2012. If you're reading this in 2015, then firstly congratulations on the world not ending on 21 Dec 2012 and secondly, Urist is apparently VERY lazy. ~Errorage
			/*if (selected_glass == 0 && selected_gold == 0 && selected_silver == 0 && selected_diamond == 1 && selected_plasma == 0 && selected_uranium == 1 && selected_iron == 0 && selected_clown == 0)
				if (ore_uranium >= 2 && ore_diamond >= 1)
					ore_uranium -= 2
					ore_diamond -= 1
					generate_mineral(/obj/item/stack/sheet/mineral/adamantine)
				else
					on = 0
				continue
			if (selected_glass == 0 && selected_gold == 0 && selected_silver == 1 && selected_diamond == 0 && selected_plasma == 1 && selected_uranium == 0 && selected_iron == 0 && selected_clown == 0)
				if (ore_silver >= 1 && ore_plasma >= 3)
					ore_silver -= 1
					ore_plasma -= 3
					generate_mineral(/obj/item/stack/sheet/mineral/mythril)
				else
					on = 0
				continue*/

			//if a non valid combination is selected

			var/b = 1 //this part checks if all required ores are available

			if (!(selected_gold || selected_silver ||selected_diamond || selected_uranium | selected_plasma || selected_iron || selected_iron))
				b = 0

			if (selected_gold == 1)
				if (ore_gold <= 0)
					b = 0
			if (selected_silver == 1)
				if (ore_silver <= 0)
					b = 0
			if (selected_diamond == 1)
				if (ore_diamond <= 0)
					b = 0
			if (selected_uranium == 1)
				if (ore_uranium <= 0)
					b = 0
			if (selected_plasma == 1)
				if (ore_plasma <= 0)
					b = 0
			if (selected_iron == 1)
				if (ore_iron <= 0)
					b = 0
			if (selected_glass == 1)
				if (ore_glass <= 0)
					b = 0
			if (selected_clown == 1)
				if (ore_clown <= 0)
					b = 0

			if (b) //if they are, deduct one from each, produce slag and shut the machine off
				if (selected_gold == 1)
					ore_gold--
				if (selected_silver == 1)
					ore_silver--
				if (selected_diamond == 1)
					ore_diamond--
				if (selected_uranium == 1)
					ore_uranium--
				if (selected_plasma == 1)
					ore_plasma--
				if (selected_iron == 1)
					ore_iron--
				if (selected_clown == 1)
					ore_clown--
				generate_mineral(/obj/item/weapon/ore/slag)
				on = 0
			else
				on = 0
				break
			break
		else
			break
	var/turf/T = get_step(src,input_dir)
	if(T)
		var/n = 0
		for(var/obj/item/O in T)
			n++
			if(n>10)
				break
			if (istype(O,/obj/item/weapon/ore/iron))
				ore_iron++;
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/glass))
				ore_glass++;
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/diamond))
				ore_diamond++;
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/plasma))
				ore_plasma++
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/gold))
				ore_gold++
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/silver))
				ore_silver++
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/uranium))
				ore_uranium++
				O.loc = null
				continue
			if (istype(O,/obj/item/weapon/ore/bananium))
				ore_clown++
				O.loc = null
				continue
			unload_mineral(O)

/obj/machinery/mineral/processing_unit/proc/generate_mineral(var/P)
	var/O = new P(src)
	unload_mineral(O)