code:
/**
 * Main Fusion processes
 * process() Organizes all other calls, and is the best starting point for top-level logic.
 * fusion_process() handles all the main fusion reaction logic and consequences (lightning, radiation, particles) from an active fusion reaction.
 */

/obj/machinery/atmospherics/components/unary/hypertorus/core/process(delta_time)
	/*
	 *Pre-checks
	 */
	//first check if the machine is active
	if(!active)
		return

	//then check if the other machines are still there
	if(!check_part_connectivity())
		deactivate()
		return

	assert_gases()

	// Run the reaction if it is either live or being started
	if (start_power || power_level)
		play_ambience()
		fusion_process(delta_time)
		// Note that we process damage/healing even if the fusion process aborts.
		// Running out of fuel won't save you if your moderator and coolant are exploding on their own.
		check_spill()
		process_damageheal(delta_time)
		check_alert()
	if (start_power)
		remove_waste(delta_time)
	update_pipenets()

	check_deconstructable()

/**
 * Called by process()
 * Contains the main fusion calculations and checks, for more informations check the comments along the code.
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/fusion_process(delta_time)
//fusion: a terrible idea that was fun but broken. Now reworked to be less broken and more interesting. Again (and again, and again). Again! Again but with machine!
//Fusion Rework Counter: Please increment this if you make a major overhaul to this system again.
//7 reworks

	if (check_power_use())
		if (start_cooling)
			inject_from_side_components(delta_time)
			process_internal_cooling(delta_time)
	else
		// No power forces bad settings
		magnetic_constrictor = 100
		heating_conductor = 500
		current_damper = 0
		fuel_injection_rate = 20
		moderator_injection_rate = 50
		waste_remove = FALSE
		iron_content += 0.02 * power_level * delta_time

	update_temperature_status(delta_time)

	//Store the temperature of the gases after one cicle of the fusion reaction
	var/archived_heat = internal_fusion.temperature
	//Store the volume of the fusion reaction multiplied by the force of the magnets that controls how big it will be
	var/volume = internal_fusion.volume * (magnetic_constrictor * 0.01)

	var/energy_concentration_multiplier = 1
	var/positive_temperature_multiplier = 1
	var/negative_temperature_multiplier = 1

	//We scale it down by volume/2 because for fusion conditions, moles roughly = 2*volume, but we want it to be based off something constant between reactions.
	var/scale_factor = volume * 0.5

	/// Store the fuel gases and the byproduct gas quantities
	var/list/fuel_list = list()
	/// Scaled down moles of gases, no less than 0
	var/list/scaled_fuel_list = list()

	if (selected_fuel)
		energy_concentration_multiplier = selected_fuel.energy_concentration_multiplier
		positive_temperature_multiplier = selected_fuel.positive_temperature_multiplier
		negative_temperature_multiplier = selected_fuel.negative_temperature_multiplier

		for(var/gas_id in selected_fuel.requirements | selected_fuel.primary_products)
			var/amount = internal_fusion.gases[gas_id][MOLES]
			fuel_list[gas_id] = amount
			scaled_fuel_list[gas_id] = max((amount - FUSION_MOLE_THRESHOLD) / scale_factor, 0)

	/// Store the moderators gases quantities
	var/list/moderator_list = list()
	/// Scaled down moles of gases, no less than 0
	var/list/scaled_moderator_list = list()
	for(var/gas_id in moderator_internal.gases)
		var/amount = moderator_internal.gases[gas_id][MOLES]
		moderator_list[gas_id] = amount
		scaled_moderator_list[gas_id] = max((amount - FUSION_MOLE_THRESHOLD) / scale_factor, 0)

	/*
	 *FUSION MAIN PROCESS
	 */
	//This section is used for the instability calculation for the fusion reaction
	//The size of the phase space hypertorus
	var/toroidal_size = (2 * PI) + TORADIANS(arctan((volume - TOROID_VOLUME_BREAKEVEN) / TOROID_VOLUME_BREAKEVEN))
	//Calculation of the gas power, only for theoretical instability calculations
	var/gas_power = 0
	for (var/gas_id in internal_fusion.gases)
		gas_power += (internal_fusion.gases[gas_id][GAS_META][META_GAS_FUSION_POWER] * internal_fusion.gases[gas_id][MOLES])
	for (var/gas_id in moderator_internal.gases)
		gas_power += (moderator_internal.gases[gas_id][GAS_META][META_GAS_FUSION_POWER] * moderator_internal.gases[gas_id][MOLES] * 0.75)

	instability = MODULUS((gas_power * INSTABILITY_GAS_POWER_FACTOR)**2, toroidal_size) + (current_damper * 0.01) - iron_content * 0.05
	//Effective reaction instability (determines if the energy is used/released)
	var/internal_instability = 0
	if(instability * 0.5 < FUSION_INSTABILITY_ENDOTHERMALITY)
		internal_instability = 1
	else
		internal_instability = -1

	/*
	 *Modifiers
	 */
	///Those are the scaled gases that gets consumed and adjust energy
	// Gases that increase the amount of energy
	var/energy_modifiers = scaled_moderator_list[/datum/gas/nitrogen] * 0.35 + \
								scaled_moderator_list[/datum/gas/carbon_dioxide] * 0.55 + \
								scaled_moderator_list[/datum/gas/nitrous_oxide] * 0.95 + \
								scaled_moderator_list[/datum/gas/zauker] * 1.55 + \
								scaled_moderator_list[/datum/gas/antinoblium] * 20
	// Gases that decrease the amount of energy
	energy_modifiers -= scaled_moderator_list[/datum/gas/hypernoblium] * 10 + \
								scaled_moderator_list[/datum/gas/water_vapor] * 0.75 + \
								scaled_moderator_list[/datum/gas/nitryl] * 0.15 + \
								scaled_moderator_list[/datum/gas/healium] * 0.45 + \
								scaled_moderator_list[/datum/gas/freon] * 1.15
	///Between 0.25 and 100, this value is used to modify the behaviour of the internal energy and the core temperature based on the gases present in the mix
	var/power_modifier = scaled_moderator_list[/datum/gas/oxygen] * 0.55 + \
								scaled_moderator_list[/datum/gas/carbon_dioxide] * 0.95 + \
								scaled_moderator_list[/datum/gas/nitryl] * 1.45 + \
								scaled_moderator_list[/datum/gas/zauker] * 5.55 + \
								scaled_moderator_list[/datum/gas/plasma] * 0.05 - \
								scaled_moderator_list[/datum/gas/nitrous_oxide] * 0.05 - \
								scaled_moderator_list[/datum/gas/freon] * 0.75
	///Minimum 0.25, this value is used to modify the behaviour of the energy emission based on the gases present in the mix
	var/heat_modifier = scaled_moderator_list[/datum/gas/plasma] * 1.25 - \
								scaled_moderator_list[/datum/gas/nitrogen] * 0.75 - \
								scaled_moderator_list[/datum/gas/nitrous_oxide] * 1.45 - \
								scaled_moderator_list[/datum/gas/freon] * 0.95
	///Between 0.005 and 1000, this value modify the radiation emission of the reaction, higher values increase the emission
	var/radiation_modifier = scaled_moderator_list[/datum/gas/freon] * 1.15 - \
									scaled_moderator_list[/datum/gas/nitrogen] * 0.45 - \
									scaled_moderator_list[/datum/gas/plasma] * 0.95 + \
									scaled_moderator_list[/datum/gas/bz] * 1.9 + \
									scaled_moderator_list[/datum/gas/proto_nitrate] * 0.1 + \
									scaled_moderator_list[/datum/gas/antinoblium] * 10

	if (selected_fuel)
		// These should probably be static coefficients read from a table rather than things that depend on the current recipe
		// the same is true for the effects above
		energy_modifiers += scaled_fuel_list[selected_fuel.requirements[1]] + \
									scaled_fuel_list[selected_fuel.requirements[2]]
		energy_modifiers -= scaled_fuel_list[selected_fuel.primary_products[1]]

		power_modifier += scaled_fuel_list[selected_fuel.requirements[2]] * 1.05 - \
									scaled_fuel_list[selected_fuel.primary_products[1]] * 0.55

		heat_modifier += scaled_fuel_list[selected_fuel.requirements[1]] * 1.15 + \
									scaled_fuel_list[selected_fuel.primary_products[1]] * 1.05

		radiation_modifier += scaled_fuel_list[selected_fuel.primary_products[1]]

	power_modifier = clamp(power_modifier, 0.25, 100)
	heat_modifier = clamp(heat_modifier, 0.25, 100)
	radiation_modifier = clamp(radiation_modifier, 0.005, 1000)

	/*
	 *Main calculations (energy, internal power, core temperature, delta temperature,
	 *conduction, radiation, efficiency, power output, heat limiter modifier and heat output)
	 */
	internal_power = 0
	efficiency = VOID_CONDUCTION * 1

	if (selected_fuel)
		// Power of the gas mixture
		internal_power = (scaled_fuel_list[selected_fuel.requirements[1]] * power_modifier / 100) * (scaled_fuel_list[selected_fuel.requirements[2]] * power_modifier / 100) * (PI * (2 * (scaled_fuel_list[selected_fuel.requirements[1]] * CALCULATED_H2RADIUS) * (scaled_fuel_list[selected_fuel.requirements[2]] * CALCULATED_TRITRADIUS))**2) * energy

		// Efficiency of the reaction, it increases with the amount of byproduct
		efficiency = VOID_CONDUCTION * clamp(scaled_fuel_list[selected_fuel.primary_products[1]], 1, 100)

	//Can go either positive or negative depending on the instability and the negative energy modifiers
	//E=mc^2 with some changes for gameplay purposes
	energy = (energy_modifiers * LIGHT_SPEED ** 2) * max(internal_fusion.temperature * heat_modifier / 100, 1)
	energy = energy / energy_concentration_multiplier
	energy = clamp(energy, 0, 1e35) //ugly way to prevent NaN error
	//Temperature inside the center of the gas mixture
	core_temperature = internal_power * power_modifier / 1000
	core_temperature = max(TCMB, core_temperature)
	//Difference between the gases temperature and the internal temperature of the reaction
	delta_temperature = archived_heat - core_temperature
	//Energy from the reaction lost from the molecule colliding between themselves.
	conduction = - delta_temperature * (magnetic_constrictor * 0.001)
	//The remaining wavelength that actually can do damage to mobs.
	radiation = max(-(PLANCK_LIGHT_CONSTANT / 5e-18) * radiation_modifier * delta_temperature, 0)
	power_output = efficiency * (internal_power - conduction - radiation)
	//Hotter air is easier to heat up and cool down
	heat_limiter_modifier = 10 * (10 ** power_level) * (heating_conductor / 100)
	//The amount of heat that is finally emitted, based on the power output. Min and max are variables that depends of the modifier
	heat_output_min = - heat_limiter_modifier * 0.01 * negative_temperature_multiplier
	heat_output_max = heat_limiter_modifier * positive_temperature_multiplier
	heat_output = clamp(internal_instability * power_output * heat_modifier / 100, heat_output_min, heat_output_max)

	// Is the fusion process actually going to run?
	// Note we have to always perform the above calculations to keep the UI updated, so we can't use this to early return.
	if (!check_fuel())
		return

	// Phew. Lets calculate what this means in practice.
	var/fuel_consumption_rate = clamp(fuel_injection_rate * 0.01 * 5 * power_level, 0.05, 30)
	var/consumption_amount = fuel_consumption_rate * delta_time
	var/production_amount
	switch(power_level)
		if(3,4)
			production_amount = clamp(heat_output * 5e-4, 0, fuel_consumption_rate) * delta_time
		else
			production_amount = clamp(heat_output / 10 ** (power_level+1), 0, fuel_consumption_rate) * delta_time

	// antinob production is special, and uses its own calculations from how stale the fusion mix is (via byproduct ratio and fresh fuel rate)
	var/dirty_production_rate = scaled_fuel_list[scaled_fuel_list[3]] / fuel_injection_rate

	// Run the effects of our selected fuel recipe

	var/datum/gas_mixture/internal_output = new
	moderator_fuel_process(delta_time, production_amount, consumption_amount, internal_output, moderator_list, selected_fuel, fuel_list)

	// Run the common effects, committing changes where applicable

	// This is repetition, but is here as a placeholder for what will need to be done to allow concurrently running multiple recipes
	var/common_production_amount = production_amount * selected_fuel.gas_production_multiplier
	moderator_common_process(delta_time, common_production_amount, internal_output, moderator_list, dirty_production_rate, heat_output, radiation_modifier)

