code:
#define FLOODLIGHT_OFF 1
#define FLOODLIGHT_LOW 2
#define FLOODLIGHT_MED 3
#define FLOODLIGHT_HIGH 4

/obj/structure/floodlight_frame
	name = "floodlight frame"
	desc = "A bare metal frame looking vaguely like a floodlight. Requires wiring."
	max_integrity = 100
	icon = 'icons/obj/lighting.dmi'
	icon_state = "floodlight_c1"
	density = TRUE
	var/state = FLOODLIGHT_NEEDS_WIRES

/obj/structure/floodlight_frame/attackby(obj/item/O, mob/user, params)
	if(istype(O, /obj/item/stack/cable_coil) && state == FLOODLIGHT_NEEDS_WIRES)
		var/obj/item/stack/S = O
		if(S.use(5))
			to_chat(user, span_notice("You wire [src]."))
			name = "wired [name]"
			desc = "A bare metal frame looking vaguely like a floodlight. Requires securing with a screwdriver."
			icon_state = "floodlight_c2"
			state = FLOODLIGHT_NEEDS_SECURING
			return
		else
			to_chat(user, "You need 5 cables to wire [src].")
			return
	if(O.tool_behaviour == TOOL_SCREWDRIVER && state == FLOODLIGHT_NEEDS_SECURING)
		to_chat(user, span_notice("You fasten the wiring and electronics in [src]."))
		name = "secured [name]"
		desc = "A bare metal frame that looks like a floodlight. Requires a light tube to complete."
		icon_state = "floodlight_c3"
		state = FLOODLIGHT_NEEDS_LIGHTS
		return
	if(istype(O, /obj/item/light/tube))
		var/obj/item/light/tube/L = O
		if(state == FLOODLIGHT_NEEDS_LIGHTS && L.status != 2) //Ready for a light tube, and not broken.
			to_chat(user, span_notice("You put lights in [src]."))
			new /obj/machinery/power/floodlight(loc)
			qdel(src)
			qdel(O)
			return
		else //A minute of silence for all the accidentally broken light tubes.
			return
	if(istype(O, /obj/item/lightreplacer))
		var/obj/item/lightreplacer/L = O
		if(state == FLOODLIGHT_NEEDS_LIGHTS && L.CanUse(user))
			L.Use(user)
			to_chat(user, span_notice("You put lights in [src]."))
			new /obj/machinery/power/floodlight(loc)
			qdel(src)
			return
	..()

/obj/machinery/power/floodlight
	name = "floodlight"
	desc = "A pole with powerful mounted lights on it. Due to its high power draw, it must be powered by a direct connection to a wire node."
	icon = 'icons/obj/lighting.dmi'
	icon_state = "floodlight"
	density = TRUE
	max_integrity = 100
	integrity_failure = 0.8
	idle_power_usage = 100
	active_power_usage = 1000
	anchored = FALSE
	light_power = 1.75
	var/list/light_setting_list = list(0, 5, 10, 15)
	var/light_power_coefficient = 200
	var/setting = FLOODLIGHT_OFF

/obj/machinery/power/floodlight/process()
	var/turf/T = get_turf(src)
	var/obj/structure/cable/C = locate() in T
	if(!C && powernet)
		disconnect_from_network()
	if(setting > FLOODLIGHT_OFF) //If on
		if(avail(active_power_usage))
			add_load(active_power_usage)
		else
			change_setting(FLOODLIGHT_OFF)
	else if(avail(idle_power_usage))
		add_load(idle_power_usage)

/obj/machinery/power/floodlight/proc/change_setting(newval, mob/user)
	if((newval < FLOODLIGHT_OFF) || (newval > light_setting_list.len))
		return
	setting = newval
	active_power_usage = light_setting_list[setting] * light_power_coefficient
	if(!avail(active_power_usage) && setting > FLOODLIGHT_OFF)
		return change_setting(setting - 1)
	set_light(light_setting_list[setting], light_power)
	var/setting_text = ""
	if(setting > FLOODLIGHT_OFF)
		icon_state = "[initial(icon_state)]_on"
	else
		icon_state = initial(icon_state)
	switch(setting)
		if(1)
			setting_text = "OFF"
		if(2)
			setting_text = "low power"
		if(3)
			setting_text = "standard lighting"
		if(4)
			setting_text = "high power"
	if(user)
		to_chat(user, span_notice("You set [src] to [setting_text]."))

/obj/machinery/power/floodlight/attackby(obj/item/O, mob/user, params)
	if(O.tool_behaviour == TOOL_WRENCH)
		default_unfasten_wrench(user, O, time = 20)
		change_setting(1)
		if(anchored)
			connect_to_network()
		else
			disconnect_from_network()
	else
		. = ..()

/obj/machinery/power/floodlight/attack_hand(mob/user, list/modifiers)
	. = ..()
	if(.)
		return
	var/current = setting
	if(current == 1)
		current = light_setting_list.len
	else
		current--
	change_setting(current, user)

/obj/machinery/power/floodlight/atom_break(damage_flag)
	. = ..()
	if(!.)
		return
	playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE)
	var/obj/structure/floodlight_frame/F = new(loc)
	F.state = FLOODLIGHT_NEEDS_LIGHTS
	new /obj/item/light/tube/broken(loc)
	qdel(src)

/obj/machinery/power/floodlight/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
	playsound(src, 'sound/effects/glasshit.ogg', 75, TRUE)


code:
/obj/machinery/power/generator
	name = "thermoelectric generator"
	desc = "It's a high efficiency thermoelectric generator."
	icon_state = "teg"
	density = TRUE
	use_power = NO_POWER_USE

	circuit = /obj/item/circuitboard/machine/generator

	var/obj/machinery/atmospherics/components/binary/circulator/cold_circ
	var/obj/machinery/atmospherics/components/binary/circulator/hot_circ

	var/lastgen = 0
	var/lastgenlev = -1
	var/lastcirc = "00"

/obj/machinery/power/generator/Initialize(mapload)
	. = ..()
	find_circs()
	connect_to_network()
	SSair.start_processing_machine(src)
	update_appearance()

/obj/machinery/power/generator/ComponentInitialize()
	. = ..()
	AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS )

/obj/machinery/power/generator/Destroy()
	kill_circs()
	SSair.stop_processing_machine(src)
	return ..()

/obj/machinery/power/generator/update_overlays()
	. = ..()
	if(machine_stat & (NOPOWER|BROKEN))
		return

	var/L = min(round(lastgenlev / 100000), 11)
	if(L != 0)
		. += mutable_appearance('icons/obj/power.dmi', "teg-op[L]")
	if(hot_circ && cold_circ)
		. += "teg-oc[lastcirc]"

#define GENRATE 800 // generator output coefficient from Q

/obj/machinery/power/generator/process_atmos()

	if(!cold_circ || !hot_circ)
		return

	if(powernet)
		var/datum/gas_mixture/cold_air = cold_circ.return_transfer_air()
		var/datum/gas_mixture/hot_air = hot_circ.return_transfer_air()

		if(cold_air && hot_air)

			var/cold_air_heat_capacity = cold_air.heat_capacity()
			var/hot_air_heat_capacity = hot_air.heat_capacity()

			var/delta_temperature = hot_air.temperature - cold_air.temperature

			if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0)
				var/efficiency = 0.65

				var/energy_transfer = delta_temperature*hot_air_heat_capacity*cold_air_heat_capacity/(hot_air_heat_capacity+cold_air_heat_capacity)

				var/heat = energy_transfer*(1-efficiency)
				lastgen += energy_transfer*efficiency

				hot_air.temperature = hot_air.temperature - energy_transfer/hot_air_heat_capacity
				cold_air.temperature = cold_air.temperature + heat/cold_air_heat_capacity

				//add_avail(lastgen) This is done in process now
		// update icon overlays only if displayed level has changed

		if(hot_air)
			var/datum/gas_mixture/hot_circ_air1 = hot_circ.airs[1]
			hot_circ_air1.merge(hot_air)

		if(cold_air)
			var/datum/gas_mixture/cold_circ_air1 = cold_circ.airs[1]
			cold_circ_air1.merge(cold_air)

		update_appearance()

	var/circ = "[cold_circ?.last_pressure_delta > 0 ? "1" : "0"][hot_circ?.last_pressure_delta > 0 ? "1" : "0"]"
	if(circ != lastcirc)
		lastcirc = circ
		update_appearance()

	src.updateDialog()

/obj/machinery/power/generator/process()
	//Setting this number higher just makes the change in power output slower, it doesnt actualy reduce power output cause **math**
	var/power_output = round(lastgen / 10)
	add_avail(power_output)
	lastgenlev = power_output
	lastgen -= power_output
	..()

/obj/machinery/power/generator/proc/get_menu(include_link = TRUE)
	var/t = ""
	if(!powernet)
		t += "Unable to connect to the power network!"
	else if(cold_circ && hot_circ)
		var/datum/gas_mixture/cold_circ_air1 = cold_circ.airs[1]
		var/datum/gas_mixture/cold_circ_air2 = cold_circ.airs[2]
		var/datum/gas_mixture/hot_circ_air1 = hot_circ.airs[1]
		var/datum/gas_mixture/hot_circ_air2 = hot_circ.airs[2]

		t += "
"

		t += "Output: [display_power(lastgenlev)]"

		t += "
"

		t += "Cold loop
"
		t += "Temperature Inlet: [round(cold_circ_air2.temperature, 0.1)] K / Outlet: [round(cold_circ_air1.temperature, 0.1)] K
"
		t += "Pressure Inlet: [round(cold_circ_air2.return_pressure(), 0.1)] kPa /  Outlet: [round(cold_circ_air1.return_pressure(), 0.1)] kPa
"

		t += "Hot loop
"
		t += "Temperature Inlet: [round(hot_circ_air2.temperature, 0.1)] K / Outlet: [round(hot_circ_air1.temperature, 0.1)] K
"
		t += "Pressure Inlet: [round(hot_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(hot_circ_air1.return_pressure(), 0.1)] kPa
"

		t += "
"
	else if(!hot_circ && cold_circ)
		t += "Unable to locate hot circulator!"
	else if(hot_circ && !cold_circ)
		t += "Unable to locate cold circulator!"
	else
		t += "Unable to locate any parts!"
	if(include_link)
		t += "
Close"

	return t

/obj/machinery/power/generator/ui_interact(mob/user)
	. = ..()
	var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300)
	popup.set_content(get_menu())
	popup.open()

/obj/machinery/power/generator/Topic(href, href_list)
	if(..())
		return
	if( href_list["close"] )
		usr << browse(null, "window=teg")
		usr.unset_machine()
		return FALSE
	return TRUE

/obj/machinery/power/generator/proc/find_circs()
	kill_circs()
	var/list/circs = list()
	var/obj/machinery/atmospherics/components/binary/circulator/C
	var/circpath = /obj/machinery/atmospherics/components/binary/circulator
	if(dir == NORTH || dir == SOUTH)
		C = locate(circpath) in get_step(src, EAST)
		if(C && C.dir == WEST)
			circs += C

		C = locate(circpath) in get_step(src, WEST)
		if(C && C.dir == EAST)
			circs += C

	else
		C = locate(circpath) in get_step(src, NORTH)
		if(C && C.dir == SOUTH)
			circs += C

		C = locate(circpath) in get_step(src, SOUTH)
		if(C && C.dir == NORTH)
			circs += C

	if(circs.len)
		for(C in circs)
			if(C.mode == CIRCULATOR_COLD && !cold_circ)
				cold_circ = C
				C.generator = src
			else if(C.mode == CIRCULATOR_HOT && !hot_circ)
				hot_circ = C
				C.generator = src

/obj/machinery/power/generator/wrench_act(mob/living/user, obj/item/I)
	. = ..()
	if(!panel_open)
		return
	set_anchored(!anchored)
	I.play_tool_sound(src)
	if(!anchored)
		kill_circs()
	connect_to_network()
	to_chat(user, span_notice("You [anchored?"secure":"unsecure"] [src]."))
	return TRUE

/obj/machinery/power/generator/multitool_act(mob/living/user, obj/item/I)
	. = ..()
	if(!anchored)
		return
	find_circs()
	to_chat(user, span_notice("You update [src]'s circulator links."))
	return TRUE

/obj/machinery/power/generator/screwdriver_act(mob/user, obj/item/I)
	if(..())
		return TRUE
	panel_open = !panel_open
	I.play_tool_sound(src)
	to_chat(user, span_notice("You [panel_open?"open":"close"] the panel on [src]."))
	return TRUE

/obj/machinery/power/generator/crowbar_act(mob/user, obj/item/I)
	default_deconstruction_crowbar(I)
	return TRUE

/obj/machinery/power/generator/on_deconstruction()
	kill_circs()

