code:
/obj/machinery/radiocarbon_spectrometer
	name = "Radiocarbon spectrometer"
	desc = "A specialised, complex scanner for gleaning information on all manner of small things."
	anchored = 1
	density = 1
	icon = 'icons/obj/virology.dmi'
	icon_state = "analyser"

	use_power = 1			//1 = idle, 2 = active
	idle_power_usage = 20
	active_power_usage = 300

	//var/obj/item/weapon/reagent_containers/glass/coolant_container
	var/scanning = 0
	var/report_num = 0
	//
	var/obj/item/scanned_item
	var/last_scan_data = "No scans on record."
	//
	var/last_process_worldtime = 0
	//
	var/scanner_progress = 0
	var/scanner_rate = 1.25			//80 seconds per scan
	var/scanner_rpm = 0
	var/scanner_rpm_dir = 1
	var/scanner_temperature = 0
	var/scanner_seal_integrity = 100
	//
	var/coolant_usage_rate = 0		//measured in u/microsec
	var/fresh_coolant = 0
	var/coolant_purity = 0
	var/datum/reagents/coolant_reagents
	var/used_coolant = 0
	var/list/coolant_reagents_purity = list()
	//
	var/maser_wavelength = 0
	var/optimal_wavelength = 0
	var/optimal_wavelength_target = 0
	var/tleft_retarget_optimal_wavelength = 0
	var/maser_efficiency = 0
	//
	var/radiation = 0				//0-100 mSv
	var/t_left_radspike = 0
	var/rad_shield = 0

/obj/machinery/radiocarbon_spectrometer/New()
	..()
	create_reagents(500)
	coolant_reagents_purity["water"] = 0.5
	coolant_reagents_purity["icecoffee"] = 0.6
	coolant_reagents_purity["icetea"] = 0.6
	coolant_reagents_purity["milkshake"] = 0.6
	coolant_reagents_purity["leporazine"] = 0.7
	coolant_reagents_purity["kelotane"] = 0.7
	coolant_reagents_purity["sterilizine"] = 0.7
	coolant_reagents_purity["dermaline"] = 0.7
	coolant_reagents_purity["hyperzine"] = 0.8
	coolant_reagents_purity["cryoxadone"] = 0.9
	coolant_reagents_purity["coolant"] = 1
	coolant_reagents_purity["adminordrazine"] = 2

/obj/machinery/radiocarbon_spectrometer/attack_hand(var/mob/user as mob)
	ui_interact(user)

/obj/machinery/radiocarbon_spectrometer/attackby(var/obj/I as obj, var/mob/user as mob)
	if(scanning)
		user << "You can't do that while [src] is scanning!"
	else
		if(istype(I, /obj/item/stack/nanopaste))
			var/choice = alert("What do you want to do with the nanopaste?","Radiometric Scanner","Scan nanopaste","Fix seal integrity")
			if(choice == "Fix seal integrity")
				var/obj/item/stack/nanopaste/N = I
				var/amount_used = min(N.amount, 10 - scanner_seal_integrity / 10)
				N.use(amount_used)
				scanner_seal_integrity = round(scanner_seal_integrity + amount_used * 10)
				return
		if(istype(I, /obj/item/weapon/reagent_containers/glass))
			var/choice = alert("What do you want to do with the container?","Radiometric Scanner","Add coolant","Empty coolant","Scan container")
			if(choice == "Add coolant")
				var/obj/item/weapon/reagent_containers/glass/G = I
				var/amount_transferred = min(src.reagents.maximum_volume - src.reagents.total_volume, G.reagents.total_volume)
				G.reagents.trans_to(src, amount_transferred)
				user << "You empty [amount_transferred]u of coolant into [src]."
				update_coolant()
				return
			else if(choice == "Empty coolant")
				var/obj/item/weapon/reagent_containers/glass/G = I
				var/amount_transferred = min(G.reagents.maximum_volume - G.reagents.total_volume, src.reagents.total_volume)
				src.reagents.trans_to(G, amount_transferred)
				user << "You remove [amount_transferred]u of coolant from [src]."
				update_coolant()
				return
		user.drop_item()
		I.loc = src
		scanned_item = I

/obj/machinery/radiocarbon_spectrometer/proc/update_coolant()
	var/total_purity = 0
	fresh_coolant = 0
	coolant_purity = 0
	var/num_reagent_types = 0
	for (var/datum/reagent/current_reagent in src.reagents.reagent_list)
		if (!current_reagent)
			continue
		var/cur_purity = coolant_reagents_purity[current_reagent.id]
		if(!cur_purity)
			cur_purity = 0.1
		else if(cur_purity > 1)
			cur_purity = 1
		total_purity += cur_purity * current_reagent.volume
		fresh_coolant += current_reagent.volume
		num_reagent_types += 1
	if(total_purity && fresh_coolant)
		coolant_purity = total_purity / fresh_coolant

/obj/machinery/radiocarbon_spectrometer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)

	if(user.stat)
		return

	// this is the data which will be sent to the ui
	var/data[0]
	data["scanned_item"] = (scanned_item ? scanned_item.name : "")
	data["scanned_item_desc"] = (scanned_item ? (scanned_item.desc ? scanned_item.desc : "No information on record.") : "")
	data["last_scan_data"] = last_scan_data
	//
	data["scan_progress"] = round(scanner_progress)
	data["scanning"] = scanning
	//
	data["scanner_seal_integrity"] = round(scanner_seal_integrity)
	data["scanner_rpm"] = round(scanner_rpm)
	data["scanner_temperature"] = round(scanner_temperature)
	//
	data["coolant_usage_rate"] = "[coolant_usage_rate]"
	data["unused_coolant_abs"] = round(fresh_coolant)
	data["unused_coolant_per"] = round(fresh_coolant / reagents.maximum_volume * 100)
	data["coolant_purity"] = "[coolant_purity * 100]"
	//
	data["optimal_wavelength"] = round(optimal_wavelength)
	data["maser_wavelength"] = round(maser_wavelength)
	data["maser_efficiency"] = round(maser_efficiency * 100)
	//
	data["radiation"] = round(radiation)
	data["t_left_radspike"] = round(t_left_radspike)
	data["rad_shield_on"] = rad_shield

	// update the ui if it exists, returns null if no ui is passed/found
	ui = nanomanager.try_update_ui(user, src, ui_key, ui, data)
	if (!ui)
		// the ui does not exist, so we'll create a new() one
        // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
		ui = new(user, src, ui_key, "geoscanner.tmpl", "High Res Radiocarbon Spectrometer", 900, 825)
		// when the ui is first opened this is the data it will use
		ui.set_initial_data(data)
		// open the new ui window
		ui.open()
		// auto update every Master Controller tick
		ui.set_auto_update(1)

/obj/machinery/radiocarbon_spectrometer/process()
	if(scanning)
		if(!scanned_item || scanned_item.loc != src)
			scanned_item = null
			stop_scanning()
		else if(scanner_progress >= 100)
			complete_scan()
		else
			//calculate time difference
			var/deltaT = (world.time - last_process_worldtime) * 0.1

			//modify the RPM over time
			//i want 1u to last for 10 sec at 500 RPM, scaling linearly
			scanner_rpm += scanner_rpm_dir * 50 * deltaT
			if(scanner_rpm > 1000)
				scanner_rpm = 1000
				scanner_rpm_dir = -1 * pick(0.5, 2.5, 5.5)
			else if(scanner_rpm < 1)
				scanner_rpm = 1
				scanner_rpm_dir = 1 * pick(0.5, 2.5, 5.5)

			//heat up according to RPM
			//each unit of coolant
			scanner_temperature += scanner_rpm * deltaT * 0.05

			//radiation
			t_left_radspike -= deltaT
			if(t_left_radspike > 0)
				//ordinary radiation
				radiation = rand() * 15
			else
				//radspike
				if(t_left_radspike > -5)
					radiation = rand() * 15 + 85
					if(!rad_shield)
						//irradiate nearby mobs
						for(var/mob/living/M in view(7,src))
							M.apply_effect(radiation / 25, IRRADIATE, 0)
				else
					t_left_radspike = pick(10,15,25)

			//use some coolant to cool down
			if(coolant_usage_rate > 0)
				var/coolant_used = min(fresh_coolant, coolant_usage_rate * deltaT)
				if(coolant_used > 0)
					fresh_coolant -= coolant_used
					used_coolant += coolant_used
					scanner_temperature = max(scanner_temperature - coolant_used * coolant_purity * 20, 0)

			//modify the optimal wavelength
			tleft_retarget_optimal_wavelength -= deltaT
			if(tleft_retarget_optimal_wavelength <= 0)
				tleft_retarget_optimal_wavelength = pick(4,8,15)
				optimal_wavelength_target = rand() * 9900 + 100
			//
			if(optimal_wavelength < optimal_wavelength_target)
				optimal_wavelength = min(optimal_wavelength + 700 * deltaT, optimal_wavelength_target)
			else if(optimal_wavelength > optimal_wavelength_target)
				optimal_wavelength = max(optimal_wavelength - 700 * deltaT, optimal_wavelength_target)
			//
			maser_efficiency = 1 - max(min(10000, abs(optimal_wavelength - maser_wavelength) * 3), 1) / 10000

			//make some scan progress
			if(!rad_shield)
				scanner_progress = min(100, scanner_progress + scanner_rate * maser_efficiency * deltaT)

				//degrade the seal over time according to temperature
				//i want temperature of 50K to degrade at 1%/sec
				scanner_seal_integrity -= (max(scanner_temperature, 1) / 1000) * deltaT

			//emergency stop if seal integrity reaches 0
			if(scanner_seal_integrity <= 0 || (scanner_temperature >= 1273 && !rad_shield))
				stop_scanning()
				src.visible_message("\icon[src] buzzes unhappily. It has failed mid-scan!", 2)

			if(prob(5))
				src.visible_message("\icon[src] [pick("whirrs","chuffs","clicks")][pick(" excitedly"," energetically"," busily")].", 2)
	else
		//gradually cool down over time
		if(scanner_temperature > 0)
			scanner_temperature = max(scanner_temperature - 5 - 10 * rand(), 0)
		if(prob(0.75))
			src.visible_message("\icon[src] [pick("plinks","hisses")][pick(" quietly"," softly"," sadly"," plaintively")].", 2)
	last_process_worldtime = world.time

/obj/machinery/radiocarbon_spectrometer/proc/stop_scanning()
	scanning = 0
	scanner_rpm_dir = 1
	scanner_rpm = 0
	optimal_wavelength = 0
	maser_efficiency = 0
	maser_wavelength = 0
	coolant_usage_rate = 0
	radiation = 0
	t_left_radspike = 0
	if(used_coolant)
		src.reagents.remove_any(used_coolant)
		used_coolant = 0

/obj/machinery/radiocarbon_spectrometer/proc/complete_scan()
	src.visible_message("\icon[src] makes an insistent chime.", 2)

	if(scanned_item)
		//create report
		var/obj/item/weapon/paper/P = new(src)
		P.name = "[src] report #[++report_num]: [scanned_item.name]"
		P.stamped = list(/obj/item/weapon/stamp)
		P.overlays = list("paper_stamped")

		//work out data
		var/data = " - Mundane object: [scanned_item.desc ? scanned_item.desc : "No information on record."]
"
		var/datum/geosample/G
		switch(scanned_item.type)
			if(/obj/item/weapon/ore)
				var/obj/item/weapon/ore/O = scanned_item
				if(O.geologic_data)
					G = O.geologic_data

			if(/obj/item/weapon/rocksliver)
				var/obj/item/weapon/rocksliver/O = scanned_item
				if(O.geological_data)
					G = O.geological_data

			if(/obj/item/weapon/archaeological_find)
				data = " - Mundane object (archaic xenos origins)
"

				var/obj/item/weapon/archaeological_find/A = scanned_item
				if(A.speaking_to_players)
					data = " - Exhibits properties consistent with sonic reproduction.
"
				if(A.listening_to_players)
					data = " - Exhibits properties similar to audio capture technology.