/**
 * Perform recipe specific actions. Fuel consumption and recipe based gas production happens here.
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/moderator_fuel_process(delta_time, production_amount, consumption_amount, datum/gas_mixture/internal_output, moderator_list, datum/hfr_fuel/fuel, fuel_list)
	// Adjust fusion consumption/production based on this recipe's characteristics
	var/fuel_consumption = consumption_amount * 0.85 * selected_fuel.fuel_consumption_multiplier
	var/scaled_production = production_amount * selected_fuel.gas_production_multiplier

	for(var/gas_id in fuel.requirements)
		internal_fusion.gases[gas_id][MOLES] -= min(fuel_list[gas_id], fuel_consumption)
	for(var/gas_id in fuel.primary_products)
		internal_fusion.gases[gas_id][MOLES] += fuel_consumption * 0.5

	// Each recipe provides a tier list of six output gases.
	// Which gases are produced depend on what the fusion level is.
	var/list/tier = fuel.secondary_products
	switch(power_level)
		if(1)
			moderator_internal.gases[tier[1]][MOLES] += scaled_production * 0.95
			moderator_internal.gases[tier[2]][MOLES] += scaled_production * 0.75
		if(2)
			moderator_internal.gases[tier[1]][MOLES] += scaled_production * 1.65
			moderator_internal.gases[tier[2]][MOLES] += scaled_production
			if(moderator_list[/datum/gas/plasma] > 50)
				moderator_internal.gases[tier[3]][MOLES] += scaled_production * 1.15
		if(3)
			moderator_internal.gases[tier[2]][MOLES] += scaled_production * 0.5
			moderator_internal.gases[tier[3]][MOLES] += scaled_production * 0.45
		if(4)
			moderator_internal.gases[tier[3]][MOLES] += scaled_production * 1.65
			moderator_internal.gases[tier[4]][MOLES] += scaled_production * 1.25
		if(5)
			moderator_internal.gases[tier[4]][MOLES] += scaled_production * 0.65
			moderator_internal.gases[tier[5]][MOLES] += scaled_production
			moderator_internal.gases[tier[6]][MOLES] += scaled_production * 0.75
		if(6)
			moderator_internal.gases[tier[5]][MOLES] += scaled_production * 0.35
			moderator_internal.gases[tier[6]][MOLES] += scaled_production

/**
 * Perform common fusion actions:
 *
 * - Gases that get produced irrespective of recipe
 * - Temperature modifiers, radiation modifiers, and the application of each
 * - Committing staged output, performing filtering, and making !FUN! emissions
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/moderator_common_process(delta_time, scaled_production, datum/gas_mixture/internal_output, moderator_list, dirty_production_rate, heat_output, radiation_modifier)
	switch(power_level)
		if(1)
			if(moderator_list[/datum/gas/plasma] > 100)
				moderator_internal.gases[/datum/gas/nitrous_oxide] += scaled_production * 0.5
				moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 0.85)
			if(moderator_list[/datum/gas/bz] > 150)
				internal_output.assert_gases(/datum/gas/halon)
				internal_output.gases[/datum/gas/halon][MOLES] += scaled_production * 0.55
				moderator_internal.gases[/datum/gas/bz][MOLES] -= min(moderator_internal.gases[/datum/gas/bz][MOLES], scaled_production * 0.95)
		if(2)
			if(moderator_list[/datum/gas/plasma] > 50)
				internal_output.assert_gases(/datum/gas/bz)
				internal_output.gases[/datum/gas/bz][MOLES] += scaled_production * 1.8
				moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 1.75)
			if(moderator_list[/datum/gas/proto_nitrate] > 20)
				radiation *= 1.55
				heat_output *= 1.025
				internal_output.assert_gases(/datum/gas/stimulum)
				internal_output.gases[/datum/gas/stimulum][MOLES] += scaled_production * 1.05
				moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 1.35)
		if(3, 4)
			if(moderator_list[/datum/gas/plasma] > 10)
				internal_output.assert_gases(/datum/gas/freon, /datum/gas/stimulum)
				internal_output.gases[/datum/gas/freon][MOLES] += scaled_production * 0.15
				internal_output.gases[/datum/gas/stimulum][MOLES] += scaled_production * 1.05
				moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 0.45)
			if(moderator_list[/datum/gas/freon] > 50)
				heat_output *= 0.9
				radiation *= 0.8
			if(moderator_list[/datum/gas/proto_nitrate]> 15)
				internal_output.assert_gases(/datum/gas/stimulum, /datum/gas/halon)
				internal_output.gases[/datum/gas/stimulum][MOLES] += scaled_production * 1.25
				internal_output.gases[/datum/gas/halon][MOLES] += scaled_production * 1.15
				moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 1.55)
				radiation *= 1.95
				heat_output *= 1.25
			if(moderator_list[/datum/gas/bz] > 100)
				internal_output.assert_gases(/datum/gas/healium, /datum/gas/proto_nitrate)
				internal_output.gases[/datum/gas/proto_nitrate][MOLES] += scaled_production * 1.5
				internal_output.gases[/datum/gas/healium][MOLES] += scaled_production * 1.5
				induce_hallucination(50 * power_level, delta_time)
		if(5)
			if(moderator_list[/datum/gas/plasma] > 15)
				internal_output.assert_gases(/datum/gas/freon)
				internal_output.gases[/datum/gas/freon][MOLES] += scaled_production *0.25
				moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 1.45)
			if(moderator_list[/datum/gas/freon] > 500)
				heat_output *= 0.5
				radiation *= 0.2
			if(moderator_list[/datum/gas/proto_nitrate] > 50)
				internal_output.assert_gases(/datum/gas/stimulum, /datum/gas/pluoxium)
				internal_output.gases[/datum/gas/stimulum][MOLES] += scaled_production * 1.95
				internal_output.gases[/datum/gas/pluoxium][MOLES] += scaled_production
				moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 1.35)
				radiation *= 1.95
				heat_output *= 1.25
			if(moderator_list[/datum/gas/bz] > 100)
				internal_output.assert_gases(/datum/gas/healium)
				internal_output.gases[/datum/gas/healium][MOLES] += scaled_production
				induce_hallucination(500, delta_time)
				moderator_internal.gases[/datum/gas/freon][MOLES] += scaled_production * 1.15
			if(moderator_list[/datum/gas/healium] > 100)
				if(critical_threshold_proximity > 400)
					critical_threshold_proximity = max(critical_threshold_proximity - (moderator_list[/datum/gas/healium] / 100 * delta_time ), 0)
					moderator_internal.gases[/datum/gas/healium][MOLES] -= min(moderator_internal.gases[/datum/gas/healium][MOLES], scaled_production * 20)
			if(moderator_internal.temperature < 1e7 || (moderator_list[/datum/gas/plasma] > 100 && moderator_list[/datum/gas/bz] > 50))
				internal_output.assert_gases(/datum/gas/antinoblium)
				internal_output.gases[/datum/gas/antinoblium][MOLES] += dirty_production_rate * 0.9 / 0.065 * delta_time
		if(6)
			if(moderator_list[/datum/gas/plasma] > 30)
				moderator_internal.gases[/datum/gas/bz][MOLES] += scaled_production * 1.15
				moderator_internal.gases[/datum/gas/plasma][MOLES] -= min(moderator_internal.gases[/datum/gas/plasma][MOLES], scaled_production * 1.45)
			if(moderator_list[/datum/gas/proto_nitrate])
				internal_output.assert_gases(/datum/gas/zauker, /datum/gas/stimulum)
				internal_output.gases[/datum/gas/zauker][MOLES] += scaled_production * 5.35
				internal_output.gases[/datum/gas/stimulum][MOLES] += scaled_production * 2.15
				moderator_internal.gases[/datum/gas/proto_nitrate][MOLES] -= min(moderator_internal.gases[/datum/gas/proto_nitrate][MOLES], scaled_production * 3.35)
				radiation *= 2
				heat_output *= 2.25
			if(moderator_list[/datum/gas/bz])
				induce_hallucination(900, delta_time, force=TRUE)
				moderator_internal.gases[/datum/gas/antinoblium][MOLES] += clamp(dirty_production_rate / 0.045, 0, 10) * delta_time
			if(moderator_list[/datum/gas/healium] > 100)
				if(critical_threshold_proximity > 400)
					critical_threshold_proximity = max(critical_threshold_proximity - (moderator_list[/datum/gas/healium] / 100 * delta_time ), 0)
					moderator_internal.gases[/datum/gas/healium][MOLES] -= min(moderator_internal.gases[/datum/gas/healium][MOLES], scaled_production * 20)
			internal_fusion.gases[/datum/gas/antinoblium][MOLES] += dirty_production_rate * 0.01 / 0.095 * delta_time

	//Modifies the internal_fusion temperature with the amount of heat output
	var/temperature_modifier = selected_fuel.temperature_change_multiplier
	if(internal_fusion.temperature <= FUSION_MAXIMUM_TEMPERATURE * temperature_modifier)
		internal_fusion.temperature = clamp(internal_fusion.temperature + heat_output,TCMB,FUSION_MAXIMUM_TEMPERATURE * temperature_modifier)
	else
		internal_fusion.temperature -= heat_limiter_modifier * 0.01 * delta_time

	//heat up and output what's in the internal_output into the linked_output port
	if(internal_output.total_moles() > 0)
		if(moderator_internal.total_moles() > 0)
			internal_output.temperature = moderator_internal.temperature * HIGH_EFFICIENCY_CONDUCTIVITY
		else
			internal_output.temperature = internal_fusion.temperature * METALLIC_VOID_CONDUCTIVITY
		linked_output.airs[1].merge(internal_output)

	evaporate_moderator(delta_time)

	check_nuclear_particles(moderator_list)

	check_lightning_arcs(moderator_list)

	// Oxygen burns away iron content rapidly
	if(moderator_list[/datum/gas/oxygen] > 150)
		if(iron_content > 0)
			var/max_iron_removable = IRON_OXYGEN_HEAL_PER_SECOND
			var/iron_removed = min(max_iron_removable * delta_time, iron_content)
			iron_content -= iron_removed
			moderator_internal.gases[/datum/gas/oxygen][MOLES] -= iron_removed * OXYGEN_MOLES_CONSUMED_PER_IRON_HEAL

	check_gravity_pulse(delta_time)

	emit_rads(radiation)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/evaporate_moderator(delta_time)
	// Don't evaporate if the reaction is dead
	if (!power_level)
		return
	// All gases in the moderator slowly burn away over time, whether used for production or not
	if(moderator_internal.total_moles() > 0)
		moderator_internal.remove(moderator_internal.total_moles() * (1 - (1 - 0.0005 * power_level) ** delta_time))

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/process_damageheal(delta_time)
	// Archive current health for damage cap purposes
	critical_threshold_proximity_archived = critical_threshold_proximity

	// If we're operating at an extreme power level, take increasing damage for the amount of fusion mass over a low threshold
	if(power_level >= HYPERTORUS_OVERFULL_MIN_POWER_LEVEL)
		var/overfull_damage_taken = HYPERTORUS_OVERFULL_MOLAR_SLOPE * internal_fusion.total_moles() + HYPERTORUS_OVERFULL_TEMPERATURE_SLOPE * coolant_temperature + HYPERTORUS_OVERFULL_CONSTANT
		critical_threshold_proximity = max(critical_threshold_proximity + max(overfull_damage_taken * delta_time, 0), 0)

	// If we're running on a thin fusion mix, heal up
	if(internal_fusion.total_moles() < HYPERTORUS_SUBCRITICAL_MOLES && power_level <= 5)
		var/subcritical_heal_restore = (internal_fusion.total_moles() - HYPERTORUS_SUBCRITICAL_MOLES) / HYPERTORUS_SUBCRITICAL_SCALE
		critical_threshold_proximity = max(critical_threshold_proximity + min(subcritical_heal_restore * delta_time, 0), 0)

	// If coolant is sufficiently cold, heal up
	if(internal_fusion.total_moles() > 0 && (airs[1].total_moles() && coolant_temperature < HYPERTORUS_COLD_COOLANT_THRESHOLD) && power_level <= 4)
		var/cold_coolant_heal_restore = log(10, max(coolant_temperature, 1) * HYPERTORUS_COLD_COOLANT_SCALE) - (HYPERTORUS_COLD_COOLANT_MAX_RESTORE * 2)
		critical_threshold_proximity = max(critical_threshold_proximity + min(cold_coolant_heal_restore * delta_time, 0), 0)

	critical_threshold_proximity += max(iron_content - HYPERTORUS_MAX_SAFE_IRON, 0) * delta_time

	// Apply damage cap
	critical_threshold_proximity = min(critical_threshold_proximity_archived + (delta_time * DAMAGE_CAP_MULTIPLIER * melting_point), critical_threshold_proximity)

	// If we have a preposterous amount of mass in the fusion mix, things get bad extremely fast
	if(internal_fusion.total_moles() >= HYPERTORUS_HYPERCRITICAL_MOLES)
		var/hypercritical_damage_taken = max((internal_fusion.total_moles() - HYPERTORUS_HYPERCRITICAL_MOLES) * HYPERTORUS_HYPERCRITICAL_SCALE, 0)
		critical_threshold_proximity = max(critical_threshold_proximity + min(hypercritical_damage_taken, HYPERTORUS_HYPERCRITICAL_MAX_DAMAGE), 0) * delta_time

	// High power fusion might create other matter other than helium, iron is dangerous inside the machine, damage can be seen
	if(power_level > 4 && prob(IRON_CHANCE_PER_FUSION_LEVEL * power_level))//at power level 6 is 100%
		iron_content += IRON_ACCUMULATED_PER_SECOND * delta_time
	if(iron_content > 0 && power_level <= 4 && prob(25 / (power_level + 1)))
		iron_content = max(iron_content - 0.01 * delta_time, 0)
	iron_content = clamp(iron_content, 0, 1)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_nuclear_particles(moderator_list)
	// New nuclear particle emission sytem.
	if(power_level < 4)
		return
	if(moderator_list[/datum/gas/bz] < (150 / power_level))
		return
	var/obj/machinery/hypertorus/corner/picked_corner = pick(corners)
	picked_corner.loc.fire_nuclear_particle(turn(picked_corner.dir, 180))

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_lightning_arcs(moderator_list)
	if(power_level < 4)
		return
	if(moderator_list[/datum/gas/antinoblium] <= 50 && critical_threshold_proximity <= 500)
		return
	var/zap_number = power_level - 2

	if(critical_threshold_proximity > 650 && prob(20))
		zap_number += 1

	var/cutoff = 1500
	cutoff = clamp(3000 - (power_level * (internal_fusion.total_moles() * 0.45)), 450, 3000)

	var/zaps_aspect = DEFAULT_ZAP_ICON_STATE
	var/flags = ZAP_SUPERMATTER_FLAGS
	switch(power_level)
		if(5)
			zaps_aspect = SLIGHTLY_CHARGED_ZAP_ICON_STATE
			flags |= (ZAP_MOB_DAMAGE)
		if(6)
			zaps_aspect = OVER_9000_ZAP_ICON_STATE
			flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)

	playsound(loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
	for(var/i in 1 to zap_number)
		supermatter_zap(src, 5, power_level * 300, flags, zap_cutoff = cutoff, power_level = src.power_level * 1000, zap_icon = zaps_aspect)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_gravity_pulse(delta_time)
	if(DT_PROB(100 - critical_threshold_proximity / 15, delta_time))
		return
	var/grav_range = round(log(2.5, critical_threshold_proximity))
	for(var/mob/alive_mob in GLOB.alive_mob_list)
		if(alive_mob.z != z || get_dist(alive_mob, src) > grav_range || alive_mob.mob_negates_gravity())
			continue
		step_towards(alive_mob, loc)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/remove_waste(delta_time)
	//Gases can be removed from the moderator internal by using the interface.
	if(!waste_remove)
		return
	var/filtering_amount = moderator_scrubbing.len
	for(var/gas in moderator_internal.gases & moderator_scrubbing)
		var/datum/gas_mixture/removed = moderator_internal.remove_specific(gas, (moderator_filtering_rate / filtering_amount) * delta_time)
		if(removed)
			linked_output.airs[1].merge(removed)

	if (selected_fuel)
		var/datum/gas_mixture/internal_remove
		for(var/gas_id in selected_fuel.primary_products)
			if(internal_fusion.gases[gas_id][MOLES] > 0)
				internal_remove = internal_fusion.remove_specific(gas_id, internal_fusion.gases[gas_id][MOLES] * (1 - (1 - 0.25) ** delta_time))
				linked_output.airs[1].merge(internal_remove)
	internal_fusion.garbage_collect()
	moderator_internal.garbage_collect()

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/process_internal_cooling(delta_time)
	if(moderator_internal.total_moles() > 0 && internal_fusion.total_moles() > 0)
		//Modifies the moderator_internal temperature based on energy conduction and also the fusion by the same amount
		var/fusion_temperature_delta = internal_fusion.temperature - moderator_internal.temperature
		var/fusion_heat_amount = (1 - (1 - METALLIC_VOID_CONDUCTIVITY) ** delta_time) * fusion_temperature_delta * (internal_fusion.heat_capacity() * moderator_internal.heat_capacity() / (internal_fusion.heat_capacity() + moderator_internal.heat_capacity()))
		internal_fusion.temperature = max(internal_fusion.temperature - fusion_heat_amount / internal_fusion.heat_capacity(), TCMB)
		moderator_internal.temperature = max(moderator_internal.temperature + fusion_heat_amount / moderator_internal.heat_capacity(), TCMB)

	if(airs[1].total_moles() * 0.05 <= MINIMUM_MOLE_COUNT)
		return
	var/datum/gas_mixture/cooling_port = airs[1]
	var/datum/gas_mixture/cooling_remove = cooling_port.remove(0.05 * cooling_port.total_moles())
	//Cooling of the moderator gases with the cooling loop in and out the core
	if(moderator_internal.total_moles() > 0)
		var/coolant_temperature_delta = cooling_remove.temperature - moderator_internal.temperature
		var/cooling_heat_amount = (1 - (1 - HIGH_EFFICIENCY_CONDUCTIVITY) ** delta_time) * coolant_temperature_delta * (cooling_remove.heat_capacity() * moderator_internal.heat_capacity() / (cooling_remove.heat_capacity() + moderator_internal.heat_capacity()))
		cooling_remove.temperature = max(cooling_remove.temperature - cooling_heat_amount / cooling_remove.heat_capacity(), TCMB)
		moderator_internal.temperature = max(moderator_internal.temperature + cooling_heat_amount / moderator_internal.heat_capacity(), TCMB)

	else if(internal_fusion.total_moles() > 0)
		var/coolant_temperature_delta = cooling_remove.temperature - internal_fusion.temperature
		var/cooling_heat_amount = (1 - (1 - METALLIC_VOID_CONDUCTIVITY) ** delta_time) * coolant_temperature_delta * (cooling_remove.heat_capacity() * internal_fusion.heat_capacity() / (cooling_remove.heat_capacity() + internal_fusion.heat_capacity()))
		cooling_remove.temperature = max(cooling_remove.temperature - cooling_heat_amount / cooling_remove.heat_capacity(), TCMB)
		internal_fusion.temperature = max(internal_fusion.temperature + cooling_heat_amount / internal_fusion.heat_capacity(), TCMB)
	cooling_port.merge(cooling_remove)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/inject_from_side_components(delta_time)
	update_pipenets()

	//Check and stores the gases from the moderator input in the moderator internal gasmix
	var/datum/gas_mixture/moderator_port = linked_moderator.airs[1]
	if(start_moderator && moderator_port.total_moles())
		moderator_internal.merge(moderator_port.remove(moderator_injection_rate * delta_time))
		linked_moderator.update_parents()

	//Check if the fuels are present and move them inside the fuel internal gasmix
	if(!start_fuel || !selected_fuel || !check_gas_requirements())
		return

	var/datum/gas_mixture/fuel_port = linked_input.airs[1]
	for(var/gas_type in selected_fuel.requirements)
		internal_fusion.assert_gas(gas_type)
		internal_fusion.merge(fuel_port.remove_specific(gas_type, fuel_injection_rate * delta_time / length(selected_fuel.requirements)))
		linked_input.update_parents()

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_deconstructable()
	if(!active)
		return
	if(power_level > 0)
		fusion_started = TRUE
		linked_input.fusion_started = TRUE
		linked_output.fusion_started = TRUE
		linked_moderator.fusion_started = TRUE
		linked_interface.fusion_started = TRUE
		for(var/obj/machinery/hypertorus/corner/corner in corners)
			corner.fusion_started = TRUE
	else
		fusion_started = FALSE
		linked_input.fusion_started = FALSE
		linked_output.fusion_started = FALSE
		linked_moderator.fusion_started = FALSE
		linked_interface.fusion_started = FALSE
		for(var/obj/machinery/hypertorus/corner/corner in corners)
			corner.fusion_started = FALSE


code:
/**
 * This file contain the eight parts surrounding the main core, those are: fuel input, moderator input, waste output, interface and the corners
 * The file also contain the guicode of the machine
 */