/obj/machinery/power/generator/proc/kill_circs()
	if(hot_circ)
		hot_circ.generator = null
		hot_circ = null
	if(cold_circ)
		cold_circ.generator = null
		cold_circ = null

repo_name: AnalWerewolf/sundown

license: mit


code:
//
// Gravity Generator
//

GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding new gravity generators to the list, and keying it with the z level.

#define POWER_IDLE 0
#define POWER_UP 1
#define POWER_DOWN 2

#define GRAV_NEEDS_SCREWDRIVER 0
#define GRAV_NEEDS_WELDING 1
#define GRAV_NEEDS_PLASTEEL 2
#define GRAV_NEEDS_WRENCH 3

//
// Abstract Generator
//

/obj/machinery/gravity_generator
	name = "gravitational generator"
	desc = "A device which produces a graviton field when set up."
	icon = 'icons/obj/machines/gravity_generator.dmi'
	density = TRUE
	move_resist = INFINITY
	use_power = NO_POWER_USE
	resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
	var/sprite_number = 0
	///Audio for when the gravgen is on
	var/datum/looping_sound/gravgen/soundloop

/obj/machinery/gravity_generator/main/Initialize(mapload)
	. = ..()
	soundloop = new(src, TRUE)

/obj/machinery/gravity_generator/main/Destroy()
	. = ..()
	QDEL_NULL(soundloop)

/obj/machinery/gravity_generator/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE)
	return FALSE

/obj/machinery/gravity_generator/ex_act(severity, target)
	if(severity >= EXPLODE_DEVASTATE) // Very sturdy.
		set_broken()

/obj/machinery/gravity_generator/blob_act(obj/structure/blob/B)
	if(prob(20))
		set_broken()

/obj/machinery/gravity_generator/zap_act(power, zap_flags)
	. = ..()
	if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
		qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over

/obj/machinery/gravity_generator/update_icon_state()
	icon_state = "[get_status()]_[sprite_number]"
	return ..()

/obj/machinery/gravity_generator/proc/get_status()
	return "off"

// You aren't allowed to move.
/obj/machinery/gravity_generator/Move()
	. = ..()
	qdel(src)

/obj/machinery/gravity_generator/proc/set_broken()
	atom_break()

/obj/machinery/gravity_generator/proc/set_fix()
	set_machine_stat(machine_stat & ~BROKEN)

/obj/machinery/gravity_generator/part/Destroy()
	if(main_part)
		qdel(main_part)
	set_broken()
	QDEL_NULL(soundloop)
	return ..()

//
// Part generator which is mostly there for looks
//

/obj/machinery/gravity_generator/part
	var/obj/machinery/gravity_generator/main/main_part = null

/obj/machinery/gravity_generator/part/attackby(obj/item/I, mob/user, params)
	return main_part.attackby(I, user)

/obj/machinery/gravity_generator/part/get_status()
	return main_part?.get_status()

/obj/machinery/gravity_generator/part/attack_hand(mob/user, list/modifiers)
	return main_part.attack_hand(user, modifiers)

/obj/machinery/gravity_generator/part/set_broken()
	..()
	if(main_part && !(main_part.machine_stat & BROKEN))
		main_part.set_broken()

/// Used to eat args
/obj/machinery/gravity_generator/part/proc/on_update_icon(obj/machinery/gravity_generator/source, updates, updated)
	SIGNAL_HANDLER
	return update_appearance(updates)

//
// Generator which spawns with the station.
//

/obj/machinery/gravity_generator/main/station/Initialize(mapload)
	. = ..()
	setup_parts()
	middle.add_overlay("activated")
	update_list()

//
// Generator an admin can spawn
//
/obj/machinery/gravity_generator/main/station/admin
	use_power = NO_POWER_USE

//
// Main Generator with the main code
//

/obj/machinery/gravity_generator/main
	icon_state = "on_8"
	idle_power_usage = 0
	active_power_usage = 3000
	power_channel = AREA_USAGE_ENVIRON
	sprite_number = 8
	use_power = IDLE_POWER_USE
	interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OFFLINE
	var/on = TRUE
	var/breaker = TRUE
	var/list/parts = list()
	var/obj/middle = null
	var/charging_state = POWER_IDLE
	var/charge_count = 100
	var/current_overlay = null
	var/broken_state = 0
	var/setting = 1 //Gravity value when on

/obj/machinery/gravity_generator/main/Destroy() // If we somehow get deleted, remove all of our other parts.
	investigate_log("was destroyed!", INVESTIGATE_GRAVITY)
	on = FALSE
	update_list()
	for(var/obj/machinery/gravity_generator/part/O in parts)
		O.main_part = null
		if(!QDESTROYING(O))
			qdel(O)
	return ..()

/obj/machinery/gravity_generator/main/proc/setup_parts()
	var/turf/our_turf = get_turf(src)
	// 9x9 block obtained from the bottom middle of the block
	var/list/spawn_turfs = block(locate(our_turf.x - 1, our_turf.y + 2, our_turf.z), locate(our_turf.x + 1, our_turf.y, our_turf.z))
	var/count = 10
	for(var/turf/T in spawn_turfs)
		count--
		if(T == our_turf) // Skip our turf.
			continue
		var/obj/machinery/gravity_generator/part/part = new(T)
		if(count == 5) // Middle
			middle = part
		if(count <= 3) // Their sprite is the top part of the generator
			part.set_density(FALSE)
			part.layer = WALL_OBJ_LAYER
		part.sprite_number = count
		part.main_part = src
		parts += part
		part.update_appearance()
		part.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /obj/machinery/gravity_generator/part/proc/on_update_icon)

/obj/machinery/gravity_generator/main/proc/connected_parts()
	return parts.len == 8

/obj/machinery/gravity_generator/main/set_broken()
	..()
	for(var/obj/machinery/gravity_generator/M in parts)
		if(!(M.machine_stat & BROKEN))
			M.set_broken()
	middle.cut_overlays()
	charge_count = 0
	breaker = FALSE
	set_power()
	set_state(0)
	investigate_log("has broken down.", INVESTIGATE_GRAVITY)

/obj/machinery/gravity_generator/main/set_fix()
	..()
	for(var/obj/machinery/gravity_generator/M in parts)
		if(M.machine_stat & BROKEN)
			M.set_fix()
	broken_state = FALSE
	update_appearance()
	set_power()

// Interaction

// Fixing the gravity generator.
/obj/machinery/gravity_generator/main/attackby(obj/item/I, mob/user, params)
	switch(broken_state)
		if(GRAV_NEEDS_SCREWDRIVER)
			if(I.tool_behaviour == TOOL_SCREWDRIVER)
				to_chat(user, span_notice("You secure the screws of the framework."))
				I.play_tool_sound(src)
				broken_state++
				update_appearance()
				return
		if(GRAV_NEEDS_WELDING)
			if(I.tool_behaviour == TOOL_WELDER)
				if(I.use_tool(src, user, 0, volume=50, amount=1))
					to_chat(user, span_notice("You mend the damaged framework."))
					broken_state++
					update_appearance()
				return
		if(GRAV_NEEDS_PLASTEEL)
			if(istype(I, /obj/item/stack/sheet/plasteel))
				var/obj/item/stack/sheet/plasteel/PS = I
				if(PS.get_amount() >= 10)
					PS.use(10)
					to_chat(user, span_notice("You add the plating to the framework."))
					playsound(src.loc, 'sound/machines/click.ogg', 75, TRUE)
					broken_state++
					update_appearance()
				else
					to_chat(user, span_warning("You need 10 sheets of plasteel!"))
				return
		if(GRAV_NEEDS_WRENCH)
			if(I.tool_behaviour == TOOL_WRENCH)
				to_chat(user, span_notice("You secure the plating to the framework."))
				I.play_tool_sound(src)
				set_fix()
				return
	return ..()

/obj/machinery/gravity_generator/main/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "GravityGenerator", name)
		ui.open()

/obj/machinery/gravity_generator/main/ui_data(mob/user)
	var/list/data = list()

	data["breaker"] = breaker
	data["charge_count"] = charge_count
	data["charging_state"] = charging_state
	data["on"] = on
	data["operational"] = (machine_stat & BROKEN) ? FALSE : TRUE

	return data

/obj/machinery/gravity_generator/main/ui_act(action, params)
	. = ..()
	if(.)
		return

	switch(action)
		if("gentoggle")
			breaker = !breaker
			investigate_log("was toggled [breaker ? "ON" : "OFF"] by [key_name(usr)].", INVESTIGATE_GRAVITY)
			set_power()
			. = TRUE

// Power and Icon States

/obj/machinery/gravity_generator/main/power_change()
	. = ..()
	investigate_log("has [machine_stat & NOPOWER ? "lost" : "regained"] power.", INVESTIGATE_GRAVITY)
	set_power()

/obj/machinery/gravity_generator/main/get_status()
	if(machine_stat & BROKEN)
		return "fix[min(broken_state, 3)]"
	return on || charging_state != POWER_IDLE ? "on" : "off"

// Set the charging state based on power/breaker.
/obj/machinery/gravity_generator/main/proc/set_power()
	var/new_state = FALSE
	if(machine_stat & (NOPOWER|BROKEN) || !breaker)
		new_state = FALSE
	else if(breaker)
		new_state = TRUE

	charging_state = new_state ? POWER_UP : POWER_DOWN // Startup sequence animation.
	investigate_log("is now [charging_state == POWER_UP ? "charging" : "discharging"].", INVESTIGATE_GRAVITY)
	update_appearance()

// Set the state of the gravity.
/obj/machinery/gravity_generator/main/proc/set_state(new_state)
	charging_state = POWER_IDLE
	on = new_state
	update_use_power(on ? ACTIVE_POWER_USE : IDLE_POWER_USE)
	// Sound the alert if gravity was just enabled or disabled.
	var/alert = FALSE
	if(SSticker.IsRoundInProgress())
		if(on) // If we turned on and the game is live.
			soundloop.start()
			if(gravity_in_level() == FALSE)
				alert = TRUE
				investigate_log("was brought online and is now producing gravity for this level.", INVESTIGATE_GRAVITY)
				message_admins("The gravity generator was brought online [ADMIN_VERBOSEJMP(src)]")
		else
			soundloop.stop()
			if(gravity_in_level() == TRUE)
				alert = TRUE
				investigate_log("was brought offline and there is now no gravity for this level.", INVESTIGATE_GRAVITY)
				message_admins("The gravity generator was brought offline with no backup generator. [ADMIN_VERBOSEJMP(src)]")

	update_appearance()
	update_list()
	src.updateUsrDialog()
	if(alert)
		shake_everyone()

// Charge/Discharge and turn on/off gravity when you reach 0/100 percent.
// Also emit radiation and handle the overlays.
/obj/machinery/gravity_generator/main/process()
	if(machine_stat & BROKEN)
		return
	if(charging_state != POWER_IDLE)
		if(charging_state == POWER_UP && charge_count >= 100)
			set_state(1)
		else if(charging_state == POWER_DOWN && charge_count <= 0)
			set_state(0)
		else
			if(charging_state == POWER_UP)
				charge_count += 2
			else if(charging_state == POWER_DOWN)
				charge_count -= 2

			if(charge_count % 4 == 0 && prob(75)) // Let them know it is charging/discharging.
				playsound(src.loc, 'sound/effects/empulse.ogg', 100, TRUE)

			updateDialog()
			if(prob(25)) // To help stop "Your clothes feel warm." spam.
				pulse_radiation()

			var/overlay_state = null
			switch(charge_count)
				if(0 to 20)
					overlay_state = null
				if(21 to 40)
					overlay_state = "startup"
				if(41 to 60)
					overlay_state = "idle"
				if(61 to 80)
					overlay_state = "activating"
				if(81 to 100)
					overlay_state = "activated"

			if(overlay_state != current_overlay)
				if(middle)
					middle.cut_overlays()
					if(overlay_state)
						middle.add_overlay(overlay_state)
					current_overlay = overlay_state

/obj/machinery/gravity_generator/main/proc/pulse_radiation()
	radiation_pulse(src, 200)

// Shake everyone on the z level to let them know that gravity was enagaged/disenagaged.
/obj/machinery/gravity_generator/main/proc/shake_everyone()
	var/turf/T = get_turf(src)
	var/sound/alert_sound = sound('sound/effects/alert.ogg')
	for(var/i in GLOB.mob_list)
		var/mob/M = i
		if(M.z != z && !(SSmapping.level_trait(z, ZTRAITS_STATION) && SSmapping.level_trait(M.z, ZTRAITS_STATION)))
			continue
		M.update_gravity(M.mob_has_gravity())
		if(M.client)
			shake_camera(M, 15, 1)
			M.playsound_local(T, null, 100, 1, 0.5, S = alert_sound)