"

		var/anom_found = 0
		if(G)
			data = " - Spectometric analysis on mineral sample has determined type [finds_as_strings[responsive_carriers.Find(G.source_mineral)]]
"
			if(G.age_billion > 0)
				data += " - Radiometric dating shows age of [G.age_billion].[G.age_million] billion years
"
			else if(G.age_million > 0)
				data += " - Radiometric dating shows age of [G.age_million].[G.age_thousand] million years
"
			else
				data += " - Radiometric dating shows age of [G.age_thousand * 1000 + G.age] years
"
			data += " - Chromatographic analysis shows the following materials present:
"
			for(var/carrier in G.find_presence)
				if(G.find_presence[carrier])
					var/index = responsive_carriers.Find(carrier)
					if(index > 0 && index <= finds_as_strings.len)
						data += "	> [100 * G.find_presence[carrier]]% [finds_as_strings[index]]
"

			if(G.artifact_id && G.artifact_distance >= 0)
				anom_found = 1
				data += " - Hyperspectral imaging reveals exotic energy wavelength detected with ID: [G.artifact_id]
"
				data += " - Fourier transform analysis on anomalous energy absorption indicates energy source located inside emission radius of [G.artifact_distance]m
"

		if(!anom_found)
			data += " - No anomalous data
"

		P.info = "[src] analysis report #[report_num]
"
		P.info += "Scanned item: [scanned_item.name]

" + data
		last_scan_data = P.info
		P.loc = src.loc

		scanned_item.loc = src.loc
		scanned_item = null

/obj/machinery/radiocarbon_spectrometer/Topic(href, href_list)
	if(stat & (NOPOWER|BROKEN))
		return 0 // don't update UIs attached to this object
	if(href_list["close"])
		if(usr.machine == src) usr.unset_machine()
		return 1
	if(href_list["scanItem"])
		if(scanning)
			stop_scanning()
		else
			if(scanned_item)
				if(scanner_seal_integrity > 0)
					scanner_progress = 0
					scanning = 1
					t_left_radspike = pick(5,10,15)
					usr << "Scan initiated."
				else
					usr << "Could not initiate scan, seal requires replacing."
			else
				usr << "Insert an item to scan."

	if(href_list["maserWavelength"])
		maser_wavelength = max(min(maser_wavelength + 1000 * text2num(href_list["maserWavelength"]), 10000), 1)

	if(href_list["coolantRate"])
		coolant_usage_rate = max(min(coolant_usage_rate + text2num(href_list["coolantRate"]), 10000), 0)

	if(href_list["toggle_rad_shield"])
		if(rad_shield)
			rad_shield = 0
		else
			rad_shield = 1

	if(href_list["ejectItem"])
		if(scanned_item)
			scanned_item.loc = src.loc
			scanned_item = null

	add_fingerprint(usr)
	return 1 // update UIs attached to this object