/obj/machinery/atmospherics/components/unary/hypertorus
	icon = 'icons/obj/atmospherics/components/hypertorus.dmi'
	icon_state = "core"

	name = "thermomachine"
	desc = "Heats or cools gas in connected pipes."
	anchored = TRUE
	density = TRUE
	resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
	layer = OBJ_LAYER
	pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
	circuit = /obj/item/circuitboard/machine/thermomachine
	///Vars for the state of the icon of the object (open, off, active)
	var/icon_state_open
	var/icon_state_off
	var/icon_state_active
	///Check if the machine has been activated
	var/active = FALSE
	///Check if fusion has started
	var/fusion_started = FALSE
	///Check if the machine is cracked open
	var/cracked = FALSE

/obj/machinery/atmospherics/components/unary/hypertorus/Initialize(mapload)
	. = ..()
	initialize_directions = dir

/obj/machinery/atmospherics/components/unary/hypertorus/examine(mob/user)
	. = ..()
	. += span_notice("[src] can be rotated by first opening the panel with a screwdriver and then using a wrench on it.")

/obj/machinery/atmospherics/components/unary/hypertorus/attackby(obj/item/I, mob/user, params)
	if(!fusion_started)
		if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_off, I))
			return
	if(default_change_direction_wrench(user, I))
		return
	if(default_deconstruction_crowbar(I))
		return
	return ..()

/obj/machinery/atmospherics/components/unary/hypertorus/welder_act(mob/living/user, obj/item/tool)
	if(!cracked)
		return FALSE
	if(user.combat_mode)
		return FALSE
	balloon_alert(user, "You start repairing the crack...")
	if(tool.use_tool(src, user, 10 SECONDS, volume=30, amount=5))
		balloon_alert(user, "You repaired the crack.")
		cracked = FALSE
		update_appearance()

/obj/machinery/atmospherics/components/unary/hypertorus/default_change_direction_wrench(mob/user, obj/item/I)
	. = ..()
	if(.)
		set_init_directions()
		var/obj/machinery/atmospherics/node = nodes[1]
		if(node)
			node.disconnect(src)
			nodes[1] = null
			if(parents[1])
				nullify_pipenet(parents[1])
		atmos_init()
		node = nodes[1]
		if(node)
			node.atmos_init()
			node.add_member(src)
		SSair.add_to_rebuild_queue(src)

/obj/machinery/atmospherics/components/unary/hypertorus/update_icon_state()
	if(panel_open)
		icon_state = icon_state_open
		return ..()
	if(active)
		icon_state = icon_state_active
		return ..()
	icon_state = icon_state_off
	return ..()

/obj/machinery/atmospherics/components/unary/hypertorus/update_overlays()
	. = ..()
	if(!cracked)
		return
	var/image/crack = image(icon, icon_state = "crack")
	crack.dir = dir
	. += crack

/obj/machinery/atmospherics/components/unary/hypertorus/fuel_input
	name = "HFR fuel input port"
	desc = "Input port for the Hypertorus Fusion Reactor, designed to take in only Hydrogen and Tritium in gas forms."
	icon_state = "fuel_input_off"
	icon_state_open = "fuel_input_open"
	icon_state_off = "fuel_input_off"
	icon_state_active = "fuel_input_active"
	circuit = /obj/item/circuitboard/machine/HFR_fuel_input

/obj/machinery/atmospherics/components/unary/hypertorus/waste_output
	name = "HFR waste output port"
	desc = "Waste port for the Hypertorus Fusion Reactor, designed to output the hot waste gases coming from the core of the machine."
	icon_state = "waste_output_off"
	icon_state_open = "waste_output_open"
	icon_state_off = "waste_output_off"
	icon_state_active = "waste_output_active"
	circuit = /obj/item/circuitboard/machine/HFR_waste_output

/obj/machinery/atmospherics/components/unary/hypertorus/moderator_input
	name = "HFR moderator input port"
	desc = "Moderator port for the Hypertorus Fusion Reactor, designed to move gases inside the machine to cool and control the flow of the reaction."
	icon_state = "moderator_input_off"
	icon_state_open = "moderator_input_open"
	icon_state_off = "moderator_input_off"
	icon_state_active = "moderator_input_active"
	circuit = /obj/item/circuitboard/machine/HFR_moderator_input