/obj/machinery/gravity_generator/main/proc/gravity_in_level()
	var/turf/T = get_turf(src)
	if(!T)
		return FALSE
	if(GLOB.gravity_generators["[T.z]"])
		return length(GLOB.gravity_generators["[T.z]"])
	return FALSE

/obj/machinery/gravity_generator/main/proc/update_list()
	var/turf/T = get_turf(src.loc)
	if(T)
		var/list/z_list = list()
		// Multi-Z, station gravity generator generates gravity on all ZTRAIT_STATION z-levels.
		if(SSmapping.level_trait(T.z, ZTRAIT_STATION))
			for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
				z_list += z
		else
			z_list += T.z
		for(var/z in z_list)
			if(!GLOB.gravity_generators["[z]"])
				GLOB.gravity_generators["[z]"] = list()
			if(on)
				GLOB.gravity_generators["[z]"] |= src
			else
				GLOB.gravity_generators["[z]"] -= src

/obj/machinery/gravity_generator/main/proc/change_setting(value)
	if(value != setting)
		setting = value
		shake_everyone()

// Misc

/obj/item/paper/guides/jobs/engi/gravity_gen
	name = "paper- 'Generate your own gravity!'"
	info = {"
Gravity Generator Instructions For Dummies

Surprisingly, gravity isn't that hard to make! All you have to do is inject deadly radioactive minerals into a ball of
	energy and you have yourself gravity! You can turn the machine on or off when required but you must remember that the generator
	will EMIT RADIATION when charging or discharging, you can tell it is charging or discharging by the noise it makes, so please WEAR PROTECTIVE CLOTHING.

It blew up!

Don't panic! The gravity generator was designed to be easily repaired. If, somehow, the sturdy framework did not survive then
	please proceed to panic; otherwise follow these steps.

Secure the screws of the framework with a screwdriver.

Mend the damaged framework with a welding tool.

Add additional plasteel plating.

Secure the additional plating with a wrench.

"}


code:
///How much power emergency lights will consume per tick
#define LIGHT_EMERGENCY_POWER_USE 0.2
// status values shared between lighting fixtures and items
#define LIGHT_OK 0
#define LIGHT_EMPTY 1
#define LIGHT_BROKEN 2
#define LIGHT_BURNED 3

///Min time for a spark to happen in a broken light
#define BROKEN_SPARKS_MIN (3 MINUTES)
///Max time for a spark to happen in a broken light
#define BROKEN_SPARKS_MAX (9 MINUTES)

///Amount of time that takes an ethereal to take energy from the lights
#define LIGHT_DRAIN_TIME 2.5 SECONDS
///Amount of charge the ethereal gain after the drain
#define LIGHT_POWER_GAIN 35

///How many reagents the lights can hold
#define LIGHT_REAGENT_CAPACITY 5

//Status for light constructs
#define LIGHT_CONSTRUCT_EMPTY 1
#define LIGHT_CONSTRUCT_WIRED 2
#define LIGHT_CONSTRUCT_CLOSED 3


code:
// the standard tube light fixture
/obj/machinery/light
	name = "light fixture"
	icon = 'icons/obj/lighting.dmi'
	icon_state = "tube"
	desc = "A lighting fixture."
	layer = WALL_OBJ_LAYER
	max_integrity = 100
	use_power = ACTIVE_POWER_USE
	idle_power_usage = 2
	active_power_usage = 20
	power_channel = AREA_USAGE_LIGHT //Lights are calc'd via area so they dont need to be in the machine list
	///What overlay the light should use
	var/overlay_icon = 'icons/obj/lighting_overlay.dmi'
	///base description and icon_state
	var/base_state = "tube"
	///Is the light on?
	var/on = FALSE
	///compared to the var/on for static calculations
	var/on_gs = FALSE
	///Amount of power used
	var/static_power_used = 0
	///Luminosity when on, also used in power calculation
	var/brightness = 8
	///Basically the alpha of the emitted light source
	var/bulb_power = 1
	///Default colour of the light.
	var/bulb_colour = "#f3fffa"
	///LIGHT_OK, _EMPTY, _BURNED or _BROKEN
	var/status = LIGHT_OK
	///Should we flicker?
	var/flickering = FALSE
	///The type of light item
	var/light_type = /obj/item/light/tube
	///String of the light type, used in descriptions and in examine
	var/fitting = "tube"
	///Count of number of times switched on/off, this is used to calculate the probability the light burns out
	var/switchcount = 0
	///True if rigged to explode
	var/rigged = FALSE
	///Cell reference
	var/obj/item/stock_parts/cell/cell
	///If true, this fixture generates a very weak cell at roundstart
	var/start_with_cell = TRUE
	///Currently in night shift mode?
	var/nightshift_enabled = FALSE
	///Set to FALSE to never let this light get switched to night mode.
	var/nightshift_allowed = TRUE
	///Brightness of the nightshift light
	var/nightshift_brightness = 8
	///Alpha of the nightshift light
	var/nightshift_light_power = 0.45
	///Basecolor of the nightshift light
	var/nightshift_light_color = "#FFDDCC"
	///If true, the light is in emergency mode
	var/emergency_mode = FALSE
	///If true, this light cannot ever have an emergency mode
	var/no_emergency = FALSE
	///Multiplier for this light's base brightness in emergency power mode
	var/bulb_emergency_brightness_mul = 0.25
	///Determines the colour of the light while it's in emergency mode
	var/bulb_emergency_colour = "#FF3232"
	///The multiplier for determining the light's power in emergency mode
	var/bulb_emergency_pow_mul = 0.75
	///The minimum value for the light's power in emergency mode
	var/bulb_emergency_pow_min = 0.5

/obj/machinery/light/Move()
	if(status != LIGHT_BROKEN)
		break_light_tube(TRUE)
	return ..()

// create a new lighting fixture
/obj/machinery/light/Initialize(mapload)
	. = ..()

	if(!mapload) //sync up nightshift lighting for player made lights
		var/area/local_area = get_area(src)
		var/obj/machinery/power/apc/temp_apc = local_area.apc
		nightshift_enabled = temp_apc?.nightshift_lights

	if(start_with_cell && !no_emergency)
		cell = new/obj/item/stock_parts/cell/emergency_light(src)

	RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, .proc/on_light_eater)
	AddElement(/datum/element/atmos_sensitive, mapload)
	return INITIALIZE_HINT_LATELOAD

/obj/machinery/light/LateInitialize()
	. = ..()
	switch(fitting)
		if("tube")
			brightness = 8
			if(prob(2))
				break_light_tube(TRUE)
		if("bulb")
			brightness = 4
			if(prob(5))
				break_light_tube(TRUE)
	addtimer(CALLBACK(src, .proc/update, FALSE), 0.1 SECONDS)

/obj/machinery/light/Destroy()
	var/area/local_area = get_area(src)
	if(local_area)
		on = FALSE
	QDEL_NULL(cell)
	return ..()

/obj/machinery/light/update_icon_state()
	switch(status) // set icon_states
		if(LIGHT_OK)
			var/area/local_area = get_area(src)
			if(emergency_mode || (local_area?.fire))
				icon_state = "[base_state]_emergency"
			else
				icon_state = "[base_state]"
		if(LIGHT_EMPTY)
			icon_state = "[base_state]-empty"
		if(LIGHT_BURNED)
			icon_state = "[base_state]-burned"
		if(LIGHT_BROKEN)
			icon_state = "[base_state]-broken"
	return ..()

/obj/machinery/light/update_overlays()
	. = ..()
	if(!on || status != LIGHT_OK)
		return

	var/area/local_area = get_area(src)
	if(emergency_mode || (local_area?.fire))
		. += mutable_appearance(overlay_icon, "[base_state]_emergency", layer, plane)
		return
	if(nightshift_enabled)
		. += mutable_appearance(overlay_icon, "[base_state]_nightshift", layer, plane)
		return
	. += mutable_appearance(overlay_icon, base_state, layer, plane)

// update the icon_state and luminosity of the light depending on its state
/obj/machinery/light/proc/update(trigger = TRUE)
	switch(status)
		if(LIGHT_BROKEN,LIGHT_BURNED,LIGHT_EMPTY)
			on = FALSE
	emergency_mode = FALSE
	if(on)
		var/brightness_set = brightness
		var/power_set = bulb_power
		var/color_set = bulb_colour
		if(color)
			color_set = color
		var/area/local_area = get_area(src)
		if (local_area?.fire)
			color_set = bulb_emergency_colour
		else if (nightshift_enabled)
			brightness_set = nightshift_brightness
			power_set = nightshift_light_power
			if(!color)
				color_set = nightshift_light_color
		var/matching = light && brightness_set == light.light_range && power_set == light.light_power && color_set == light.light_color
		if(!matching)
			switchcount++
			if(rigged)
				if(status == LIGHT_OK && trigger)
					explode()
			else if( prob( min(60, (switchcount**2)*0.01) ) )
				if(trigger)
					burn_out()
			else
				use_power = ACTIVE_POWER_USE
				set_light(
					l_range = brightness_set,
					l_power = power_set,
					l_color = color_set
					)
	else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off())
		use_power = IDLE_POWER_USE
		emergency_mode = TRUE
		START_PROCESSING(SSmachines, src)
	else
		use_power = IDLE_POWER_USE
		set_light(l_range = 0)
	update_appearance()

	active_power_usage = (brightness * 10)
	if(on != on_gs)
		on_gs = on
		if(on)
			static_power_used = brightness * 20 //20W per unit luminosity
			addStaticPower(static_power_used, AREA_USAGE_STATIC_LIGHT)
		else
			removeStaticPower(static_power_used, AREA_USAGE_STATIC_LIGHT)

	broken_sparks(start_only=TRUE)

/obj/machinery/light/update_atom_colour()
	..()
	update()

/obj/machinery/light/proc/broken_sparks(start_only=FALSE)
	if(!QDELETED(src) && status == LIGHT_BROKEN && has_power() && Master.current_runlevel)
		if(!start_only)
			do_sparks(3, TRUE, src)
		var/delay = rand(BROKEN_SPARKS_MIN, BROKEN_SPARKS_MAX)
		addtimer(CALLBACK(src, .proc/broken_sparks), delay, TIMER_UNIQUE | TIMER_NO_HASH_WAIT)

/obj/machinery/light/process()
	if (!cell)
		return PROCESS_KILL
	if(has_power())
		if (cell.charge == cell.maxcharge)
			return PROCESS_KILL
		cell.charge = min(cell.maxcharge, cell.charge + LIGHT_EMERGENCY_POWER_USE) //Recharge emergency power automatically while not using it
	if(emergency_mode && !use_emergency_power(LIGHT_EMERGENCY_POWER_USE))
		update(FALSE) //Disables emergency mode and sets the color to normal

/obj/machinery/light/proc/burn_out()
	if(status == LIGHT_OK)
		status = LIGHT_BURNED
		icon_state = "[base_state]-burned"
		on = FALSE
		set_light(l_range = 0)

// attempt to set the light's on/off status
// will not switch on if broken/burned/empty
/obj/machinery/light/proc/set_on(turn_on)
	on = (turn_on && status == LIGHT_OK)
	update()

/obj/machinery/light/get_cell()
	return cell

// examine verb
/obj/machinery/light/examine(mob/user)
	. = ..()
	switch(status)
		if(LIGHT_OK)
			. += "It is turned [on? "on" : "off"]."
		if(LIGHT_EMPTY)
			. += "The [fitting] has been removed."
		if(LIGHT_BURNED)
			. += "The [fitting] is burnt out."
		if(LIGHT_BROKEN)
			. += "The [fitting] has been smashed."
	if(cell)
		. += "Its backup power charge meter reads [round((cell.charge / cell.maxcharge) * 100, 0.1)]%."

// attack with item - insert light (if right type), otherwise try to break the light

/obj/machinery/light/attackby(obj/item/tool, mob/living/user, params)

	//Light replacer code
	if(istype(tool, /obj/item/lightreplacer))
		var/obj/item/lightreplacer/replacer = tool
		replacer.ReplaceLight(src, user)
		return

	// attempt to insert light
	if(istype(tool, /obj/item/light))
		if(status == LIGHT_OK)
			to_chat(user, span_warning("There is a [fitting] already inserted!"))
			return
		add_fingerprint(user)
		var/obj/item/light/light_object = tool
		if(!istype(light_object, light_type))
			to_chat(user, span_warning("This type of light requires a [fitting]!"))
			return
		if(!user.temporarilyRemoveItemFromInventory(light_object))
			return

		add_fingerprint(user)
		if(status != LIGHT_EMPTY)
			drop_light_tube(user)
			to_chat(user, span_notice("You replace [light_object]."))
		else
			to_chat(user, span_notice("You insert [light_object]."))
		status = light_object.status
		switchcount = light_object.switchcount
		rigged = light_object.rigged
		brightness = light_object.brightness
		on = has_power()
		update()

		qdel(light_object)

		if(on && rigged)
			explode()
		return

	// attempt to stick weapon into light socket
	if(status != LIGHT_EMPTY)
		return ..()
	if(tool.tool_behaviour == TOOL_SCREWDRIVER) //If it's a screwdriver open it.
		tool.play_tool_sound(src, 75)
		user.visible_message(span_notice("[user.name] opens [src]'s casing."), \
			span_notice("You open [src]'s casing."), span_hear("You hear a noise."))
		deconstruct()
		return
	to_chat(user, span_userdanger("You stick \the [tool] into the light socket!"))
	if(has_power() && (tool.flags_1 & CONDUCT_1))
		do_sparks(3, TRUE, src)
		if (prob(75))
			electrocute_mob(user, get_area(src), src, (rand(7,10) * 0.1), TRUE)