code:
/obj/item/weapon/book/manual/excavation
	name = "Out on the dig"
	icon_state = "excavation"
	author = "Professor Patrick Mason, Curator of the Antiquities Museum on Ichar VII"
	title = "Out on the dig"
	dat = {"

Contents

Prepping the expedition

Knowing your tools

Finding the dig

Analysing deposits

Extracting your first find

Prepping the expedition

				Every digsite I've been to, someone has forgotten something and I've never yet been to a dig that hasn't had me hiking to get to it - so gather your gear
				and get it to the site the first time. You learn quick that time is money, when you've got a shipful of bandits searching for you the next valley over,
				but don't be afraid to clear some space if there are any inconvenient boulders in the way.

				<list>

Floodlights (if it's dark)

Wooden trestle tables (for holding tools and finds)

Suspension field generator

Load bearing servitors (such as a mulebot, or hover-tray)

Spare energy packs

				</list>

				Contents

Knowing your tools

				Every archaeologist has a plethora of tools at their disposal, but here's the important ones:

				<list>

Picks, pickaxes and brushes - don't underestimate the the smallest or largest in your arsenal, each one clears a different amount
					of the rockface so each one has a use.

Measuring tape - don't leave home without it, you can use it to measure the depth a rock face has been excavated to.

GPS locater - knowing where you are is the first step to not be lost.

Core sampler - use this to take core samples from rock faces, which you can then run to the lab for analysis.

Depth scanner - uses x-ray diffraction to locate anomalous densities in rock, indicating archaeological deposits or mineral veins.
					Comes with a handy reference log containing co-ordinates and time of each scan.

Alden-Saraspova counter - uses a patented application of Fourier Transform analysis to determine the difference between background and
					exotic radiation. Use it to determine how far you are from anomalous energy sources.

Radio beacon locater - leave a beacon at an item of interest, then track it down later with this handy gadget. Watch for interference from other
					devices though.

Flashlight or portable light source - Self explanatory, I hope.

Environmental safety gear - This one's dependant on the environment you're working in, but enclosed footwear and pack of internals
					could save your life.

Anomaly safety gear - A biosealed and catalysis-resistant suit along with eye shielding, tinted hood and non-reactive disposable gloves are
				the best kind of protection you can hope for from the errors our forbears may have unleashed.

Personal defence weapon - Never know what you'll find on the dig: pirates, natives, ancient guardians, carnivorous wildlife...
					it pays in blood to be prepared.

				</list>

				Contents

Finding the dig

				Wouldn't be an archaeologist without their dig, but everyone has to start somewhere. Here's a basic procedure I go through when cataloguing a new planet:

				<list>

Get in touch with the locals (in particular geologists, miners and farmers) - Never know what's been turned up by accident, then left to
					gather dust on a shelf.

Check the obvious areas first - even if you're pressed for time, these ones are the generally easiest to search, and the most likely targets
					of your rivals.

Do some prospecting - the earth mother isn't in the habit of displaying her secrets to the world (although sometimes you get lucky).
					Drop a shaft and clear away a bit of surface rock here and there, you never know what might be lurking below the surface.

Tips on unearthing a deposit - How do you know when you're golden? Look for telltale white strata that looks strange or out of place, or if
					something has broken under your pick while you're digging. Your depth scanner is your best friend, but even it can't distinguish between
					ordinary minerals and ancient leavings, if in doubt then err on the side of caution.

				</list>

				Contents

Analysing the contents of a dig

				You've found some unusual strata, but it's not all peaches from here. No archaeologist ever managed to pull a bone from the earth without doing thorough
				chemical analysis on every two meters of rock face nearby.

				<list>

Take core samples - Grab a rock core for every 4m^2.

Clear around any potential finds - Clear away ordinary rock, leaving your prizes reachable in a clearly marked area.

Haul off excess rock - It's easy for a dig to get cluttered, and a neat archaeologist is a successful archaeologist.

Don't be afraid to be cautious - It's slower sometimes, but the extra time will be worth the payoff when you find an Exolitic relic.

Chemical analysis - I won't go into detail here, but the labwork is essential to any successful extraction. Marshal your core samples, and
					send them off to the labcoated geniuses

				</list>

				Contents

Extracting your first find

				<list>

Scan the rock - Use a depth scanner to determine the find's depth and clearance. DON'T FORGET THESE.

Choose stasis field - Chemical analysis on a core sample from the rock face will tell you which field is necessary to extract the find safely

Setup field gen - Bolt it down, choose the field, check the charge and activate it. If you forget it, you'll wish you hadn't when that priceless
					Uryom vase crumbles as it sees the light of day.

FUNCTIONAL AND SAFE digging - Dig into the rock until you've cleared away a depth equal to (the anomaly depth MINUS the clearance range). The find
					should come loose on it's own, but it will be in the midst of a chunk of rock. Use a welder or miniature excavation tool to clear away the excess.

FANCY AND SPEEDY digging - Dig into the rock until you've cleared away a depth equal to the anomaly depth, but without any of your strokes
					entering the clearance range.

The Big Find - Sometimes, you'll chance upon something big, both literally and figuratively. Giant statues and functioning remnants of Precursor
					technology are just as exciting, to the right buyers. If your digging leaves a large boulder behind, dig into it normally and see if anything's hidden
						inside.

				</list>

				Contents

			"}

/obj/item/weapon/book/manual/mass_spectrometry
	name = "High power mass spectrometry, a comprehensive guide"
	icon_state = "analysis"
	author = "Winton Rice, Chief Mass Spectrometry Technician at the Institute of Applied Sciences on Arcadia"
	title = "High powered mass spectrometry, a comprehensive guide"
	dat = {"

Contents

A note on terms

Analysis progression

Heat management

Ambient radiation

A note on terms

				<list>

Mass spectrometry - MS is the procedure used used to measure and quantify the components of matter. The most prized tool in the field of
					'Materials analysis'

Radiometric dating - MS applied using the right carrier reagents can be used to accurately determine the age of a sample.

Dissonance ratio - This is a pseudoarbitrary value indicating the overal presence of a particular element in a greater composite.
					It takes into account volume, density, molecular excitation and isotope spread.

Vacuum seal integrity - A reference to how close an airtight seal is to failure.

				</list>

				Contents

Analysis progression

				Modern mass spectrometry requires constant attention from the diligant researcher in order to be successul. There are many different elements to juggle,
					and later chapters will delve into them. For the spectrometry assistant, the first thing you need to know is that the scanner wavelength is automatically
					calculated for you. Just tweak the settings and try to match it with the actual wavelength as closely as possible.

				Contents

Seal integrity

				In order to maintain sterile and environmentally static procedures, a special chamber is setup inside the spectrometer. It's protected by a proprietary vacuum seal
					produced by top tier industrial science. It will only last for a certain number of scans before failing outright, but it can be resealed through use of nanite paste.
					Unfortunately, it's susceptible to malforming under heat stress so exposing it to higher temperatures will cause it's operation life to drop significantly.

				Contents

Heat management

				The scanner relies on a gyro-rotational system that varies in speed and intensity. Over the course of an ordinary scan, the RPMs can change dramatically. Higher RPMs
					means greater heat generation, but is necessary for the ongoing continuation of the scan. To offset heat production, spectrometers have an inbuilt cooling system.
					Researchers can modify the flow rate of coolant to aid in dropping temperature as necessary, but are advised that frequent coolant replacements may be necessary
					depending on coolant purity. Water and substances such as cryoxadone are viable substitutes, but nowhere near as effective as pure coolant itself.

				Contents

Ambient radiation

				Researchers are warned that while operational, mass spectrometers emit period bursts of radiation and are thus advised to wear protective gear. In the event of
					radiation spikes, there is also a special shield that can be lowered to block emissions. Lowering this, however, will have the effect of blocking the scanner
					so use it sparingly.

				Contents

			"}

/obj/item/weapon/book/manual/anomaly_spectroscopy
	name = "Spectroscopy: Analysing the anomalies of the cosmos"
	icon_state = "anomaly"
	author = "Doctor Martin Boyle, Director Research at the Lower Hydrolian Sector Listening Array"
	title = "Spectroscopy: Analysing the anomalies of the cosmos"
	dat = {"

				It's perhaps one of the most exciting times to be alive, with the recent breakthroughs in understanding and categorisation of things we may one day no longer call
				'anomalies,' but rather 'infrequent or rare occurrences of certain celestial weather or phenomena.' Perhaps a little more long winded, but no less eloquent all the
				same! Why, look at the strides we're making in piercing the walls of bluespace or our steadily improving ability to clarify and stabilise subspace emissions; it's
				certainly an exciting time to be alive. For the moment the Hydrolian hasn't seen two spatial anomalies alike but the day will come and it is soon, I can feel it.
			"}

/obj/item/weapon/book/manual/materials_chemistry_analysis
	name = "Materials analysis and the chemical implications"
	icon_state = "chemistry"
	author = "Jasper Pascal, Senior Lecturer in Materials Analysis at the University of Jol'Nar"
	title = "Materials analysis and the chemical implications"
	dat = {"

				In today's high tech research fields, leaps and bounds are being made every day. Whether it's great strides forward in our understanding of the physical universe
				or the operation of some fancy new piece of equipment, it seems like all the cool fields are producing new toys to play with leaving doddery old fields such as
				materials analysis and chemistry relegated to the previous few centuries, when we were still learning the makeup and structure of the elements.

				Well, when you're out there building the next gryo-whatsitron or isotope mobility thingummy, remember how the field of archaeology experienced a massive new rebirth
				following the excavations at Paranol IV and consider how all of the scientific greats will come crawling back to basic paradigms of natural philosophy when they discover
				a new element that defies classification. I defy you to classify it without reviving this once great field!
			"}

/obj/item/weapon/book/manual/anomaly_testing
	name = "Anomalous materials and energies"
	icon_state = "triangulate"
	author = "Norman York, formerly of the Tyrolion Institute on Titan"
	title = "Anomalous materials and energies"
	dat = {"

Contents

Forward: Modern attitude towards anomalies

Triangulating anomalous energy readings

Harvesting and utilising anomalous energy signatures

Modern attitude towards anomalies

				It's only when confronted with things we don't know, that we may push back our knowledge of the world around us. Nowhere is this more obvious than the
				vast and inscrutable mysterious of the cosmos that scholars from such august institutions as the Elysian Institute of the Sciences present
				formulas and hypotheses for every few decades.

				Using our vast telescopic array installations and deep space satellite networks, we are able to detect anomalous energy fields and formations in deep space,
				but are limited to those that are large enough to output energy that will stretch across light years worth of distance between stars.

				While some sectors (such as the Hydrolian Rift and Keppel's Run) are replete with inexplicable energetic activity and unique phenomena found nowhere else in
				the galaxy, the majority of space is dry, barren and cold - and if past experience has told us anything, it is that there are always more things we are
				unable to explain.

				Indeed, a great source of knowledge and technology has always been those who come before us, in the form of the multitudinous ancient alien precursors that
				have left scattered remnants of their great past all over settled (and unexplored) space.

				It is from xenoarchaeologists, high energy materials researchers and technology reconstruction authorities that we are able to theorise on the gifts these
				species have left behind, and in some cases even reverse engineer or rebuild the technology in question. My colleague Doctor Raymond Ward of the
				Tyrolian Institute on Titan has made great breakthroughs in a related field through his pioneering development of universally reflective materials capable
				of harvesting and 'bottling' up virtually any energy emissions yet encountered by spacefaring civilisations.

				And yet, there are some amongst us who do not see the benefits of those who have come before us - indeed, some among them profess the opinion that there
				is no species that could possibly match humanity in it's achievements and knowledge, or simply that employing non-human technology is dangerous and unethical.
				Folly, say I. If it is their desire to throw onto the wayside the greatest achievements in the history of the galaxy, simply for preferment of the
				greatest achievements in the history of mankind, then they have no business in the establishment of science.

				Contents

Triangulating anomalous energy readings

				Strong energy emissions, when remaining constant from any one fixed location for millenia, can leave an 'imprint' or distinctive energy signature on other
				matter composites that are spatially fixed relative to the source.

				By taking samples of such 'fixed' matter, we can apply complex analytics such as the modified Fourier Transform Procedure to reverse engineer the path of the
				energy, and determine the approximate distance and direction that the energy source is, relative to the sample's point in space. Modern portable devices can do
				all this purely by taking readings of local radiation.

				A canny researcher can thusly analyse radiationat pre-chosen points strategically scattered around an area, and if there are any anomalous energy
				emissions in range of those points, combined the researcher can triangulate the source.

				Contents

Harvesting and utilising anomalous energy signatures

				As mentioned in the forward, my colleague from the Tyrolian Institute on Saturn's moon of Titan, in the Sol System, Doctor Raymond Ward has made great strides
				in the area of harvesting and application of the energy emitted by anomalous phenomena from around the galaxy (although I profess I have not yet seen him
				venture further from his birthplace on Earth than the comfortable distance of the Sol Cis-Oort Satellite Sphere).

				By employing a patented semi-phased alloy with unique and fascinating bluespace interaction properties, Ward's contraption is able to 'harvest' energy, store
				it and redirect it later at will (with appropriate electronic mechanisms, of course). Although he professes to see or desire no commercial or material gain
				for the application and use of said energy once it is harvested, there are no doubt myriad ways we can come to benefit from such things beyond mere research,
				such as the reconstruction of torn cartiligenous tissue that a peculiar radiation from an amphibious species on Brachis IV was found to emit.

				Contents

			"}

/obj/item/weapon/book/manual/stasis
	name = "Cellular suspension, the new Cryogenics?"
	icon_state = "stasis"
	author = "Elvin Schmidt"
	title = "Cellular suspension, the new Cryogenics?"
	dat = {"

Contents

Forward: A replacement for cryosleep?

The breakthrough

Applying this new principle

Forward: A replacement for cryosleep?

				The development of rudimentary cryofreezing in the 20th and 21st centuries was hailed as a crank science by some, but many early visionaries recognised the
				potential it had to change the way we approach so many fields, such as medicine, therapeutics and space travel. It was breakthroughs in the field in the 22nd and
				later centures that turned the procedure from science fiction to science fact, however. Since then, cryogenics has become a hallmark of modern science, and
				regarded as one of the great achievements of our era. As with all sciences however, they have their time and are superseded by newer technological miracles when
				it is over.

				Contents

The breakthrough

				It was in examining the effects of decellerated, blue-space high energy particles when transphased through bluespace that the effects where primarily noticed.
				Due to exigent properties of that dimension, transphasing those particles capable of existing in bluespace with high stability levels has the effect of bringing
				some of those effects into realspace. Examining the Hoffman emissions in particular, it was discovered that they exhibited a-entropic behaviour, and in what is
				now termed the 'Effete Hoffman Principle,' it was found that metastabilising the Hoffman radiation resulted in the effect being applied across other physical
				interactions, in particular forces and reactions.

				Contents

Applying this new principle

				When combined with an appropriate energy-effect replicate for cryogenics (slowing down biological activity, thus stabilising the organics), the effect is
				effectively identical to cryogenics, and while it consumes vastly more power and requires extremely complex equipment, it's (for all intents and purposes) superior
				to cryogenics, all that remains is to 'commercialise' the process by enabling cheaper development and mass production.

				The Effete Hoffman Principle can be tweak-combined with other effects however, for different purposes. A division of PMC Research initially developed the application
				in prisons as a literal 'suspension field' where convincts are held immobile in the air, and the use quickly spread to numerous other areas.

				By examining the material resonance properties of certain strong waveforms when combined with Hoffman radiation, an effect was produced able to reverse energy
				transferral through matter, and to slow the effects of gravity. When combined with energy repulse technology, the triple effects compound themselves into a much
				stronger field, although all three componenets do slightly different things. High energy researchers assure me of the following key points:

The energy repulsion factor provides a 'shell' capable of weak suspension.

The Hoffman emissions nullify energy transferral and other kinetic activity, maintaining stability inside the field.

The resonant waveform combines the effects of the above two points, and applies it magnified onto it's synched 'resonance' materials.

				As an interesting aside, a carbon waveform was chosen for the field in prison suspension fields, due to it's resonance with organic matter.

				Contents

			"}

license: mit


code:
#define XENOARCH_SPAWN_CHANCE 0.5
#define XENOARCH_SPREAD_CHANCE 15
#define ARTIFACT_SPAWN_CHANCE 20

proc/SetupXenoarch()
	for(var/turf/simulated/mineral/M in block(locate(1,1,1), locate(world.maxx, world.maxy, world.maxz)))
		if(!prob(XENOARCH_SPAWN_CHANCE))
			continue

		var/digsite = get_random_digsite_type()
		var/list/processed_turfs = list()
		var/list/turfs_to_process = list(M)

		while(turfs_to_process.len)
			var/turf/simulated/mineral/archeo_turf = turfs_to_process[1]

			for(var/turf/simulated/mineral/T in orange(1, archeo_turf))
				if(T.finds.len)
					continue

				if(T in processed_turfs)
					continue

				if(prob(XENOARCH_SPREAD_CHANCE))
					turfs_to_process.Add(T)

			turfs_to_process.Remove(archeo_turf)
			processed_turfs.Add(archeo_turf)

			if(!archeo_turf.finds || !archeo_turf.finds.len)

				if(prob(50))
					archeo_turf.finds.Add(new /datum/find(digsite, rand(5,95)))
				else if(prob(75))
					archeo_turf.finds.Add(new /datum/find(digsite, rand(5,45)))
					archeo_turf.finds.Add(new /datum/find(digsite, rand(55,95)))
				else
					archeo_turf.finds.Add(new /datum/find(digsite, rand(5,30)))
					archeo_turf.finds.Add(new /datum/find(digsite, rand(35,75)))
					archeo_turf.finds.Add(new /datum/find(digsite, rand(75,95)))

				//sometimes a find will be close enough to the surface to show
				var/datum/find/F = archeo_turf.finds[1]

				if(F.excavation_required <= F.view_range)
					archeo_turf.archaeo_overlay = "overlay_archaeo[rand(1,3)]"
					archeo_turf.overlays += archeo_turf.archaeo_overlay

		//dont create artifact machinery in animal or plant digsites, or if we already have one
		if(!M.artifact_find && digsite != 1 && digsite != 2 && prob(ARTIFACT_SPAWN_CHANCE))
			M.artifact_find = new()
			master_controller.artifact_spawning_turfs.Add(M)

		if(isnull(M.geologic_data))
			M.geologic_data = new/datum/geosample(M)

#undef XENOARCH_SPAWN_CHANCE
#undef XENOARCH_SPREAD_CHANCE
#undef ARTIFACT_SPAWN_CHANCE

//---- Noticeboard

/obj/structure/noticeboard/anomaly
	notices = 5
	icon_state = "nboard05"

/obj/structure/noticeboard/anomaly/New()
	//add some memos
	var/obj/item/weapon/paper/P = new()
	P.name = "Memo RE: proper analysis procedure"
	P.info = "
We keep test dummies in pens here for a reason, so standard procedure should be to activate newfound alien artifacts and place the two in close proximity. Promising items I might even approve monkey testing on."
	P.stamped = list(/obj/item/weapon/stamp/rd)
	P.overlays = list("paper_stamped_rd")
	src.contents += P

	P = new()
	P.name = "Memo RE: materials gathering"
	P.info = "Corasang,
the hands-on approach to gathering our samples may very well be slow at times, but it's safer than allowing the blundering miners to roll willy-nilly over our dig sites in their mechs, destroying everything in the process. And don't forget the escavation tools on your way out there!
- R.W"
	P.stamped = list(/obj/item/weapon/stamp/rd)
	P.overlays = list("paper_stamped_rd")
	src.contents += P

	P = new()
	P.name = "Memo RE: ethical quandaries"
	P.info = "Darion-

I don't care what his rank is, our business is that of science and knowledge - questions of moral application do not come into this. Sure, so there are those who would employ the energy-wave particles my modified device has managed to abscond for their own personal gain, but I can hardly see the practical benefits of some of these artifacts our benefactors left behind. Ward--"
	P.stamped = list(/obj/item/weapon/stamp/rd)
	P.overlays = list("paper_stamped_rd")
	src.contents += P

	P = new()
	P.name = "READ ME! Before you people destroy any more samples"
	P.info = "how many times do i have to tell you people, these xeno-arch samples are del-i-cate, and should be handled so! careful application of a focussed, concentrated heat or some corrosive liquids should clear away the extraneous carbon matter, while application of an energy beam will most decidedly destroy it entirely - like someone did to the chemical dispenser! W, the one who signs your paychecks"
	P.stamped = list(/obj/item/weapon/stamp/rd)
	P.overlays = list("paper_stamped_rd")
	src.contents += P

	P = new()
	P.name = "Reminder regarding the anomalous material suits"
	P.info = "Do you people think the anomaly suits are cheap to come by? I'm about a hair trigger away from instituting a log book for the damn things. Only wear them if you're going out for a dig, and for god's sake don't go tramping around in them unless you're field testing something, R"
	P.stamped = list(/obj/item/weapon/stamp/rd)
	P.overlays = list("paper_stamped_rd")
	src.contents += P

//---- Bookcase

/obj/structure/bookcase/manuals/xenoarchaeology
	name = "Xenoarchaeology Manuals bookcase"

	New()
		..()
		new /obj/item/weapon/book/manual/excavation(src)
		new /obj/item/weapon/book/manual/mass_spectrometry(src)
		new /obj/item/weapon/book/manual/materials_chemistry_analysis(src)
		new /obj/item/weapon/book/manual/anomaly_testing(src)
		new /obj/item/weapon/book/manual/anomaly_spectroscopy(src)
		new /obj/item/weapon/book/manual/stasis(src)
		update_icon()

//---- Lockers and closets

/obj/structure/closet/secure_closet/xenoarchaeologist
	name = "Xenoarchaeologist Locker"
	req_access = list(access_tox_storage)
	icon_state = "science"
	New()
		..()
		sleep(2)
		new /obj/item/clothing/under/rank/scientist(src)
		new /obj/item/clothing/suit/toggle/labcoat/science(src)
		new /obj/item/clothing/shoes/sneakers/white(src)
		new /obj/item/clothing/glasses/science(src)
		new /obj/item/device/radio/headset/headset_sci(src)
		new /obj/item/weapon/storage/belt/archaeology(src)
		return

/obj/structure/closet/excavation
	name = "Excavation tools"
	icon_state = "eng"
	icon_door = "eng_tool"

	New()
		..()
		sleep(2)
		new /obj/item/weapon/storage/belt/archaeology(src)
//		new /obj/item/weapon/storage/box/excavation(src)
		new /obj/item/device/flashlight/lantern(src)
		new /obj/item/device/depth_scanner(src)
//		new /obj/item/device/core_sampler(src)
		new /obj/item/device/gps/science(src)
//		new /obj/item/device/beacon_locator(src)
		new /obj/item/device/ano_scanner(src)
		new /obj/item/device/radio/beacon(src)
		new /obj/item/clothing/glasses/meson(src)
		new /obj/item/weapon/pickaxe(src)
		new /obj/item/device/measuring_tape(src)
		new /obj/item/weapon/pickaxe/excavationdrill(src)
		return

//---- Isolation room air alarms

/obj/machinery/alarm/isolation
	name = "Isolation room air control"
	req_access = list(access_research)


code:
//coming soon
//there'll probably be some stuff on the wiki at some point

//original code from alfie275 / lunacode, artifacts are from isaidno


code:
#define EFFECT_TOUCH 0
#define EFFECT_AURA 1
#define EFFECT_PULSE 2

/obj/item/weapon/anobattery
	name = "Anomaly power battery"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "anobattery0"
	var/datum/artifact_effect/battery_effect
	var/capacity = 200
	var/stored_charge = 0
	var/effect_id = ""

/obj/item/weapon/anobattery/New()
	. = ..()
	battery_effect = new()

/obj/item/weapon/anobattery/proc/UpdateSprite()
	var/p = (stored_charge/capacity)*100
	p = min(p, 100)
	icon_state = "anobattery[round(p,25)]"

/obj/item/weapon/anodevice
	name = "Anomaly power utilizer"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "anodev"
	var/cooldown = 0
	var/activated = 0
	var/timing = 0
	var/time = 50
	var/archived_time = 50
	var/obj/item/weapon/anobattery/inserted_battery
	var/turf/archived_loc

/obj/item/weapon/anodevice/New()
	. = ..()
	SSobj.processing.Add(src)

/obj/item/weapon/anodevice/attackby(var/obj/I as obj, var/mob/user as mob)
	if(istype(I, /obj/item/weapon/anobattery))
		if(!inserted_battery)
			user << "You insert the battery."
			user.drop_item()
			I.loc = src
			inserted_battery = I
			UpdateSprite()
	else
		return ..()

/obj/item/weapon/anodevice/attack_self(var/mob/user as mob)
	if(in_range(src, user))
		return src.interact(user)

/obj/item/weapon/anodevice/interact(var/mob/user)

	user.set_machine(src)
	var/dat = "Anomalous Materials Energy Utilizer
"
	if(inserted_battery)
		if(cooldown)
			dat += "Cooldown in progress, please wait.
"
		else if(activated)
			if(timing)
				dat += "Device active.
"
			else
				dat += "Device active in timed mode.
"

		dat += "[inserted_battery] inserted, anomaly ID: [inserted_battery.battery_effect.artifact_id ? inserted_battery.battery_effect.artifact_id : "NA"]
"
		dat += "Total Power: [inserted_battery.stored_charge]/[inserted_battery.capacity]

"
		dat += "Timed activation: -- - [time >= 1000 ? "[time/10]" : time >= 100 ? " [time/10]" : "  [time/10]" ] + ++
"
		if(cooldown)
			dat += "Cooldown in progress.
"
			dat += "
"
		else if(!activated)
			dat += "Start
"
			dat += "Start in timed mode
"
		else
			dat += "Shutdown emission
"
			dat += "
"
		dat += "Eject battery
"
	else
		dat += "Please insert battery
"

		dat += "
"
		dat += "
"
		dat += "
"

		dat += "
"
		dat += "
"
		dat += "
"

	dat += "
"
	dat += "Refresh Close"

	user << browse(dat, "window=anodevice;size=400x500")
	onclose(user, "anodevice")

/obj/item/weapon/anodevice/process()
	if(cooldown > 0)
		cooldown -= 1
		if(cooldown <= 0)
			cooldown = 0
			src.visible_message("\icon[src] [src] chimes.", "\icon[src] You hear something chime.")
	else if(activated)
		if(inserted_battery && inserted_battery.battery_effect)
			//make sure the effect is active
			if(!inserted_battery.battery_effect.activated)
				inserted_battery.battery_effect.ToggleActivate(1)

			//update the effect loc
			var/turf/T = get_turf(src)
			if(T != archived_loc)
				archived_loc = T
				inserted_battery.battery_effect.UpdateMove()

			//process the effect
			inserted_battery.battery_effect.process()
			//if someone is holding the device, do the effect on them
			if(inserted_battery.battery_effect.effect == EFFECT_TOUCH && ismob(src.loc))
				inserted_battery.battery_effect.DoEffectTouch(src.loc)

			//handle charge
			inserted_battery.stored_charge -= 1
			if(inserted_battery.stored_charge <= 0)
				shutdown_emission()

			//handle timed mode
			if(timing)
				time -= 1
				if(time <= 0)
					shutdown_emission()
		else
			shutdown()

/obj/item/weapon/anodevice/proc/shutdown_emission()
	if(activated)
		activated = 0
		timing = 0
		src.visible_message("\icon[src] [src] buzzes.", "\icon[src]You hear something buzz.")

		cooldown = archived_time / 2

		if(inserted_battery.battery_effect.activated)
			inserted_battery.battery_effect.ToggleActivate(1)

/obj/item/weapon/anodevice/Topic(href, href_list)

	if ((get_dist(src, usr) > 1))
		return
	usr.set_machine(src)
	if(href_list["neg_changetime_max"])
		time += -100
		if(time > inserted_battery.capacity)
			time = inserted_battery.capacity
		else if (time < 0)
			time = 0
	if(href_list["neg_changetime"])
		time += -10
		if(time > inserted_battery.capacity)
			time = inserted_battery.capacity
		else if (time < 0)
			time = 0
	if(href_list["changetime"])
		time += 10
		if(time > inserted_battery.capacity)
			time = inserted_battery.capacity
		else if (time < 0)
			time = 0
	if(href_list["changetime_max"])
		time += 100
		if(time > inserted_battery.capacity)
			time = inserted_battery.capacity
		else if (time < 0)
			time = 0
	if(href_list["startup"])
		activated = 1
		timing = 0
		if(!inserted_battery.battery_effect.activated)
			inserted_battery.battery_effect.ToggleActivate(1)
	if(href_list["shutdown"])
		activated = 0
	if(href_list["starttimer"])
		timing = 1
		archived_time = time
	if(href_list["ejectbattery"])
		shutdown_emission()
		inserted_battery.loc = get_turf(src)
		inserted_battery = null
		UpdateSprite()
	if(href_list["close"])
		usr << browse(null, "window=anodevice")
		usr.unset_machine(src)
		return
	src.interact(usr)
	..()
	updateDialog()

/obj/item/weapon/anodevice/proc/UpdateSprite()
	if(!inserted_battery)
		icon_state = "anodev"
		return
	var/p = (inserted_battery.stored_charge/inserted_battery.capacity)*100
	p = min(p, 100)
	icon_state = "anodev[round(p,25)]"

/obj/item/weapon/anodevice/Destroy()
	SSobj.processing.Remove(src)
	..()


code:
//changes: rad protection up to 100 from 20/50 respectively
/obj/item/clothing/suit/bio_suit/anomaly
	name = "Anomaly suit"
	desc = "A sealed bio suit capable of insulating against exotic alien energies"
	icon_state = "engspace_suit"
	item_state = "engspace_suit"
	armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100)

/obj/item/clothing/head/bio_hood/anomaly
	name = "Anomaly hood"
	desc = "A sealed bio hood capable of insulating against exotic alien energies."
	icon_state = "engspace_helmet"
	item_state = "engspace_helmet"
	armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100)

/obj/item/clothing/suit/space/anomaly
	name = "Excavation suit"
	desc = "A pressure resistant excavation suit partially capable of insulating against exotic alien energies."
	icon_state = "cespace_suit"
	item_state = "cespace_suit"
	armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100)
	allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank)

/obj/item/clothing/head/helmet/space/anomaly
	name = "Excavation hood"
	desc = "A pressure resistant excavation hood partially capable of insulating against exotic alien energies."
	icon_state = "cespace_helmet"
	item_state = "cespace_helmet"
	armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100)


code:
/obj/machinery/bunsen_burner
	name = "bunsen burner"
	desc = "A flat, self-heating device designed for bringing chemical mixtures to boil."
	icon = 'icons/obj/device.dmi'
	icon_state = "bunsen0"
	var/heating = 0		//whether the bunsen is turned on
	var/heated = 0		//whether the bunsen has been on long enough to let stuff react
	var/obj/item/weapon/reagent_containers/held_container
	var/heat_time = 50
	ghost_read = 0

/obj/machinery/bunsen_burner/attackby(obj/item/weapon/W as obj, mob/user as mob)
	if(istype(W, /obj/item/weapon/reagent_containers))
		if(held_container)
			user << "You must remove the [held_container] first."
		else
			user.drop_item(W, src)
			held_container = W
			user << "You put the [held_container] onto the [src]."
			var/image/I = image("icon"=W, "layer"=FLOAT_LAYER)
			underlays += I
			if(heating)
				spawn(heat_time)
					try_heating()

			return 1 // avoid afterattack() being called
	else
		user << "You can't put the [W] onto the [src]."

/obj/machinery/bunsen_burner/attack_hand(mob/user as mob)
	if(held_container)
		underlays = null
		user << "You remove the [held_container] from the [src]."
		held_container.loc = src.loc
		held_container.attack_hand(user)
		held_container = null
	else
		user << "There is nothing on the [src]."

/obj/machinery/bunsen_burner/proc/try_heating()
	src.visible_message("\icon[src] [src] hisses.")
	if(held_container && heating)
		heated = 1
		held_container.reagents.handle_reactions()
		heated = 0
		spawn(heat_time)
			try_heating()

/obj/machinery/bunsen_burner/verb/toggle()
	set src in view(1)
	set name = "Toggle bunsen burner"
	set category = "Object"

	heating = !heating
	icon_state = "bunsen[heating]"
	if(heating)
		spawn(heat_time)
			try_heating()


code:
/obj/item/weapon/storage/belt/archaeology
	name = "excavation gear-belt"
	desc = "Can hold various excavation gear."
	icon_state = "gearbelt"
	item_state = "utility"
	w_class = 4 //Lets it hold mining satchels.
	max_w_class = 4
	max_combined_w_class = 28
	can_hold = list(
		/obj/item/weapon/storage/box/samplebags,
		/obj/item/device/core_sampler,
		/obj/item/device/ano_scanner,
		/obj/item/device/radio/beacon,
		/obj/item/device/gps,
		/obj/item/device/measuring_tape,
		/obj/item/device/flashlight,
		/obj/item/weapon/pickaxe,
		/obj/item/device/depth_scanner,
		/obj/item/device/camera,
		/obj/item/weapon/paper,
		/obj/item/weapon/photo,
		/obj/item/weapon/folder,
		/obj/item/weapon/pen,
		/obj/item/weapon/folder,
		/obj/item/weapon/clipboard,
		/obj/item/weapon/anodevice,
		/obj/item/clothing/glasses,
		/obj/item/weapon/wrench,
		/obj/item/weapon/storage/box/excavation,
		/obj/item/weapon/anobattery,
		/obj/item/weapon/weldingtool)


code:
/obj/machinery/suspension_gen
	name = "suspension field generator"
	desc = "It has stubby legs bolted up against it's body for stabilising."
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "suspension2-b"
	density = 1
	req_access = list(access_research)
	var/obj/item/weapon/stock_parts/cell/cell
	var/obj/item/weapon/card/id/auth_card
	var/locked = 1
	var/open = 0
	var/screwed = 1
	var/field_type = ""
	var/power_use = 25
	var/obj/effect/suspension_field/suspension_field
	var/list/secured_mobs = list()

/obj/machinery/suspension_gen/power_change()
	return

/obj/machinery/suspension_gen/New()
	src.cell = new/obj/item/weapon/stock_parts/cell/high(src)
	..()

/obj/machinery/suspension_gen/process()
	//set background = 1

	if (suspension_field)
		cell.charge -= power_use

		var/turf/T = get_turf(suspension_field)
		if(field_type == "carbon")
			for(var/mob/living/carbon/M in T)
				M.weakened = max(M.weakened, 3)
				cell.charge -= power_use
				if(prob(5))
					M << "[pick("You feel tingly.","You feel like floating.","It is hard to speak.","You can barely move.")]"

		if(field_type == "iron")
			for(var/mob/living/silicon/M in T)
				M.weakened = max(M.weakened, 3)
				cell.charge -= power_use
				if(prob(5))
					M << "[pick("You feel tingly.","You feel like floating.","It is hard to speak.","You can barely move.")]"

		for(var/obj/item/I in T)
			if(!suspension_field.contents.len)
				suspension_field.icon_state = "energynet"
				suspension_field.overlays += "shield2"
			I.loc = suspension_field

		for(var/mob/living/simple_animal/M in T)
			M.weakened = max(M.weakened, 3)
			cell.charge -= power_use
			if(prob(5))
				M << "[pick("You feel tingly.","You feel like floating.","It is hard to speak.","You can barely move.")]"

		if(cell.charge <= 0)
			deactivate()

/obj/machinery/suspension_gen/interact(mob/user as mob)
	var/dat = "Multi-phase mobile suspension field generator MK II \"Steadfast\"
"
	if(cell)
		var/colour = "red"
		if(cell.charge / cell.maxcharge > 0.66)
			colour = "green"
		else if(cell.charge / cell.maxcharge > 0.33)
			colour = "orange"
		dat += "Energy cell: [100 * cell.charge / cell.maxcharge]%
"
	else
		dat += "Energy cell: None
"
	if(auth_card)
		dat += "\[[auth_card]\]
"
		if(!locked)
			dat += "[suspension_field ? "Disable" : "Enable"] field
"
		else
			dat += "
"
	else
		dat += "\[------\]
"
		if(!locked)
			dat += "[suspension_field ? "Disable" : "Enable"] field
"
		else
			dat += "Enter your ID to begin.
"

	dat += "
"
	if(!locked)
		dat += "Select field mode
"
		dat += "[field_type=="carbon"?"":""			]Diffracted carbon dioxide laser
"
		dat += "[field_type=="nitrogen"?"":""		]Nitrogen tracer field
"
		dat += "[field_type=="potassium"?"":""		]Potassium refrigerant cloud
"
		dat += "[field_type=="mercury"?"":""	]Mercury dispersion wave
"
		dat += "[field_type=="iron"?"":""		]Iron wafer conduction field
"
		dat += "[field_type=="calcium"?"":""	]Calcium binary deoxidiser
"
		dat += "[field_type=="chlorine"?"":""	]Chlorine diffusion emissions
"
		dat += "[field_type=="plasma"?"":""	]Plasma saturated field
"
	else
		dat += "
"
		dat += "
"
		dat += "
"
		dat += "
"
		dat += "
"
		dat += "
"
		dat += "
"
		dat += "
"
	dat += "
"
	dat += "Always wear safety gear and consult a field manual before operation.
"
	if(!locked)
		dat += "Lock console
"
	else
		dat += "
"
	dat += "Refresh console
"
	dat += "Close console"
	user << browse(dat, "window=suspension;size=500x400")
	onclose(user, "suspension")

/obj/machinery/suspension_gen/Topic(href, href_list)
	if(..()) return
	usr.set_machine(src)

	if(href_list["toggle_field"])
		if(!suspension_field)
			if(cell.charge > 0)
				if(anchored)
					activate()
				else
					usr << "You are unable to activate [src] until it is properly secured on the ground."
		else
			deactivate()
	if(href_list["select_field"])
		field_type = href_list["select_field"]
	else if(href_list["insertcard"])
		var/obj/item/I = usr.get_active_hand()
		if (istype(I, /obj/item/weapon/card))
			usr.unEquip(I, src)
			I.loc = src
			auth_card = I
			if(attempt_unlock(I))
				usr << "You insert [I], the console flashes \'Access granted.\'"
			else
				usr << "You insert [I], the console flashes \'Access denied.\'"
	else if(href_list["ejectcard"])
		if(auth_card)
			if(ishuman(usr))
				auth_card.loc = usr.loc
				usr.put_in_hands(auth_card)
				auth_card = null
			else
				auth_card.loc = loc
				auth_card = null
	else if(href_list["lock"])
		locked = 1
	else if(href_list["close"])
		usr.unset_machine()
		usr << browse(null, "window=suspension")

	updateUsrDialog()

/obj/machinery/suspension_gen/attack_hand(mob/user as mob)
	if(!open)
		interact(user)
	else if(cell)
		if(isobserver(user))
			return 0
		cell.loc = loc
		cell.add_fingerprint(user)
		cell.updateicon()

		if(anchored)
			icon_state = "suspension0"
		else
			icon_state = "suspension0-b"
		cell = null
		user << "You remove the power cell"

/obj/machinery/suspension_gen/attackby(obj/item/weapon/W as obj, mob/user as mob)
	if (istype(W, /obj/item/weapon/screwdriver))
		if(!open)
			if(screwed)
				screwed = 0
			else
				screwed = 1
			user << "You [screwed ? "screw" : "unscrew"] the battery panel."
	else if (istype(W, /obj/item/weapon/crowbar))
		if(!locked)
			if(!screwed)
				if(!suspension_field)
					if(open)
						open = 0
					else
						open = 1
					user << "You crowbar the battery panel [open ? "open" : "in place"]."
					icon_state = "suspension[anchored ? (open ? (cell ? "1" : "0") : "2") : (open ? (cell ? "1-b" : "0-b") : "2-b")]"
				else
					user << "[src]'s safety locks are engaged, shut it down first."
			else
				user << "Unscrew [src]'s battery panel first."
		else
			user << "[src]'s security locks are engaged."
	else if (istype(W, /obj/item/weapon/wrench))
		if(!suspension_field)
			if(anchored)
				anchored = 0
			else
				anchored = 1
			icon_state = "suspension[anchored ? (open ? (cell ? "1" : "0") : "2") : (open ? (cell ? "1-b" : "0-b") : "2-b")]"
			user << "You wrench the stabilising legs [anchored ? "into place" : "up against the body"]."
			if(anchored)
				desc = "It is resting securely on four stubby legs."
			else
				desc = "It has stubby legs bolted up against it's body for stabilising."
		else
			user << "You are unable to secure [src] while it is active!"
	else if (istype(W, /obj/item/weapon/stock_parts/cell))
		if(open)
			if(cell)
				user << "There is a power cell already installed."
			else
				user.drop_item(W, src)
				cell = W
				user << "You insert the power cell."
				if(anchored)
					icon_state = "suspension1"
				else
					icon_state = "suspension1-b"
	else if(istype(W, /obj/item/weapon/card))
		var/obj/item/weapon/card/I = W
		if(!auth_card)
			if(attempt_unlock(I))
				user << "You swipe [I], the console flashes \'Access granted.\'"
			else
				user << "You swipe [I], console flashes \'Access denied.\'"
		else
			user << "Remove [auth_card] first."

/obj/machinery/suspension_gen/proc/attempt_unlock(var/obj/item/weapon/card/C)
	if(!open)
		if(istype(C, /obj/item/weapon/card/emag) && cell.charge > 0)
			//put sparks here
			if(prob(95))
				locked = 0
		else if(istype(C, /obj/item/weapon/card/id) && check_access(C))
			locked = 0

		if(!locked)
			return 1

//checks for whether the machine can be activated or not should already have occurred by this point
/obj/machinery/suspension_gen/proc/activate()
	//depending on the field type, we might pickup certain items
	var/turf/T = get_turf(get_step(src,dir))
	var/success = 0
	var/collected = 0
	switch(field_type)
		if("carbon")
			success = 1
			for(var/mob/living/carbon/C in T)
				C.weakened += 5
				C.visible_message("\icon[C] [C] begins to float in the air!","You feel tingly and light, but it is difficult to move.")
		if("nitrogen")
			success = 1
			//
		if("mercury")
			success = 1
			//
		if("chlorine")
			success = 1
			//
		if("potassium")
			success = 1
			//
		if("plasma")
			success = 1
			//
		if("calcium")
			success = 1
			//
		if("iron")
			success = 1
			for(var/mob/living/silicon/R in T)
				R.weakened += 5
				R.visible_message("\icon[R] [R] begins to float in the air!","You feel tingly and light, but it is difficult to move.")
			//
	//in case we have a bad field type
	if(!success)
		return

	for(var/mob/living/simple_animal/C in T)
		C.visible_message("\icon[C] [C] begins to float in the air!","You feel tingly and light, but it is difficult to move.")
		C.weakened += 5

	suspension_field = new(T)
	suspension_field.field_type = field_type
	src.visible_message("\icon[src] [src] activates with a low hum.")
	icon_state = "suspension3"

	for(var/obj/item/I in T)
		I.loc = suspension_field
		collected++

	if(collected)
		suspension_field.icon_state = "energynet"
		suspension_field.overlays += "shield2"
		src.visible_message("\icon[suspension_field] [suspension_field] gently absconds [collected > 1 ? "something" : "several things"].")
	else
		if(istype(T,/turf/simulated/mineral) || istype(T,/turf/simulated/wall))
			suspension_field.icon_state = "shieldsparkles"
		else
			suspension_field.icon_state = "shield2"

/obj/machinery/suspension_gen/proc/deactivate()
	//drop anything we picked up
	var/turf/T = get_turf(suspension_field)

	for(var/mob/M in T)
		M << "You no longer feel like floating."
		M.weakened = min(M.weakened, 3)

	src.visible_message("\icon[src] [src] deactivates with a gentle shudder.")
	qdel(suspension_field)
	suspension_field = null
	icon_state = "suspension2"

/obj/machinery/suspension_gen/Destroy()
	//safety checks: clear the field and drop anything it's holding
	deactivate()
	..()

/obj/machinery/suspension_gen/verb/toggle()
	set src in view(1)
	set name = "Rotate suspension gen (clockwise)"
	set category = "Object"

	if(anchored)
		usr << "You cannot rotate [src], it has been firmly fixed to the floor."
	else
		dir = turn(dir, 90)

/obj/effect/suspension_field
	name = "energy field"
	icon = 'icons/effects/effects.dmi'
	anchored = 1
	density = 1
	var/field_type = "chlorine"

/obj/effect/suspension_field/Destroy()
	for(var/obj/I in src)
		I.loc = src.loc
	..()


code:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Miscellaneous xenoarchaeology tools
/*
/obj/item/device/gps
	name = "relay positioning device"
	desc = "Triangulates the approximate co-ordinates using a nearby satellite network."
	icon = 'icons/obj/device.dmi'
	icon_state = "locator"
	item_state = "locator"
	w_class = 2

/obj/item/device/gps/attack_self(var/mob/user as mob)
	var/turf/T = get_turf(src)
	user << "\icon[src] [src] flashes [T.x-WORLD_X_OFFSET[T.z]].[rand(0,9)]:[T.y-WORLD_Y_OFFSET[T.z]].[rand(0,9)]:[T.z].[rand(0,9)]."
*/
/obj/item/device/measuring_tape
	name = "measuring tape"
	desc = "A coiled metallic tape used to check dimensions and lengths."
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "measuring"
	w_class = 2

//todo: dig site tape


code:
/obj/item/device/ano_scanner
	name = "Alden-Saraspova counter"
	desc = "Aids in triangulation of exotic particles."
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "ano_scanner"
	item_state = "lampgreen"
	w_class = 1.0
	flags = FPRINT
	slot_flags = SLOT_BELT
	var/nearest_artifact_id = "unknown"
	var/nearest_artifact_distance = -1
	var/last_scan_time = 0
	var/scan_delay = 25

/obj/item/device/ano_scanner/attack_self(var/mob/user as mob)
	return src.interact(user)

/obj/item/device/ano_scanner/interact(var/mob/user as mob)
	var/message = "Background radiation levels detected."

	if(nearest_artifact_distance >= 0)
		message = "Exotic energy detected on wavelength '[nearest_artifact_id]' in a radius of [nearest_artifact_distance]m"

	user << "[message]"

	if(world.time - last_scan_time >= scan_delay)
		spawn(0)
			scan()

/obj/item/device/ano_scanner/proc/scan()
	last_scan_time = world.time
	nearest_artifact_distance = -1

	var/turf/cur_turf = get_turf(src)

	if(master_controller) //Sanity check due to runtimes ~Z
		for(var/turf/simulated/mineral/T in master_controller.artifact_spawning_turfs)
			if(T.artifact_find)
				if(T.z == cur_turf.z)
					var/cur_dist = get_dist(cur_turf, T) * 2

					if((nearest_artifact_distance < 0 || cur_dist < nearest_artifact_distance) && cur_dist <= T.artifact_find.artifact_detect_range)
						nearest_artifact_distance = cur_dist + rand() * 2 - 1
						nearest_artifact_id = T.artifact_find.artifact_id
			else
				master_controller.artifact_spawning_turfs.Remove(T)

	cur_turf.visible_message("[src] clicks.")


code:
//device to take core samples from mineral turfs - used for various types of analysis

/obj/item/weapon/storage/box/samplebags
	name = "sample bag box"
	desc = "A box claiming to contain sample bags."

/obj/item/weapon/storage/box/samplebags/New()
	for(var/i=0, i<7, i++)
		var/obj/item/weapon/evidencebag/S = new(src)
		S.name = "sample bag"
		S.desc = "a bag for holding research samples."
	..()
	return

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

/obj/item/device/core_sampler
	name = "core sampler"
	desc = "Used to extract geological core samples."
	icon = 'icons/obj/device.dmi'
	icon_state = "sampler0"
	item_state = "screwdriver_brown"
	w_class = 1.0
	flags = FPRINT
	//slot_flags = SLOT_BELT
	var/sampled_turf = ""
	var/num_stored_bags = 10
	var/obj/item/weapon/evidencebag/filled_bag

/obj/item/device/core_sampler/examine(mob/user)
	..()
	if(get_dist(src, user) < 2)
		user << "This one is [sampled_turf ? "full" : "empty"], and has [num_stored_bags] bag[num_stored_bags != 1 ? "s" : ""] remaining."

/obj/item/device/core_sampler/attackby(obj/item/weapon/W as obj, mob/user as mob)
	if(istype(W,/obj/item/weapon/evidencebag))
		if(num_stored_bags < 10)
			del(W)
			num_stored_bags += 1
			user << "You insert the [W] into the core sampler."
		else
			user << "The core sampler can not fit any more bags!"
	else
		return ..()

/obj/item/device/core_sampler/proc/sample_item(var/item_to_sample, var/mob/user as mob)
	var/datum/geosample/geo_data
	if(istype(item_to_sample, /turf/simulated/mineral))
		var/turf/simulated/mineral/T = item_to_sample
		T.geologic_data.UpdateNearbyArtifactInfo(T)
		geo_data = T.geologic_data
	else if(istype(item_to_sample, /obj/item/weapon/strangerock))
		var/obj/item/weapon/strangerock/O = item_to_sample
		geo_data = O.geologic_data

	if(geo_data)
		if(filled_bag)
			user << "The core sampler is full!"
		else if(num_stored_bags < 1)
			user << "The core sampler is out of sample bags!"
		else
			//create a new sample bag which we'll fill with rock samples
			filled_bag = new /obj/item/weapon/evidencebag(src)
			filled_bag.name = "sample bag"
			filled_bag.desc = "a bag for holding research samples."

			icon_state = "sampler1"
			num_stored_bags--

			//put in a rock sliver
			var/obj/item/weapon/rocksliver/R = new()
			R.geological_data = geo_data
			R.loc = filled_bag

			//update the sample bag
			filled_bag.icon_state = "evidence"
			var/image/I = image("icon"=R, "layer"=FLOAT_LAYER)
			filled_bag.underlays += I
			filled_bag.w_class = 1

			user << "You take a core sample of the [item_to_sample]."
	else
		user << "You are unable to take a sample of [item_to_sample]."

/obj/item/device/core_sampler/attack_self()
	if(filled_bag)
		usr << "You eject the full sample bag."
		var/success = 0
		if(istype(src.loc, /mob))
			var/mob/M = src.loc
			success = M.put_in_inactive_hand(filled_bag)
		if(!success)
			filled_bag.loc = get_turf(src)
		filled_bag = null
		icon_state = "sampler0"
	else
		usr << "The core sampler is empty."


code:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Depth scanner - scans rock turfs / boulders and tells players if there is anything interesting inside, logs all finds + coordinates + times

//also known as the x-ray diffractor
/obj/item/device/depth_scanner
	name = "depth analysis scanner"
	desc = "Used to check spatial depth and density of rock outcroppings."
	icon = 'icons/obj/pda.dmi'
	icon_state = "crap"
	item_state = "analyzer"
	w_class = 1.0
	flags = FPRINT
	slot_flags = SLOT_BELT
	var/list/positive_locations = list()
	var/datum/depth_scan/current

/datum/depth_scan
	var/time = ""
	var/coords = ""
	var/depth = 0
	var/clearance = 0
	var/record_index = 1
	var/dissonance_spread = 1
	var/material = "unknown"

/obj/item/device/depth_scanner/proc/scan_atom(var/mob/user, var/atom/A)
	user.visible_message("[user] scans [A], the air around them humming gently.")
	if(istype(A,/turf/simulated/mineral))
		var/turf/simulated/mineral/M = A
		if(M.finds.len || M.artifact_find)

			//create a new scanlog entry
			var/datum/depth_scan/D = new()
			D.coords = "[M.x].[rand(0,9)]:[M.y].[rand(0,9)]:[10 * M.z].[rand(0,9)]"
			D.time = worldtime2text()
			D.record_index = positive_locations.len + 1
			D.material = M.mineralName ? M.mineralName : "Rock"

			//find the first artifact and store it
			if(M.finds.len)
				var/datum/find/F = M.finds[1]
				D.depth = F.excavation_required * 2		//0-100% and 0-200cm
				D.clearance = F.clearance_range * 2
				D.material = get_responsive_reagent(F.find_type)
			/*
			if(M.excavation_minerals.len)
				if(M.excavation_minerals[1] < D.depth)
					D.depth = M.excavation_minerals[1]
					D.clearance = rand(2,6)
					D.dissonance_spread = rand(1,1000) / 100
			*/

			positive_locations.Add(D)

			for(var/mob/L in range(src, 1))
				L << "\icon[src] [src] pings."

	else if(istype(A,/obj/structure/boulder))
		var/obj/structure/boulder/B = A
		if(B.artifact_find)
			//create a new scanlog entry
			var/datum/depth_scan/D = new()
			D.coords = "[10 * (B.x)].[rand(0,9)]:[10 * (B.y)].[rand(0,9)]:[10 * B.z].[rand(0,9)]"
			D.time = worldtime2text()
			D.record_index = positive_locations.len + 1

			//these values are arbitrary
			D.depth = rand(75,100)
			D.clearance = rand(5,25)
			D.dissonance_spread = rand(750,2500) / 100

			positive_locations.Add(D)

			for(var/mob/L in range(src, 1))
				L << "\icon[src] [src] pings [pick("madly","wildly","excitedly","crazily")]!."

/obj/item/device/depth_scanner/attack_self(var/mob/user as mob)
	return src.interact(user)

/obj/item/device/depth_scanner/interact(var/mob/user as mob)
	var/dat = "Co-ordinates with positive matches
"
	dat += "== Clear all ==
"
	if(current)
		dat += "Time: [current.time]
"
		dat += "Coords: [current.coords]
"
		dat += "Anomaly depth: [current.depth] cm
"
		dat += "Clearance above anomaly depth: [current.clearance] cm
"
		dat += "Dissonance spread: [current.dissonance_spread]
"
		var/index = responsive_carriers.Find(current.material)
		if(index > 0 && index <= finds_as_strings.len)
			dat += "Anomaly material: [finds_as_strings[index]]
"
		else
			dat += "Anomaly material: Unknown
"
		dat += "clear entry
"
	else
		dat += "Select an entry from the list
"
		dat += "
"
		dat += "
"
		dat += "
"
		dat += "
"
	dat += "
"
	if(positive_locations.len)
		for(var/index=1, index<=positive_locations.len, index++)
			var/datum/depth_scan/D = positive_locations[index]
			dat += "[D.time], coords: [D.coords]
"
	else
		dat += "No entries recorded."
	dat += "
"
	dat += "Refresh
"
	dat += "Close
"
	user << browse(dat,"window=depth_scanner;size=300x500")
	onclose(user, "depth_scanner")

/obj/item/device/depth_scanner/Topic(href, href_list)
	..()
	usr.set_machine(src)

	if(href_list["select"])
		var/index = text2num(href_list["select"])
		if(index && index <= positive_locations.len)
			current = positive_locations[index]
	else if(href_list["clear"])
		var/index = text2num(href_list["clear"])
		if(index)
			if(index <= positive_locations.len)
				var/datum/depth_scan/D = positive_locations[index]
				positive_locations.Remove(D)
				del(D)
		else
			//GC will hopefully pick them up before too long
			positive_locations = list()
			del(current)
	else if(href_list["close"])
		usr.unset_machine()
		usr << browse(null, "window=depth_scanner")

	updateSelfDialog()


code:
/obj/item/weapon/pickaxe/excavationdrill
	name = "excavation drill"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "excavationdrill0"
//	inhand_states = list("left_hand" = 'icons/mob/in-hand/left/xenoarch.dmi', "right_hand" = 'icons/mob/in-hand/right/xenoarch.dmi')
	item_state = "excavationdrill"
	excavation_amount = 0.5
	digspeed = 30
	desc = "Advanced archaeological drill combining ultrasonic excitation and bluespace manipulation to provide extreme precision. The diamond tip is adjustable from 1 to 30 cms."
	force = 15.0
	w_class = 2
//	w_type = RECYK_ELECTRONIC
	attack_verb = list("drilled")
	hitsound = 'sound/weapons/circsawhit.ogg'

/obj/item/weapon/pickaxe/excavationdrill/attack_self(mob/user as mob)
	var/depth = input("Put the desired depth (1-30 centimeters).", "Set Depth", excavation_amount*2) as num
	if(depth>30 || depth<1)
		user << "Invalid depth."
		return
	excavation_amount = depth/2
	user << "You set the depth to [depth]cm."
	if (depth<4)
		icon_state = "excavationdrill0"
	else if (depth >=4 && depth <8)
		icon_state = "excavationdrill1"
	else if (depth >=8 && depth <12)
		icon_state = "excavationdrill2"
	else if (depth >=12 && depth <16)
		icon_state = "excavationdrill3"
	else if (depth >=16 && depth <20)
		icon_state = "excavationdrill4"
	else if (depth >=20 && depth <24)
		icon_state = "excavationdrill5"
	else if (depth >=24 && depth <28)
		icon_state = "excavationdrill6"
	else
		icon_state = "excavationdrill7"

/obj/item/weapon/pickaxe/excavationdrill/examine(mob/user)
	..()
	var/depth = excavation_amount*2
	user << "It is currently set at [depth]cm."


code:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Excavation pickaxes - sorted in order of delicacy. Players will have to choose the right one for each part of excavation.

/obj/item/weapon/pickaxe/brush
	name = "brush"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick_brush"
	item_state = "syringe_0"
	digspeed = 20
	desc = "Thick metallic wires for clearing away dust and loose scree (1 centimetre excavation depth)."
	excavation_amount = 0.5
	w_class = 2

/obj/item/weapon/pickaxe/one_pick
	name = "1/6 pick"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick1"
	item_state = "syringe_0"
	digspeed = 20
	desc = "A miniature excavation tool for precise digging (2 centimetre excavation depth)."
	excavation_amount = 1

	w_class = 2

/obj/item/weapon/pickaxe/two_pick
	name = "1/3 pick"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick2"
	item_state = "syringe_0"
	digspeed = 20
	desc = "A miniature excavation tool for precise digging (4 centimetre excavation depth)."
	excavation_amount = 2
	w_class = 2

/obj/item/weapon/pickaxe/three_pick
	name = "1/2 pick"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick3"
	item_state = "syringe_0"
	digspeed = 20
	desc = "A miniature excavation tool for precise digging (6 centimetre excavation depth)."
	excavation_amount = 3
	w_class = 2

/obj/item/weapon/pickaxe/four_pick
	name = "2/3 pick"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick4"
	item_state = "syringe_0"
	digspeed = 20
	desc = "A miniature excavation tool for precise digging (8 centimetre excavation depth)."
	excavation_amount = 4
	w_class = 2

/obj/item/weapon/pickaxe/five_pick
	name = "5/6 pick"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick5"
	item_state = "syringe_0"
	digspeed = 20
	desc = "A miniature excavation tool for precise digging (10 centimetre excavation depth)."
	excavation_amount = 5
	w_class = 2

/obj/item/weapon/pickaxe/six_pick
	name = "1/1 pick"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick6"
	item_state = "syringe_0"
	digspeed = 20
	desc = "A miniature excavation tool for precise digging (12 centimetre excavation depth)."
	excavation_amount = 6
	w_class = 2

/obj/item/weapon/pickaxe/hand
	name = "hand pickaxe"
	icon = 'icons/obj/xenoarchaeology.dmi'
	icon_state = "pick_hand"
	item_state = "syringe_0"
	digspeed = 30
	desc = "A smaller, more precise version of the pickaxe (30 centimetre excavation depth)."
	excavation_amount = 15
	w_class = 3

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Pack for holding pickaxes

/obj/item/weapon/storage/box/excavation
	name = "excavation pick set"
	icon = 'icons/obj/storage.dmi'
	icon_state = "excavation"
	desc = "A set of picks for excavation."
	item_state = "syringe_kit"
	foldable = /obj/item/stack/sheet/cardboard //BubbleWrap
	storage_slots = 7
	w_class = 2
	can_hold = list(/obj/item/weapon/pickaxe/brush,\
	/obj/item/weapon/pickaxe/one_pick,\
	/obj/item/weapon/pickaxe/two_pick,\
	/obj/item/weapon/pickaxe/three_pick,\
	/obj/item/weapon/pickaxe/four_pick,\
	/obj/item/weapon/pickaxe/five_pick,\
	/obj/item/weapon/pickaxe/six_pick)
	max_combined_w_class = 17
	max_w_class = 4
	use_to_pickup = 1 // for picking up broken bulbs, not that most people will try

/obj/item/weapon/storage/box/excavation/New()
	..()
	new /obj/item/weapon/pickaxe/brush(src)
	new /obj/item/weapon/pickaxe/one_pick(src)
	new /obj/item/weapon/pickaxe/two_pick(src)
	new /obj/item/weapon/pickaxe/three_pick(src)
	new /obj/item/weapon/pickaxe/four_pick(src)
	new /obj/item/weapon/pickaxe/five_pick(src)
	new /obj/item/weapon/pickaxe/six_pick(src)


code:
/*
	File: AST Nodes
	An abstract syntax tree (AST) is a representation of source code in a computer-friendly format. It is composed of nodes,
	each of which represents a certain part of the source code. For example, an <IfStatement> node represents an if statement in the
	script's source code. Because it is a representation of the source code in memory, it is independent of any specific scripting language.
	This allows a script in any language for which a parser exists to be run by the interpreter.

	The AST is produced by an <n_Parser> object. It consists of a <GlobalBlock> with an arbitrary amount of statements. These statements are
	run in order by an <n_Interpreter> object. A statement may in turn run another block (such as an if statement might if its condition is
	met).

	Articles:
	- <http://en.wikipedia.org/wiki/Abstract_syntax_tree>
*/
var
	const
/*
	Constants: Operator Precedence
	OOP_OR				- Logical or
	OOP_AND				- Logical and
	OOP_BIT				- Bitwise operations
	OOP_EQUAL			- Equality checks
	OOP_COMPARE		- Greater than, less then, etc
	OOP_ADD				- Addition and subtraction
	OOP_MULTIPLY	- Multiplication and division
	OOP_POW				- Exponents
	OOP_UNARY			- Unary Operators
	OOP_GROUP			- Parentheses
*/
		OOP_OR      = 							1   //||
		OOP_AND     = OOP_OR			+ 1   	//&&
		OOP_BIT     = OOP_AND			+ 1   //&, |
		OOP_EQUAL   = OOP_BIT			+ 1   //==, !=
		OOP_COMPARE = OOP_EQUAL		+ 1   //>, <, >=, <=
		OOP_ADD     = OOP_COMPARE	+ 1 	//+, -
		OOP_MULTIPLY= OOP_ADD			+ 1   //*, /, %
		OOP_POW     = OOP_MULTIPLY+ 1		//^
		OOP_UNARY   = OOP_POW			+ 1   //!
		OOP_GROUP   = OOP_UNARY		+ 1   //()

/*
	Class: node
*/
/node
	proc
		ToString()
			return "[src.type]"
/*
	Class: identifier
*/
/node/identifier
	var
		id_name

	New(id)
		.=..()
		src.id_name=id

	ToString()
		return id_name

/*
	Class: expression
*/
/node/expression
/*
	Class: operator
	See <Binary Operators> and <Unary Operators> for subtypes.
*/
/node/expression/operator
	var
		node/expression/exp
		tmp
			name
			precedence

	New()
		.=..()
		if(!src.name) src.name="[src.type]"

	ToString()
		return "operator: [name]"

/*
	Class: FunctionCall
*/
/node/expression/FunctionCall
	//Function calls can also be expressions or statements.
	var
		func_name
		node/identifier/object
		list/parameters=new

/*
	Class: literal
*/
/node/expression/value/literal
	var
		value

	New(value)
		.=..()
		src.value=value

	ToString()
		return src.value

/*
	Class: variable
*/
/node/expression/value/variable
	var
		node
			object		//Either a node/identifier or another node/expression/value/variable which points to the object
		node/identifier
			id

	New(ident)
		.=..()
		id=ident
		if(istext(id))id=new(id)

	ToString()
		return src.id.ToString()

/*
	Class: reference
*/
/node/expression/value/reference
	var
		datum/value

	New(value)
		.=..()
		src.value=value

	ToString()
		return "ref: [src.value] ([src.value.type])"

path: code/modules/scripting/AST/AST Nodes.dm


code:
/*
	File: Block Types
*/
/*
	Class: BlockDefinition
	An object representing a set of actions to perform independently from the rest of the script. Blocks are basically just
	lists of statements to execute which also contain some local variables and methods. Note that since functions are local to a block,
	it is possible to have a function definition inside of any type of block (such as in an if statement or another function),
	and not just in the global scope as in many languages.
*/
/node/BlockDefinition
	var/list
		statements = new
		functions  = new
		initial_variables = new

	proc
/*
	Proc: SetVar
	Defines a permanent variable. The variable will not be deleted when it goes out of scope.

	Notes:
	Since all pre-existing temporary variables are deleted, it is not generally desirable to use this proc after the interpreter has been instantiated.
	Instead, use <n_Interpreter.SetVar()>.

	See Also:
	- <n_Interpreter.SetVar()>
*/
		SetVar(name, value)
			initial_variables[name]=value

/*
	Class: GlobalBlock
	A block object representing the global scope.
*/
//
	GlobalBlock
		New()
			initial_variables["null"]=null
			return ..()

/*
	Class: FunctionBlock
	A block representing a function body.
*/
//
	FunctionBlock


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

/*
	File: Binary Operators
*/
/*
	Class: binary
	Represents a binary operator in the AST. A binary operator takes two operands (ie x and y) and returns a value.
*/
/node/expression/operator/binary
	var/node/expression/exp2

////////// Comparison Operators //////////
/*
	Class: Equal
	Returns true if x = y.
*/
//
	Equal
		precedence=OOP_EQUAL

/*
	Class: NotEqual
	Returns true if x and y aren't equal.
*/
//
	NotEqual
		precedence=OOP_EQUAL

/*
	Class: Greater
	Returns true if x > y.
*/
//
	Greater
		precedence=OOP_COMPARE

/*
	Class: Less
	Returns true if x < y.
*/
//
	Less
		precedence=OOP_COMPARE

/*
	Class: GreaterOrEqual
	Returns true if x >= y.
*/
//
	GreaterOrEqual
		precedence=OOP_COMPARE

/*
	Class: LessOrEqual
	Returns true if x <= y.
*/
//
	LessOrEqual
		precedence=OOP_COMPARE

////////// Logical Operators //////////

/*
	Class: LogicalAnd
	Returns true if x and y are true.
*/
//
	LogicalAnd
		precedence=OOP_AND

/*
	Class: LogicalOr
	Returns true if x, y, or both are true.
*/
//
	LogicalOr
		precedence=OOP_OR

/*
	Class: LogicalXor
	Returns true if either x or y but not both are true.
*/
//
	LogicalXor					//Not implemented in nS
		precedence=OOP_OR

////////// Bitwise Operators //////////

/*
	Class: BitwiseAnd
	Performs a bitwise and operation.

	Example:
	011 & 110 = 010
*/
//
	BitwiseAnd
		precedence=OOP_BIT

/*
	Class: BitwiseOr
	Performs a bitwise or operation.

	Example:
	011 | 110 = 111
*/
//
	BitwiseOr
		precedence=OOP_BIT

/*
	Class: BitwiseXor
	Performs a bitwise exclusive or operation.

	Example:
	011 xor 110 = 101
*/
//
	BitwiseXor
		precedence=OOP_BIT

////////// Arithmetic Operators //////////

/*
	Class: Add
	Returns the sum of x and y.
*/
//
	Add
		precedence=OOP_ADD

/*
	Class: Subtract
	Returns the difference of x and y.
*/
//
	Subtract
		precedence=OOP_ADD

/*
	Class: Multiply
	Returns the product of x and y.
*/
//
	Multiply
		precedence=OOP_MULTIPLY

/*
	Class: Divide
	Returns the quotient of x and y.
*/
//
	Divide
		precedence=OOP_MULTIPLY

/*
	Class: Power
	Returns x raised to the power of y.
*/
//
	Power
		precedence=OOP_POW

/*
	Class: Modulo
	Returns the remainder of x / y.
*/
//
	Modulo
		precedence=OOP_MULTIPLY


code:
/*
	File: Unary Operators
*/
/*
	Class: unary
	Represents a unary operator in the AST. Unary operators take a single operand (referred to as x below) and return a value.
*/
/node/expression/operator/unary
	precedence=OOP_UNARY

/*
	Class: LogicalNot
	Returns !x.

	Example:
	!true = false and !false = true
*/
//
	LogicalNot
		name="logical not"

/*
	Class: BitwiseNot
	Returns the value of a bitwise not operation performed on x.

	Example:
	~10 (decimal 2) = 01 (decimal 1).
*/
//
	BitwiseNot
		name="bitwise not"

/*
	Class: Minus
	Returns -x.
*/
//
	Minus
		name="minus"

/*
	Class: group
	A special unary operator representing a value in parentheses.
*/
//
	group
		precedence=OOP_GROUP

	New(node/expression/exp)
		src.exp=exp
		return ..()


code:
/*
	File: Statement Types
*/
/*
	Class: statement
	An object representing a single instruction run by an interpreter.
*/
/node/statement
/*
	Class: FunctionCall
	Represents a call to a function.
*/
//
	FunctionCall
		var
			func_name
			node/identifier/object
			list/parameters=new

/*
	Class: FunctionDefinition
	Defines a function.
*/
//
	FunctionDefinition
		var
			func_name
			list/parameters=new
			node/BlockDefinition/FunctionBlock/block

/*
	Class: VariableAssignment
	Sets a variable in an accessible scope to the given value if one exists, otherwise initializes a new local variable to the given value.

	Notes:
	If a variable with the same name exists in a higher block, the value will be assigned to it. If not,
	a new variable is created in the current block. To force creation of a new variable, use <VariableDeclaration>.

	See Also:
	- <VariableDeclaration>
*/
//
	VariableAssignment
		var
			node
				identifier
					object
					var_name
				expression/value

/*
	Class: VariableDeclaration
	Intializes a local variable to a null value.

	See Also:
	- <VariableAssignment>
*/
//
	VariableDeclaration
		var
			node
				identifier
					object
					var_name

/*
	Class: IfStatement
*/
//
	IfStatement
		var
			skip = 0
			node
				BlockDefinition
					block
					else_block //may be null
				expression/cond
				statement/else_if

		ElseIf

/*
	Class: WhileLoop
	Loops while a given condition is true.
*/
//
	WhileLoop
		var
			node
				BlockDefinition/block
				expression/cond

/*
	Class: ForLoop
	Loops while test is true, initializing a variable, increasing the variable
*/
	ForLoop
		var
			node
				BlockDefinition/block
				expression/test
				expression/init
				expression/increment

/*
	Class: BreakStatement
	Ends a loop.
*/
//
	BreakStatement

/*
	Class: ContinueStatement
	Skips to the next iteration of a loop.
*/
//
	ContinueStatement

/*
	Class: ReturnStatement
	Ends the function and returns a value.
*/
//
	ReturnStatement
		var
			node/expression/value


code:
/*
	File: Errors
*/
/*
	Class: scriptError
	An error scanning or parsing the source code.
*/
/scriptError
	var
/*
	Var: message
	A message describing the problem.
*/
		message
	New(msg=null)
		if(msg)message=msg

	BadToken
		message="Unexpected token: "
		var/token/token
		New(token/t)
			token=t
			if(t&&t.line) message="[t.line]: [message]"
			if(istype(t))message+="[t.value]"
			else message+="[t]"

	InvalidID
		parent_type=/scriptError/BadToken
		message="Invalid identifier name: "

	ReservedWord
		parent_type=/scriptError/BadToken
		message="Identifer using reserved word: "

	BadNumber
		parent_type=/scriptError/BadToken
		message = "Bad number: "

	BadReturn
		var/token/token
		message = "Unexpected return statement outside of a function."
		New(token/t)
			src.token=t

	EndOfFile
		message = "Unexpected end of file."

	ExpectedToken
		message="Expected: '"
		New(id, token/T)
			if(T && T.line) message="[T.line]: [message]"
			message+="[id]'. "
			if(T)message+="Found '[T.value]'."

	UnterminatedComment
		message="Unterminated multi-line comment statement: expected */"

	DuplicateFunction
		New(name, token/t)
			message="Function '[name]' defined twice."

	ParameterFunction
		message = "You cannot use a function inside a parameter."

		New(token/t)
			var/line = "?"
			if(t)
				line = t.line
			message = "[line]: [message]"

/*
	Class: runtimeError
	An error thrown by the interpreter in running the script.
*/
/runtimeError
	var
		name
/*
	Var: message
	A basic description as to what went wrong.
*/
		message
		stack/stack

	proc
/*
	Proc: ToString
	Returns a description of the error suitable for showing to the user.
*/
		ToString()
			. = "[name]: [message]"
			if(!stack.Top()) return
			.+="\nStack:"
			while(stack.Top())
				var/node/statement/FunctionCall/stmt=stack.Pop()
				. += "\n\t [stmt.func_name]()"

	TypeMismatch
		name="TypeMismatchError"
		New(op, a, b)
			message="Type mismatch: '[a]' [op] '[b]'"

	UnexpectedReturn
		name="UnexpectedReturnError"
		message="Unexpected return statement."

	UnknownInstruction
		name="UnknownInstructionError"
		message="Unknown instruction type. This may be due to incompatible compiler and interpreter versions or a lack of implementation."

	UndefinedVariable
		name="UndefinedVariableError"
		New(variable)
			message="Variable '[variable]' has not been declared."

	UndefinedFunction
		name="UndefinedFunctionError"
		New(function)
			message="Function '[function]()' has not been defined."

	DuplicateVariableDeclaration
		name="DuplicateVariableError"
		New(variable)
			message="Variable '[variable]' was already declared."

	IterationLimitReached
		name="MaxIterationError"
		message="A loop has reached its maximum number of iterations."

	RecursionLimitReached
		name="MaxRecursionError"
		message="The maximum amount of recursion has been reached."

	DivisionByZero
		name="DivideByZeroError"
		message="Division by zero attempted."

	MaxCPU
		name="MaxComputationalUse"
		message="Maximum amount of computational cycles reached (>= 1000)."


code:
client/verb/tcssave()
	set hidden = 1
	if(mob.machine || issilicon(mob))
		if(telecomms_check(mob))
			var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
			if(Machine.editingcode != mob)
				return

			if(Machine.SelectedServer)
				var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
				var/tcscode=winget(src, "tcscode", "text")
				Server.setcode( tcscode ) // this actually saves the code from input to the server
				src << output(null, "tcserror") // clear the errors
			else
				src << output(null, "tcserror")
				src << output("Failed to save: Unable to locate server machine. (Back up your code before exiting the window!)", "tcserror")
		else
			src << output(null, "tcserror")
			src << output("Failed to save: Unable to locate machine. (Back up your code before exiting the window!)", "tcserror")
	else
		src << output(null, "tcserror")
		src << output("Failed to save: Unable to locate machine. (Back up your code before exiting the window!)", "tcserror")

client/verb/tcscompile()
	set hidden = 1
	if(mob.machine || issilicon(mob))
		if(telecomms_check(mob))
			var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
			if(Machine.editingcode != mob)
				return

			if(Machine.SelectedServer)
				var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
				Server.setcode( winget(src, "tcscode", "text") ) // save code first

				spawn(0)
					// Output all the compile-time errors
					src << output(null, "tcserror")
					src << output("Please wait, compiling...", "tcserror")

					var/list/compileerrors = Server.compile(mob) // then compile the code!
					if(!telecomms_check(mob))
						return

					if(compileerrors.len)
						src << output("Compile Errors", "tcserror")
						for(var/scriptError/e in compileerrors)
							src << output("\t>[e.message]", "tcserror")
						src << output("([compileerrors.len] errors)", "tcserror")

						// Output compile errors to all other people viewing the code too
						for(var/mob/M in Machine.viewingcode)
							if(M.client)
								M << output(null, "tcserror")
								M << output("Compile Errors", "tcserror")
								for(var/scriptError/e in compileerrors)
									M << output("\t>[e.message]", "tcserror")
								M << output("([compileerrors.len] errors)", "tcserror")

					else
						src << output("TCS compilation successful!", "tcserror")
						src << output("(0 errors)", "tcserror")

						for(var/mob/M in Machine.viewingcode)
							if(M.client)
								M << output("TCS compilation successful!", "tcserror")
								M << output("(0 errors)", "tcserror")

			else
				src << output(null, "tcserror")
				src << output("Failed to compile: Unable to locate server machine. (Back up your code before exiting the window!)", "tcserror")
		else
			src << output(null, "tcserror")
			src << output("Failed to compile: Unable to locate machine. (Back up your code before exiting the window!)", "tcserror")
	else
		src << output(null, "tcserror")
		src << output("Failed to compile: Unable to locate machine. (Back up your code before exiting the window!)", "tcserror")

client/verb/tcsrun()
	set hidden = 1
	if(mob.machine || issilicon(mob))
		if(telecomms_check(mob))
			var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
			if(Machine.editingcode != mob)
				return

			if(Machine.SelectedServer)
				var/obj/machinery/telecomms/server/Server = Machine.SelectedServer

				var/datum/signal/signal = new()
				signal.data["message"] = ""
				if(Server.freq_listening.len > 0)
					signal.frequency = Server.freq_listening[1]
				else
					signal.frequency = 1459
				signal.data["name"] = ""
				signal.data["job"] = ""
				signal.data["reject"] = 0
				signal.data["server"] = Server

				Server.Compiler.Run(signal)

			else
				src << output(null, "tcserror")
				src << output("Failed to run: Unable to locate server machine. (Back up your code before exiting the window!)", "tcserror")
		else
			src << output(null, "tcserror")
			src << output("Failed to run: Unable to locate machine. (Back up your code before exiting the window!)", "tcserror")
	else
		src << output(null, "tcserror")
		src << output("Failed to run: Unable to locate machine. (Back up your code before exiting the window!)", "tcserror")

client/verb/exittcs()
	set hidden = 1
	if(mob.machine || issilicon(mob))
		if(telecomms_check(mob))
			var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
			if(Machine.editingcode == mob)
				Machine.storedcode = "[winget(mob, "tcscode", "text")]"
				Machine.editingcode = null
			else
				if(mob in Machine.viewingcode)
					Machine.viewingcode.Remove(mob)

client/verb/tcsrevert()
	set hidden = 1
	if(mob.machine || issilicon(mob))
		if(telecomms_check(mob))
			var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
			if(Machine.editingcode != mob)
				return

			if(Machine.SelectedServer)
				var/obj/machinery/telecomms/server/Server = Machine.SelectedServer

				// Replace quotation marks with quotation macros for proper winset() compatibility
				var/showcode = replacetext(Server.rawcode, "\\\"", "\\\\\"")
				showcode = replacetext(showcode, "\"", "\\\"")

				winset(mob, "tcscode", "text=\"[showcode]\"")

				src << output(null, "tcserror") // clear the errors
			else
				src << output(null, "tcserror")
				src << output("Failed to revert: Unable to locate server machine.", "tcserror")
		else
			src << output(null, "tcserror")
			src << output("Failed to revert: Unable to locate machine.", "tcserror")
	else
		src << output(null, "tcserror")
		src << output("Failed to revert: Unable to locate machine.", "tcserror")

client/verb/tcsclearmem()
	set hidden = 1
	if(mob.machine || issilicon(mob))
		if(telecomms_check(mob))
			var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
			if(Machine.editingcode != mob)
				return

			if(Machine.SelectedServer)
				var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
				Server.memory = list() // clear the memory
				// Show results
				src << output(null, "tcserror")
				src << output("Server memory cleared!", "tcserror")
				for(var/mob/M in Machine.viewingcode)
					if(M.client)
						M << output("Server memory cleared!", "tcserror")
			else
				src << output(null, "tcserror")
				src << output("Failed to clear memory: Unable to locate server machine.", "tcserror")
		else
			src << output(null, "tcserror")
			src << output("Failed to clear memory: Unable to locate machine.", "tcserror")
	else
		src << output(null, "tcserror")
		src << output("Failed to clear memory: Unable to locate machine.", "tcserror")

/proc/telecomms_check(var/mob/mob)
	if(mob && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && in_range(mob.machine, mob) || issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic))
		return 1
	return 0