/*
* Interface and corners
*/
/obj/machinery/hypertorus
	name = "hypertorus_core"
	desc = "hypertorus_core"
	icon = 'icons/obj/atmospherics/components/hypertorus.dmi'
	icon_state = "core"
	move_resist = INFINITY
	anchored = TRUE
	density = TRUE
	resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF
	power_channel = AREA_USAGE_ENVIRON
	var/active = FALSE
	var/icon_state_open
	var/icon_state_off
	var/icon_state_active
	var/fusion_started = FALSE

/obj/machinery/hypertorus/examine(mob/user)
	. = ..()
	. += span_notice("[src] can be rotated by first opening the panel with a screwdriver and then using a wrench on it.")

/obj/machinery/hypertorus/attackby(obj/item/I, mob/user, params)
	if(!fusion_started)
		if(default_deconstruction_screwdriver(user, icon_state_open, icon_state_off, I))
			return
	if(default_change_direction_wrench(user, I))
		return
	if(default_deconstruction_crowbar(I))
		return
	return ..()

/obj/machinery/hypertorus/update_icon_state()
	if(panel_open)
		icon_state = icon_state_open
		return ..()
	if(active)
		icon_state = icon_state_active
		return ..()
	icon_state = icon_state_off
	return ..()

/obj/machinery/hypertorus/interface
	name = "HFR interface"
	desc = "Interface for the HFR to control the flow of the reaction."
	icon_state = "interface_off"
	circuit = /obj/item/circuitboard/machine/HFR_interface
	var/obj/machinery/atmospherics/components/unary/hypertorus/core/connected_core
	icon_state_off = "interface_off"
	icon_state_open = "interface_open"
	icon_state_active = "interface_active"

/obj/machinery/hypertorus/interface/Destroy()
	if(connected_core)
		connected_core = null
	return..()

/obj/machinery/hypertorus/interface/multitool_act(mob/living/user, obj/item/I)
	. = ..()
	var/turf/T = get_step(src,turn(dir,180))
	var/obj/machinery/atmospherics/components/unary/hypertorus/core/centre = locate() in T

	if(!centre || !centre.check_part_connectivity())
		to_chat(user, span_notice("Check all parts and then try again."))
		return TRUE
	new/obj/item/paper/guides/jobs/atmos/hypertorus(loc)
	connected_core = centre

	connected_core.activate(user)
	return TRUE

/obj/machinery/hypertorus/interface/ui_interact(mob/user, datum/tgui/ui)
	if(active)
		ui = SStgui.try_update_ui(user, src, ui)
		if(!ui)
			ui = new(user, src, "Hypertorus", name)
			ui.open()
	else
		to_chat(user, span_notice("Activate the machine first by using a multitool on the interface."))

/obj/machinery/hypertorus/interface/proc/gas_list_to_gasid_list(list/gas_list)
	var/list/gasid_list = list()
	for(var/gas_type in gas_list)
		var/datum/gas/gas = gas_type
		gasid_list += initial(gas.id)
	return gasid_list

/obj/machinery/hypertorus/interface/ui_static_data()
	var/data = list()
	data["base_max_temperature"] = FUSION_MAXIMUM_TEMPERATURE
	data["selectable_fuel"] = list(list("name" = "Nothing", "id" = null))
	for(var/path in GLOB.hfr_fuels_list)
		var/datum/hfr_fuel/recipe = GLOB.hfr_fuels_list[path]

		data["selectable_fuel"] += list(list(
			"name" = recipe.name,
			"id" = recipe.id,
			"requirements" = gas_list_to_gasid_list(recipe.requirements),
			"fusion_byproducts" = gas_list_to_gasid_list(recipe.primary_products),
			"product_gases" = gas_list_to_gasid_list(recipe.secondary_products),
			"recipe_cooling_multiplier" = recipe.negative_temperature_multiplier,
			"recipe_heating_multiplier" = recipe.positive_temperature_multiplier,
			"energy_loss_multiplier" = recipe.energy_concentration_multiplier,
			"fuel_consumption_multiplier" = recipe.fuel_consumption_multiplier,
			"gas_production_multiplier" = recipe.gas_production_multiplier,
			"temperature_multiplier" = recipe.temperature_change_multiplier,
		))
	return data

/obj/machinery/hypertorus/interface/ui_data()
	var/data = list()

	if(connected_core.selected_fuel)
		data["selected"] = connected_core.selected_fuel.id
	else
		data["selected"] = ""

	//Internal Fusion gases
	var/list/fusion_gasdata = list()
	if(connected_core.internal_fusion.total_moles())
		for(var/gas_type in connected_core.internal_fusion.gases)
			var/datum/gas/gas = gas_type
			fusion_gasdata.Add(list(list(
			"id"= initial(gas.id),
			"amount" = round(connected_core.internal_fusion.gases[gas][MOLES], 0.01),
			)))
	else
		for(var/gas_type in connected_core.internal_fusion.gases)
			var/datum/gas/gas = gas_type
			fusion_gasdata.Add(list(list(
				"id"= initial(gas.id),
				"amount" = 0,
				)))
	//Moderator gases
	var/list/moderator_gasdata = list()
	if(connected_core.moderator_internal.total_moles())
		for(var/gas_type in connected_core.moderator_internal.gases)
			var/datum/gas/gas = gas_type
			moderator_gasdata.Add(list(list(
			"id"= initial(gas.id),
			"amount" = round(connected_core.moderator_internal.gases[gas][MOLES], 0.01),
			)))
	else
		for(var/gas_type in connected_core.moderator_internal.gases)
			var/datum/gas/gas = gas_type
			moderator_gasdata.Add(list(list(
				"id"= initial(gas.id),
				"amount" = 0,
				)))

	data["fusion_gases"] = fusion_gasdata
	data["moderator_gases"] = moderator_gasdata

	data["energy_level"] = connected_core.energy
	data["heat_limiter_modifier"] = connected_core.heat_limiter_modifier
	data["heat_output_min"] = connected_core.heat_output_min
	data["heat_output_max"] = connected_core.heat_output_max
	data["heat_output"] = connected_core.heat_output
	data["instability"] = connected_core.instability

	data["heating_conductor"] = connected_core.heating_conductor
	data["magnetic_constrictor"] = connected_core.magnetic_constrictor
	data["fuel_injection_rate"] = connected_core.fuel_injection_rate
	data["moderator_injection_rate"] = connected_core.moderator_injection_rate
	data["current_damper"] = connected_core.current_damper

	data["power_level"] = connected_core.power_level
	data["apc_energy"] = connected_core.get_area_cell_percent()
	data["iron_content"] = connected_core.iron_content
	data["integrity"] = connected_core.get_integrity_percent()

	data["start_power"] = connected_core.start_power
	data["start_cooling"] = connected_core.start_cooling
	data["start_fuel"] = connected_core.start_fuel
	data["start_moderator"] = connected_core.start_moderator

	data["internal_fusion_temperature"] = connected_core.fusion_temperature
	data["moderator_internal_temperature"] = connected_core.moderator_temperature
	data["internal_output_temperature"] = connected_core.output_temperature
	data["internal_coolant_temperature"] = connected_core.coolant_temperature

	data["internal_fusion_temperature_archived"] = connected_core.fusion_temperature_archived
	data["moderator_internal_temperature_archived"] = connected_core.moderator_temperature_archived
	data["internal_output_temperature_archived"] = connected_core.output_temperature_archived
	data["internal_coolant_temperature_archived"] = connected_core.coolant_temperature_archived
	data["temperature_period"] = connected_core.temperature_period

	data["waste_remove"] = connected_core.waste_remove
	data["filter_types"] = list()
	for(var/path in GLOB.meta_gas_info)
		var/list/gas = GLOB.meta_gas_info[path]
		data["filter_types"] += list(list("gas_id" = gas[META_GAS_ID], "gas_name" = gas[META_GAS_NAME], "enabled" = (path in connected_core.moderator_scrubbing)))

	data["cooling_volume"] = connected_core.airs[1].volume
	data["mod_filtering_rate"] = connected_core.moderator_filtering_rate

	return data

/obj/machinery/hypertorus/interface/ui_act(action, params)
	. = ..()
	if(.)
		return
	switch(action)
		if("start_power")
			connected_core.start_power = !connected_core.start_power
			connected_core.update_use_power(connected_core.start_power ? ACTIVE_POWER_USE : IDLE_POWER_USE)
			. = TRUE
		if("start_cooling")
			connected_core.start_cooling = !connected_core.start_cooling
			. = TRUE
		if("start_fuel")
			connected_core.start_fuel = !connected_core.start_fuel
			. = TRUE
		if("start_moderator")
			connected_core.start_moderator = !connected_core.start_moderator
			. = TRUE
		if("heating_conductor")
			var/heating_conductor = text2num(params["heating_conductor"])
			if(heating_conductor != null)
				connected_core.heating_conductor = clamp(heating_conductor, 50, 500)
				. = TRUE
		if("magnetic_constrictor")
			var/magnetic_constrictor = text2num(params["magnetic_constrictor"])
			if(magnetic_constrictor != null)
				connected_core.magnetic_constrictor = clamp(magnetic_constrictor, 50, 1000)
				. = TRUE
		if("fuel_injection_rate")
			var/fuel_injection_rate = text2num(params["fuel_injection_rate"])
			if(fuel_injection_rate != null)
				connected_core.fuel_injection_rate = clamp(fuel_injection_rate, 0.5, 150)
				. = TRUE
		if("moderator_injection_rate")
			var/moderator_injection_rate = text2num(params["moderator_injection_rate"])
			if(moderator_injection_rate != null)
				connected_core.moderator_injection_rate = clamp(moderator_injection_rate, 0.5, 150)
				. = TRUE
		if("current_damper")
			var/current_damper = text2num(params["current_damper"])
			if(current_damper != null)
				connected_core.current_damper = clamp(current_damper, 0, 1000)
				. = TRUE
		if("waste_remove")
			connected_core.waste_remove = !connected_core.waste_remove
			. = TRUE
		if("filter")
			connected_core.moderator_scrubbing ^= gas_id2path(params["mode"])
			. = TRUE
		if("mod_filtering_rate")
			var/mod_filtering_rate = text2num(params["mod_filtering_rate"])
			if(mod_filtering_rate != null)
				connected_core.moderator_filtering_rate = clamp(mod_filtering_rate, 5, 200)
				. = TRUE
		if("fuel")
			connected_core.selected_fuel = null
			var/fuel_mix = "nothing"
			var/datum/hfr_fuel/fuel = null
			if(params["mode"] != "")
				fuel = GLOB.hfr_fuels_list[params["mode"]]
			if(fuel)
				connected_core.selected_fuel = fuel
				fuel_mix = fuel.name
			if(connected_core.internal_fusion.total_moles())
				connected_core.dump_gases()
			connected_core.update_parents() //prevent the machine from stopping because of the recipe change and the pipenet not updating
			connected_core.linked_input.update_parents()
			connected_core.linked_output.update_parents()
			connected_core.linked_moderator.update_parents()
			investigate_log("was set to recipe [fuel_mix ? fuel_mix : "null"] by [key_name(usr)]", INVESTIGATE_ATMOS)
			. = TRUE
		if("cooling_volume")
			var/cooling_volume = text2num(params["cooling_volume"])
			if(cooling_volume != null)
				connected_core.airs[1].volume = clamp(cooling_volume, 50, 2000)
				. = TRUE

/obj/machinery/hypertorus/corner
	name = "HFR corner"
	desc = "Structural piece of the machine."
	icon_state = "corner_off"
	circuit = /obj/item/circuitboard/machine/HFR_corner
	icon_state_off = "corner_off"
	icon_state_open = "corner_open"
	icon_state_active = "corner_active"
	dir = SOUTHEAST

/obj/item/paper/guides/jobs/atmos/hypertorus
	name = "paper- 'Quick guide to safe handling of the HFR'"
	info = "How to safely(TM) operate the Hypertorus
\
	-Build the machine as it�s shown in the main guide.
\
	-Make a 50/50 gasmix of tritium and hydrogen totalling around 2000 moles.
\
	-Start the machine, fill up the cooling loop with plasma/hypernoblium and use space or freezers to cool it.
\
	-Connect the fuel mix into the fuel injector port, allow only 1000 moles into the machine to ease the kickstart of the reaction
\
	-Set the Heat conductor to 500 when starting the reaction, reset it to 100 when power level is higher than 1