/obj/machinery/light/deconstruct(disassembled = TRUE)
	if(flags_1 & NODECONSTRUCT_1)
		qdel(src)
		return
	var/obj/structure/light_construct/new_light = null
	var/current_stage = 2
	if(!disassembled)
		current_stage = 1
	switch(fitting)
		if("tube")
			new_light = new /obj/structure/light_construct(loc)
			new_light.icon_state = "tube-construct-stage[current_stage]"

		if("bulb")
			new_light = new /obj/structure/light_construct/small(loc)
			new_light.icon_state = "bulb-construct-stage[current_stage]"
	new_light.setDir(dir)
	new_light.stage = current_stage
	if(!disassembled)
		new_light.take_damage(new_light.max_integrity * 0.5, sound_effect=FALSE)
		if(status != LIGHT_BROKEN)
			break_light_tube()
		if(status != LIGHT_EMPTY)
			drop_light_tube()
		new /obj/item/stack/cable_coil(loc, 1, "red")
	transfer_fingerprints_to(new_light)
	if(!QDELETED(cell))
		new_light.cell = cell
		cell.forceMove(new_light)
		cell = null
	qdel(src)

/obj/machinery/light/attacked_by(obj/item/attacking_object, mob/living/user)
	..()
	if(status != LIGHT_BROKEN && status != LIGHT_EMPTY)
		return
	if(!on || !(attacking_object.flags_1 & CONDUCT_1))
		return
	if(prob(12))
		electrocute_mob(user, get_area(src), src, 0.3, TRUE)

/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
	. = ..()
	if(. && !QDELETED(src))
		if(prob(damage_amount * 5))
			break_light_tube()

/obj/machinery/light/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
	switch(damage_type)
		if(BRUTE)
			switch(status)
				if(LIGHT_EMPTY)
					playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE)
				if(LIGHT_BROKEN)
					playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 90, TRUE)
				else
					playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE)
		if(BURN)
			playsound(loc, 'sound/items/welder.ogg', 100, TRUE)

// returns if the light has power /but/ is manually turned off
// if a light is turned off, it won't activate emergency power
/obj/machinery/light/proc/turned_off()
	var/area/local_area = get_area(src)
	return !local_area.lightswitch && local_area.power_light || flickering

// returns whether this light has power
// true if area has power and lightswitch is on
/obj/machinery/light/proc/has_power()
	var/area/local_area = get_area(src)
	return local_area.lightswitch && local_area.power_light

// returns whether this light has emergency power
// can also return if it has access to a certain amount of that power
/obj/machinery/light/proc/has_emergency_power(power_usage_amount)
	if(no_emergency || !cell)
		return FALSE
	if(power_usage_amount ? cell.charge >= power_usage_amount : cell.charge)
		return status == LIGHT_OK

// attempts to use power from the installed emergency cell, returns true if it does and false if it doesn't
/obj/machinery/light/proc/use_emergency_power(power_usage_amount = LIGHT_EMERGENCY_POWER_USE)
	if(!has_emergency_power(power_usage_amount))
		return FALSE
	if(cell.charge > 300) //it's meant to handle 120 W, ya doofus
		visible_message(span_warning("[src] short-circuits from too powerful of a power cell!"))
		burn_out()
		return FALSE
	cell.use(power_usage_amount)
	set_light(
		l_range = brightness * bulb_emergency_brightness_mul,
		l_power = max(bulb_emergency_pow_min, bulb_emergency_pow_mul * (cell.charge / cell.maxcharge)),
		l_color = bulb_emergency_colour
		)
	return TRUE

/obj/machinery/light/proc/flicker(amount = rand(10, 20))
	set waitfor = FALSE
	if(flickering)
		return
	flickering = TRUE
	if(on && status == LIGHT_OK)
		for(var/i = 0; i < amount; i++)
			if(status != LIGHT_OK)
				break
			on = !on
			update(FALSE)
			sleep(rand(5, 15))
		on = (status == LIGHT_OK)
		update(FALSE)
	flickering = FALSE

// ai attack - make lights flicker, because why not

/obj/machinery/light/attack_ai(mob/user)
	no_emergency = !no_emergency
	to_chat(user, span_notice("Emergency lights for this fixture have been [no_emergency ? "disabled" : "enabled"]."))
	update(FALSE)
	return

// attack with hand - remove tube/bulb
// if hands aren't protected and the light is on, burn the player

/obj/machinery/light/attack_hand(mob/living/carbon/human/user, list/modifiers)
	. = ..()
	if(.)
		return
	user.changeNext_move(CLICK_CD_MELEE)
	add_fingerprint(user)

	if(status == LIGHT_EMPTY)
		to_chat(user, span_warning("There is no [fitting] in this light!"))
		return

	// make it burn hands unless you're wearing heat insulated gloves or have the RESISTHEAT/RESISTHEATHANDS traits
	if(!on)
		to_chat(user, span_notice("You remove the light [fitting]."))
		// create a light tube/bulb item and put it in the user's hand
		drop_light_tube(user)
		return
	var/protection_amount = 0
	var/mob/living/carbon/human/electrician = user

	if(istype(electrician))
		var/obj/item/organ/stomach/maybe_stomach = electrician.getorganslot(ORGAN_SLOT_STOMACH)
		if(istype(maybe_stomach, /obj/item/organ/stomach/ethereal))
			var/obj/item/organ/stomach/ethereal/stomach = maybe_stomach
			if(stomach.drain_time > world.time)
				return
			to_chat(electrician, span_notice("You start channeling some power through the [fitting] into your body."))
			stomach.drain_time = world.time + LIGHT_DRAIN_TIME
			if(do_after(user, LIGHT_DRAIN_TIME, target = src))
				if(istype(stomach))
					to_chat(electrician, span_notice("You receive some charge from the [fitting]."))
					stomach.adjust_charge(LIGHT_POWER_GAIN)
				else
					to_chat(electrician, span_warning("You can't receive charge from the [fitting]!"))
			return

		if(electrician.gloves)
			var/obj/item/clothing/gloves/electrician_gloves = electrician.gloves
			if(electrician_gloves.max_heat_protection_temperature)
				protection_amount = (electrician_gloves.max_heat_protection_temperature > 360)
	else
		protection_amount = 1

	if(protection_amount > 0 || HAS_TRAIT(user, TRAIT_RESISTHEAT) || HAS_TRAIT(user, TRAIT_RESISTHEATHANDS))
		to_chat(user, span_notice("You remove the light [fitting]."))
	else if(istype(user) && user.dna.check_mutation(TK))
		to_chat(user, span_notice("You telekinetically remove the light [fitting]."))
	else
		var/obj/item/bodypart/affecting = electrician.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
		if(affecting?.receive_damage( 0, 5 )) // 5 burn damage
			electrician.update_damage_overlays()
		if(HAS_TRAIT(user, TRAIT_LIGHTBULB_REMOVER))
			to_chat(user, span_notice("You feel like you're burning, but you can push through."))
			if(!do_after(user, 5 SECONDS, target = src))
				return
			if(affecting?.receive_damage( 0, 10 )) // 10 more burn damage
				electrician.update_damage_overlays()
			to_chat(user, span_notice("You manage to remove the light [fitting], shattering it in process."))
			break_light_tube()
		else
			to_chat(user, span_warning("You try to remove the light [fitting], but you burn your hand on it!"))
			return
	// create a light tube/bulb item and put it in the user's hand
	drop_light_tube(user)

/obj/machinery/light/proc/drop_light_tube(mob/user)
	var/obj/item/light/light_object = new light_type()
	light_object.status = status
	light_object.rigged = rigged
	light_object.brightness = brightness

	// light item inherits the switchcount, then zero it
	light_object.switchcount = switchcount
	switchcount = 0

	light_object.update()
	light_object.forceMove(loc)

	if(user) //puts it in our active hand
		light_object.add_fingerprint(user)
		user.put_in_active_hand(light_object)

	status = LIGHT_EMPTY
	update()
	return light_object

/obj/machinery/light/attack_tk(mob/user)
	if(status == LIGHT_EMPTY)
		to_chat(user, span_warning("There is no [fitting] in this light!"))
		return

	to_chat(user, span_notice("You telekinetically remove the light [fitting]."))
	// create a light tube/bulb item and put it in the user's hand
	var/obj/item/light/light_tube = drop_light_tube()
	return light_tube.attack_tk(user)

// break the light and make sparks if was on
/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = FALSE)
	if(status == LIGHT_EMPTY || status == LIGHT_BROKEN)
		return

	if(!skip_sound_and_sparks)
		if(status == LIGHT_OK || status == LIGHT_BURNED)
			playsound(loc, 'sound/effects/glasshit.ogg', 75, TRUE)
		if(on)
			do_sparks(3, TRUE, src)
	status = LIGHT_BROKEN
	update()

/obj/machinery/light/proc/fix()
	if(status == LIGHT_OK)
		return
	status = LIGHT_OK
	brightness = initial(brightness)
	on = TRUE
	update()

/obj/machinery/light/zap_act(power, zap_flags)
	var/explosive = zap_flags & ZAP_MACHINE_EXPLOSIVE
	zap_flags &= ~(ZAP_MACHINE_EXPLOSIVE | ZAP_OBJ_DAMAGE)
	. = ..()
	if(explosive)
		explosion(src, flame_range = 5, adminlog = FALSE)
		qdel(src)

// called when area power state changes
/obj/machinery/light/power_change()
	SHOULD_CALL_PARENT(FALSE)
	var/area/local_area = get_area(src)
	set_on(local_area.lightswitch && local_area.power_light)

// called when heated

/obj/machinery/light/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
	return exposed_temperature > 673

/obj/machinery/light/atmos_expose(datum/gas_mixture/air, exposed_temperature)
	if(prob(max(0, exposed_temperature - 673)))   //0% at <400C, 100% at >500C
		break_light_tube()

// explode the light

/obj/machinery/light/proc/explode()
	set waitfor = 0
	break_light_tube() // break it first to give a warning
	sleep(2)
	explosion(src, light_impact_range = 2, flash_range = -1)
	sleep(1)
	qdel(src)

/obj/machinery/light/proc/on_light_eater(obj/machinery/light/source, datum/light_eater)
	SIGNAL_HANDLER
	. = COMPONENT_BLOCK_LIGHT_EATER
	if(status == LIGHT_EMPTY)
		return
	var/obj/item/light/tube = drop_light_tube()
	tube?.burn()
	return

/obj/machinery/light/floor
	name = "floor light"
	icon = 'icons/obj/lighting.dmi'
	base_state = "floor" // base description and icon_state
	icon_state = "floor"
	brightness = 4
	layer = 2.5
	light_type = /obj/item/light/bulb
	fitting = "bulb"


code:
/obj/structure/light_construct
	name = "light fixture frame"
	desc = "A light fixture under construction."
	icon = 'icons/obj/lighting.dmi'
	icon_state = "tube-construct-stage1"
	anchored = TRUE
	layer = WALL_OBJ_LAYER
	max_integrity = 200
	armor = list(MELEE = 50, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 50)

	///Light construction stage (LIGHT_CONSTRUCT_EMPTY, LIGHT_CONSTRUCT_WIRED, LIGHT_CONSTRUCT_CLOSED)
	var/stage = LIGHT_CONSTRUCT_EMPTY
	///Type of fixture for icon state
	var/fixture_type = "tube"
	///Amount of sheets gained on deconstruction
	var/sheets_refunded = 2
	///Reference for light object
	var/obj/machinery/light/new_light = null
	///Reference for the internal cell
	var/obj/item/stock_parts/cell/cell
	///Can we support a cell?
	var/cell_connectors = TRUE

/obj/structure/light_construct/Initialize(mapload, ndir, building)
	. = ..()
	if(building)
		setDir(ndir)

/obj/structure/light_construct/Destroy()
	QDEL_NULL(cell)
	return ..()

/obj/structure/light_construct/get_cell()
	return cell