\
	-In the event of a meltdown, set the heat conductor to max and set the current damper to max. Set the fuel injection to min. \
	If the heat output doesn�t go negative, try changing the magnetic costrictors untill heat output goes negative. \
	Make the cooling stronger, put high heat capacity gases inside the moderator (hypernoblium will help dealing with the problem)

\
	Warnings:
\
	-You cannot dismantle the machine if the power level is over 0
\
	-You cannot power of the machine if the power level is over 0
\
	-You cannot dispose of waste gases if power level is over 5
\
	-You cannot remove gases from the fusion mix if they are not helium and antinoblium
\
	-Hypernoblium will decrease the power of the mix by a lot
\
	-Antinoblium will INCREASE the power of the mix by a lot more
\
	-High heat capacity gases are harder to heat/cool
\
	-Low heat capacity gases are easier to heat/cool
\
	-The machine consumes 50 KW per power level, reaching 350 KW at power level 6 so prepare the SM accordingly
\
	-In case of a power shortage, the fusion reaction will CONTINUE but the cooling will STOP

\
	The writer of the quick guide will not be held responsible for misuses and meltdown caused by the use of the guide, \
	use more advanced guides to understando how the various gases will act as moderators."

/obj/item/hfr_box
	name = "HFR box"
	desc = "If you see this, call the police."
	icon = 'icons/obj/atmospherics/components/hypertorus.dmi'
	icon_state = "box"
	///What kind of box are we handling?
	var/box_type = "impossible"
	///What's the path of the machine we making
	var/part_path

/obj/item/hfr_box/corner
	name = "HFR box corner"
	desc = "Place this as the corner of your 3x3 multiblock fusion reactor"
	icon_state = "box_corner"
	box_type = "corner"
	part_path = /obj/machinery/hypertorus/corner

/obj/item/hfr_box/body
	name = "HFR box body"
	desc = "Place this on the sides of the core box of your 3x3 multiblock fusion reactor"
	box_type = "body"
	icon_state = "box_body"

/obj/item/hfr_box/body/fuel_input
	name = "HFR box fuel input"
	part_path = /obj/machinery/atmospherics/components/unary/hypertorus/fuel_input

/obj/item/hfr_box/body/moderator_input
	name = "HFR box moderator input"
	part_path = /obj/machinery/atmospherics/components/unary/hypertorus/moderator_input

/obj/item/hfr_box/body/waste_output
	name = "HFR box waste output"
	part_path = /obj/machinery/atmospherics/components/unary/hypertorus/waste_output

/obj/item/hfr_box/body/interface
	name = "HFR box interface"
	part_path = /obj/machinery/hypertorus/interface

/obj/item/hfr_box/core
	name = "HFR box core"
	desc = "Activate this with a multitool to deploy the full machine after setting up the other boxes"
	icon_state = "box_core"
	box_type = "core"
	part_path = /obj/machinery/atmospherics/components/unary/hypertorus/core

/obj/item/hfr_box/core/multitool_act(mob/living/user, obj/item/I)
	. = ..()
	var/list/parts = list()
	for(var/obj/item/hfr_box/box in orange(1,src))
		var/direction = get_dir(src, box)
		if(box.box_type == "corner")
			if(ISDIAGONALDIR(direction))
				box.dir = direction
				parts |= box
			continue
		if(box.box_type == "body")
			if(direction in GLOB.cardinals)
				box.dir = direction
				parts |= box
			continue
	if(parts.len == 8)
		build_reactor(parts)
	return

/obj/item/hfr_box/core/proc/build_reactor(list/parts)
	for(var/obj/item/hfr_box/box in parts)
		if(box.box_type == "corner")
			var/obj/machinery/hypertorus/corner/corner = new box.part_path(box.loc)
			corner.dir = box.dir
			qdel(box)
			continue
		if(box.box_type == "body")
			var/location = get_turf(box)
			if(box.part_path != /obj/machinery/hypertorus/interface)
				var/obj/machinery/atmospherics/components/unary/hypertorus/part = new box.part_path(location, TRUE, box.dir)
				part.dir = box.dir
			else
				var/obj/machinery/hypertorus/interface/part = new box.part_path(location)
				part.dir = box.dir
			qdel(box)
			continue

	new/obj/machinery/atmospherics/components/unary/hypertorus/core(loc, TRUE)
	qdel(src)

repo_name: AnalWerewolf/sundown

license: mit


code:
/**
 * This section contain all procs that helps building, destroy and control the hfr
 */

/**
 * Called by multitool_act() in hfr_parts.dm, by atmos_process() in hfr_main_processes.dm and by fusion_process() in the same file
 * This proc checks the surrounding of the core to ensure that the machine has been build correctly, returns false if there is a missing piece/wrong placed one
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_part_connectivity()
	. = TRUE
	if(!anchored || panel_open)
		return FALSE

	for(var/obj/machinery/hypertorus/object in orange(1,src))
		if(. == FALSE)
			break

		if(object.panel_open)
			. = FALSE

		if(istype(object,/obj/machinery/hypertorus/corner))
			var/dir = get_dir(src,object)
			if(dir in GLOB.cardinals)
				. =  FALSE
			switch(dir)
				if(SOUTHEAST)
					if(object.dir != dir)
						. = FALSE
				if(SOUTHWEST)
					if(object.dir != dir)
						. =  FALSE
				if(NORTHEAST)
					if(object.dir != dir)
						. =  FALSE
				if(NORTHWEST)
					if(object.dir != dir)
						. =  FALSE
			corners |= object
			continue

		if(get_step(object,turn(object.dir,180)) != loc)
			. =  FALSE

		if(istype(object,/obj/machinery/hypertorus/interface))
			if(linked_interface && linked_interface != object)
				. =  FALSE
			linked_interface = object

	for(var/obj/machinery/atmospherics/components/unary/hypertorus/object in orange(1,src))
		if(. == FALSE)
			break

		if(object.panel_open)
			. = FALSE

		if(get_step(object,turn(object.dir,180)) != loc)
			. =  FALSE

		if(istype(object,/obj/machinery/atmospherics/components/unary/hypertorus/fuel_input))
			if(linked_input && linked_input != object)
				. =  FALSE
			linked_input = object
			machine_parts |= object

		if(istype(object,/obj/machinery/atmospherics/components/unary/hypertorus/waste_output))
			if(linked_output && linked_output != object)
				. =  FALSE
			linked_output = object
			machine_parts |= object

		if(istype(object,/obj/machinery/atmospherics/components/unary/hypertorus/moderator_input))
			if(linked_moderator && linked_moderator != object)
				. =  FALSE
			linked_moderator = object
			machine_parts |= object

	if(!linked_interface || !linked_input || !linked_moderator || !linked_output || corners.len != 4)
		. = FALSE

/**
 * Called by multitool_act() in hfr_parts.dm
 * It sets the pieces to active, allowing the player to start the main reaction
 * Arguments:
 * * -user: the player doing the action
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/activate(mob/living/user)
	if(active)
		to_chat(user, span_notice("You already activated the machine."))
		return
	to_chat(user, span_notice("You link all parts toghether."))
	active = TRUE
	update_appearance()
	linked_interface.active = TRUE
	linked_interface.update_appearance()
	RegisterSignal(linked_interface, COMSIG_PARENT_QDELETING, .proc/unregister_signals)
	linked_input.active = TRUE
	linked_input.update_appearance()
	RegisterSignal(linked_input, COMSIG_PARENT_QDELETING, .proc/unregister_signals)
	linked_output.active = TRUE
	linked_output.update_appearance()
	RegisterSignal(linked_output, COMSIG_PARENT_QDELETING, .proc/unregister_signals)
	linked_moderator.active = TRUE
	linked_moderator.update_appearance()
	RegisterSignal(linked_moderator, COMSIG_PARENT_QDELETING, .proc/unregister_signals)
	for(var/obj/machinery/hypertorus/corner/corner in corners)
		corner.active = TRUE
		corner.update_appearance()
		RegisterSignal(corner, COMSIG_PARENT_QDELETING, .proc/unregister_signals)
	soundloop = new(src, TRUE)
	soundloop.volume = 5

/**
 * Called when a part gets deleted around the hfr, called on Destroy() of the hfr core in hfr_core.dm
 * Unregister the signals attached to the core from the various machines, if only_signals is false it will also call deactivate()
 * Arguments:
 * * only_signals: default FALSE, if true the proc will not call the deactivate() proc
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/unregister_signals(only_signals = FALSE)
	SIGNAL_HANDLER
	if(linked_interface)
		UnregisterSignal(linked_interface, COMSIG_PARENT_QDELETING)
	if(linked_input)
		UnregisterSignal(linked_input, COMSIG_PARENT_QDELETING)
	if(linked_output)
		UnregisterSignal(linked_output, COMSIG_PARENT_QDELETING)
	if(linked_moderator)
		UnregisterSignal(linked_moderator, COMSIG_PARENT_QDELETING)
	for(var/obj/machinery/hypertorus/corner/corner in corners)
		UnregisterSignal(corner, COMSIG_PARENT_QDELETING)
	if(!only_signals)
		deactivate()

/**
 * Called by unregister_signals() in this file, called when the main fusion processes check_part_connectivity() returns false
 * Deactivate the various machines by setting the active var to false, updates the machines icon and set the linked machine vars to null
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/deactivate()
	if(!active)
		return
	active = FALSE
	update_appearance()
	if(linked_interface)
		linked_interface.active = FALSE
		linked_interface.update_appearance()
		linked_interface = null
	if(linked_input)
		linked_input.active = FALSE
		linked_input.update_appearance()
		linked_input = null
	if(linked_output)
		linked_output.active = FALSE
		linked_output.update_appearance()
		linked_output = null
	if(linked_moderator)
		linked_moderator.active = FALSE
		linked_moderator.update_appearance()
		linked_moderator = null
	if(corners.len)
		for(var/obj/machinery/hypertorus/corner/corner in corners)
			corner.active = FALSE
			corner.update_appearance()
		corners = list()
	QDEL_NULL(soundloop)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/assert_gases()
	//Assert the gases that will be used/created during the process

	internal_fusion.assert_gas(/datum/gas/antinoblium)

	moderator_internal.assert_gases(arglist(GLOB.meta_gas_info))

	if (!selected_fuel)
		return

	internal_fusion.assert_gases(arglist(selected_fuel.requirements | selected_fuel.primary_products))

/**
 * Updates all related pipenets from all connected components
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/update_pipenets()
	update_parents()
	linked_input.update_parents()
	linked_output.update_parents()
	linked_moderator.update_parents()

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/update_temperature_status(delta_time)
	fusion_temperature_archived = fusion_temperature
	fusion_temperature = internal_fusion.temperature
	moderator_temperature_archived = moderator_temperature
	moderator_temperature = moderator_internal.temperature
	coolant_temperature_archived = coolant_temperature
	coolant_temperature = airs[1].temperature
	output_temperature_archived = output_temperature
	output_temperature = linked_output.airs[1].temperature
	temperature_period = delta_time

	//Set the power level of the fusion process
	switch(fusion_temperature)
		if(-INFINITY to 500)
			power_level = 0
		if(500 to 1e3)
			power_level = 1
		if(1e3 to 1e4)
			power_level = 2
		if(1e4 to 1e5)
			power_level = 3
		if(1e5 to 1e6)
			power_level = 4
		if(1e6 to 1e7)
			power_level = 5
		else
			power_level = 6

/**
 * Infrequently plays accent sounds, and adjusts main loop parameters
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/play_ambience()
	// We play delam/neutral sounds at a rate determined by power and critical_threshold_proximity
	if(last_accent_sound < world.time && prob(20))
		var/aggression = min(((critical_threshold_proximity / 800) * ((power_level) / 5)), 1.0) * 100
		if(critical_threshold_proximity >= 300)
			playsound(src, "hypertorusmelting", max(50, aggression), FALSE, 40, 30, falloff_distance = 10)
		else
			playsound(src, "hypertoruscalm", max(50, aggression), FALSE, 25, 25, falloff_distance = 10)
		var/next_sound = round((100 - aggression) * 5) + 5
		last_accent_sound = world.time + max(HYPERTORUS_ACCENT_SOUND_MIN_COOLDOWN, next_sound)

	var/ambient_hum = 1
	if (check_fuel())
		ambient_hum = power_level + 1
	soundloop.volume = clamp(ambient_hum * 8, 0, 50)

/**
 * Called by the main fusion processes in hfr_main_processes.dm
 * Getter for fusion fuel moles
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_fuel()
	if(!selected_fuel)
		return FALSE
	if(!internal_fusion.total_moles())
		return FALSE
	for(var/gas_type in selected_fuel.requirements)
		internal_fusion.assert_gas(gas_type)
		if(internal_fusion.gases[gas_type][MOLES] < FUSION_MOLE_THRESHOLD)
			return FALSE
	return TRUE

/**
 * Called by the main fusion processes in hfr_main_processes.dm
 * Check the power use of the machine, return TRUE if there is enough power in the powernet
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_power_use()
	if(machine_stat & (NOPOWER|BROKEN))
		return FALSE
	if(use_power == ACTIVE_POWER_USE)
		update_mode_power_usage(ACTIVE_POWER_USE, (power_level + 1) * MIN_POWER_USAGE) //Max around 350 KW

	return TRUE

///Checks if the gases in the input are the ones needed by the recipe
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_gas_requirements()
	var/datum/gas_mixture/contents = linked_input.airs[1]
	for(var/gas_type in selected_fuel.requirements)
		if(!contents.gases[gas_type] || !contents.gases[gas_type][MOLES])
			return FALSE
	return TRUE

///Removes the gases from the internal gasmix when the recipe is changed
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/dump_gases()
	var/datum/gas_mixture/remove = internal_fusion.remove(internal_fusion.total_moles())
	linked_output.airs[1].merge(remove)
	internal_fusion.garbage_collect()
	linked_input.airs[1].garbage_collect()

/**
 * Called by alarm() in this file
 * Check the integrity level and returns the status of the machine
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/get_status()
	var/integrity = get_integrity_percent()
	if(integrity < HYPERTORUS_MELTING_PERCENT)
		return HYPERTORUS_MELTING

	if(integrity < HYPERTORUS_EMERGENCY_PERCENT)
		return HYPERTORUS_EMERGENCY

	if(integrity < HYPERTORUS_DANGER_PERCENT)
		return HYPERTORUS_DANGER

	if(integrity < HYPERTORUS_WARNING_PERCENT)
		return HYPERTORUS_WARNING

	if(power_level > 0)
		return HYPERTORUS_NOMINAL
	return HYPERTORUS_INACTIVE

/**
 * Called by check_alert() in this file
 * Play a sound from the machine, the type depends on the status of the hfr
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/alarm()
	switch(get_status())
		if(HYPERTORUS_MELTING)
			playsound(src, 'sound/misc/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10)
		if(HYPERTORUS_EMERGENCY)
			playsound(src, 'sound/machines/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
		if(HYPERTORUS_DANGER)
			playsound(src, 'sound/machines/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
		if(HYPERTORUS_WARNING)
			playsound(src, 'sound/machines/terminal_alert.ogg', 75)

/**
 * Getter for the machine integrity
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/get_integrity_percent()
	var/integrity = critical_threshold_proximity / melting_point
	integrity = round(100 - integrity * 100, 0.01)
	integrity = integrity < 0 ? 0 : integrity
	return integrity

/**
 * Get how charged the area's APC is
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/get_area_cell_percent()
	// Make sure to get APC levels from the same area the core draws from
	// Just in case people build an HFR across boundaries
	var/area/area = get_area(src)
	if (!area)
		return 0
	var/obj/machinery/power/apc/apc = area.apc
	if (!apc)
		return 0
	var/obj/item/stock_parts/cell/cell = apc.cell
	if (!cell)
		return 0
	return cell.percent()

/**
 * Called by process_atmos() in hfr_main_processes.dm
 * Called after checking the damage of the machine, calls alarm() and countdown()
 * Broadcast messages into engi and common radio
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_alert()
	if(critical_threshold_proximity < warning_point)
		return
	if((REALTIMEOFDAY - lastwarning) / 10 >= WARNING_TIME_DELAY)
		alarm()

		if(critical_threshold_proximity > emergency_point)
			radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity_percent()]%", common_channel)
			lastwarning = REALTIMEOFDAY
			if(!has_reached_emergency)
				investigate_log("has reached the emergency point for the first time.", INVESTIGATE_HYPERTORUS)
				message_admins("[src] has reached the emergency point [ADMIN_JMP(src)].")
				has_reached_emergency = TRUE
		else if(critical_threshold_proximity >= critical_threshold_proximity_archived) // The damage is still going up
			radio.talk_into(src, "[warning_alert] Integrity: [get_integrity_percent()]%", engineering_channel)
			lastwarning = REALTIMEOFDAY - (WARNING_TIME_DELAY * 5)

		else // Phew, we're safe
			radio.talk_into(src, "[safe_alert] Integrity: [get_integrity_percent()]%", engineering_channel)
			lastwarning = REALTIMEOFDAY

	//Melt
	if(critical_threshold_proximity > melting_point)
		countdown()

/**
 * Called by check_alert() in this file
 * Called when the damage has reached critical levels, start the countdown before the destruction, calls meltdown()
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/countdown()
	set waitfor = FALSE

	if(final_countdown) // We're already doing it go away
		return
	final_countdown = TRUE

	var/critical = selected_fuel.meltdown_flags & HYPERTORUS_FLAG_CRITICAL_MELTDOWN
	if(critical)
		priority_announce("WARNING - The explosion will likely cover a big part of the station and the coming EMP will wipe out most of the electronics. \
				Get as far away as possible from the reactor or find a way to shut it down.", "Alert")
	var/speaking = "[emergency_alert] The Hypertorus fusion reactor has reached critical integrity failure. Emergency magnetic dampeners online."
	radio.talk_into(src, speaking, common_channel, language = get_selected_language())
	for(var/i in HYPERTORUS_COUNTDOWN_TIME to 0 step -10)
		if(critical_threshold_proximity < melting_point) // Cutting it a bit close there engineers
			radio.talk_into(src, "[safe_alert] Failsafe has been disengaged.", common_channel)
			final_countdown = FALSE
			return
		else if((i % 50) != 0 && i > 50) // A message once every 5 seconds until the final 5 seconds which count down individualy
			sleep(10)
			continue
		else if(i > 50)
			if(i == 10 SECONDS && critical)
				sound_to_playing_players('sound/machines/hypertorus/HFR_critical_explosion.ogg')
			speaking = "[DisplayTimeText(i, TRUE)] remain before total integrity failure."
		else
			speaking = "[i*0.1]..."
		radio.talk_into(src, speaking, common_channel)
		sleep(10)

	meltdown()

/**
 * Called by countdown() in this file
 * Create the explosion + the gas emission before deleting the machine core.
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/meltdown()
	var/flash_explosion = 0
	var/light_impact_explosion = 0
	var/heavy_impact_explosion = 0
	var/devastating_explosion = 0
	var/em_pulse = selected_fuel.meltdown_flags & HYPERTORUS_FLAG_EMP
	var/rad_pulse = selected_fuel.meltdown_flags & HYPERTORUS_FLAG_RADIATION_PULSE
	var/emp_light_size = 0
	var/emp_heavy_size = 0
	var/rad_pulse_size = 0
	var/rad_pulse_strength = 0
	var/gas_spread = 0
	var/gas_pockets = 0
	var/critical = selected_fuel.meltdown_flags & HYPERTORUS_FLAG_CRITICAL_MELTDOWN

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_BASE_EXPLOSION)
		flash_explosion = power_level * 3
		light_impact_explosion = power_level * 2

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_MEDIUM_EXPLOSION)
		flash_explosion = power_level * 6
		light_impact_explosion = power_level * 5
		heavy_impact_explosion = power_level * 0.5

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_DEVASTATING_EXPLOSION)
		flash_explosion = power_level * 8
		light_impact_explosion = power_level * 7
		heavy_impact_explosion = power_level * 2
		devastating_explosion = power_level

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_MINIMUM_SPREAD)
		if(em_pulse)
			emp_light_size = power_level * 3
			emp_heavy_size = power_level * 1
		if(rad_pulse)
			rad_pulse_size = (1 / (power_level + 1))
			rad_pulse_strength = power_level * 3000
		gas_pockets = 5
		gas_spread = power_level * 2

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_MEDIUM_SPREAD)
		if(em_pulse)
			emp_light_size = power_level * 5
			emp_heavy_size = power_level * 3
		if(rad_pulse)
			rad_pulse_size = (1 / (power_level + 3))
			rad_pulse_strength = power_level * 5000
		gas_pockets = 7
		gas_spread = power_level * 4

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_BIG_SPREAD)
		if(em_pulse)
			emp_light_size = power_level * 7
			emp_heavy_size = power_level * 5
		if(rad_pulse)
			rad_pulse_size = (1 / (power_level + 5))
			rad_pulse_strength = power_level * 7000
		gas_pockets = 10
		gas_spread = power_level * 6

	if(selected_fuel.meltdown_flags & HYPERTORUS_FLAG_MASSIVE_SPREAD)
		if(em_pulse)
			emp_light_size = power_level * 9
			emp_heavy_size = power_level * 7
		if(rad_pulse)
			rad_pulse_size = (1 / (power_level + 7))
			rad_pulse_strength = power_level * 9000
		gas_pockets = 15
		gas_spread = power_level * 8

	var/list/around_turfs = circle_range_turfs(src, gas_spread)
	for(var/turf/turf as anything in around_turfs)
		if(isclosedturf(turf) || isspaceturf(turf))
			around_turfs -= turf
			continue
	var/datum/gas_mixture/remove_fusion
	if(internal_fusion.total_moles() > 0)
		remove_fusion = internal_fusion.remove_ratio(0.2)
		var/datum/gas_mixture/remove
		for(var/i in 1 to gas_pockets)
			remove = remove_fusion.remove_ratio(1/gas_pockets)
			var/turf/local = pick(around_turfs)
			local.assume_air(remove)
		loc.assume_air(internal_fusion)
	var/datum/gas_mixture/remove_moderator
	if(moderator_internal.total_moles() > 0)
		remove_moderator = moderator_internal.remove_ratio(0.2)
		var/datum/gas_mixture/remove
		for(var/i in 1 to gas_pockets)
			remove = remove_moderator.remove_ratio(1/gas_pockets)
			var/turf/local = pick(around_turfs)
			local.assume_air(remove)
		loc.assume_air(moderator_internal)

	//Max explosion ranges: devastation = 12, heavy = 24, light = 42
	explosion(
		origin = src,
		devastation_range = critical ? devastating_explosion * 2 : devastating_explosion,
		heavy_impact_range = critical ?  heavy_impact_explosion * 2 : heavy_impact_explosion,
		light_impact_range = light_impact_explosion,
		flash_range = flash_explosion,
		adminlog = TRUE,
		ignorecap = TRUE
		)

	if(rad_pulse)
		radiation_pulse(
			source = loc,
			intensity = rad_pulse_strength,
			range_modifier = rad_pulse_size,
			log = TRUE
			)

	if(em_pulse)
		empulse(
			epicenter = loc,
			heavy_range = critical ? emp_heavy_size * 2 : emp_heavy_size,
			light_range = critical ? emp_light_size * 2 : emp_heavy_size,
			log = TRUE
			)

	qdel(src)

/**
 * Induce hallucinations in nearby humans.
 *
 * force will make hallucinations ignore meson protection.
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/induce_hallucination(strength, delta_time, force=FALSE)
	for(var/mob/living/carbon/human/human in view(src, HALLUCINATION_HFR(heat_output)))
		if(!force && istype(human.glasses, /obj/item/clothing/glasses/meson))
			continue
		var/distance_root = sqrt(1 / max(1, get_dist(human, src)))
		human.hallucination += strength * distance_root * delta_time
		human.hallucination = clamp(human.hallucination, 0, 200)

/**
 * Emit radiation
 */
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/emit_rads(radiation)
	rad_power = clamp(radiation / 1e5, 0, FUSION_RAD_MAX)
	radiation_pulse(loc, rad_power)

/*
 * HFR cracking related procs
 */

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_cracked_parts()
	for(var/obj/machinery/atmospherics/components/unary/hypertorus/part in machine_parts)
		if(part.cracked)
			return TRUE
	return FALSE

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/create_crack()
	var/obj/machinery/atmospherics/components/unary/hypertorus/part = pick(machine_parts)
	part.cracked = TRUE
	part.update_appearance()
	return part

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/spill_gases(obj/origin, datum/gas_mixture/target_mix, ratio)
	var/datum/gas_mixture/remove_mixture = target_mix.remove_ratio(ratio)
	var/turf/origin_turf = origin.loc
	if(!origin_turf)
		return
	origin_turf.assume_air(remove_mixture)