/obj/structure/light_construct/examine(mob/user)
	. = ..()
	switch(stage)
		if(LIGHT_CONSTRUCT_EMPTY)
			. += "It's an empty frame."
		if(LIGHT_CONSTRUCT_WIRED)
			. += "It's wired."
		if(LIGHT_CONSTRUCT_CLOSED)
			. += "The casing is closed."
	if(cell_connectors)
		if(cell)
			. += "You see [cell] inside the casing."
		else
			. += "The casing has no power cell for backup power."
	else
		. += span_danger("This casing doesn't support power cells for backup power.")

/obj/structure/light_construct/attack_hand(mob/user, list/modifiers)
	if(!cell)
		return
	user.visible_message(span_notice("[user] removes [cell] from [src]!"), span_notice("You remove [cell]."))
	user.put_in_hands(cell)
	cell.update_appearance()
	cell = null
	add_fingerprint(user)

/obj/structure/light_construct/attack_tk(mob/user)
	if(!cell)
		return
	to_chat(user, span_notice("You telekinetically remove [cell]."))
	var/obj/item/stock_parts/cell/cell_reference = cell
	cell = null
	cell_reference.forceMove(drop_location())
	return cell_reference.attack_tk(user)

/obj/structure/light_construct/attackby(obj/item/tool, mob/user, params)
	add_fingerprint(user)
	if(istype(tool, /obj/item/stock_parts/cell))
		if(!cell_connectors)
			to_chat(user, span_warning("This [name] can't support a power cell!"))
			return
		if(HAS_TRAIT(tool, TRAIT_NODROP))
			to_chat(user, span_warning("[tool] is stuck to your hand!"))
			return
		if(cell)
			to_chat(user, span_warning("There is a power cell already installed!"))
			return
		if(user.temporarilyRemoveItemFromInventory(tool))
			user.visible_message(span_notice("[user] hooks up [tool] to [src]."), \
			span_notice("You add [tool] to [src]."))
			playsound(src, 'sound/machines/click.ogg', 50, TRUE)
			tool.forceMove(src)
			cell = tool
			add_fingerprint(user)
			return
	if(istype(tool, /obj/item/light))
		to_chat(user, span_warning("This [name] isn't finished being setup!"))
		return

	switch(stage)
		if(LIGHT_CONSTRUCT_EMPTY)
			if(tool.tool_behaviour == TOOL_WRENCH)
				if(cell)
					to_chat(user, span_warning("You have to remove the cell first!"))
					return
				to_chat(user, span_notice("You begin deconstructing [src]..."))
				if (tool.use_tool(src, user, 30, volume=50))
					new /obj/item/stack/sheet/iron(drop_location(), sheets_refunded)
					user.visible_message(span_notice("[user.name] deconstructs [src]."), \
						span_notice("You deconstruct [src]."), span_hear("You hear a ratchet."))
					playsound(src, 'sound/items/deconstruct.ogg', 75, TRUE)
					qdel(src)
				return

			if(istype(tool, /obj/item/stack/cable_coil))
				var/obj/item/stack/cable_coil/coil = tool
				if(coil.use(1))
					icon_state = "[fixture_type]-construct-stage2"
					stage = LIGHT_CONSTRUCT_WIRED
					user.visible_message(span_notice("[user.name] adds wires to [src]."), \
						span_notice("You add wires to [src]."))
				else
					to_chat(user, span_warning("You need one length of cable to wire [src]!"))
				return
		if(LIGHT_CONSTRUCT_WIRED)
			if(tool.tool_behaviour == TOOL_WRENCH)
				to_chat(usr, span_warning("You have to remove the wires first!"))
				return

			if(tool.tool_behaviour == TOOL_WIRECUTTER)
				stage = LIGHT_CONSTRUCT_EMPTY
				icon_state = "[fixture_type]-construct-stage1"
				new /obj/item/stack/cable_coil(drop_location(), 1, "red")
				user.visible_message(span_notice("[user.name] removes the wiring from [src]."), \
					span_notice("You remove the wiring from [src]."), span_hear("You hear clicking."))
				tool.play_tool_sound(src, 100)
				return

			if(tool.tool_behaviour == TOOL_SCREWDRIVER)
				user.visible_message(span_notice("[user.name] closes [src]'s casing."), \
					span_notice("You close [src]'s casing."), span_hear("You hear screwing."))
				tool.play_tool_sound(src, 75)
				switch(fixture_type)
					if("tube")
						new_light = new /obj/machinery/light/built(loc)
					if("bulb")
						new_light = new /obj/machinery/light/small/built(loc)
				new_light.setDir(dir)
				transfer_fingerprints_to(new_light)
				if(cell)
					new_light.cell = cell
					cell.forceMove(new_light)
					cell = null
				qdel(src)
				return
	return ..()

/obj/structure/light_construct/blob_act(obj/structure/blob/attacking_blob)
	if(attacking_blob && attacking_blob.loc == loc)
		qdel(src)

/obj/structure/light_construct/deconstruct(disassembled = TRUE)
	if(!(flags_1 & NODECONSTRUCT_1))
		new /obj/item/stack/sheet/iron(loc, sheets_refunded)
	qdel(src)

/obj/structure/light_construct/small
	name = "small light fixture frame"
	icon_state = "bulb-construct-stage1"
	fixture_type = "bulb"
	sheets_refunded = 1


code:
// the light item
// can be tube or bulb subtypes
// will fit into empty /obj/machinery/light of the corresponding type

/obj/item/light
	icon = 'icons/obj/lighting.dmi'
	force = 2
	throwforce = 5
	w_class = WEIGHT_CLASS_TINY
	custom_materials = list(/datum/material/glass=100)
	grind_results = list(/datum/reagent/silicon = 5, /datum/reagent/nitrogen = 10) //Nitrogen is used as a cheaper alternative to argon in incandescent lighbulbs
	///True if rigged to explode
	var/rigged = FALSE
	///How much light it gives off
	var/brightness = 2
	///LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN
	var/status = LIGHT_OK
	///Base icon state for each bulb types
	var/base_state
	///Number of times switched on and off
	var/switchcount = 0

/obj/item/light/suicide_act(mob/living/carbon/user)
	if (status == LIGHT_BROKEN)
		user.visible_message(span_suicide("[user] begins to stab [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
		return BRUTELOSS
	else
		user.visible_message(span_suicide("[user] begins to eat \the [src]! It looks like [user.p_theyre()] not very bright!"))
		shatter()
		return BRUTELOSS

/obj/item/light/tube
	name = "light tube"
	desc = "A replacement light tube."
	icon_state = "ltube"
	base_state = "ltube"
	inhand_icon_state = "c_tube"
	brightness = 8
	custom_price = PAYCHECK_EASY * 0.5

/obj/item/light/tube/broken
	status = LIGHT_BROKEN

/obj/item/light/bulb
	name = "light bulb"
	desc = "A replacement light bulb."
	icon_state = "lbulb"
	base_state = "lbulb"
	inhand_icon_state = "contvapour"
	lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
	righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
	brightness = 4
	custom_price = PAYCHECK_EASY * 0.4

/obj/item/light/bulb/broken
	status = LIGHT_BROKEN

/obj/item/light/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
	if(!..()) //not caught by a mob
		shatter()

// update the icon state and description of the light

/obj/item/light/proc/update()
	switch(status)
		if(LIGHT_OK)
			icon_state = base_state
			desc = "A replacement [name]."
		if(LIGHT_BURNED)
			icon_state = "[base_state]-burned"
			desc = "A burnt-out [name]."
		if(LIGHT_BROKEN)
			icon_state = "[base_state]-broken"
			desc = "A broken [name]."

/obj/item/light/Initialize(mapload)
	. = ..()
	create_reagents(LIGHT_REAGENT_CAPACITY, INJECTABLE | DRAINABLE)
	AddComponent(/datum/component/caltrop, min_damage = force)
	update()
	var/static/list/loc_connections = list(
		COMSIG_ATOM_ENTERED = .proc/on_entered,
	)
	AddElement(/datum/element/connect_loc, loc_connections)

/obj/item/light/proc/on_entered(datum/source, atom/movable/moving_atom)
	SIGNAL_HANDLER
	if(!isliving(moving_atom))
		return
	var/mob/living/moving_mob = moving_atom
	if(!(moving_mob.movement_type & (FLYING|FLOATING)) || moving_mob.buckled)
		playsound(src, 'sound/effects/glass_step.ogg', HAS_TRAIT(moving_mob, TRAIT_LIGHT_STEP) ? 30 : 50, TRUE)
		if(status == LIGHT_BURNED || status == LIGHT_OK)
			shatter()

/obj/item/light/create_reagents(max_vol, flags)
	. = ..()
	RegisterSignal(reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), .proc/on_reagent_change)
	RegisterSignal(reagents, COMSIG_PARENT_QDELETING, .proc/on_reagents_del)

/**
 * Handles rigging the cell if it contains enough plasma.
 */
/obj/item/light/proc/on_reagent_change(datum/reagents/holder, ...)
	SIGNAL_HANDLER
	rigged = (reagents.has_reagent(/datum/reagent/toxin/plasma, LIGHT_REAGENT_CAPACITY)) ? TRUE : FALSE //has_reagent returns the reagent datum, we don't want to hold a reference to prevent hard dels
	return NONE

/**
 * Handles the reagent holder datum being deleted for some reason. Probably someone making pizza lights.
 */
/obj/item/light/proc/on_reagents_del(datum/reagents/holder)
	SIGNAL_HANDLER
	UnregisterSignal(holder, list(
		COMSIG_PARENT_QDELETING,
		COMSIG_REAGENTS_NEW_REAGENT,
		COMSIG_REAGENTS_ADD_REAGENT,
		COMSIG_REAGENTS_REM_REAGENT,
		COMSIG_REAGENTS_DEL_REAGENT,
	))
	return NONE

/obj/item/light/attack(mob/living/M, mob/living/user, def_zone)
	..()
	shatter()

/obj/item/light/attack_atom(obj/O, mob/living/user, params)
	..()
	shatter()

/obj/item/light/proc/shatter()
	if(status == LIGHT_OK || status == LIGHT_BURNED)
		visible_message(span_danger("[src] shatters."),span_hear("You hear a small glass object shatter."))
		status = LIGHT_BROKEN
		force = 5
		playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE)
		if(rigged)
			atmos_spawn_air("plasma=5") //5u of plasma are required to rig a light bulb/tube
		update()


code:
/obj/machinery/light/broken
	status = LIGHT_BROKEN
	icon_state = "tube-broken"

/obj/machinery/light/built
	icon_state = "tube-empty"
	start_with_cell = FALSE

/obj/machinery/light/built/Initialize(mapload)
	. = ..()
	status = LIGHT_EMPTY
	update(0)

/obj/machinery/light/no_nightlight
	nightshift_enabled = FALSE

/obj/machinery/light/warm
	bulb_colour = "#fae5c1"

/obj/machinery/light/warm/no_nightlight
	nightshift_allowed = FALSE

/obj/machinery/light/cold
	bulb_colour = "#deefff"
	nightshift_light_color = "#deefff"

/obj/machinery/light/cold/no_nightlight
	nightshift_allowed = FALSE

/obj/machinery/light/red
	bulb_colour = "#FF3232"
	nightshift_allowed = FALSE
	no_emergency = TRUE
	brightness = 2
	bulb_power = 0.7

/obj/machinery/light/blacklight
	bulb_colour = "#A700FF"
	nightshift_allowed = FALSE
	brightness = 2
	bulb_power = 0.8

/obj/machinery/light/dim
	nightshift_allowed = FALSE
	bulb_colour = "#FFDDCC"
	bulb_power = 0.6

// the smaller bulb light fixture

/obj/machinery/light/small
	icon_state = "bulb"
	base_state = "bulb"
	fitting = "bulb"
	brightness = 4
	nightshift_brightness = 4
	bulb_colour = "#FFD6AA"
	desc = "A small lighting fixture."
	light_type = /obj/item/light/bulb

/obj/machinery/light/small/broken
	status = LIGHT_BROKEN
	icon_state = "bulb-broken"

/obj/machinery/light/small/built
	icon_state = "bulb-empty"
	start_with_cell = FALSE

/obj/machinery/light/small/built/Initialize(mapload)
	. = ..()
	status = LIGHT_EMPTY
	update(0)

/obj/machinery/light/small/red
	bulb_colour = "#FF3232"
	no_emergency = TRUE
	nightshift_allowed = FALSE
	brightness = 1
	bulb_power = 0.8

/obj/machinery/light/small/blacklight
	bulb_colour = "#A700FF"
	nightshift_allowed = FALSE
	brightness = 1
	bulb_power = 0.9

// -------- Directional presets
// The directions are backwards on the lights we have now
/obj/machinery/light/directional/north
	dir = NORTH

/obj/machinery/light/directional/south
	dir = SOUTH

/obj/machinery/light/directional/east
	dir = EAST

/obj/machinery/light/directional/west
	dir = WEST

// ---- Broken tube
/obj/machinery/light/broken/directional/north
	dir = NORTH

/obj/machinery/light/broken/directional/south
	dir = SOUTH

/obj/machinery/light/broken/directional/east
	dir = EAST

/obj/machinery/light/broken/directional/west
	dir = WEST

// ---- Tube construct
/obj/structure/light_construct/directional/north
	dir = NORTH

/obj/structure/light_construct/directional/south
	dir = SOUTH

/obj/structure/light_construct/directional/east
	dir = EAST

/obj/structure/light_construct/directional/west
	dir = WEST

// ---- Tube frames
/obj/machinery/light/built/directional/north
	dir = NORTH

/obj/machinery/light/built/directional/south
	dir = SOUTH

/obj/machinery/light/built/directional/east
	dir = EAST

/obj/machinery/light/built/directional/west
	dir = WEST

// ---- No nightlight tubes
/obj/machinery/light/no_nightlight/directional/north
	dir = NORTH

/obj/machinery/light/no_nightlight/directional/south
	dir = SOUTH

/obj/machinery/light/no_nightlight/directional/east
	dir = EAST

/obj/machinery/light/no_nightlight/directional/west
	dir = WEST

// ---- Warm light tubes
/obj/machinery/light/warm/directional/north
	dir = NORTH

/obj/machinery/light/warm/directional/south
	dir = SOUTH

/obj/machinery/light/warm/directional/east
	dir = EAST

/obj/machinery/light/warm/directional/west
	dir = WEST

// ---- No nightlight warm light tubes
/obj/machinery/light/warm/no_nightlight/directional/north
	dir = NORTH

/obj/machinery/light/warm/no_nightlight/directional/south
	dir = SOUTH

/obj/machinery/light/warm/no_nightlight/directional/east
	dir = EAST

/obj/machinery/light/warm/no_nightlight/directional/west
	dir = WEST

// ---- Cold light tubes
/obj/machinery/light/cold/directional/north
	dir = NORTH

/obj/machinery/light/cold/directional/south
	dir = SOUTH

/obj/machinery/light/cold/directional/east
	dir = EAST

/obj/machinery/light/cold/directional/west
	dir = WEST

// ---- No nightlight cold light tubes
/obj/machinery/light/cold/no_nightlight/directional/north
	dir = NORTH

/obj/machinery/light/cold/no_nightlight/directional/south
	dir = SOUTH

/obj/machinery/light/cold/no_nightlight/directional/east
	dir = EAST

/obj/machinery/light/cold/no_nightlight/directional/west
	dir = WEST

// ---- Red tubes
/obj/machinery/light/red/directional/north
	dir = NORTH

/obj/machinery/light/red/directional/south
	dir = SOUTH

/obj/machinery/light/red/directional/east
	dir = EAST

/obj/machinery/light/red/directional/west
	dir = WEST

// ---- Blacklight tubes
/obj/machinery/light/blacklight/directional/north
	dir = NORTH

/obj/machinery/light/blacklight/directional/south
	dir = SOUTH

/obj/machinery/light/blacklight/directional/east
	dir = EAST

/obj/machinery/light/blacklight/directional/west
	dir = WEST

// ---- Dim tubes
/obj/machinery/light/dim/directional/north
	dir = NORTH

/obj/machinery/light/dim/directional/south
	dir = SOUTH

/obj/machinery/light/dim/directional/east
	dir = EAST

/obj/machinery/light/dim/directional/west
	dir = WEST

// -------- Bulb lights
/obj/machinery/light/small/directional/north
	dir = NORTH

/obj/machinery/light/small/directional/south
	dir = SOUTH

/obj/machinery/light/small/directional/east
	dir = EAST

/obj/machinery/light/small/directional/west
	dir = WEST

// ---- Bulb construct
/obj/structure/light_construct/small/directional/north
	dir = NORTH

/obj/structure/light_construct/small/directional/south
	dir = SOUTH

/obj/structure/light_construct/small/directional/east
	dir = EAST

/obj/structure/light_construct/small/directional/west
	dir = WEST

// ---- Bulb frames
/obj/machinery/light/small/built/directional/north
	dir = NORTH

/obj/machinery/light/small/built/directional/south
	dir = SOUTH

/obj/machinery/light/small/built/directional/east
	dir = EAST

/obj/machinery/light/small/built/directional/west
	dir = WEST

// ---- Broken bulbs
/obj/machinery/light/small/broken/directional/north
	dir = NORTH

/obj/machinery/light/small/broken/directional/south
	dir = SOUTH

/obj/machinery/light/small/broken/directional/east
	dir = EAST

/obj/machinery/light/small/broken/directional/west
	dir = WEST

// ---- Red bulbs
/obj/machinery/light/small/red/directional/north
	dir = NORTH

/obj/machinery/light/small/red/directional/south
	dir = SOUTH

/obj/machinery/light/small/red/directional/east
	dir = EAST

/obj/machinery/light/small/red/directional/west
	dir = WEST

// ---- Blacklight bulbs
/obj/machinery/light/small/blacklight/directional/north
	dir = NORTH

/obj/machinery/light/small/blacklight/directional/south
	dir = SOUTH

/obj/machinery/light/small/blacklight/directional/east
	dir = EAST

/obj/machinery/light/small/blacklight/directional/west
	dir = WEST


code:
/obj/item/wallframe/light_fixture
	name = "light fixture frame"
	desc = "Used for building lights."
	icon = 'icons/obj/lighting.dmi'
	icon_state = "tube-construct-item"
	result_path = /obj/structure/light_construct
	inverse = TRUE

/obj/item/wallframe/light_fixture/small
	name = "small light fixture frame"
	icon_state = "bulb-construct-item"
	result_path = /obj/structure/light_construct/small
	custom_materials = list(/datum/material/iron=MINERAL_MATERIAL_AMOUNT)

/obj/item/wallframe/light_fixture/try_build(turf/on_wall, user)
	if(!..())
		return
	var/area/local_area = get_area(user)
	if(!local_area.static_lighting)
		to_chat(user, span_warning("You cannot place [src] in this area!"))
		return
	return TRUE


code:
//modular computer program version is located in code\modules\modular_computers\file_system\programs\powermonitor.dm, /datum/computer_file/program/power_monitor

/obj/machinery/computer/monitor
	name = "power monitoring console"
	desc = "It monitors power levels across the station."
	icon_screen = "power"
	icon_keyboard = "power_key"
	light_color = LIGHT_COLOR_YELLOW
	use_power = ACTIVE_POWER_USE
	idle_power_usage = 20
	active_power_usage = 100
	circuit = /obj/item/circuitboard/computer/powermonitor
	tgui_id = "PowerMonitor"

	var/obj/structure/cable/attached_wire
	var/obj/machinery/power/apc/local_apc

	var/list/history = list()
	var/record_size = 60
	var/record_interval = 50
	var/next_record = 0
	var/is_secret_monitor = FALSE

/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming.
	name = "outdated power monitoring console"
	desc = "It monitors power levels across the local powernet."
	circuit = /obj/item/circuitboard/computer/powermonitor/secret
	is_secret_monitor = TRUE

/obj/machinery/computer/monitor/secret/examine(mob/user)
	. = ..()
	. += span_notice("It's operating system seems quite outdated... It doesn't seem like it'd be compatible with the latest remote NTOS monitoring systems.")

/obj/machinery/computer/monitor/Initialize(mapload)
	. = ..()
	search()
	history["supply"] = list()
	history["demand"] = list()

/obj/machinery/computer/monitor/process()
	if(!get_powernet())
		update_use_power(IDLE_POWER_USE)
		search()
	else
		update_use_power(ACTIVE_POWER_USE)
		record()

/obj/machinery/computer/monitor/proc/search() //keep in sync with /datum/computer_file/program/power_monitor's version
	var/turf/T = get_turf(src)
	attached_wire = locate(/obj/structure/cable) in T
	if(attached_wire)
		return
	var/area/A = get_area(src) //if the computer isn't directly connected to a wire, attempt to find the APC powering it to pull it's powernet instead
	if(!A)
		return
	local_apc = A.apc
	if(!local_apc)
		return
	if(!local_apc.terminal) //this really shouldn't happen without badminnery.
		local_apc = null

/obj/machinery/computer/monitor/proc/get_powernet() //keep in sync with /datum/computer_file/program/power_monitor's version
	if(attached_wire || (local_apc?.terminal))
		return attached_wire ? attached_wire.powernet : local_apc.terminal.powernet
	return FALSE

/obj/machinery/computer/monitor/proc/record() //keep in sync with /datum/computer_file/program/power_monitor's version
	if(world.time >= next_record)
		next_record = world.time + record_interval

		var/datum/powernet/connected_powernet = get_powernet()

		var/list/supply = history["supply"]
		if(connected_powernet)
			supply += connected_powernet.viewavail
		if(supply.len > record_size)
			supply.Cut(1, 2)

		var/list/demand = history["demand"]
		if(connected_powernet)
			demand += connected_powernet.viewload
		if(demand.len > record_size)
			demand.Cut(1, 2)

/obj/machinery/computer/monitor/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "PowerMonitor", name)
		ui.open()

/obj/machinery/computer/monitor/ui_data()
	var/datum/powernet/connected_powernet = get_powernet()
	var/list/data = list()
	data["stored"] = record_size
	data["interval"] = record_interval / 10
	data["attached"] = connected_powernet ? TRUE : FALSE
	data["history"] = history
	data["areas"] = list()

	if(connected_powernet)
		data["supply"] = display_power(connected_powernet.viewavail)
		data["demand"] = display_power(connected_powernet.viewload)
		for(var/obj/machinery/power/terminal/term in connected_powernet.nodes)
			var/obj/machinery/power/apc/A = term.master
			if(istype(A))
				var/cell_charge
				if(!A.cell)
					cell_charge = 0
				else
					cell_charge = A.cell.percent()
				data["areas"] += list(list(
					"name" = A.area.name,
					"charge" = cell_charge,
					"load" = display_power(A.lastused_total),
					"charging" = A.charging,
					"eqp" = A.equipment,
					"lgt" = A.lighting,
					"env" = A.environ
				))

	return data


code:
/obj/structure/cable/multilayer/multiz //This bridges powernets betwen Z levels
	name = "multi z layer cable hub"
	desc = "A flexible, superconducting insulated multi Z layer hub for heavy-duty multi Z power transfer."
	icon = 'icons/obj/power.dmi'
	icon_state = "cablerelay-on"
	cable_layer = CABLE_LAYER_1|CABLE_LAYER_2|CABLE_LAYER_3
	machinery_layer = null

/obj/structure/cable/multilayer/multiz/get_cable_connections(powernetless_only)
	. = ..()
	var/turf/T = get_turf(src)
	. +=  locate(/obj/structure/cable/multilayer/multiz) in (SSmapping.get_turf_below(T))
	. +=  locate(/obj/structure/cable/multilayer/multiz) in (SSmapping.get_turf_above(T))

/obj/structure/cable/multilayer/examine(mob/user)
	. += ..()
	var/turf/T = get_turf(src)
	. += span_notice("[locate(/obj/structure/cable/multilayer/multiz) in (SSmapping.get_turf_below(T)) ? "Detected" : "Undetected"] hub UP.")
	. += span_notice("[locate(/obj/structure/cable/multilayer/multiz) in (SSmapping.get_turf_above(T)) ? "Detected" : "Undetected"] hub DOWN.")


code:
GLOBAL_LIST_INIT(pipe_cleaner_colors, list(
	"blue" = COLOR_STRONG_BLUE,
	"cyan" = COLOR_CYAN,
	"green" = COLOR_DARK_LIME,
	"orange" = COLOR_MOSTLY_PURE_ORANGE,
	"pink" = COLOR_LIGHT_PINK,
	"red" = COLOR_RED,
	"white" = COLOR_WHITE,
	"yellow" = COLOR_YELLOW
	))

//This is the old cable code, but minus any actual powernet logic
//Wireart is fun

///////////////////////////////
//CABLE STRUCTURE
///////////////////////////////

////////////////////////////////
// Definitions
////////////////////////////////

/* Cable directions (d1 and d2)
 * 9   1   5
 * \ | /
 * 8 - 0 - 4
 * / | \
 * 10  2   6

If d1 = 0 and d2 = 0, there's no pipe_cleaner
If d1 = 0 and d2 = dir, it's a O-X pipe_cleaner, getting from the center of the tile to dir (knot pipe_cleaner)
If d1 = dir1 and d2 = dir2, it's a full X-X pipe_cleaner, getting from dir1 to dir2
By design, d1 is the smallest direction and d2 is the highest
*/