/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/check_spill(delta_time)
	var/obj/machinery/atmospherics/components/unary/hypertorus/cracked_part = check_cracked_parts()
	if (cracked_part)
		// We have an existing crack
		var/leak_rate
		if (moderator_internal.return_pressure() < HYPERTORUS_MEDIUM_SPILL_PRESSURE)
			// Not high pressure, but can still leak
			if (!prob(HYPERTORUS_WEAK_SPILL_CHANCE))
				return
			leak_rate = HYPERTORUS_WEAK_SPILL_RATE
		else if (moderator_internal.return_pressure() < HYPERTORUS_STRONG_SPILL_PRESSURE)
			// Lots of gas in here, out we go
			leak_rate = HYPERTORUS_MEDIUM_SPILL_RATE
		else
			// Gotta go fast
			leak_rate = HYPERTORUS_STRONG_SPILL_RATE
		spill_gases(cracked_part, moderator_internal, ratio = 1 - (1 - leak_rate) ** delta_time)
		return

	if (moderator_internal.total_moles() < HYPERTORUS_HYPERCRITICAL_MOLES)
		return
	cracked_part = create_crack()
	// See if we do anything in the initial rupture
	if (moderator_internal.return_pressure() < HYPERTORUS_MEDIUM_SPILL_PRESSURE)
		return
	if (moderator_internal.return_pressure() < HYPERTORUS_STRONG_SPILL_PRESSURE)
		// Medium explosion on initial rupture
		explosion(
			origin = cracked_part,
			devastation_range = 0,
			heavy_impact_range = 0,
			light_impact_range = 1,
			flame_range = 3,
			flash_range = 3
			)
		spill_gases(cracked_part, moderator_internal, ratio = HYPERTORUS_MEDIUM_SPILL_INITIAL)
		return
	// Enough pressure for a strong explosion. Oh dear, oh dear.
	explosion(
		origin = cracked_part,
		devastation_range = 0,
		heavy_impact_range = 1,
		light_impact_range = 3,
		flame_range = 5,
		flash_range = 5
		)
	spill_gases(cracked_part, moderator_internal, ratio = HYPERTORUS_STRONG_SPILL_INITIAL)
	return


code:
///Global list of recipes for atmospheric machines to use
GLOBAL_LIST_INIT(gas_recipe_meta, gas_recipes_list())
///Defines for the recipes var
#define ENDOTHERMIC_REACTION "endothermic"
#define EXOTHERMIC_REACTION "exothermic"

/*
 * Global proc to build the gas recipe global list
 */
/proc/gas_recipes_list()
	. = list()
	for(var/recipe_path in subtypesof(/datum/gas_recipe))
		var/datum/gas_recipe/recipe = new recipe_path()

		.[recipe.id] = recipe

/datum/gas_recipe
	///Id of the recipe for easy identification in the code
	var/id = ""
	///What machine the recipe is for
	var/machine_type = ""
	///Displayed name of the recipe
	var/name = ""
	///Minimum temperature for the recipe
	var/min_temp = TCMB
	///Maximum temperature for the recipe
	var/max_temp = INFINITY
	///Type of reaction (either endothermic or exothermic)
	var/reaction_type = ""
	///Amount of energy released/consumed by the reaction (always positive)
	var/energy_release = 0
	var/dangerous = FALSE
	///Gas required for the recipe to work
	var/list/requirements
	///Products made from the recipe
	var/list/products

/datum/gas_recipe/crystallizer
	machine_type = "Crystallizer"

/datum/gas_recipe/crystallizer/hypern_crystalium
	id = "hyper_crystalium"
	name = "Hypernoblium Crystal"
	min_temp = 3
	max_temp = 250
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 250000
	requirements = list(/datum/gas/oxygen = 2000, /datum/gas/hypernoblium = 175)
	products = list(/obj/item/hypernoblium_crystal = 1)

/datum/gas_recipe/crystallizer/metallic_hydrogen
	id = "metal_h"
	name = "Metallic hydrogen"
	min_temp = 50000
	max_temp = 150000
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 2500000
	requirements = list(/datum/gas/hydrogen = 300, /datum/gas/bz = 50)
	products = list(/obj/item/stack/sheet/mineral/metal_hydrogen = 1)

/datum/gas_recipe/crystallizer/healium_grenade
	id = "healium_g"
	name = "Healium crystal"
	min_temp = 200
	max_temp = 400
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 2000000
	requirements = list(/datum/gas/healium = 100, /datum/gas/oxygen = 120, /datum/gas/plasma = 50)
	products = list(/obj/item/grenade/gas_crystal/healium_crystal = 1)

/datum/gas_recipe/crystallizer/proto_nitrate_grenade
	id = "proto_nitrate_g"
	name = "Proto nitrate crystal"
	min_temp = 200
	max_temp = 400
	reaction_type = EXOTHERMIC_REACTION
	energy_release = 1500000
	requirements = list(/datum/gas/proto_nitrate = 100, /datum/gas/nitrogen = 80, /datum/gas/oxygen = 80)
	products = list(/obj/item/grenade/gas_crystal/proto_nitrate_crystal = 1)

/datum/gas_recipe/crystallizer/hot_ice
	id = "hot_ice"
	name = "Hot ice"
	min_temp = 15
	max_temp = 35
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 3000000
	requirements = list(/datum/gas/freon = 60, /datum/gas/plasma = 160, /datum/gas/oxygen = 80)
	products = list(/obj/item/stack/sheet/hot_ice = 1)

/datum/gas_recipe/crystallizer/ammonia_crystal
	id = "ammonia_crystal"
	name = "Ammonia crystal"
	min_temp = 200
	max_temp = 240
	reaction_type = EXOTHERMIC_REACTION
	energy_release = 950000
	requirements = list(/datum/gas/hydrogen = 50, /datum/gas/nitrogen = 40)
	products = list(/obj/item/stack/ammonia_crystals = 2)

/datum/gas_recipe/crystallizer/shard
	id = "crystal_shard"
	name = "Supermatter crystal shard"
	min_temp = TCMB
	max_temp = 5
	reaction_type = EXOTHERMIC_REACTION
	energy_release = 150000000
	dangerous = TRUE
	requirements = list(/datum/gas/hypernoblium = 250, /datum/gas/antinoblium = 250, /datum/gas/bz = 200, /datum/gas/plasma = 5000, /datum/gas/oxygen = 4500)
	products = list(/obj/machinery/power/supermatter_crystal/shard = 1)

/datum/gas_recipe/crystallizer/n2o_crystal
	id = "n2o_crystal"
	name = "Nitrous oxide crystal"
	min_temp = 50
	max_temp = 350
	reaction_type = EXOTHERMIC_REACTION
	energy_release = 3500000
	requirements = list(/datum/gas/nitrous_oxide = 150, /datum/gas/bz = 30)
	products = list(/obj/item/grenade/gas_crystal/nitrous_oxide_crystal = 1)

/datum/gas_recipe/crystallizer/diamond
	id = "diamond"
	name = "Diamond"
	min_temp = 10000
	max_temp = 30000
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 9500000
	requirements = list(/datum/gas/carbon_dioxide = 1500)
	products = list(/obj/item/stack/sheet/mineral/diamond = 1)

/datum/gas_recipe/crystallizer/plasma_sheet
	id = "plasma_sheet"
	name = "Plasma sheet"
	min_temp = 10
	max_temp = 20
	reaction_type = EXOTHERMIC_REACTION
	energy_release = 3500000
	requirements = list(/datum/gas/plasma = 450)
	products = list(/obj/item/stack/sheet/mineral/plasma = 1)

/datum/gas_recipe/crystallizer/crystal_cell
	id = "crystal_cell"
	name = "Crystal Cell"
	min_temp = 50
	max_temp = 90
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 800000
	requirements = list(/datum/gas/plasma = 800, /datum/gas/helium = 100, /datum/gas/bz = 50)
	products = list(/obj/item/stock_parts/cell/crystal_cell = 1)

/datum/gas_recipe/crystallizer/zaukerite
	id = "zaukerite"
	name = "Zaukerite sheet"
	min_temp = 5
	max_temp = 20
	reaction_type = EXOTHERMIC_REACTION
	energy_release = 2900000
	requirements = list(/datum/gas/antinoblium = 5, /datum/gas/zauker = 20, /datum/gas/bz = 7.5)
	products = list(/obj/item/stack/sheet/mineral/zaukerite = 2)

/datum/gas_recipe/crystallizer/fuel_pellet
	id = "fuel_basic"
	name = "standard fuel pellet"
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 6000000
	requirements = list(/datum/gas/oxygen = 50, /datum/gas/plasma = 100)
	products = list(/obj/item/fuel_pellet = 1)

/datum/gas_recipe/crystallizer/fuel_pellet_advanced
	id = "fuel_advanced"
	name = "advanced fuel pellet"
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 6000000
	requirements = list(/datum/gas/tritium = 100, /datum/gas/hydrogen = 100)
	products = list(/obj/item/fuel_pellet/advanced = 1)

/datum/gas_recipe/crystallizer/fuel_pellet_exotic
	id = "fuel_exotic"
	name = "exotic fuel pellet"
	reaction_type = ENDOTHERMIC_REACTION
	energy_release = 6000000
	requirements = list(/datum/gas/hypernoblium = 100, /datum/gas/stimulum = 100)
	products = list(/obj/item/fuel_pellet/exotic = 1)

/datum/gas_recipe/crystallizer/crystal_foam
	id = "crystal_foam"
	name = "Crystal foam grenade"
	reaction_type =  EXOTHERMIC_REACTION
	energy_release = 140000
	requirements = list(/datum/gas/carbon_dioxide = 150, /datum/gas/nitrous_oxide = 100, /datum/gas/water_vapor = 25)
	products = list(/obj/item/grenade/gas_crystal/crystal_foam = 1)


code:
#define MIN_PROGRESS_AMOUNT 3
#define MIN_DEVIATION_RATE 0.90
#define MAX_DEVIATION_RATE 1.1
#define HIGH_CONDUCTIVITY_RATIO 0.95

/obj/machinery/atmospherics/components/binary/crystallizer
	icon = 'icons/obj/atmospherics/components/machines.dmi'
	icon_state = "crystallizer-off"
	name = "crystallizer"
	desc = "Used to crystallize or solidify gases."
	layer = ABOVE_MOB_LAYER
	density = TRUE
	max_integrity = 300
	armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 100, BOMB = 0, BIO = 100, RAD = 100, FIRE = 80, ACID = 30)
	circuit = /obj/item/circuitboard/machine/crystallizer
	pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
	vent_movement = NONE

	///Base icon state for the machine to be used in update_icon()
	var/base_icon = "crystallizer"
	///Internal Gas mix used for processing the gases that have been put in
	var/datum/gas_mixture/internal
	///Var that controls how much gas gets injected in moles per tick
	var/gas_input = 0
	///Saves the progress during the processing of the items
	var/progress_bar = 0
	///Stores the amount of lost quality
	var/quality_loss = 0
	///Stores the recipe selected by the user in the GUI
	var/datum/gas_recipe/selected_recipe = null
	///Stores the total amount of moles needed for the current recipe
	var/total_recipe_moles = 0

/obj/machinery/atmospherics/components/binary/crystallizer/Initialize(mapload)
	. = ..()
	internal = new

/obj/machinery/atmospherics/components/binary/crystallizer/attackby(obj/item/I, mob/user, params)
	if(!on)
		if(default_deconstruction_screwdriver(user, "[base_icon]-open", "[base_icon]-off", I))
			return
	if(default_change_direction_wrench(user, I))
		return
	if(default_deconstruction_crowbar(I))
		return
	return ..()

/obj/machinery/atmospherics/components/binary/crystallizer/default_change_direction_wrench(mob/user, obj/item/I)
	. = ..()
	if(!.)
		return FALSE
	set_init_directions()
	var/obj/machinery/atmospherics/node1 = nodes[1]
	var/obj/machinery/atmospherics/node2 = nodes[2]
	if(node1)
		if(src in node1.nodes) //Only if it's actually connected. On-pipe version would is one-sided.
			node1.disconnect(src)
		nodes[1] = null
	if(node2)
		if(src in node2.nodes) //Only if it's actually connected. On-pipe version would is one-sided.
			node2.disconnect(src)
		nodes[2] = null

	if(parents[1])
		nullify_pipenet(parents[1])
	if(parents[2])
		nullify_pipenet(parents[2])

	atmos_init()
	node1 = nodes[1]
	if(node1)
		node1.atmos_init()
		node1.add_member(src)
	node2 = nodes[2]
	if(node2)
		node2.atmos_init()
		node2.add_member(src)
	SSair.add_to_rebuild_queue(src)
	return TRUE

/obj/machinery/atmospherics/components/binary/crystallizer/update_overlays()
	. = ..()
	cut_overlays()
	var/mutable_appearance/pipe_appearance1 = mutable_appearance('icons/obj/atmospherics/pipes/pipe_underlays.dmi', "intact_[dir]_[piping_layer]", layer = GAS_SCRUBBER_LAYER)
	pipe_appearance1.color = COLOR_LIME
	var/mutable_appearance/pipe_appearance2 = mutable_appearance('icons/obj/atmospherics/pipes/pipe_underlays.dmi', "intact_[turn(dir, 180)]_[piping_layer]", layer = GAS_SCRUBBER_LAYER)
	pipe_appearance2.color = COLOR_MOSTLY_PURE_RED
	. += pipe_appearance1
	. += pipe_appearance2

/obj/machinery/atmospherics/components/binary/crystallizer/update_icon_state()
	. = ..()
	if(panel_open)
		icon_state = "[base_icon]-open"
	else if(on && is_operational)
		icon_state = "[base_icon]-on"
	else
		icon_state = "[base_icon]-off"

/obj/machinery/atmospherics/components/binary/crystallizer/attackby_secondary(mob/user)
	if(!can_interact(user))
		return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
	on = !on
	investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
	update_icon()

///Checks if the reaction temperature is inside the range of temperature + a little deviation
/obj/machinery/atmospherics/components/binary/crystallizer/proc/check_temp_requirements()
	if(internal.temperature >= selected_recipe.min_temp * MIN_DEVIATION_RATE && internal.temperature <= selected_recipe.max_temp * MAX_DEVIATION_RATE)
		return TRUE
	return FALSE

///Injects the gases from the input inside the internal gasmix, the amount is dependant on the gas_input var
/obj/machinery/atmospherics/components/binary/crystallizer/proc/inject_gases()
	var/datum/gas_mixture/contents = airs[2]
	for(var/gas_type in selected_recipe.requirements)
		if(!contents.gases[gas_type] || !contents.gases[gas_type][MOLES])
			continue
		if(internal.gases[gas_type] && internal.gases[gas_type][MOLES] >= selected_recipe.requirements[gas_type] * 2)
			continue
		internal.merge(contents.remove_specific(gas_type, contents.gases[gas_type][MOLES] * gas_input))

///Checks if the gases required are all inside
/obj/machinery/atmospherics/components/binary/crystallizer/proc/internal_check()
	var/gas_check = 0
	for(var/gas_type in selected_recipe.requirements)
		if(!internal.gases[gas_type] || !internal.gases[gas_type][MOLES])
			return FALSE
		if(internal.gases[gas_type][MOLES] >= selected_recipe.requirements[gas_type])
			gas_check++
	if(gas_check == selected_recipe.requirements.len)
		return TRUE
	return FALSE

///Calculation for the heat of the various gas mixes and controls the quality of the item
/obj/machinery/atmospherics/components/binary/crystallizer/proc/heat_calculations()
	if(	(internal.temperature >= (selected_recipe.min_temp * MIN_DEVIATION_RATE) && internal.temperature <= selected_recipe.min_temp) || \
		(internal.temperature >= selected_recipe.max_temp && internal.temperature <= (selected_recipe.max_temp * MAX_DEVIATION_RATE)))
		quality_loss = min(quality_loss + 1.5, 100)

	var/median_temperature = (selected_recipe.max_temp - selected_recipe.min_temp) * 0.5
	if(internal.temperature >= (median_temperature * MIN_DEVIATION_RATE) && internal.temperature <= (median_temperature * MAX_DEVIATION_RATE))
		quality_loss = max(quality_loss - 5.5, -100)

	if(selected_recipe.reaction_type == "endothermic")
		internal.temperature = max(internal.temperature - (selected_recipe.energy_release / internal.heat_capacity()), TCMB)
		update_parents()
	else if(selected_recipe.reaction_type == "exothermic")
		internal.temperature = max(internal.temperature + (selected_recipe.energy_release / internal.heat_capacity()), TCMB)
		update_parents()

///Conduction between the internal gasmix and the moderating (cooling/heating) gasmix.
/obj/machinery/atmospherics/components/binary/crystallizer/proc/heat_conduction()
	var/datum/gas_mixture/cooling_port = airs[1]
	if(cooling_port.total_moles() > MINIMUM_MOLE_COUNT)
		if(internal.total_moles() > 0)
			var/coolant_temperature_delta = cooling_port.temperature - internal.temperature
			var/cooling_heat_capacity = cooling_port.heat_capacity()
			var/internal_heat_capacity = internal.heat_capacity()
			var/cooling_heat_amount = HIGH_CONDUCTIVITY_RATIO * coolant_temperature_delta * (cooling_heat_capacity * internal_heat_capacity / (cooling_heat_capacity + internal_heat_capacity))
			cooling_port.temperature = max(cooling_port.temperature - cooling_heat_amount / cooling_heat_capacity, TCMB)
			internal.temperature = max(internal.temperature + cooling_heat_amount / internal_heat_capacity, TCMB)
			update_parents()

///Calculate the total moles needed for the recipe
/obj/machinery/atmospherics/components/binary/crystallizer/proc/moles_calculations()
	var/amounts = 0
	for(var/gas_type in selected_recipe.requirements)
		amounts += selected_recipe.requirements[gas_type]
	total_recipe_moles = amounts

///Removes the gases from the internal gasmix when the recipe is changed
/obj/machinery/atmospherics/components/binary/crystallizer/proc/dump_gases()
	var/datum/gas_mixture/remove = internal.remove(internal.total_moles())
	airs[2].merge(remove)
	internal.garbage_collect()

/obj/machinery/atmospherics/components/binary/crystallizer/process_atmos()
	if(!on || !is_operational || selected_recipe == null)
		return

	inject_gases()

	if(!internal.total_moles())
		return

	heat_conduction()

	if(internal_check())
		if(check_temp_requirements())
			heat_calculations()
			progress_bar = min(progress_bar + (MIN_PROGRESS_AMOUNT * 5 / (round(log(10, total_recipe_moles * 0.1), 0.01))), 100)
		else
			quality_loss = min(quality_loss + 0.5, 100)
			progress_bar = max(progress_bar - 1, 0)
	if(progress_bar != 100)
		update_parents()
		return
	progress_bar = 0

	for(var/gas_type in selected_recipe.requirements)
		var/amount_consumed = selected_recipe.requirements[gas_type] + quality_loss * 5
		if(internal.gases[gas_type][MOLES] < amount_consumed)
			quality_loss = min(quality_loss + 10, 100)
		internal.remove_specific(gas_type, amount_consumed)

	var/total_quality = clamp(50 - quality_loss, 0, 100)
	var/quality_control
	switch(total_quality)
		if(100)
			quality_control = "Masterwork"
		if(95 to 99)
			quality_control = "Supreme"
		if(75 to 94)
			quality_control = "Good"
		if(65 to 74)
			quality_control = "Decent"
		if(55 to 64)
			quality_control = "Average"
		if(35 to 54)
			quality_control = "Ok"
		if(15 to 34)
			quality_control = "Poor"
		if(5 to 14)
			quality_control = "Ugly"
		if(1 to 4)
			quality_control = "Cracked"
		if(0)
			quality_control = "Oh God why"

	for(var/path in selected_recipe.products)
		var/amount_produced = selected_recipe.products[path]
		for(var/i in 1 to amount_produced)
			var/obj/creation = new path(get_step(src, SOUTH))
			creation.name = "[quality_control] [creation.name]"
			if(selected_recipe.dangerous)
				investigate_log("has been created in the crystallizer.", INVESTIGATE_SUPERMATTER)
				message_admins("[src] has been created in the crystallizer [ADMIN_JMP(src)].")

	quality_loss = 0
	update_parents()

/obj/machinery/atmospherics/components/binary/crystallizer/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "Crystallizer", name)
		ui.open()

/obj/machinery/atmospherics/components/binary/crystallizer/ui_static_data()
	var/data = list()
	data["selected_recipes"] = list(list("name" = "Nothing", "id" = ""))
	for(var/path in GLOB.gas_recipe_meta)
		var/datum/gas_recipe/recipe = GLOB.gas_recipe_meta[path]
		if(recipe.machine_type != "Crystallizer")
			continue
		data["selected_recipes"] += list(list("name" = recipe.name, "id" = recipe.id))
	return data

/obj/machinery/atmospherics/components/binary/crystallizer/ui_data()
	var/data = list()
	data["on"] = on

	if(selected_recipe)
		data["selected"] = selected_recipe.id
	else
		data["selected"] = ""

	var/list/internal_gas_data = list()
	if(internal.total_moles())
		for(var/gasid in internal.gases)
			internal_gas_data.Add(list(list(
			"name"= internal.gases[gasid][GAS_META][META_GAS_NAME],
			"amount" = round(internal.gases[gasid][MOLES], 0.01),
			)))
	else
		for(var/gasid in internal.gases)
			internal_gas_data.Add(list(list(
				"name"= internal.gases[gasid][GAS_META][META_GAS_NAME],
				"amount" = 0,
				)))
	data["internal_gas_data"] = internal_gas_data

	var/list/requirements
	if(!selected_recipe)
		requirements = list("Select a recipe to see the requirements")
	else
		requirements = list("To create [selected_recipe.name] you will need:")
		for(var/gas_type in selected_recipe.requirements)
			var/datum/gas/gas_required = gas_type
			var/amount_consumed = selected_recipe.requirements[gas_type]
			requirements += "-[amount_consumed] moles of [initial(gas_required.name)]"
		requirements += "In a temperature range between [selected_recipe.min_temp] K and [selected_recipe.max_temp] K"
		requirements += "The crystallization reaction will be [selected_recipe.reaction_type]"
	data["requirements"] = requirements.Join("\n")

	var/temperature
	if(internal.total_moles())
		temperature = internal.temperature
	else
		temperature = 0
	data["internal_temperature"] = temperature
	data["progress_bar"] = progress_bar
	data["gas_input"] = gas_input
	return data

/obj/machinery/atmospherics/components/binary/crystallizer/ui_act(action, params)
	. = ..()
	if(.)
		return
	switch(action)
		if("power")
			on = !on
			investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", INVESTIGATE_ATMOS)
			. = TRUE
		if("recipe")
			selected_recipe = null
			var/recipe_name = "nothing"
			var/datum/gas_recipe/recipe = GLOB.gas_recipe_meta[params["mode"]]
			if(internal.total_moles())
				dump_gases()
			quality_loss = 0
			progress_bar = 0
			if(recipe && recipe.id != "")
				selected_recipe = recipe
				recipe_name = recipe.name
				update_parents() //prevent the machine from stopping because of the recipe change and the pipenet not updating
				moles_calculations()
			investigate_log("was set to recipe [recipe_name ? recipe_name : "null"] by [key_name(usr)]", INVESTIGATE_ATMOS)
			. = TRUE
		if("gas_input")
			var/_gas_input = params["gas_input"]
			gas_input = clamp(_gas_input, 0, 250)
	update_icon()

#undef MIN_PROGRESS_AMOUNT
#undef MIN_DEVIATION_RATE
#undef MAX_DEVIATION_RATE
#undef HIGH_CONDUCTIVITY_RATIO


code:
/obj/item/hypernoblium_crystal
	name = "Hypernoblium Crystal"
	desc = "Crystalized oxygen and hypernoblium stored in a bottle to pressureproof your clothes."
	icon = 'icons/obj/chemical.dmi'
	icon_state = "potblue"
	var/uses = 2

/obj/item/hypernoblium_crystal/afterattack(obj/item/clothing/worn_item, mob/user, proximity)
	. = ..()
	if(!proximity)
		return
	if(!istype(worn_item))
		to_chat(user, span_warning("The crystal can only be used on clothing!"))
		return
	if(istype(worn_item, /obj/item/clothing/suit/space))
		to_chat(user, span_warning("The [worn_item] is already pressure-resistant!"))
		return
	if(worn_item.min_cold_protection_temperature == SPACE_SUIT_MIN_TEMP_PROTECT && worn_item.clothing_flags & STOPSPRESSUREDAMAGE)
		to_chat(user, span_warning("[worn_item] is already pressure-resistant!"))
		return
	to_chat(user, span_notice("You see how the [worn_item] changes color, it's now pressure proof."))
	worn_item.name = "pressure-resistant [worn_item.name]"
	worn_item.remove_atom_colour(WASHABLE_COLOUR_PRIORITY)
	worn_item.add_atom_colour("#00fff7", FIXED_COLOUR_PRIORITY)
	worn_item.min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
	worn_item.cold_protection = worn_item.body_parts_covered
	worn_item.clothing_flags |= STOPSPRESSUREDAMAGE
	uses--
	if(!uses)
		qdel(src)