/obj/structure/pipe_cleaner
	name = "pipe cleaner"
	desc = "A bendable piece of wire covered in fuzz. Fun for arts and crafts!"
	icon = 'icons/obj/power_cond/pipe_cleaner.dmi'
	icon_state = "0-1"
	layer = WIRE_LAYER //Above hidden pipes, GAS_PIPE_HIDDEN_LAYER
	anchored = TRUE
	obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
	color = COLOR_RED
	/// Pipe_cleaner direction 1 (see above)
	var/d1 = 0
	/// pipe_cleaner direction 2 (see above)
	var/d2 = 1
	/// Internal cable stack
	var/obj/item/stack/pipe_cleaner_coil/stored

/obj/structure/pipe_cleaner/yellow
	color = COLOR_YELLOW

/obj/structure/pipe_cleaner/green
	color = COLOR_DARK_LIME

/obj/structure/pipe_cleaner/blue
	color = COLOR_STRONG_BLUE

/obj/structure/pipe_cleaner/pink
	color = COLOR_LIGHT_PINK

/obj/structure/pipe_cleaner/orange
	color = COLOR_MOSTLY_PURE_ORANGE

/obj/structure/pipe_cleaner/cyan
	color = COLOR_CYAN

/obj/structure/pipe_cleaner/white
	color = COLOR_WHITE

// the power pipe_cleaner object
/obj/structure/pipe_cleaner/Initialize(mapload, param_color)
	. = ..()

	// ensure d1 & d2 reflect the icon_state for entering and exiting pipe_cleaner
	var/dash = findtext(icon_state, "-")
	d1 = text2num(copytext(icon_state, 1, dash))
	d2 = text2num(copytext(icon_state, dash + length(icon_state[dash])))

	if(d1)
		stored = new/obj/item/stack/pipe_cleaner_coil(null, 2, null, null, null, color)
	else
		stored = new/obj/item/stack/pipe_cleaner_coil(null, 1, null, null, null, color)

	color = param_color || color
	if(!color)
		var/list/pipe_cleaner_colors = GLOB.pipe_cleaner_colors
		var/random_color = pick(pipe_cleaner_colors)
		color = pipe_cleaner_colors[random_color]
	update_appearance()

/obj/structure/pipe_cleaner/Destroy() // called when a pipe_cleaner is deleted
	//If we have a stored item at this point, lets just delete it, since that should be
	//handled by deconstruction
	if(stored)
		QDEL_NULL(stored)
	return ..() // then go ahead and delete the pipe_cleaner

/obj/structure/pipe_cleaner/deconstruct(disassembled = TRUE)
	if(!(flags_1 & NODECONSTRUCT_1))
		var/turf/T = get_turf(loc)
		if(T)
			stored.forceMove(T)
			stored = null
		else
			qdel(stored)
	qdel(src)

///////////////////////////////////
// General procedures
///////////////////////////////////

/obj/structure/pipe_cleaner/update_icon_state()
	icon_state = "[d1]-[d2]"
	return ..()

/obj/structure/pipe_cleaner/update_icon()
	. = ..()
	add_atom_colour(color, FIXED_COLOUR_PRIORITY)

// Items usable on a pipe_cleaner :
//   - Wirecutters : cut it duh !
//   - pipe cleaner coil : merge pipe cleaners
//
/obj/structure/pipe_cleaner/proc/handlecable(obj/item/W, mob/user, params)
	if(W.tool_behaviour == TOOL_WIRECUTTER)
		cut_pipe_cleaner(user)
		return

	else if(istype(W, /obj/item/stack/pipe_cleaner_coil))
		var/obj/item/stack/pipe_cleaner_coil/coil = W
		if (coil.get_amount() < 1)
			to_chat(user, span_warning("Not enough pipe cleaner!"))
			return
		coil.pipe_cleaner_join(src, user)

	add_fingerprint(user)

/obj/structure/pipe_cleaner/proc/cut_pipe_cleaner(mob/user)
	user.visible_message(span_notice("[user] pulls up the pipe cleaner."), span_notice("You pull up the pipe cleaner."))
	stored.add_fingerprint(user)
	investigate_log("was pulled up by [key_name(usr)] in [AREACOORD(src)]", INVESTIGATE_WIRES)
	deconstruct()

/obj/structure/pipe_cleaner/attackby(obj/item/W, mob/user, params)
	handlecable(W, user, params)

/obj/structure/pipe_cleaner/singularity_pull(S, current_size)
	..()
	if(current_size >= STAGE_FIVE)
		deconstruct()

/obj/structure/pipe_cleaner/proc/update_stored(length = 1, colorC = COLOR_RED)
	stored.amount = length
	stored.color = colorC
	stored.update_appearance()

/obj/structure/pipe_cleaner/AltClick(mob/living/user)
	if(!user.canUseTopic(src, BE_CLOSE))
		return
	cut_pipe_cleaner(user)

///////////////////////////////////////////////
// The pipe cleaner coil object, used for laying pipe cleaner
///////////////////////////////////////////////

////////////////////////////////
// Definitions
////////////////////////////////

/obj/item/stack/pipe_cleaner_coil
	name = "pipe cleaner coil"
	desc = "A coil of pipe cleaners. Good for arts and crafts, not to build with."
	custom_price = PAYCHECK_ASSISTANT * 0.5
	gender = NEUTER //That's a pipe_cleaner coil sounds better than that's some pipe_cleaner coils
	icon = 'icons/obj/power.dmi'
	icon_state = "pipecleaner"
	inhand_icon_state = "pipecleaner"
	worn_icon_state = "coil"
	lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
	righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
	max_amount = MAXCOIL
	amount = MAXCOIL
	merge_type = /obj/item/stack/pipe_cleaner_coil // This is here to let its children merge between themselves
	throwforce = 0
	w_class = WEIGHT_CLASS_SMALL
	throw_speed = 3
	throw_range = 5
	mats_per_unit = list(/datum/material/iron=10, /datum/material/glass=5)
	flags_1 = CONDUCT_1
	slot_flags = ITEM_SLOT_BELT
	attack_verb_continuous = list("whips", "lashes", "disciplines", "flogs")
	attack_verb_simple = list("whip", "lash", "discipline", "flog")
	singular_name = "pipe cleaner piece"
	full_w_class = WEIGHT_CLASS_SMALL
	grind_results = list("copper" = 2) //2 copper per pipe_cleaner in the coil
	usesound = 'sound/items/deconstruct.ogg'
	cost = 1
	source = /datum/robot_energy_storage/pipe_cleaner
	color = COLOR_RED

/obj/item/stack/pipe_cleaner_coil/cyborg/attack_self(mob/user)
	var/list/pipe_cleaner_colors = GLOB.pipe_cleaner_colors
	var/list/possible_colors = list()
	for(var/color in pipe_cleaner_colors)
		var/image/pipe_icon = image(icon = src.icon, icon_state = src.icon_state)
		pipe_icon.color = pipe_cleaner_colors[color]
		possible_colors += list("[color]" = pipe_icon)

	var/selected_color = show_radial_menu(user, src, possible_colors, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 40, require_near = TRUE)
	if(!selected_color)
		return
	color = pipe_cleaner_colors[selected_color]
	update_appearance()

/**
 * Checks if we are allowed to interact with a radial menu
 *
 * Arguments:
 * * user The mob interacting with the menu
 */
/obj/item/stack/pipe_cleaner_coil/cyborg/proc/check_menu(mob/user)
	if(!istype(user))
		return FALSE
	if(!user.is_holding(src))
		return FALSE
	if(user.incapacitated())
		return FALSE
	return TRUE

/obj/item/stack/pipe_cleaner_coil/suicide_act(mob/user)
	if(locate(/obj/structure/chair/stool) in get_turf(user))
		user.visible_message(span_suicide("[user] is making a noose with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
	else
		user.visible_message(span_suicide("[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
	return(OXYLOSS)

/obj/item/stack/pipe_cleaner_coil/Initialize(mapload, new_amount = null, list/mat_override=null, mat_amt=1, param_color = null)
	. = ..()

	if(param_color)
		color = param_color
	if(!color)
		var/list/pipe_cleaner_colors = GLOB.pipe_cleaner_colors
		var/random_color = pick(pipe_cleaner_colors)
		color = pipe_cleaner_colors[random_color]

	pixel_x = base_pixel_x + rand(-2, 2)
	pixel_y = base_pixel_y + rand(-2, 2)
	update_appearance()

///////////////////////////////////
// General procedures
///////////////////////////////////

/obj/item/stack/pipe_cleaner_coil/update_name()
	. = ..()
	name = "pipe cleaner [amount < 3 ? "piece" : "coil"]"

/obj/item/stack/pipe_cleaner_coil/update_icon_state()
	. = ..()
	icon_state = "[initial(inhand_icon_state)][amount < 3 ? amount : ""]"

/obj/item/stack/pipe_cleaner_coil/update_icon()
	. = ..()
	add_atom_colour(color, FIXED_COLOUR_PRIORITY)

/obj/item/stack/pipe_cleaner_coil/attack_hand(mob/user, list/modifiers)
	. = ..()
	if(.)
		return
	var/obj/item/stack/pipe_cleaner_coil/new_pipe_cleaner = ..()
	if(istype(new_pipe_cleaner))
		new_pipe_cleaner.color = color
		new_pipe_cleaner.update_appearance()

//add pipe_cleaners to the stack
/obj/item/stack/pipe_cleaner_coil/proc/give(extra)
	if(amount + extra > max_amount)
		amount = max_amount
	else
		amount += extra
	update_appearance()

///////////////////////////////////////////////
// Cable laying procedures
//////////////////////////////////////////////

/obj/item/stack/pipe_cleaner_coil/proc/get_new_pipe_cleaner(location)
	var/path = /obj/structure/pipe_cleaner
	return new path(location, color)

// called when pipe_cleaner_coil is clicked on a turf
/obj/item/stack/pipe_cleaner_coil/proc/place_turf(turf/T, mob/user, dirnew)
	if(!isturf(user.loc))
		return

	if(!isturf(T) || !T.can_have_cabling())
		to_chat(user, span_warning("You can only lay pipe cleaners on a solid floor!"))
		return

	if(get_amount() < 1) // Out of pipe_cleaner
		to_chat(user, span_warning("There is no pipe cleaner left!"))
		return

	if(get_dist(T,user) > 1) // Too far
		to_chat(user, span_warning("You can't lay pipe cleaner at a place that far away!"))
		return

	var/dirn
	if(!dirnew) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm)
		if(user.loc == T)
			dirn = user.dir //If laying on the tile we're on, lay in the direction we're facing
		else
			dirn = get_dir(T, user)
	else
		dirn = dirnew

	for(var/obj/structure/pipe_cleaner/LC in T)
		if(LC.d2 == dirn && LC.d1 == 0)
			to_chat(user, span_warning("There's already a pipe leaner at that position!"))
			return

	var/obj/structure/pipe_cleaner/C = get_new_pipe_cleaner(T)

	//set up the new pipe_cleaner
	C.d1 = 0 //it's a O-X node pipe_cleaner
	C.d2 = dirn
	C.add_fingerprint(user)
	C.update_appearance()

	use(1)

	return C

// called when pipe_cleaner_coil is click on an installed obj/pipe_cleaner
// or click on a turf that already contains a "node" pipe_cleaner
/obj/item/stack/pipe_cleaner_coil/proc/pipe_cleaner_join(obj/structure/pipe_cleaner/C, mob/user, showerror = TRUE, forceddir)
	var/turf/U = user.loc
	if(!isturf(U))
		return

	var/turf/T = C.loc

	if(!isturf(T)) // sanity check
		return

	if(get_dist(C, user) > 1) // make sure it's close enough
		to_chat(user, span_warning("You can't lay pipe cleaner at a place that far away!"))
		return

	if(U == T && !forceddir) //if clicked on the turf we're standing on and a direction wasn't supplied, try to put a pipe_cleaner in the direction we're facing
		place_turf(T,user)
		return

	var/dirn = get_dir(C, user)
	if(forceddir)
		dirn = forceddir

	// one end of the clicked pipe_cleaner is pointing towards us and no direction was supplied
	if((C.d1 == dirn || C.d2 == dirn) && !forceddir)
		if(!U.can_have_cabling()) //checking if it's a plating or catwalk
			if (showerror)
				to_chat(user, span_warning("You can only lay pipe cleaners on catwalks and plating!"))
			return
		else
			// pipe_cleaner is pointing at us, we're standing on an open tile
			// so create a stub pointing at the clicked pipe_cleaner on our tile

			var/fdirn = turn(dirn, 180) // the opposite direction

			for(var/obj/structure/pipe_cleaner/LC in U) // check to make sure there's not a pipe_cleaner there already
				if(LC.d1 == fdirn || LC.d2 == fdirn)
					if (showerror)
						to_chat(user, span_warning("There's already a pipe cleaner at that position!"))
					return

			var/obj/structure/pipe_cleaner/NC = get_new_pipe_cleaner(U)

			NC.d1 = 0
			NC.d2 = fdirn
			NC.add_fingerprint(user)
			NC.update_appearance()

			use(1)

			return

	// exisiting pipe_cleaner doesn't point at our position or we have a supplied direction, so see if it's a stub
	else if(C.d1 == 0)
							// if so, make it a full pipe_cleaner pointing from it's old direction to our dirn
		var/nd1 = C.d2 // these will be the new directions
		var/nd2 = dirn

		if(nd1 > nd2) // swap directions to match icons/states
			nd1 = dirn
			nd2 = C.d2

		for(var/obj/structure/pipe_cleaner/LC in T) // check to make sure there's no matching pipe_cleaner
			if(LC == C) // skip the pipe_cleaner we're interacting with
				continue
			if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no pipe_cleaner matches either direction
				if (showerror)
					to_chat(user, span_warning("There's already a pipe cleaner at that position!"))

				return

		C.update_appearance()

		C.d1 = nd1
		C.d2 = nd2

		//updates the stored pipe_cleaner coil
		C.update_stored(2, color)

		C.add_fingerprint(user)
		C.update_appearance()

		use(1)

		return

//////////////////////////////
// Misc.
/////////////////////////////

/obj/item/stack/pipe_cleaner_coil/red
	color = COLOR_RED

/obj/item/stack/pipe_cleaner_coil/yellow
	color = COLOR_YELLOW

/obj/item/stack/pipe_cleaner_coil/blue
	color = COLOR_STRONG_BLUE

/obj/item/stack/pipe_cleaner_coil/green
	color = COLOR_DARK_LIME

/obj/item/stack/pipe_cleaner_coil/pink
	color = COLOR_LIGHT_PINK

/obj/item/stack/pipe_cleaner_coil/orange
	color = COLOR_MOSTLY_PURE_ORANGE

/obj/item/stack/pipe_cleaner_coil/cyan
	color = COLOR_CYAN

/obj/item/stack/pipe_cleaner_coil/white
	color = COLOR_WHITE

/obj/item/stack/pipe_cleaner_coil/random
	color = null

/obj/item/stack/pipe_cleaner_coil/random/five
	amount = 5

/obj/item/stack/pipe_cleaner_coil/cut
	amount = null
	icon_state = "pipecleaner2"

/obj/item/stack/pipe_cleaner_coil/cut/Initialize(mapload)
	if(!amount)
		amount = rand(1,2)
	. = ..()
	pixel_x = base_pixel_x + rand(-2, 2)
	pixel_y = base_pixel_y + rand(-2, 2)
	update_appearance()

/obj/item/stack/pipe_cleaner_coil/cut/red
	color = COLOR_RED

/obj/item/stack/pipe_cleaner_coil/cut/yellow
	color = COLOR_YELLOW

/obj/item/stack/pipe_cleaner_coil/cut/blue
	color = COLOR_STRONG_BLUE

/obj/item/stack/pipe_cleaner_coil/cut/green
	color = COLOR_DARK_LIME

/obj/item/stack/pipe_cleaner_coil/cut/pink
	color = COLOR_LIGHT_PINK

/obj/item/stack/pipe_cleaner_coil/cut/orange
	color = COLOR_MOSTLY_PURE_ORANGE

/obj/item/stack/pipe_cleaner_coil/cut/cyan
	color = COLOR_CYAN

/obj/item/stack/pipe_cleaner_coil/cut/white
	color = COLOR_WHITE

/obj/item/stack/pipe_cleaner_coil/cut/random
	color = null


code:
//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power).
/obj/machinery/power/port_gen
	name = "portable generator"
	desc = "A portable generator for emergency backup power."
	icon = 'icons/obj/power.dmi'
	icon_state = "portgen0_0"
	density = TRUE
	anchored = FALSE
	use_power = NO_POWER_USE

	var/active = FALSE
	var/power_gen = 5000
	var/power_output = 1
	var/consumption = 0
	var/base_icon = "portgen0"
	var/datum/looping_sound/generator/soundloop

	interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT | INTERACT_ATOM_REQUIRES_ANCHORED

/obj/machinery/power/port_gen/Initialize(mapload)
	. = ..()
	soundloop = new(src, active)

/obj/machinery/power/port_gen/Destroy()
	QDEL_NULL(soundloop)
	return ..()

/obj/machinery/power/port_gen/should_have_node()
	return anchored

/obj/machinery/power/port_gen/connect_to_network()
	if(!anchored)
		return FALSE
	. = ..()

/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check.
	return TRUE

/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use.
	return

/obj/machinery/power/port_gen/proc/DropFuel()
	return

/obj/machinery/power/port_gen/proc/handleInactive()
	return

/obj/machinery/power/port_gen/proc/TogglePower()
	if(active)
		active = FALSE
		update_appearance()
		soundloop.stop()
	else if(HasFuel())
		active = TRUE
		START_PROCESSING(SSmachines, src)
		update_appearance()
		soundloop.start()

/obj/machinery/power/port_gen/update_icon_state()
	icon_state = "[base_icon]_[active]"
	return ..()

/obj/machinery/power/port_gen/process()
	if(active)
		if(!HasFuel() || !anchored)
			TogglePower()
			return
		if(powernet)
			add_avail(power_gen * power_output)
		UseFuel()
	else
		handleInactive()

/obj/machinery/power/port_gen/examine(mob/user)
	. = ..()
	. += "It is[!active?"n't":""] running."

/////////////////
// P.A.C.M.A.N //
/////////////////
/obj/machinery/power/port_gen/pacman
	name = "\improper P.A.C.M.A.N.-type portable generator"
	circuit = /obj/item/circuitboard/machine/pacman
	var/sheets = 0
	var/max_sheets = 100
	var/sheet_name = ""
	var/sheet_path = /obj/item/stack/sheet/mineral/plasma
	var/sheet_left = 0 // How much is left of the sheet
	var/time_per_sheet = 260
	var/current_heat = 0

/obj/machinery/power/port_gen/pacman/Initialize(mapload)
	. = ..()
	if(anchored)
		connect_to_network()

/obj/machinery/power/port_gen/pacman/Initialize(mapload)
	. = ..()

	var/obj/S = sheet_path
	sheet_name = initial(S.name)

/obj/machinery/power/port_gen/pacman/Destroy()
	DropFuel()
	return ..()

/obj/machinery/power/port_gen/pacman/RefreshParts()
	var/temp_rating = 0
	var/consumption_coeff = 0
	for(var/obj/item/stock_parts/SP in component_parts)
		if(istype(SP, /obj/item/stock_parts/matter_bin))
			max_sheets = SP.rating * SP.rating * 50
		else if(istype(SP, /obj/item/stock_parts/capacitor))
			temp_rating += SP.rating
		else
			consumption_coeff += SP.rating
	power_gen = round(initial(power_gen) * temp_rating * 2)
	consumption = consumption_coeff

/obj/machinery/power/port_gen/pacman/examine(mob/user)
	. = ..()
	. += span_notice("The generator has [sheets] units of [sheet_name] fuel left, producing [display_power(power_gen)] per cycle.")
	if(anchored)
		. += span_notice("It is anchored to the ground.")
	if(in_range(user, src) || isobserver(user))
		. += span_notice("The status display reads: Fuel efficiency increased by <b>[(consumption*100)-100]%</b>.")

/obj/machinery/power/port_gen/pacman/HasFuel()
	if(sheets >= 1 / (time_per_sheet / power_output) - sheet_left)
		return TRUE
	return FALSE

/obj/machinery/power/port_gen/pacman/DropFuel()
	if(sheets)
		new sheet_path(drop_location(), sheets)
		sheets = 0

/obj/machinery/power/port_gen/pacman/UseFuel()
	var/needed_sheets = 1 / (time_per_sheet * consumption / power_output)
	var/temp = min(needed_sheets, sheet_left)
	needed_sheets -= temp
	sheet_left -= temp
	sheets -= round(needed_sheets)
	needed_sheets -= round(needed_sheets)
	if (sheet_left <= 0 && sheets > 0)
		sheet_left = 1 - needed_sheets
		sheets--

	var/lower_limit = 56 + power_output * 10
	var/upper_limit = 76 + power_output * 10
	var/bias = 0
	if (power_output > 4)
		upper_limit = 400
		bias = power_output - consumption * (4 - consumption)
	if (current_heat < lower_limit)
		current_heat += 4 - consumption
	else
		current_heat += rand(-7 + bias, 7 + bias)
		if (current_heat < lower_limit)
			current_heat = lower_limit
		if (current_heat > upper_limit)
			current_heat = upper_limit

	if (current_heat > 300)
		overheat()
		qdel(src)

/obj/machinery/power/port_gen/pacman/handleInactive()
	current_heat = max(current_heat - 2, 0)
	if(current_heat == 0)
		STOP_PROCESSING(SSmachines, src)

/obj/machinery/power/port_gen/pacman/proc/overheat()
	explosion(src, devastation_range = 2, heavy_impact_range = 5, light_impact_range = 2, flash_range = -1)

/obj/machinery/power/port_gen/pacman/set_anchored(anchorvalue)
	. = ..()
	if(isnull(.))
		return //no need to process if we didn't change anything.
	if(anchorvalue)
		connect_to_network()
	else
		disconnect_from_network()

/obj/machinery/power/port_gen/pacman/attackby(obj/item/O, mob/user, params)
	if(istype(O, sheet_path))
		var/obj/item/stack/addstack = O
		var/amount = min((max_sheets - sheets), addstack.amount)
		if(amount < 1)
			to_chat(user, span_notice("The [src.name] is full!"))
			return
		to_chat(user, span_notice("You add [amount] sheets to the [src.name]."))
		sheets += amount
		addstack.use(amount)
		return
	else if(!active)
		if(O.tool_behaviour == TOOL_WRENCH)
			if(!anchored && !isinspace())
				set_anchored(TRUE)
				to_chat(user, span_notice("You secure the generator to the floor."))
			else if(anchored)
				set_anchored(FALSE)
				to_chat(user, span_notice("You unsecure the generator from the floor."))

			playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
			return
		else if(O.tool_behaviour == TOOL_SCREWDRIVER)
			panel_open = !panel_open
			O.play_tool_sound(src)
			if(panel_open)
				to_chat(user, span_notice("You open the access panel."))
			else
				to_chat(user, span_notice("You close the access panel."))
			return
		else if(default_deconstruction_crowbar(O))
			return
	return ..()

/obj/machinery/power/port_gen/pacman/emag_act(mob/user)
	if(obj_flags & EMAGGED)
		return
	obj_flags |= EMAGGED
	emp_act(EMP_HEAVY)

/obj/machinery/power/port_gen/pacman/attack_ai(mob/user)
	interact(user)

/obj/machinery/power/port_gen/pacman/attack_paw(mob/user, list/modifiers)
	interact(user)

/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "PortableGenerator", name)
		ui.open()

/obj/machinery/power/port_gen/pacman/ui_data()
	var/data = list()

	data["active"] = active
	data["sheet_name"] = capitalize(sheet_name)
	data["sheets"] = sheets
	data["stack_percent"] = round(sheet_left * 100, 0.1)

	data["anchored"] = anchored
	data["connected"] = (powernet == null ? 0 : 1)
	data["ready_to_boot"] = anchored && HasFuel()
	data["power_generated"] = display_power(power_gen)
	data["power_output"] = display_power(power_gen * power_output)
	data["power_available"] = (powernet == null ? 0 : display_power(avail()))
	data["current_heat"] = current_heat
	. =  data

/obj/machinery/power/port_gen/pacman/ui_act(action, params)
	. = ..()
	if(.)
		return
	switch(action)
		if("toggle_power")
			TogglePower()
			. = TRUE

		if("eject")
			if(!active)
				DropFuel()
				. = TRUE

		if("lower_power")
			if (power_output > 1)
				power_output--
				. = TRUE

		if("higher_power")
			if (power_output < 4 || (obj_flags & EMAGGED))
				power_output++
				. = TRUE

/obj/machinery/power/port_gen/pacman/super
	name = "\improper S.U.P.E.R.P.A.C.M.A.N.-type portable generator"
	icon_state = "portgen1_0"
	base_icon = "portgen1"
	circuit = /obj/item/circuitboard/machine/pacman/super
	sheet_path = /obj/item/stack/sheet/mineral/uranium
	power_gen = 15000
	time_per_sheet = 85

/obj/machinery/power/port_gen/pacman/super/overheat()
	explosion(src, devastation_range = 3, heavy_impact_range = 3, light_impact_range = 3, flash_range = -1)