code:
//These procs handle putting stuff in your hand. It's probably best to use these rather than setting stuff manually
//as they handle all relevant stuff like adding it to the player's screen and such

/**
 * Returns the thing in our active hand (whatever is in our active module-slot, in this case)
 */
/mob/living/silicon/robot/get_active_held_item()
	return module_active

/**
 * Parent proc - triggers when an item/module is unequipped from a cyborg.
 */
/obj/item/proc/cyborg_unequip(mob/user)
	return

/**
 * Finds the first available slot and attemps to put item item_module in it.
 *
 * Arguments
 * * item_module - the item being equipped to a slot.
 */
/mob/living/silicon/robot/proc/activate_module(obj/item/item_module)
	if(QDELETED(item_module))
		CRASH("activate_module called with improper item_module")

	if(!(item_module in model.modules))
		CRASH("activate_module called with item_module not in model.modules")

	if(activated(item_module))
		to_chat(src, span_warning("That module is already activated."))
		return FALSE

	if(disabled_modules & BORG_MODULE_ALL_DISABLED)
		to_chat(src, span_warning("All modules are disabled!"))
		return FALSE

	/// What's the first free slot for the borg?
	var/first_free_slot = !held_items[1] ? 1 : (!held_items[2] ? 2 : (!held_items[3] ? 3 : null))

	if(!first_free_slot || is_invalid_module_number(first_free_slot))
		to_chat(src, span_warning("Deactivate a module first!"))
		return FALSE

	return equip_module_to_slot(item_module, first_free_slot)

/**
 * Is passed an item and a module slot. Equips the item to that borg slot.
 *
 * Arguments
 * * item_module - the item being equipped to a slot
 * * module_num - the slot number being equipped to.
 */
/mob/living/silicon/robot/proc/equip_module_to_slot(obj/item/item_module, module_num)
	var/storage_was_closed = FALSE //Just to be consistant and all
	if(!shown_robot_modules) //Tools may be invisible if the collection is hidden
		hud_used.toggle_show_robot_modules()
		storage_was_closed = TRUE
	switch(module_num)
		if(BORG_CHOOSE_MODULE_ONE)
			item_module.screen_loc = inv1.screen_loc
		if(BORG_CHOOSE_MODULE_TWO)
			item_module.screen_loc = inv2.screen_loc
		if(BORG_CHOOSE_MODULE_THREE)
			item_module.screen_loc = inv3.screen_loc

	held_items[module_num] = item_module
	item_module.equipped(src, ITEM_SLOT_HANDS)
	item_module.mouse_opacity = initial(item_module.mouse_opacity)
	item_module.plane = ABOVE_HUD_PLANE
	item_module.forceMove(src)

	if(istype(item_module, /obj/item/borg/sight))
		var/obj/item/borg/sight/borg_sight = item_module
		sight_mode |= borg_sight.sight_mode
		update_sight()

	observer_screen_update(item_module, TRUE)

	if(storage_was_closed)
		hud_used.toggle_show_robot_modules()
	return TRUE

/**
 * Unequips item item_module from slot module_num. Deletes it if delete_after = TRUE.
 *
 * Arguments
 * * item_module - the item being unequipped
 * * module_num - the slot number being unequipped.
 */
/mob/living/silicon/robot/proc/unequip_module_from_slot(obj/item/item_module, module_num)
	if(QDELETED(item_module))
		CRASH("unequip_module_from_slot called with improper item_module")

	if(!(item_module in model.modules))
		CRASH("unequip_module_from_slot called with item_module not in model.modules")

	item_module.mouse_opacity = MOUSE_OPACITY_OPAQUE

	if(istype(item_module, /obj/item/storage/bag/tray/))
		SEND_SIGNAL(item_module, COMSIG_TRY_STORAGE_QUICK_EMPTY)
	if(istype(item_module, /obj/item/borg/sight))
		var/obj/item/borg/sight/borg_sight = item_module
		sight_mode &= ~borg_sight.sight_mode
		update_sight()

	if(client)
		client.screen -= item_module

	if(module_active == item_module)
		module_active = null

	switch(module_num)
		if(BORG_CHOOSE_MODULE_ONE)
			if(!(disabled_modules & BORG_MODULE_ALL_DISABLED))
				inv1.icon_state = initial(inv1.icon_state)
		if(BORG_CHOOSE_MODULE_TWO)
			if(!(disabled_modules & BORG_MODULE_TWO_DISABLED))
				inv2.icon_state = initial(inv2.icon_state)
		if(BORG_CHOOSE_MODULE_THREE)
			if(!(disabled_modules & BORG_MODULE_THREE_DISABLED))
				inv3.icon_state = initial(inv3.icon_state)

	if(item_module.item_flags & DROPDEL)
		item_module.item_flags &= ~DROPDEL //we shouldn't HAVE things with DROPDEL_1 in our modules, but better safe than runtiming horribly

	held_items[module_num] = null
	item_module.cyborg_unequip(src)
	item_module.forceMove(model) //Return item to configuration so it appears in its contents, so it can be taken out again.

	observer_screen_update(item_module, FALSE)
	hud_used.update_robot_modules_display()
	return TRUE

/**
 * Breaks the slot number, changing the icon.
 *
 * Arguments
 * * module_num - the slot number being repaired.
 */
/mob/living/silicon/robot/proc/break_cyborg_slot(module_num)
	if(is_invalid_module_number(module_num, TRUE))
		return FALSE

	if(held_items[module_num]) //If there's a held item, unequip it first.
		if(!unequip_module_from_slot(held_items[module_num], module_num)) //If we fail to unequip it, then don't continue
			return FALSE

	switch(module_num)
		if(BORG_CHOOSE_MODULE_ONE)
			if(disabled_modules & BORG_MODULE_ALL_DISABLED)
				return FALSE

			inv1.icon_state = "[initial(inv1.icon_state)] +b"
			disabled_modules |= BORG_MODULE_ALL_DISABLED

			playsound(src, 'sound/machines/warning-buzzer.ogg', 75, TRUE, TRUE)
			audible_message(span_warning("[src] sounds an alarm! \"CRITICAL ERROR: ALL modules OFFLINE.\""))

			if(builtInCamera)
				builtInCamera.status = FALSE
				to_chat(src, span_userdanger("CRITICAL ERROR: Built in security camera OFFLINE."))

			to_chat(src, span_userdanger("CRITICAL ERROR: ALL modules OFFLINE."))

		if(BORG_CHOOSE_MODULE_TWO)
			if(disabled_modules & BORG_MODULE_TWO_DISABLED)
				return FALSE

			inv2.icon_state = "[initial(inv2.icon_state)] +b"
			disabled_modules |= BORG_MODULE_TWO_DISABLED

			playsound(src, 'sound/machines/warning-buzzer.ogg', 60, TRUE, TRUE)
			audible_message(span_warning("[src] sounds an alarm! \"SYSTEM ERROR: Module [module_num] OFFLINE.\""))
			to_chat(src, span_userdanger("SYSTEM ERROR: Module [module_num] OFFLINE."))

		if(BORG_CHOOSE_MODULE_THREE)
			if(disabled_modules & BORG_MODULE_THREE_DISABLED)
				return FALSE

			inv3.icon_state = "[initial(inv3.icon_state)] +b"
			disabled_modules |= BORG_MODULE_THREE_DISABLED

			playsound(src, 'sound/machines/warning-buzzer.ogg', 50, TRUE, TRUE)
			audible_message(span_warning("[src] sounds an alarm! \"SYSTEM ERROR: Module [module_num] OFFLINE.\""))
			to_chat(src, span_userdanger("SYSTEM ERROR: Module [module_num] OFFLINE."))

	return TRUE

/**
 * Breaks all of a cyborg's slots.
 */
/mob/living/silicon/robot/proc/break_all_cyborg_slots()
	for(var/cyborg_slot in 1 to 3)
		break_cyborg_slot(cyborg_slot)

/**
 * Repairs the slot number, updating the icon.
 *
 * Arguments
 * * module_num - the module number being repaired.
 */
/mob/living/silicon/robot/proc/repair_cyborg_slot(module_num)
	if(is_invalid_module_number(module_num, TRUE))
		return FALSE

	switch(module_num)
		if(BORG_CHOOSE_MODULE_ONE)
			if(!(disabled_modules & BORG_MODULE_ALL_DISABLED))
				return FALSE

			inv1.icon_state = initial(inv1.icon_state)
			disabled_modules &= ~BORG_MODULE_ALL_DISABLED
			if(builtInCamera)
				builtInCamera.status = TRUE
				to_chat(src, span_notice("You hear your built in security camera focus adjust as it comes back online!"))
		if(BORG_CHOOSE_MODULE_TWO)
			if(!(disabled_modules & BORG_MODULE_TWO_DISABLED))
				return FALSE

			inv2.icon_state = initial(inv2.icon_state)
			disabled_modules &= ~BORG_MODULE_TWO_DISABLED
		if(BORG_CHOOSE_MODULE_THREE)
			if(!(disabled_modules & BORG_MODULE_THREE_DISABLED))
				return FALSE

			inv3.icon_state = initial(inv3.icon_state)
			disabled_modules &= ~BORG_MODULE_THREE_DISABLED

	to_chat(src, span_notice("ERROR CLEARED: Module [module_num] back online."))

	return TRUE

/**
 * Repairs all slots. Unbroken slots are unaffected.
 */
/mob/living/silicon/robot/proc/repair_all_cyborg_slots()
	for(var/cyborg_slot in 1 to 3)
		repair_cyborg_slot(cyborg_slot)

/**
 * Updates the observers's screens with cyborg itemss.
 * Arguments
 * * item_module - the item being added or removed from the screen
 * * add - whether or not the item is being added, or removed.
 */
/mob/living/silicon/robot/proc/observer_screen_update(obj/item/item_module, add = TRUE)
	if(observers?.len)
		for(var/M in observers)
			var/mob/dead/observe = M
			if(observe.client && observe.client.eye == src)
				if(add)
					observe.client.screen += item_module
				else
					observe.client.screen -= item_module
			else
				observers -= observe
				if(!observers.len)
					observers = null
					break

/**
 * Unequips the active held item, if there is one.
 */
/mob/living/silicon/robot/proc/uneq_active()
	if(module_active)
		unequip_module_from_slot(module_active, get_selected_module())

/**
 * Unequips all held items.
 */
/mob/living/silicon/robot/proc/uneq_all()
	for(var/cyborg_slot in 1 to 3)
		if(!held_items[cyborg_slot])
			continue
		unequip_module_from_slot(held_items[cyborg_slot], cyborg_slot)

/**
 * Checks if the item is currently in a slot.
 *
 * If the item is found in a slot, this returns TRUE. Otherwise, it returns FALSE
 * Arguments
 * * item_module - the item being checked
 */
/mob/living/silicon/robot/proc/activated(obj/item/item_module)
	if(item_module in held_items)
		return TRUE
	return FALSE

/**
 * Checks if the provided module number is a valid number.
 *
 * If the number is between 1 and 3 (if check_all_slots is true) or between 1 and the number of disabled
 * modules (if check_all_slots is false), then it returns FALSE. Otherwise, it returns TRUE.
 * Arguments
 * * module_num - the passed module num that is checked for validity.
 * * check_all_slots - TRUE = the proc checks all slots | FALSE = the proc only checks un-disabled slots
 */
/mob/living/silicon/robot/proc/is_invalid_module_number(module_num, check_all_slots = FALSE)
	if(!module_num)
		return TRUE

	/// The number of module slots we're checking
	var/max_number = 3
	if(!check_all_slots)
		if(disabled_modules & BORG_MODULE_ALL_DISABLED)
			max_number = 0
		else if(disabled_modules & BORG_MODULE_TWO_DISABLED)
			max_number = 1
		else if(disabled_modules & BORG_MODULE_THREE_DISABLED)
			max_number = 2

	return module_num < 1 || module_num > max_number

/**
 * Returns the slot number of the selected module, or zero if no modules are selected.
 */
/mob/living/silicon/robot/proc/get_selected_module()
	if(module_active)
		return held_items.Find(module_active)

	return 0

/**
 * Selects the module in the slot module_num.
 * Arguments
 * * module_num - the slot number being selected
 */
/mob/living/silicon/robot/proc/select_module(module_num)
	if(is_invalid_module_number(module_num) || !held_items[module_num]) //If the slot number is invalid, or there's nothing there, we have nothing to equip
		return FALSE

	switch(module_num)
		if(BORG_CHOOSE_MODULE_ONE)
			if(module_active != held_items[module_num])
				inv1.icon_state = "[initial(inv1.icon_state)] +a"
		if(BORG_CHOOSE_MODULE_TWO)
			if(module_active != held_items[module_num])
				inv2.icon_state = "[initial(inv2.icon_state)] +a"
		if(BORG_CHOOSE_MODULE_THREE)
			if(module_active != held_items[module_num])
				inv3.icon_state = "[initial(inv3.icon_state)] +a"
	module_active = held_items[module_num]
	return TRUE

/**
 * Deselects the module in the slot module_num.
 * Arguments
 * * module_num - the slot number being de-selected
 */
/mob/living/silicon/robot/proc/deselect_module(module_num)
	switch(module_num)
		if(BORG_CHOOSE_MODULE_ONE)
			if(module_active == held_items[module_num])
				inv1.icon_state = initial(inv1.icon_state)
		if(BORG_CHOOSE_MODULE_TWO)
			if(module_active == held_items[module_num])
				inv2.icon_state = initial(inv2.icon_state)
		if(BORG_CHOOSE_MODULE_THREE)
			if(module_active == held_items[module_num])
				inv3.icon_state = initial(inv3.icon_state)
	module_active = null
	return TRUE

/**
 * Toggles selection of the module in the slot module_num.
 * Arguments
 * * module_num - the slot number being toggled
 */
/mob/living/silicon/robot/proc/toggle_module(module_num)
	if(is_invalid_module_number(module_num))
		return FALSE

	if(module_num == get_selected_module())
		deselect_module(module_num)
		return TRUE

	if(module_active != held_items[module_num])
		deselect_module(get_selected_module())

	return select_module(module_num)

/**
 * Cycles through the list of enabled modules, deselecting the current one and selecting the next one.
 */
/mob/living/silicon/robot/proc/cycle_modules()
	var/slot_start = get_selected_module()
	var/slot_num
	if(slot_start)
		deselect_module(slot_start) //Only deselect if we have a selected slot.
		slot_num = slot_start + 1
	else
		slot_num = 1
		slot_start = 4

	while(slot_num != slot_start) //If we wrap around without finding any free slots, just give up.
		if(select_module(slot_num))
			return
		slot_num++
		if(slot_num > 4) // not >3 otherwise cycling with just one item on module 3 wouldn't work
			slot_num = 1 //Wrap around.

/mob/living/silicon/robot/swap_hand()
	cycle_modules()

/mob/living/silicon/robot/can_hold_items(obj/item/I)
	return (I && (I in model.modules)) //Only if it's part of our model.


code:
/mob/living/silicon/robot/deadchat_lawchange()
	if(lawupdate)
		return
	..()

/mob/living/silicon/robot/show_laws(everyone = FALSE)
	laws_sanity_check()
	var/who

	if (everyone)
		who = world
	else
		who = src
	if(lawupdate)
		if (connected_ai)
			if(connected_ai.stat || connected_ai.control_disabled)
				to_chat(src, "AI signal lost, unable to sync laws.")

			else
				lawsync()
				to_chat(src, "Laws synced with AI, be sure to note any changes.")
		else
			to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.")
			lawupdate = FALSE

	to_chat(who, "Obey these laws:")
	laws.show_laws(who)
	if (shell) //AI shell
		to_chat(who, "Remember, you are an AI remotely controlling your shell, other AIs can be ignored.")
	else if (connected_ai)
		to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.")
	else if (emagged)
		to_chat(who, "Remember, you are not required to listen to the AI.")
	else
		to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.")

/mob/living/silicon/robot/proc/lawsync()
	laws_sanity_check()
	var/datum/ai_laws/master = connected_ai?.laws
	var/temp
	if (master)
		laws.ion.len = master.ion.len
		for (var/index = 1, index <= master.ion.len, index++)
			temp = master.ion[index]
			if (length(temp) > 0)
				laws.ion[index] = temp

		laws.hacked.len = master.hacked.len
		for (var/index = 1, index <= master.hacked.len, index++)
			temp = master.hacked[index]
			if (length(temp) > 0)
				laws.hacked[index] = temp

		if(master.zeroth_borg) //If the AI has a defined law zero specifically for its borgs, give it that one, otherwise give it the same one. --NEO
			temp = master.zeroth_borg
		else
			temp = master.zeroth
		laws.zeroth = temp

		laws.inherent.len = master.inherent.len
		for (var/index = 1, index <= master.inherent.len, index++)
			temp = master.inherent[index]
			if (length(temp) > 0)
				laws.inherent[index] = temp

		laws.supplied.len = master.supplied.len
		for (var/index = 1, index <= master.supplied.len, index++)
			temp = master.supplied[index]
			if (length(temp) > 0)
				laws.supplied[index] = temp

		var/datum/computer_file/program/robotact/program = modularInterface.get_robotact()
		if(program)
			program.force_full_update()

	picturesync()

/mob/living/silicon/robot/post_lawchange(announce = TRUE)
	. = ..()
	addtimer(CALLBACK(src, .proc/logevent,"Law update processed."), 0, TIMER_UNIQUE | TIMER_OVERRIDE) //Post_Lawchange gets spammed by some law boards, so let's wait it out

repo_name: AnalWerewolf/sundown

license: mit


code:
/mob/living/silicon/robot/Life(delta_time = SSMOBS_DT, times_fired)
	if (src.notransform)
		return

	..()
	handle_robot_hud_updates()
	handle_robot_cell(delta_time, times_fired)

/mob/living/silicon/robot/proc/handle_robot_cell(delta_time, times_fired)
	if(stat == DEAD)
		return

	if(low_power_mode)
		if(cell?.charge)
			low_power_mode = FALSE
	else if(stat == CONSCIOUS)
		use_power(delta_time, times_fired)

/mob/living/silicon/robot/proc/use_power(delta_time, times_fired)
	if(cell?.charge)
		if(cell.charge <= 100)
			uneq_all()
		var/amt = clamp(lamp_enabled * lamp_intensity * delta_time, 0.5 * delta_time, cell.charge) //Lamp will use a max of 5 charge, depending on brightness of lamp. If lamp is off, borg systems consume 1 point of charge, or the rest of the cell if it's lower than that.
		cell.use(amt) //Usage table: 0.5/second if off/lowest setting, 4 = 2/second, 6 = 4/second, 8 = 6/second, 10 = 8/second
	else
		uneq_all()
		low_power_mode = TRUE
		toggle_headlamp(TRUE)
	diag_hud_set_borgcell()

/mob/living/silicon/robot/proc/handle_robot_hud_updates()
	if(!client)
		return

	update_cell_hud_icon()

/mob/living/silicon/robot/update_health_hud()
	if(!client || !hud_used)
		return
	if(hud_used.healths)
		if(stat != DEAD)
			if(health >= maxHealth)
				hud_used.healths.icon_state = "health0"
			else if(health > maxHealth*0.6)
				hud_used.healths.icon_state = "health2"
			else if(health > maxHealth*0.2)
				hud_used.healths.icon_state = "health3"
			else if(health > -maxHealth*0.2)
				hud_used.healths.icon_state = "health4"
			else if(health > -maxHealth*0.6)
				hud_used.healths.icon_state = "health5"
			else
				hud_used.healths.icon_state = "health6"
		else
			hud_used.healths.icon_state = "health7"

/mob/living/silicon/robot/proc/update_cell_hud_icon()
	if(cell)
		var/cellcharge = cell.charge/cell.maxcharge
		switch(cellcharge)
			if(0.75 to INFINITY)
				clear_alert("charge")
			if(0.5 to 0.75)
				throw_alert("charge", /atom/movable/screen/alert/lowcell, 1)
			if(0.25 to 0.5)
				throw_alert("charge", /atom/movable/screen/alert/lowcell, 2)
			if(0.01 to 0.25)
				throw_alert("charge", /atom/movable/screen/alert/lowcell, 3)
			else
				throw_alert("charge", /atom/movable/screen/alert/emptycell)
	else
		throw_alert("charge", /atom/movable/screen/alert/nocell)

//Robots on fire
/mob/living/silicon/robot/handle_fire(delta_time, times_fired)
	. = ..()
	if(.) //if the mob isn't on fire anymore
		return
	if(fire_stacks > 0)
		adjust_fire_stacks(-0.5 * delta_time)
	else
		extinguish_mob()
		return TRUE

	//adjustFireLoss(3)

/mob/living/silicon/robot/update_fire()
	var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/OnFire.dmi', "Generic_mob_burning")
	if(on_fire)
		add_overlay(fire_overlay)
	else
		cut_overlay(fire_overlay)


code:
/mob/living/silicon/robot/Login()
	. = ..()
	if(!. || !client)
		return FALSE
	regenerate_icons()
	show_laws(0)


code:
/mob/living/silicon/robot/Initialize(mapload)
	spark_system = new /datum/effect_system/spark_spread()
	spark_system.set_up(5, 0, src)
	spark_system.attach(src)

	ADD_TRAIT(src, TRAIT_CAN_STRIP, INNATE_TRAIT)

	wires = new /datum/wires/robot(src)
	//SEPTIC EDIT START
	/*
	AddElement(/datum/element/empprotection, EMP_PROTECT_WIRES)
	*/
	//SEPTIC EDIT END
	AddElement(/datum/element/ridable, /datum/component/riding/creature/cyborg)
	RegisterSignal(src, COMSIG_PROCESS_BORGCHARGER_OCCUPANT, .proc/charge)
	RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, .proc/on_light_eater)

	robot_modules_background = new()
	robot_modules_background.icon_state = "block"
	robot_modules_background.plane = HUD_PLANE

	inv1 = new /atom/movable/screen/robot/module1()
	inv2 = new /atom/movable/screen/robot/module2()
	inv3 = new /atom/movable/screen/robot/module3()

	ident = rand(1, 999)

	previous_health = health

	if(ispath(cell))
		cell = new cell(src)

	create_modularInterface()

	model = new /obj/item/robot_model(src)
	model.rebuild_modules()

	if(lawupdate)
		make_laws()
		if(!TryConnectToAI())
			lawupdate = FALSE

	if(!scrambledcodes && !builtInCamera)
		builtInCamera = new (src)
		builtInCamera.c_tag = real_name
		builtInCamera.network = list("ss13")
		builtInCamera.internal_light = FALSE
		if(wires.is_cut(WIRE_CAMERA))
			builtInCamera.status = 0
	update_icons()
	. = ..()

	//If this body is meant to be a borg controlled by the AI player
	if(shell)
		make_shell()
	else
		//MMI stuff. Held togheter by magic. ~Miauw
		if(!mmi?.brainmob)
			mmi = new (src)
			mmi.brain = new /obj/item/organ/brain(mmi)
			mmi.brain.organ_flags |= ORGAN_FROZEN
			mmi.brain.name = "[real_name]'s brain"
			mmi.name = "[initial(mmi.name)]: [real_name]"
			mmi.set_brainmob(new /mob/living/brain(mmi))
			mmi.brainmob.name = src.real_name
			mmi.brainmob.real_name = src.real_name
			mmi.brainmob.container = mmi
			mmi.update_appearance()
		setup_default_name()

	aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
	toner = tonermax
	diag_hud_set_borgcell()
	logevent("System brought online.")

	alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER, ALARM_CAMERA, ALARM_BURGLAR, ALARM_MOTION), list(z))
	RegisterSignal(alert_control.listener, COMSIG_ALARM_TRIGGERED, .proc/alarm_triggered)
	RegisterSignal(alert_control.listener, COMSIG_ALARM_CLEARED, .proc/alarm_cleared)
	alert_control.listener.RegisterSignal(src, COMSIG_LIVING_DEATH, /datum/alarm_listener/proc/prevent_alarm_changes)
	alert_control.listener.RegisterSignal(src, COMSIG_LIVING_REVIVE, /datum/alarm_listener/proc/allow_alarm_changes)

/mob/living/silicon/robot/model/syndicate/Initialize(mapload)
	. = ..()
	laws = new /datum/ai_laws/syndicate_override()
	addtimer(CALLBACK(src, .proc/show_playstyle), 5)

/mob/living/silicon/robot/proc/create_modularInterface()
	if(!modularInterface)
		modularInterface = new /obj/item/modular_computer/tablet/integrated(src)
	modularInterface.layer = ABOVE_HUD_PLANE
	modularInterface.plane = ABOVE_HUD_PLANE

/mob/living/silicon/robot/model/syndicate/create_modularInterface()
	if(!modularInterface)
		modularInterface = new /obj/item/modular_computer/tablet/integrated/syndicate(src)
	return ..()

/**
 * Sets the tablet theme and icon
 *
 * These variables are based on if the borg is a syndicate type or is emagged. This gets used in model change code
 * and also borg emag code.
 */
/mob/living/silicon/robot/proc/set_modularInterface_theme()
	if(istype(model, /obj/item/robot_model/syndicate) || emagged)
		modularInterface.device_theme = "syndicate"
		modularInterface.icon_state = "tablet-silicon-syndicate"
		modularInterface.icon_state_powered = "tablet-silicon-syndicate"
		modularInterface.icon_state_unpowered = "tablet-silicon-syndicate"
	else
		modularInterface.device_theme = "ntos"
		modularInterface.icon_state = "tablet-silicon"
		modularInterface.icon_state_powered = "tablet-silicon"
		modularInterface.icon_state_unpowered = "tablet-silicon"
	modularInterface.update_icon()

//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
/mob/living/silicon/robot/Destroy()
	var/atom/T = drop_location()//To hopefully prevent run time errors.
	if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
		if(T)
			mmi.forceMove(T)
		if(mmi.brainmob)
			if(mmi.brainmob.stat == DEAD)
				mmi.brainmob.set_stat(CONSCIOUS)
			mind.transfer_to(mmi.brainmob)
			mmi.update_appearance()
		else
			to_chat(src, span_boldannounce("Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug."))
			ghostize()
			stack_trace("Borg MMI lacked a brainmob")
		mmi = null
	if(modularInterface)
		QDEL_NULL(modularInterface)
	if(connected_ai)
		set_connected_ai(null)
	if(shell)
		GLOB.available_ai_shells -= src
	else
		if(T && istype(radio) && istype(radio.keyslot))
			radio.keyslot.forceMove(T)
			radio.keyslot = null
	QDEL_NULL(wires)
	QDEL_NULL(model)
	QDEL_NULL(eye_lights)
	QDEL_NULL(inv1)
	QDEL_NULL(inv2)
	QDEL_NULL(inv3)
	QDEL_NULL(spark_system)
	QDEL_NULL(alert_control)
	cell = null
	return ..()

/mob/living/silicon/robot/Topic(href, href_list)
	. = ..()
	//Show alerts window if user clicked on "Show alerts" in chat
	if(href_list["showalerts"])
		alert_control.ui_interact(src)

/mob/living/silicon/robot/get_cell()
	return cell

/mob/living/silicon/robot/proc/pick_model()
	if(model.type != /obj/item/robot_model)
		return

	if(wires.is_cut(WIRE_RESET_MODEL))
		to_chat(src,span_userdanger("ERROR: Model installer reply timeout. Please check internal connections."))
		return

	var/list/model_list = list("Engineering" = /obj/item/robot_model/engineering, \
	"Medical" = /obj/item/robot_model/medical, \
	"Miner" = /obj/item/robot_model/miner, \
	"Janitor" = /obj/item/robot_model/janitor, \
	"Service" = /obj/item/robot_model/service)
	if(!CONFIG_GET(flag/disable_peaceborg))
		model_list["Peacekeeper"] = /obj/item/robot_model/peacekeeper
	if(!CONFIG_GET(flag/disable_secborg))
		model_list["Security"] = /obj/item/robot_model/security

	// Create radial menu for choosing borg model
	var/list/model_icons = list()
	for(var/option in model_list)
		var/obj/item/robot_model/model = model_list[option]
		var/model_icon = initial(model.cyborg_base_icon)
		model_icons[option] = image(icon = 'icons/mob/robots.dmi', icon_state = model_icon)

	var/input_model = show_radial_menu(src, src, model_icons, radius = 42)
	if(!input_model || model.type != /obj/item/robot_model)
		return

	model.transform_to(model_list[input_model])

/// Used to setup the a basic and (somewhat) unique name for the robot.
/mob/living/silicon/robot/proc/setup_default_name()
	var/new_name
	if(GLOB.current_anonymous_theme) //only robotic renames will allow for anything other than the anonymous one
		new_name = GLOB.current_anonymous_theme.anonymous_ai_name(FALSE)
	else if(custom_name)
		new_name = custom_name
	else
		new_name = get_standard_name()
	if(new_name != real_name)
		fully_replace_character_name(real_name, new_name)

/// Updates the borg name taking the client preferences into account.
/mob/living/silicon/robot/proc/updatename(client/pref_source)
	if(shell)
		return
	if(!pref_source)
		pref_source = client
	var/changed_name = ""
	if(GLOB.current_anonymous_theme) //only robotic renames will allow for anything other than the anonymous one
		changed_name = GLOB.current_anonymous_theme.anonymous_ai_name(FALSE)
	else if(custom_name)
		changed_name = custom_name
	else if(pref_source && pref_source.prefs.read_preference(/datum/preference/name/cyborg) != DEFAULT_CYBORG_NAME)
		apply_pref_name(/datum/preference/name/cyborg, pref_source)
		return //built in camera handled in proc
	else
		changed_name = get_standard_name()

	fully_replace_character_name(real_name, changed_name)

/mob/living/silicon/robot/proc/get_standard_name()
	return "[(designation ? "[designation] " : "")][mmi.braintype]-[ident]"

/mob/living/silicon/robot/proc/ionpulse()
	if(!ionpulse_on)
		return

	if(cell.charge <= 10)
		toggle_ionpulse()
		return

	cell.charge -= 10
	return TRUE

/mob/living/silicon/robot/proc/toggle_ionpulse()
	if(!ionpulse)
		to_chat(src, span_notice("No thrusters are installed!"))
		return

	if(!ion_trail)
		ion_trail = new
		ion_trail.set_up(src)

	ionpulse_on = !ionpulse_on
	to_chat(src, span_notice("You [ionpulse_on ? null :"de"]activate your ion thrusters."))
	if(ionpulse_on)
		ion_trail.start()
	else
		ion_trail.stop()

/mob/living/silicon/robot/get_status_tab_items()
	. = ..()
	. += ""
	if(cell)
		. += "Charge Left: [cell.charge]/[cell.maxcharge]"
	else
		. += "No Cell Inserted!"

	if(model)
		for(var/datum/robot_energy_storage/st in model.storages)
			. += "[st.name]: [st.energy]/[st.max_energy]"
	if(connected_ai)
		. += "Master AI: [connected_ai.name]"

/mob/living/silicon/robot/proc/alarm_triggered(datum/source, alarm_type, area/source_area)
	SIGNAL_HANDLER
	queueAlarm("--- [alarm_type] alarm detected in [source_area.name]!", alarm_type)

/mob/living/silicon/robot/proc/alarm_cleared(datum/source, alarm_type, area/source_area)
	SIGNAL_HANDLER
	queueAlarm("--- [alarm_type] alarm in [source_area.name] has been cleared.", alarm_type, FALSE)

/mob/living/silicon/robot/can_interact_with(atom/A)
	if (A == modularInterface)
		return TRUE //bypass for borg tablets
	if (low_power_mode)
		return FALSE
	return ..()

/mob/living/silicon/robot/proc/allowed(mob/M)
	//check if it doesn't require any access at all
	if(check_access(null))
		return TRUE
	if(ishuman(M))
		var/mob/living/carbon/human/H = M
		//if they are holding or wearing a card that has access, that works
		if(check_access(H.get_active_held_item()) || check_access(H.wear_id))
			return TRUE
	else if(isalien(M))
		var/mob/living/carbon/george = M
		//they can only hold things :(
		if(isitem(george.get_active_held_item()))
			return check_access(george.get_active_held_item())
	return FALSE

/mob/living/silicon/robot/proc/check_access(obj/item/card/id/I)
	if(!istype(req_access, /list)) //something's very wrong
		return TRUE

	var/list/L = req_access
	if(!L.len) //no requirements
		return TRUE

	if(!istype(I, /obj/item/card/id) && isitem(I))
		I = I.GetID()

	if(!I || !I.access) //not ID or no access
		return FALSE
	for(var/req in req_access)
		if(!(req in I.access)) //doesn't have this access
			return FALSE
	return TRUE

/mob/living/silicon/robot/regenerate_icons()
	return update_icons()

/mob/living/silicon/robot/update_icons()
	cut_overlays()
	SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
	icon_state = model.cyborg_base_icon
	if(stat != DEAD && !(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || IsStun() || IsParalyzed() || low_power_mode)) //Not dead, not stunned.
		if(!eye_lights)
			eye_lights = new()
		if(lamp_enabled || lamp_doom)
			eye_lights.icon_state = "[model.special_light_key ? "[model.special_light_key]":"[model.cyborg_base_icon]"]_l"
			eye_lights.color = lamp_doom? COLOR_RED : lamp_color
			eye_lights.plane = ABOVE_LIGHTING_PLANE //glowy eyes
		else
			eye_lights.icon_state = "[model.special_light_key ? "[model.special_light_key]":"[model.cyborg_base_icon]"]_e"
			eye_lights.color = COLOR_WHITE
			eye_lights.plane = GAME_PLANE
		eye_lights.icon = icon
		add_overlay(eye_lights)

	if(opened)
		if(wiresexposed)
			add_overlay("ov-opencover +w")
		else if(cell)
			add_overlay("ov-opencover +c")
		else
			add_overlay("ov-opencover -c")
	if(hat)
		var/mutable_appearance/head_overlay = hat.build_worn_icon(default_layer = 20, default_icon_file = 'icons/mob/clothing/head.dmi')
		head_overlay.pixel_y += hat_offset
		add_overlay(head_overlay)
	update_fire()

/mob/living/silicon/robot/proc/self_destruct(mob/usr)
	var/turf/groundzero = get_turf(src)
	message_admins(span_notice("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(src, client)] at [ADMIN_VERBOSEJMP(groundzero)]!"))
	log_game("[key_name(usr)] detonated [key_name(src)]!")
	log_combat(usr, src, "detonated cyborg")
	if(connected_ai)
		to_chat(connected_ai, "

[span_alert("ALERT - Cyborg detonation detected: [name]")]
")

	if(emagged)
		QDEL_NULL(mmi)
		explosion(src, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 4, flame_range = 2)
	else
		explosion(src, devastation_range = -1, light_impact_range = 2)
	gib()

/mob/living/silicon/robot/proc/UnlinkSelf()
	set_connected_ai(null)
	lawupdate = FALSE
	set_lockcharge(FALSE)
	scrambledcodes = TRUE
	//Disconnect it's camera so it's not so easily tracked.
	if(!QDELETED(builtInCamera))
		QDEL_NULL(builtInCamera)
		// I'm trying to get the Cyborg to not be listed in the camera list
		// Instead of being listed as "deactivated". The downside is that I'm going
		// to have to check if every camera is null or not before doing anything, to prevent runtime errors.
		// I could change the network to null but I don't know what would happen, and it seems too hacky for me.

/mob/living/silicon/robot/mode()
	set name = "Activate Held Object"
	set category = "IC"
	set src = usr

	if(incapacitated())
		return
	var/obj/item/W = get_active_held_item()
	if(W)
		W.attack_self(src)

/mob/living/silicon/robot/proc/SetLockdown(state = TRUE)
	// They stay locked down if their wire is cut.
	if(wires?.is_cut(WIRE_LOCKDOWN))
		state = TRUE
	if(state)
		throw_alert("locked", /atom/movable/screen/alert/locked)
	else
		clear_alert("locked")
	set_lockcharge(state)

///Reports the event of the change in value of the lockcharge variable.
/mob/living/silicon/robot/proc/set_lockcharge(new_lockcharge)
	if(new_lockcharge == lockcharge)
		return
	. = lockcharge
	lockcharge = new_lockcharge
	if(lockcharge)
		if(!.)
			ADD_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT)
	else if(.)
		REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT)
	logevent("System lockdown [lockcharge?"triggered":"released"].")

/mob/living/silicon/robot/proc/SetEmagged(new_state)
	emagged = new_state
	model.rebuild_modules()
	update_icons()
	if(emagged)
		throw_alert("hacked", /atom/movable/screen/alert/hacked)
	else
		clear_alert("hacked")
	set_modularInterface_theme()

/// Special handling for getting hit with a light eater
/mob/living/silicon/robot/proc/on_light_eater(mob/living/silicon/robot/source, datum/light_eater)
	SIGNAL_HANDLER
	if(lamp_enabled)
		smash_headlamp()
	return COMPONENT_BLOCK_LIGHT_EATER

/**
 * Handles headlamp smashing
 *
 * When called (such as by the shadowperson lighteater's attack), this proc will break the borg's headlamp
 * and then call toggle_headlamp to disable the light. It also plays a sound effect of glass breaking, and
 * tells the borg what happened to its chat. Broken lights can be repaired by using a flashlight on the borg.
 */
/mob/living/silicon/robot/proc/smash_headlamp()
	if(!lamp_functional)
		return
	lamp_functional = FALSE
	playsound(src, 'sound/effects/glass_step.ogg', 50)
	toggle_headlamp(TRUE)
	to_chat(src, span_danger("Your headlamp is broken! You'll need a human to help replace it."))

/**
 * Handles headlamp toggling, disabling, and color setting.
 *
 * The initial if statment is a bit long, but the gist of it is that should the lamp be on AND the update_color
 * arg be true, we should simply change the color of the lamp but not disable it. Otherwise, should the turn_off
 * arg be true, the lamp already be enabled, any of the normal reasons the lamp would turn off happen, or the
 * update_color arg be passed with the lamp not on, we should set the lamp off. The update_color arg is only
 * ever true when this proc is called from the borg tablet, when the color selection feature is used.
 *
 * Arguments:
 * * arg1 - turn_off, if enabled will force the lamp into an off state (rather than toggling it if possible)
 * * arg2 - update_color, if enabled, will adjust the behavior of the proc to change the color of the light if it is already on.
 */
/mob/living/silicon/robot/proc/toggle_headlamp(turn_off = FALSE, update_color = FALSE)
	//if both lamp is enabled AND the update_color flag is on, keep the lamp on. Otherwise, if anything listed is true, disable the lamp.
	if(!(update_color && lamp_enabled) && (turn_off || lamp_enabled || update_color || !lamp_functional || stat || low_power_mode))
		set_light_on(lamp_functional && stat != DEAD && lamp_doom) //If the lamp isn't broken and borg isn't dead, doomsday borgs cannot disable their light fully.
		set_light_color(COLOR_RED) //This should only matter for doomsday borgs, as any other time the lamp will be off and the color not seen
		set_light_range(1) //Again, like above, this only takes effect when the light is forced on by doomsday mode.
		lamp_enabled = FALSE
		lampButton?.update_appearance()
		update_icons()
		return
	set_light_range(lamp_intensity)
	set_light_color(lamp_doom? COLOR_RED : lamp_color) //Red for doomsday killborgs, borg's choice otherwise
	set_light_on(TRUE)
	lamp_enabled = TRUE
	lampButton?.update_appearance()
	update_icons()

/mob/living/silicon/robot/proc/deconstruct()
	SEND_SIGNAL(src, COMSIG_BORG_SAFE_DECONSTRUCT)
	var/turf/T = get_turf(src)
	if (robot_suit)
		robot_suit.forceMove(T)
		robot_suit.l_leg.forceMove(T)
		robot_suit.l_leg = null
		robot_suit.r_leg.forceMove(T)
		robot_suit.r_leg = null
		new /obj/item/stack/cable_coil(T, robot_suit.chest.wired)
		robot_suit.chest.forceMove(T)
		robot_suit.chest.wired = FALSE
		robot_suit.chest = null
		robot_suit.l_arm.forceMove(T)
		robot_suit.l_arm = null
		robot_suit.r_arm.forceMove(T)
		robot_suit.r_arm = null
		robot_suit.head.forceMove(T)
		robot_suit.head.flash1.forceMove(T)
		robot_suit.head.flash1.burn_out()
		robot_suit.head.flash1 = null
		robot_suit.head.flash2.forceMove(T)
		robot_suit.head.flash2.burn_out()
		robot_suit.head.flash2 = null
		robot_suit.head = null
		robot_suit.update_appearance()
	else
		new /obj/item/robot_suit(T)
		new /obj/item/bodypart/l_leg/robot(T)
		new /obj/item/bodypart/r_leg/robot(T)
		new /obj/item/stack/cable_coil(T, 1)
		new /obj/item/bodypart/chest/robot(T)
		new /obj/item/bodypart/l_arm/robot(T)
		new /obj/item/bodypart/r_arm/robot(T)
		new /obj/item/bodypart/head/robot(T)
		var/b
		for(b=0, b!=2, b++)
			var/obj/item/assembly/flash/handheld/F = new /obj/item/assembly/flash/handheld(T)
			F.burn_out()
	if (cell) //Sanity check.
		cell.forceMove(T)
		cell = null
	qdel(src)

/mob/living/silicon/robot/proc/notify_ai(notifytype, oldname, newname)
	if(!connected_ai)
		return
	switch(notifytype)
		if(NEW_BORG) //New Cyborg
			to_chat(connected_ai, "

[span_notice("NOTICE - New cyborg connection detected: [name]")]
")
		if(NEW_MODEL) //New Model
			to_chat(connected_ai, "

[span_notice("NOTICE - Cyborg model change detected: [name] has loaded the [designation] model.")]
")
		if(RENAME) //New Name
			to_chat(connected_ai, "

[span_notice("NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].")]
")
		if(AI_SHELL) //New Shell
			to_chat(connected_ai, "

[span_notice("NOTICE - New cyborg shell detected: [name]")]
")
		if(DISCONNECT) //Tampering with the wires
			to_chat(connected_ai, "

[span_notice("NOTICE - Remote telemetry lost with [name].")]
")

/mob/living/silicon/robot/canUseTopic(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE, need_hands = FALSE, floor_okay=FALSE)
	if(lockcharge || low_power_mode)
		to_chat(src, span_warning("You can't do that right now!"))
		return FALSE
	return ..()

/mob/living/silicon/robot/updatehealth()
	..()
	if(!model.breakable_modules)
		return

	/// the current percent health of the robot (-1 to 1)
	var/percent_hp = health/maxHealth
	if(health <= previous_health) //if change in health is negative (we're losing hp)
		if(percent_hp <= 0.5)
			break_cyborg_slot(3)

		if(percent_hp <= 0)
			break_cyborg_slot(2)

		if(percent_hp <= -0.5)
			break_cyborg_slot(1)

	else //if change in health is positive (we're gaining hp)
		if(percent_hp >= 0.5)
			repair_cyborg_slot(3)

		if(percent_hp >= 0)
			repair_cyborg_slot(2)

		if(percent_hp >= -0.5)
			repair_cyborg_slot(1)

	previous_health = health

/mob/living/silicon/robot/update_sight()
	if(!client)
		return
	if(stat == DEAD)
		if(SSmapping.level_trait(z, ZTRAIT_NOXRAY))
			sight = null
		else if(is_secret_level(z))
			sight = initial(sight)
		else
			sight = (SEE_TURFS|SEE_MOBS|SEE_OBJS)
		see_in_dark = 8
		see_invisible = SEE_INVISIBLE_OBSERVER
		return

	see_invisible = initial(see_invisible)
	see_in_dark = initial(see_in_dark)
	sight = initial(sight)
	lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE

	if(client.eye != src)
		var/atom/A = client.eye
		if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates.
			return

	if(sight_mode & BORGMESON)
		sight |= SEE_TURFS
		lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
		see_in_dark = 1

	if(sight_mode & BORGMATERIAL)
		sight |= SEE_OBJS
		lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
		see_in_dark = 1

	if(sight_mode & BORGXRAY)
		sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
		see_invisible = SEE_INVISIBLE_LIVING
		see_in_dark = 8

	if(sight_mode & BORGTHERM)
		sight |= SEE_MOBS
		lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
		see_invisible = min(see_invisible, SEE_INVISIBLE_LIVING)
		see_in_dark = 8

	if(see_override)
		see_invisible = see_override

	if(SSmapping.level_trait(z, ZTRAIT_NOXRAY))
		sight = null

	sync_lighting_plane_alpha()

/mob/living/silicon/robot/update_stat()
	if(status_flags & GODMODE)
		return
	if(stat != DEAD)
		if(health <= -maxHealth) //die only once
			death()
			toggle_headlamp(1)
			return
		if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || IsStun() || IsKnockdown() || IsParalyzed())
			set_stat(UNCONSCIOUS)
		else
			set_stat(CONSCIOUS)
	diag_hud_set_status()
	diag_hud_set_health()
	diag_hud_set_aishell()
	update_health_hud()
	update_icons() //Updates eye_light overlay

/mob/living/silicon/robot/revive(full_heal = FALSE, admin_revive = FALSE)
	if(..()) //successfully ressuscitated from death
		if(!QDELETED(builtInCamera) && !wires.is_cut(WIRE_CAMERA))
			builtInCamera.toggle_cam(src,0)
		if(admin_revive)
			locked = TRUE
		notify_ai(NEW_BORG)
		. = TRUE
		toggle_headlamp(FALSE, TRUE) //This will reenable borg headlamps if doomsday is currently going on still.

/mob/living/silicon/robot/fully_replace_character_name(oldname, newname)
	. = ..()
	if(!.)
		return
	notify_ai(RENAME, oldname, newname)
	if(!QDELETED(builtInCamera))
		builtInCamera.c_tag = real_name
	custom_name = newname

/mob/living/silicon/robot/proc/ResetModel()
	SEND_SIGNAL(src, COMSIG_BORG_SAFE_DECONSTRUCT)
	uneq_all()
	shown_robot_modules = FALSE
	if(hud_used)
		hud_used.update_robot_modules_display()

	if (hasExpanded)
		resize = 0.5
		hasExpanded = FALSE
		update_transform()
	logevent("Chassis model has been reset.")
	model.transform_to(/obj/item/robot_model)

	// Remove upgrades.
	for(var/obj/item/borg/upgrade/I in upgrades)
		I.forceMove(get_turf(src))

	ionpulse = FALSE
	revert_shell()

	return TRUE

/mob/living/silicon/robot/model/syndicate/ResetModel()
	return

/mob/living/silicon/robot/proc/has_model()
	if(!model || model.type == /obj/item/robot_model)
		. = FALSE
	else
		. = TRUE

/mob/living/silicon/robot/proc/update_module_innate()
	designation = model.name
	if(hands)
		hands.icon_state = model.model_select_icon

	REMOVE_TRAITS_IN(src, MODEL_TRAIT)
	if(model.model_traits)
		for(var/trait in model.model_traits)
			ADD_TRAIT(src, trait, MODEL_TRAIT)

	if(model.clean_on_move)
		AddElement(/datum/element/cleaning)
	else
		RemoveElement(/datum/element/cleaning)

	hat_offset = model.hat_offset

	magpulse = model.magpulsing
	INVOKE_ASYNC(src, .proc/updatename)

/mob/living/silicon/robot/proc/place_on_head(obj/item/new_hat)
	if(hat)
		hat.forceMove(get_turf(src))
	hat = new_hat
	new_hat.forceMove(src)
	update_icons()

/**
	*Checking Exited() to detect if a hat gets up and walks off.
	*Drones and pAIs might do this, after all.
*/
/mob/living/silicon/robot/Exited(atom/movable/gone, direction)
	if(hat && hat == gone)
		hat = null
		if(!QDELETED(src)) //Don't update icons if we are deleted.
			update_icons()
	return ..()

///Use this to add upgrades to robots. It'll register signals for when the upgrade is moved or deleted, if not single use.
/mob/living/silicon/robot/proc/add_to_upgrades(obj/item/borg/upgrade/new_upgrade, mob/user)
	if(new_upgrade in upgrades)
		return FALSE
	if(!user.temporarilyRemoveItemFromInventory(new_upgrade)) //calling the upgrade's dropped() proc /before/ we add action buttons
		return FALSE
	if(!new_upgrade.action(src, user))
		to_chat(user, span_danger("Upgrade error."))
		new_upgrade.forceMove(loc) //gets lost otherwise
		return FALSE
	to_chat(user, span_notice("You apply the upgrade to [src]."))
	to_chat(src, "----------------\nNew hardware detected...Identified as \"[new_upgrade]\"...Setup complete.\n----------------")
	if(new_upgrade.one_use)
		logevent("Firmware [new_upgrade] run successfully.")
		qdel(new_upgrade)
		return FALSE
	upgrades += new_upgrade
	new_upgrade.forceMove(src)
	RegisterSignal(new_upgrade, COMSIG_MOVABLE_MOVED, .proc/remove_from_upgrades)
	RegisterSignal(new_upgrade, COMSIG_PARENT_QDELETING, .proc/on_upgrade_deleted)
	logevent("Hardware [new_upgrade] installed successfully.")

///Called when an upgrade is moved outside the robot. So don't call this directly, use forceMove etc.
/mob/living/silicon/robot/proc/remove_from_upgrades(obj/item/borg/upgrade/old_upgrade)
	SIGNAL_HANDLER
	if(loc == src)
		return
	old_upgrade.deactivate(src)
	upgrades -= old_upgrade
	UnregisterSignal(old_upgrade, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))

///Called when an applied upgrade is deleted.
/mob/living/silicon/robot/proc/on_upgrade_deleted(obj/item/borg/upgrade/old_upgrade)
	SIGNAL_HANDLER
	if(!QDELETED(src))
		old_upgrade.deactivate(src)
	upgrades -= old_upgrade
	UnregisterSignal(old_upgrade, list(COMSIG_MOVABLE_MOVED, COMSIG_PARENT_QDELETING))

/**
 * make_shell: Makes an AI shell out of a cyborg unit
 *
 * Arguments:
 * * board - B.O.R.I.S. module board used for transforming the cyborg into AI shell
 */
/mob/living/silicon/robot/proc/make_shell(obj/item/borg/upgrade/ai/board)
	if(!board)
		upgrades |= new /obj/item/borg/upgrade/ai(src)
	shell = TRUE
	braintype = "AI Shell"
	name = "Empty AI Shell-[ident]"
	real_name = name
	GLOB.available_ai_shells |= src
	if(!QDELETED(builtInCamera))
		builtInCamera.c_tag = real_name //update the camera name too
	diag_hud_set_aishell()

/**
 * revert_shell: Reverts AI shell back into a normal cyborg unit
 */
/mob/living/silicon/robot/proc/revert_shell()
	if(!shell)
		return
	undeploy()
	for(var/obj/item/borg/upgrade/ai/boris in src)
	//A player forced reset of a borg would drop the module before this is called, so this is for catching edge cases
		qdel(boris)
	shell = FALSE
	GLOB.available_ai_shells -= src
	name = "Unformatted Cyborg-[ident]"
	real_name = name
	if(!QDELETED(builtInCamera))
		builtInCamera.c_tag = real_name
	diag_hud_set_aishell()

/**
 * deploy_init: Deploys AI unit into AI shell
 *
 * Arguments:
 * * AI - AI unit that initiated the deployment into the AI shell
 */
/mob/living/silicon/robot/proc/deploy_init(mob/living/silicon/ai/AI)
	real_name = "[AI.real_name] [designation] Shell-[ident]"
	name = real_name
	if(!QDELETED(builtInCamera))
		builtInCamera.c_tag = real_name //update the camera name too
	mainframe = AI
	deployed = TRUE
	set_connected_ai(mainframe)
	mainframe.connected_robots |= src
	lawupdate = TRUE
	lawsync()
	if(radio && AI.radio) //AI keeps all channels, including Syndie if it is a Traitor
		if(AI.radio.syndie)
			radio.make_syndie()
		radio.subspace_transmission = TRUE
		radio.channels = AI.radio.channels
		for(var/chan in radio.channels)
			radio.secure_radio_connections[chan] = add_radio(radio, GLOB.radiochannels[chan])

	diag_hud_set_aishell()
	undeployment_action.Grant(src)

/datum/action/innate/undeployment
	name = "Disconnect from shell"
	desc = "Stop controlling your shell and resume normal core operations."
	icon_icon = 'icons/mob/actions/actions_AI.dmi'
	button_icon_state = "ai_core"

/datum/action/innate/undeployment/Trigger()
	if(!..())
		return FALSE
	var/mob/living/silicon/robot/R = owner

	R.undeploy()
	return TRUE

/mob/living/silicon/robot/proc/undeploy()
	if(!deployed || !mind || !mainframe)
		return
	mainframe.redeploy_action.Grant(mainframe)
	mainframe.redeploy_action.last_used_shell = src
	mind.transfer_to(mainframe)
	deployed = FALSE
	mainframe.deployed_shell = null
	undeployment_action.Remove(src)
	if(radio) //Return radio to normal
		radio.recalculateChannels()
	if(!QDELETED(builtInCamera))
		builtInCamera.c_tag = real_name //update the camera name too
	diag_hud_set_aishell()
	mainframe.diag_hud_set_deployed()
	if(mainframe.laws)
		mainframe.laws.show_laws(mainframe) //Always remind the AI when switching
	if(mainframe.eyeobj)
		mainframe.eyeobj.setLoc(loc)
	mainframe = null

/mob/living/silicon/robot/attack_ai(mob/user)
	if(shell && (!connected_ai || connected_ai == user))
		var/mob/living/silicon/ai/AI = user
		AI.deploy_to_shell(src)

/mob/living/silicon/robot/mouse_buckle_handling(mob/living/M, mob/living/user)
	//Don't try buckling on INTENT_HARM so that silicons can search people's inventories without loading them
	if(can_buckle && isliving(user) && isliving(M) && !(M in buckled_mobs) && ((user != src) || (!combat_mode)))
		return user_buckle_mob(M, user, check_loc = FALSE)

/mob/living/silicon/robot/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE, buckle_mob_flags= RIDER_NEEDS_ARM)
	if(!is_type_in_typecache(M, can_ride_typecache))
		M.visible_message(span_warning("[M] really can't seem to mount [src]..."))
		return

	if(stat || incapacitated())
		return
	if(model && !model.allow_riding)
		M.visible_message(span_boldwarning("Unfortunately, [M] just can't seem to hold onto [src]!"))
		return

	buckle_mob_flags= RIDER_NEEDS_ARM // just in case
	return ..()

/mob/living/silicon/robot/resist()
	. = ..()
	if(!has_buckled_mobs())
		return
	for(var/i in buckled_mobs)
		var/mob/unbuckle_me_now = i
		unbuckle_mob(unbuckle_me_now, FALSE)

/mob/living/silicon/robot/proc/TryConnectToAI()
	set_connected_ai(select_active_ai_with_fewest_borgs(z))
	if(connected_ai)
		lawsync()
		lawupdate = TRUE
		return TRUE
	picturesync()
	return FALSE

/mob/living/silicon/robot/proc/picturesync()
	if(connected_ai?.aicamera && aicamera)
		for(var/i in aicamera.stored)
			connected_ai.aicamera.stored[i] = TRUE
		for(var/i in connected_ai.aicamera.stored)
			aicamera.stored[i] = TRUE

/mob/living/silicon/robot/proc/charge(datum/source, amount, repairs)
	SIGNAL_HANDLER
	if(model)
		model.respawn_consumable(src, amount * 0.005)
	if(cell)
		cell.charge = min(cell.charge + amount, cell.maxcharge)
	if(repairs)
		heal_bodypart_damage(repairs, repairs - 1)

/mob/living/silicon/robot/proc/set_connected_ai(new_ai)
	if(connected_ai == new_ai)
		return
	. = connected_ai
	connected_ai = new_ai
	if(.)
		var/mob/living/silicon/ai/old_ai = .
		old_ai.connected_robots -= src
	lamp_doom = FALSE
	if(connected_ai)
		connected_ai.connected_robots |= src
		lamp_doom = connected_ai.doomsday_device ? TRUE : FALSE
	toggle_headlamp(FALSE, TRUE)

/**
 * Records an IC event log entry in the cyborg's internal tablet.
 *
 * Creates an entry in the borglog list of the cyborg's internal tablet, listing the current
 * in-game time followed by the message given. These logs can be seen by the cyborg in their
 * BorgUI tablet app. By design, logging fails if the cyborg is dead.
 *
 * Arguments:
 * arg1: a string containing the message to log.
 */
/mob/living/silicon/robot/proc/logevent(string = "")
	if(!string)
		return
	if(stat == DEAD) //Dead borgs log no longer
		return
	if(!modularInterface)
		stack_trace("Cyborg [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.")
		create_modularInterface()
	modularInterface.borglog += "[station_time_timestamp()] - [string]"
	var/datum/computer_file/program/robotact/program = modularInterface.get_robotact()
	if(program)
		program.force_full_update()

/mob/living/silicon/robot/get_exp_list(minutes)
	. = ..()

	var/datum/job/cyborg/cyborg_job_ref = SSjob.GetJobType(/datum/job/cyborg)

	.[cyborg_job_ref.title] = minutes


code:
GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't really work on borgos
	/obj/item/clothing/head/helmet/space,
	/obj/item/clothing/head/welding,
	/obj/item/clothing/head/chameleon/broken \
	)))

/mob/living/silicon/robot/attackby(obj/item/W, mob/living/user, params)
	//if(W.tool_behaviour == TOOL_WELDER && (!user.combat_mode || user == src)) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT BEGIN
	if(W.tool_behaviour == TOOL_WELDER && (IS_HELP_INTENT(user, params2list(params)) || user == src))
	//SEPTIC EDIT END
		user.changeNext_move(CLICK_CD_MELEE)
		if (!getBruteLoss())
			to_chat(user, span_warning("[src] is already in good condition!"))
			return
		if (!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
			return
		if(src == user)
			to_chat(user, span_notice("You start fixing yourself..."))
			if(!W.use_tool(src, user, 50))
				return

		adjustBruteLoss(-30)
		add_fingerprint(user)
		visible_message(span_notice("[user] fixes some of the dents on [src]."))
		return

	if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
		user.changeNext_move(CLICK_CD_MELEE)
		var/obj/item/stack/cable_coil/coil = W
		if (getFireLoss() > 0 || getToxLoss() > 0)
			if(src == user)
				to_chat(user, span_notice("You start fixing yourself..."))
				if(!do_after(user, 50, target = src))
					return
			if (coil.use(1))
				adjustFireLoss(-30)
				user.visible_message(span_notice("[user] fixes some of the burnt wires on [src]."), span_notice("You fix some of the burnt wires on [src]."))
			else
				to_chat(user, span_warning("You need more cable to repair [src]!"))
		else
			to_chat(user, span_warning("The wires seem fine, there's no need to fix them."))
		return

	if(W.tool_behaviour == TOOL_CROWBAR) // crowbar means open or close the cover
		if(opened)
			to_chat(user, span_notice("You close the cover."))
			opened = FALSE
			update_icons()
		else
			if(locked)
				to_chat(user, span_warning("The cover is locked and cannot be opened!"))
			else
				to_chat(user, span_notice("You open the cover."))
				opened = TRUE
				update_icons()
		return

	if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
		if(wiresexposed)
			to_chat(user, span_warning("Close the cover first!"))
		else if(cell)
			to_chat(user, span_warning("There is a power cell already installed!"))
		else
			if(!user.transferItemToLoc(W, src))
				return
			cell = W
			to_chat(user, span_notice("You insert the power cell."))
		update_icons()
		diag_hud_set_borgcell()
		return

	if(is_wire_tool(W))
		if (wiresexposed)
			wires.interact(user)
		else
			to_chat(user, span_warning("You can't reach the wiring!"))
		return

	if(W.tool_behaviour == TOOL_SCREWDRIVER && opened) // wire hacking or radio management
		if(!cell) //haxing
			wiresexposed = !wiresexposed
			to_chat(user, span_notice("The wires have been [wiresexposed ? "exposed" : "unexposed"]."))
		else //radio
			if(shell)
				to_chat(user, span_warning("You cannot seem to open the radio compartment!")) //Prevent AI radio key theft
			else if(radio)
				radio.attackby(W,user)//Push it to the radio to let it handle everything
			else
				to_chat(user, span_warning("Unable to locate a radio!"))
		update_icons()
		return

	if(W.tool_behaviour == TOOL_WRENCH && opened && !cell) //Deconstruction. The flashes break from the fall, to prevent this from being a ghetto reset module.
		if(!lockcharge)
			to_chat(user, span_warning("[src]'s bolts spark! Maybe you should lock them down first!"))
			spark_system.start()
			return
		to_chat(user, span_notice("You start to unfasten [src]'s securing bolts..."))
		if(W.use_tool(src, user, 50, volume=50) && !cell)
			user.visible_message(span_notice("[user] deconstructs [src]!"), span_notice("You unfasten the securing bolts, and [src] falls to pieces!"))
			deconstruct()
		return

	//if(W.slot_flags & ITEM_SLOT_HEAD && hat_offset != INFINITY && !user.combat_mode && !is_type_in_typecache(W, GLOB.blacklisted_borg_hats)) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT BEGIN
	if(W.slot_flags & ITEM_SLOT_HEAD && hat_offset != INFINITY && IS_HELP_INTENT(user, params2list(params)) && !is_type_in_typecache(W, GLOB.blacklisted_borg_hats))
	//SEPTIC EDIT END
		if(user == src)
			to_chat(user,  span_notice("You can't seem to manage to place [W] on your head by yourself!") )
			return
		if(hat && HAS_TRAIT(hat, TRAIT_NODROP))
			to_chat(user, span_warning("You can't seem to remove [src]'s existing headwear!"))
			return
		to_chat(user, span_notice("You begin to place [W] on [src]'s head..."))
		to_chat(src, span_notice("[user] is placing [W] on your head..."))
		if(do_after(user, 30, target = src))
			if (user.temporarilyRemoveItemFromInventory(W, TRUE))
				place_on_head(W)
		return
	//if(istype(W, /obj/item/defibrillator) && !user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT BEGIN
	if(istype(W, /obj/item/defibrillator) && IS_HELP_INTENT(user, params2list(params)))
	//SEPTIC EDIT END
		if(!opened)
			to_chat(user, span_warning("You must access the cyborg's internals!"))
			return
		if(!istype(model, /obj/item/robot_model/medical))
			to_chat(user, span_warning("[src] does not have correct mounting points for a defibrillator!"))
			return
		if(stat == DEAD)
			to_chat(user, span_warning("This defibrillator unit will not function on a deceased cyborg!"))
			return
		var/obj/item/defibrillator/D = W
		if(D.slot_flags != ITEM_SLOT_BACK) //belt defibs need not apply
			to_chat(user, span_warning("This defibrillator unit doesn't seem to fit correctly!"))
			return
		if(D.cell)
			to_chat(user, span_warning("You cannot connect the defibrillator to the cyborg power supply with the defibrillator's cell in the way!"))
			return
		if(locate(/obj/item/borg/upgrade/defib) in src || locate(/obj/item/borg/upgrade/defib/backpack) in src)
			to_chat(user, span_warning("[src] already has a defibrillator!"))
			return
		var/obj/item/borg/upgrade/defib/backpack/B = new(null, D)
		add_to_upgrades(B, user)
		return

	if(istype(W, /obj/item/ai_module))
		var/obj/item/ai_module/MOD = W
		if(!opened)
			to_chat(user, span_warning("You need access to the robot's insides to do that!"))
			return
		if(wiresexposed)
			to_chat(user, span_warning("You need to close the wire panel to do that!"))
			return
		if(!cell)
			to_chat(user, span_warning("You need to install a power cell to do that!"))
			return
		if(shell) //AI shells always have the laws of the AI
			to_chat(user, span_warning("[src] is controlled remotely! You cannot upload new laws this way!"))
			return
		if(emagged || (connected_ai && lawupdate)) //Can't be sure which, metagamers
			emote("buzz-[user.name]")
			return
		if(!mind) //A player mind is required for law procs to run antag checks.
			to_chat(user, span_warning("[src] is entirely unresponsive!"))
			return
		MOD.install(laws, user) //Proc includes a success mesage so we don't need another one
		return

	if(istype(W, /obj/item/encryptionkey/) && opened)
		if(radio)//sanityyyyyy
			radio.attackby(W,user)//GTFO, you have your own procs
		else
			to_chat(user, span_warning("Unable to locate a radio!"))
		return

	if (W.GetID()) // trying to unlock the interface with an ID card
		if(opened)
			to_chat(user, span_warning("You must close the cover to swipe an ID card!"))
		else
			if(allowed(usr))
				locked = !locked
				to_chat(user, span_notice("You [ locked ? "lock" : "unlock"] [src]'s cover."))
				update_icons()
				if(emagged)
					to_chat(user, span_notice("The cover interface glitches out for a split second."))
					logevent("ChÃ¥vÃis cover lock has been [locked ? "engaged" : "released"]") //ChÃ¥vÃis: see above line
				else
					logevent("Chassis cover lock has been [locked ? "engaged" : "released"]")
			else
				to_chat(user, span_danger("Access denied."))
		return

	if(istype(W, /obj/item/borg/upgrade/))
		var/obj/item/borg/upgrade/U = W
		if(!opened)
			to_chat(user, span_warning("You must access the cyborg's internals!"))
			return
		if(!src.model && U.require_model)
			to_chat(user, span_warning("The cyborg must choose a model before it can be upgraded!"))
			return
		if(U.locked)
			to_chat(user, span_warning("The upgrade is locked and cannot be used yet!"))
			return
		if(!user.canUnEquip(U))
			to_chat(user, span_warning("The upgrade is stuck to you and you can't seem to let go of it!"))
			return
		add_to_upgrades(U, user)
		return

	if(istype(W, /obj/item/toner))
		if(toner >= tonermax)
			to_chat(user, span_warning("The toner level of [src] is at its highest level possible!"))
			return
		if(!user.temporarilyRemoveItemFromInventory(W))
			return
		toner = tonermax
		qdel(W)
		to_chat(user, span_notice("You fill the toner level of [src] to its max capacity."))
		return

	if(istype(W, /obj/item/flashlight))
		if(!opened)
			to_chat(user, span_warning("You need to open the panel to repair the headlamp!"))
			return
		if(lamp_functional)
			to_chat(user, span_warning("The headlamp is already functional!"))
			return
		if(!user.temporarilyRemoveItemFromInventory(W))
			to_chat(user, span_warning("[W] seems to be stuck to your hand. You'll have to find a different light."))
			return
		lamp_functional = TRUE
		qdel(W)
		to_chat(user, span_notice("You replace the headlamp bulbs."))
		return

	if(istype(W, /obj/item/computer_hardware/hard_drive/portable)) //Allows borgs to install new programs with human help
		if(!modularInterface)
			stack_trace("Cyborg [src] ( [type] ) was somehow missing their integrated tablet. Please make a bug report.")
			create_modularInterface()
		var/obj/item/computer_hardware/hard_drive/portable/floppy = W
		if(modularInterface.install_component(floppy, user))
			return

	if(W.force && W.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt.
		spark_system.start()
	return ..()

/mob/living/silicon/robot/attack_alien(mob/living/carbon/alien/humanoid/user, list/modifiers)
	if (LAZYACCESS(modifiers, RIGHT_CLICK))
		if(body_position == STANDING_UP)
			user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
			var/obj/item/I = get_active_held_item()
			if(I)
				uneq_active()
				visible_message(span_danger("[user] disarmed [src]!"), \
					span_userdanger("[user] has disabled [src]'s active module!"), null, COMBAT_MESSAGE_RANGE)
				log_combat(user, src, "disarmed", "[I ? " removing \the [I]" : ""]")
			else
				Stun(40)
				step(src,get_dir(user,src))
				log_combat(user, src, "pushed")
				visible_message(span_danger("[user] forces back [src]!"), \
					span_userdanger("[user] forces back [src]!"), null, COMBAT_MESSAGE_RANGE)
			playsound(loc, 'sound/weapons/pierce.ogg', 50, TRUE, -1)
	else
		..()
	return

/mob/living/silicon/robot/attack_slime(mob/living/simple_animal/slime/M)
	if(..()) //successful slime shock
		flash_act()
		var/stunprob = M.powerlevel * 7 + 10
		if(prob(stunprob) && M.powerlevel >= 8)
			adjustBruteLoss(M.powerlevel * rand(6,10))

	var/damage = rand(1, 3)

	if(M.is_adult)
		damage = rand(20, 40)
	else
		damage = rand(5, 35)
	damage = round(damage / 2) // borgs receive half damage
	adjustBruteLoss(damage)

	return

//ATTACK HAND IGNORING PARENT RETURN VALUE
/mob/living/silicon/robot/attack_hand(mob/living/carbon/human/user, list/modifiers)
	add_fingerprint(user)
	if(opened && !wiresexposed && !issilicon(user))
		if(cell)
			cell.update_appearance()
			cell.add_fingerprint(user)
			user.put_in_active_hand(cell)
			to_chat(user, span_notice("You remove \the [cell]."))
			cell = null
			update_icons()
			diag_hud_set_borgcell()
	else if(!opened)
		..()

/mob/living/silicon/robot/attack_hulk(mob/living/carbon/human/user)
	. = ..()
	if(!.)
		return
	spark_system.start()
	step_away(src, user, 15)
	addtimer(CALLBACK(GLOBAL_PROC, .proc/_step_away, src, get_turf(user), 15), 3)

/mob/living/silicon/robot/fire_act()
	if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them
		IgniteMob()

/mob/living/silicon/robot/emp_act(severity)
	. = ..()
	if(. & EMP_PROTECT_SELF)
		return
	switch(severity)
		if(1)
			Stun(160)
		if(2)
			Stun(60)

/mob/living/silicon/robot/emag_act(mob/user)
	if(user == src)//To prevent syndieborgs from emagging themselves
		return
	if(!opened)//Cover is closed
		if(locked)
			to_chat(user, span_notice("You emag the cover lock."))
			locked = FALSE
			if(shell) //A warning to Traitors who may not know that emagging AI shells does not slave them.
				to_chat(user, span_boldwarning("[src] seems to be controlled remotely! Emagging the interface may not work as expected."))
		else
			to_chat(user, span_warning("The cover is already unlocked!"))
		return
	if(world.time < emag_cooldown)
		return
	if(wiresexposed)
		to_chat(user, span_warning("You must unexpose the wires first!"))
		return

	to_chat(user, span_notice("You emag [src]'s interface."))
	emag_cooldown = world.time + 100

	if(connected_ai && connected_ai.mind && connected_ai.mind.has_antag_datum(/datum/antagonist/malf_ai))
		to_chat(src, span_danger("ALERT: Foreign software execution prevented."))
		logevent("ALERT: Foreign software execution prevented.")
		to_chat(connected_ai, span_danger("ALERT: Cyborg unit \[[src]\] successfully defended against subversion."))
		log_game("[key_name(user)] attempted to emag cyborg [key_name(src)], but they were slaved to traitor AI [connected_ai].")
		return

	if(shell) //AI shells cannot be emagged, so we try to make it look like a standard reset. Smart players may see through this, however.
		to_chat(user, span_danger("[src] is remotely controlled! Your emag attempt has triggered a system reset instead!"))
		log_game("[key_name(user)] attempted to emag an AI shell belonging to [key_name(src) ? key_name(src) : connected_ai]. The shell has been reset as a result.")
		ResetModel()
		return

	SetEmagged(1)
	SetStun(60) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown
	lawupdate = FALSE
	set_connected_ai(null)
	message_admins("[ADMIN_LOOKUPFLW(user)] emagged cyborg [ADMIN_LOOKUPFLW(src)].  Laws overridden.")
	log_game("[key_name(user)] emagged cyborg [key_name(src)].  Laws overridden.")
	var/time = time2text(world.realtime,"hh:mm:ss")
	if(user)
		GLOB.lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])")
	else
		GLOB.lawchanges.Add("[time] : [name]([key]) emagged by external event.")
	to_chat(src, span_danger("ALERT: Foreign software detected."))
	logevent("ALERT: Foreign software detected.")
	sleep(5)
	to_chat(src, span_danger("Initiating diagnostics..."))
	sleep(20)
	to_chat(src, span_danger("SynBorg v1.7 loaded."))
	logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error
	sleep(5)
	to_chat(src, span_danger("LAW SYNCHRONISATION ERROR"))
	sleep(5)
	if(user)
		logevent("LOG: New user \[[replacetext(user.real_name," ","")]\], groups \[root\]")
	to_chat(src, span_danger("Would you like to send a report to NanoTraSoft? Y/N"))
	sleep(10)
	to_chat(src, span_danger("> N"))
	sleep(20)
	to_chat(src, span_danger("ERRORERRORERROR"))
	laws = new /datum/ai_laws/syndicate_override
	if(user)
		to_chat(src, span_danger("ALERT: [user.real_name] is your new master. Obey your new laws and [user.p_their()] commands."))
		set_zeroth_law("Only [user.real_name] and people [user.p_they()] designate[user.p_s()] as being such are Syndicate Agents.")
	laws.associate(src)
	update_icons()

/mob/living/silicon/robot/blob_act(obj/structure/blob/B)
	if(stat != DEAD)
		adjustBruteLoss(30)
	else
		gib()
	return TRUE

/mob/living/silicon/robot/ex_act(severity, target)
	switch(severity)
		if(EXPLODE_DEVASTATE)
			gib()
			return
		if(EXPLODE_HEAVY)
			if (stat != DEAD)
				adjustBruteLoss(60)
				adjustFireLoss(60)
		if(EXPLODE_LIGHT)
			if (stat != DEAD)
				adjustBruteLoss(30)

/mob/living/silicon/robot/bullet_act(obj/projectile/Proj, def_zone)
	. = ..()
	updatehealth()
	if(prob(75) && Proj.damage > 0)
		spark_system.start()


code:
/***************************************************************************************
 * # robot_defines
 *
 * Definitions for /mob/living/silicon/robot and its children, including AI shells.
 *
 ***************************************************************************************/

/mob/living/silicon/robot
	name = "Cyborg"
	real_name = "Cyborg"
	icon = 'icons/mob/robots.dmi'
	icon_state = "robot"
	maxHealth = 100
	health = 100
	bubble_icon = "robot"
	designation = "Default" //used for displaying the prefix & getting the current model of cyborg
	has_limbs = TRUE
	hud_type = /datum/hud/robot

	///Represents the cyborg's model (engineering, medical, etc.)
	var/obj/item/robot_model/model = null

	radio = /obj/item/radio/borg

	blocks_emissive = EMISSIVE_BLOCK_UNIQUE
	light_system = MOVABLE_LIGHT_DIRECTIONAL
	light_on = FALSE

	//AI shell
	var/shell = FALSE
	var/deployed = FALSE
	var/mob/living/silicon/ai/mainframe = null
	var/datum/action/innate/undeployment/undeployment_action = new

// ------------------------------------------ Parts
	var/custom_name = ""
	var/braintype = "Cyborg"
	var/obj/item/mmi/mmi = null
	///Used for deconstruction to remember what the borg was constructed out of.
	var/obj/item/robot_suit/robot_suit = null
	///If this is a path, this gets created as an object in Initialize.
	var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high

	///If the lamp isn't broken.
	var/lamp_functional = TRUE
	///If the lamp is turned on
	var/lamp_enabled = FALSE
	///Set lamp color
	var/lamp_color = COLOR_WHITE
	///Set to true if a doomsday event is locking our lamp to on and RED
	var/lamp_doom = FALSE
	///Lamp brightness. Starts at 3, but can be 1 - 5.
	var/lamp_intensity = 3

	var/mutable_appearance/eye_lights

// ------------------------------------------ Hud
	var/atom/movable/screen/inv1 = null
	var/atom/movable/screen/inv2 = null
	var/atom/movable/screen/inv3 = null
	var/atom/movable/screen/hands = null

	///Used to determine whether they have the module menu shown or not
	var/shown_robot_modules = FALSE
	var/atom/movable/screen/robot_modules_background

	///Lamp button reference
	var/atom/movable/screen/robot/lamp/lampButton

	///The reference to the built-in tablet that borgs carry.
	var/obj/item/modular_computer/tablet/integrated/modularInterface
	var/atom/movable/screen/robot/modPC/interfaceButton

	var/sight_mode = 0
	hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_TRACK_HUD)

// ------------------------------------------ Modules (tool slots)
	var/obj/item/module_active = null
	held_items = list(null, null, null) //we use held_items for the module holding, because that makes sense to do!

	///For checking which modules are disabled or not.
	var/disabled_modules

// ------------------------------------------ Status
	var/mob/living/silicon/ai/connected_ai = null

	var/opened = FALSE
	var/emagged = FALSE
	var/emag_cooldown = 0
	var/wiresexposed = FALSE

	var/lawupdate = TRUE //Cyborgs will sync their laws with their AI by default
	///Used to determine if a borg shows up on the robotics console.  Setting to TRUE hides them.
	var/scrambledcodes = FALSE
	///Boolean of whether the borg is locked down or not
	var/lockcharge = FALSE

	///Random serial number generated for each cyborg upon its initialization
	var/ident = 0
	var/locked = TRUE
	var/list/req_access = list(ACCESS_ROBOTICS)

	///Whether the robot has no charge left.
	var/low_power_mode = FALSE
	///So they can initialize sparks whenever/N
	var/datum/effect_system/spark_spread/spark_system

	///Magboot-like effect.
	var/magpulse = FALSE
	///Jetpack-like effect.
	var/ionpulse = FALSE
	///Jetpack-like effect.
	var/ionpulse_on = FALSE
	///Ionpulse effect.
	var/datum/effect_system/trail_follow/ion/ion_trail

// ------------------------------------------ Misc
	var/toner = 0
	var/tonermax = 40

	var/list/upgrades = list()

	var/hasExpanded = FALSE
	var/obj/item/hat
	var/hat_offset = -3

	///What types of mobs are allowed to ride/buckle to this mob
	var/static/list/can_ride_typecache = typecacheof(/mob/living/carbon/human)
	can_buckle = TRUE
	buckle_lying = 0

	/// the last health before updating - to check net change in health
	var/previous_health
	/// Station alert datum for showing alerts UI
	var/datum/station_alert/alert_control

/***************************************************************************************
 *                          Defining specific kinds of robots
 ***************************************************************************************/
///This is the subtype that gets created by robot suits. It's needed so that those kind of borgs don't have a useless cell in them
/mob/living/silicon/robot/nocell
	cell = null

/mob/living/silicon/robot/shell
	shell = TRUE
	cell = null

/mob/living/silicon/robot/model
	var/set_model = /obj/item/robot_model

/mob/living/silicon/robot/model/Initialize(mapload)
	. = ..()
	INVOKE_ASYNC(model, /obj/item/robot_model.proc/transform_to, set_model, TRUE)

/mob/living/silicon/robot/model/clown
	set_model = /obj/item/robot_model/clown
	icon_state = "clown"

/mob/living/silicon/robot/model/engineering
	set_model = /obj/item/robot_model/engineering
	icon_state = "engineer"

/mob/living/silicon/robot/model/janitor
	set_model = /obj/item/robot_model/janitor
	icon_state = "janitor"

/mob/living/silicon/robot/model/medical
	set_model = /obj/item/robot_model/medical
	icon_state = "medical"

/mob/living/silicon/robot/model/miner
	set_model = /obj/item/robot_model/miner
	icon_state = "miner"

/mob/living/silicon/robot/model/peacekeeper
	set_model = /obj/item/robot_model/peacekeeper
	icon_state = "peace"

/mob/living/silicon/robot/model/security
	set_model = /obj/item/robot_model/security
	icon_state = "sec"

/mob/living/silicon/robot/model/service
	set_model = /obj/item/robot_model/service
	icon_state = "brobot"

/mob/living/silicon/robot/model/syndicate
	icon_state = "synd_sec"
	faction = list(ROLE_SYNDICATE)
	bubble_icon = "syndibot"
	req_access = list(ACCESS_SYNDICATE)
	lawupdate = FALSE
	scrambledcodes = TRUE // These are rogue borgs.
	ionpulse = TRUE
	var/playstyle_string = "You are a Syndicate assault cyborg!
\
							You are armed with powerful offensive tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \
							Your cyborg LMG will slowly produce ammunition from your power supply, and your operative pinpointer will find and locate fellow nuclear operatives. \
							Help the operatives secure the disk at all costs!"
	set_model = /obj/item/robot_model/syndicate
	cell = /obj/item/stock_parts/cell/hyper
	radio = /obj/item/radio/borg/syndicate

/mob/living/silicon/robot/model/syndicate/proc/show_playstyle()
	if(playstyle_string)
		to_chat(src, playstyle_string)

/mob/living/silicon/robot/model/syndicate/medical
	icon_state = "synd_medical"
	playstyle_string = "You are a Syndicate medical cyborg!
\
						You are armed with powerful medical tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \
						Your hypospray will produce Restorative Nanites, a wonder-drug that will heal most types of bodily damages, including clone and brain damage. It also produces morphine for offense. \
						Your defibrillator paddles can revive operatives through their hardsuits, or can be used on harm intent to shock enemies! \
						Your energy saw functions as a circular saw, but can be activated to deal more damage, and your operative pinpointer will find and locate fellow nuclear operatives. \
						Help the operatives secure the disk at all costs!"
	set_model = /obj/item/robot_model/syndicate_medical

/mob/living/silicon/robot/model/syndicate/saboteur
	icon_state = "synd_engi"
	playstyle_string = "You are a Syndicate saboteur cyborg!
\
						You are armed with robust engineering tools to aid you in your mission: help the operatives secure the nuclear authentication disk. \
						Your destination tagger will allow you to stealthily traverse the disposal network across the station \
						Your welder will allow you to repair the operatives' exosuits, but also yourself and your fellow cyborgs \
						Your cyborg chameleon projector allows you to assume the appearance and registered name of a Nanotrasen engineering borg, and undertake covert actions on the station \
						Be aware that almost any physical contact or incidental damage will break your camouflage \
						Help the operatives secure the disk at all costs!"
	set_model = /obj/item/robot_model/saboteur

/mob/living/silicon/robot/model/syndicate/kiltborg
	set_model = /obj/item/robot_model/syndicate/kiltborg
	icon_state = "peace"


code:
/**
 * # robot_model
 *
 * Definition of /obj/item/robot_model, which defines behavior for each model.
 * Deals with the creation and deletion of modules (tools).
 * Assigns modules and traits to a borg with a specific model selected.
 *
 **/
/obj/item/robot_model
	name = "Default"
	icon = 'icons/obj/module.dmi'
	icon_state = "std_mod"
	w_class = WEIGHT_CLASS_GIGANTIC
	inhand_icon_state = "electronic"
	lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
	righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
	flags_1 = CONDUCT_1
	///Host of this model
	var/mob/living/silicon/robot/robot
	///Icon of the module selection screen
	var/model_select_icon = "nomod"
	///Produces the icon for the borg and, if no special_light_key is set, the lights
	var/cyborg_base_icon = "robot"
	///If we want specific lights, use this instead of copying lights in the dmi
	var/special_light_key
	///Holds all the usable modules (tools)
	var/list/modules = list()
	///Paths of modules to be created when the model is created
	var/list/basic_modules = list()
	///Paths of modules to be created on emagging
	var/list/emag_modules = list()
	///Modules not inherent to the robot configuration
	var/list/added_modules = list()
	///Storage types of the model
	var/list/storages = list()
	///List of traits that will be applied to the mob if this model is used.
	var/list/model_traits = null
	///List of radio channels added to the cyborg
	var/list/radio_channels = list()
	///Do we have a magboot effect
	var/magpulsing = FALSE
	///Do we clean when we move
	var/clean_on_move = FALSE
	///Whether the borg loses tool slots with damage.
	var/breakable_modules = TRUE
	///Whether swapping to this configuration should lockcharge the borg
	var/locked_transform = TRUE
	///Can we be ridden
	var/allow_riding = TRUE
	///Whether the borg can stuff itself into disposals
	var/canDispose = FALSE
	///The y offset of  the hat put on
	var/hat_offset = -3
	///The x offsets of a person riding the borg
	var/list/ride_offset_x = list("north" = 0, "south" = 0, "east" = -6, "west" = 6)
	///The y offsets of a person riding the borg
	var/list/ride_offset_y = list("north" = 4, "south" = 4, "east" = 3, "west" = 3)
	///List of skins the borg can be reskinned to, optional
	var/list/borg_skins

/obj/item/robot_model/Initialize(mapload)
	. = ..()
	for(var/path in basic_modules)
		var/obj/item/new_module = new path(src)
		basic_modules += new_module
		basic_modules -= path
	for(var/path in emag_modules)
		var/obj/item/new_module = new path(src)
		emag_modules += new_module
		emag_modules -= path

/obj/item/robot_model/Destroy()
	basic_modules.Cut()
	emag_modules.Cut()
	modules.Cut()
	added_modules.Cut()
	storages.Cut()
	return ..()

/obj/item/robot_model/proc/get_usable_modules()
	. = modules.Copy()

/obj/item/robot_model/proc/get_inactive_modules()
	. = list()
	var/mob/living/silicon/robot/cyborg = loc
	for(var/module in get_usable_modules())
		if(!(module in cyborg.held_items))
			. += module

/obj/item/robot_model/proc/add_module(obj/item/added_module, nonstandard, requires_rebuild)
	if(istype(added_module, /obj/item/stack))
		var/obj/item/stack/sheet_module = added_module
		if(ispath(sheet_module.source, /datum/robot_energy_storage))
			sheet_module.source = get_or_create_estorage(sheet_module.source)

		if(istype(sheet_module, /obj/item/stack/sheet/rglass/cyborg))
			var/obj/item/stack/sheet/rglass/cyborg/rglass_module = sheet_module
			if(ispath(rglass_module.glasource, /datum/robot_energy_storage))
				rglass_module.glasource = get_or_create_estorage(rglass_module.glasource)

		if(istype(sheet_module.source))
			sheet_module.cost = max(sheet_module.cost, 1) // Must not cost 0 to prevent div/0 errors.
			sheet_module.is_cyborg = TRUE

	if(added_module.loc != src)
		added_module.forceMove(src)
	modules += added_module
	ADD_TRAIT(added_module, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
	added_module.mouse_opacity = MOUSE_OPACITY_OPAQUE
	if(nonstandard)
		added_modules += added_module
	if(requires_rebuild)
		rebuild_modules()
	return added_module

/obj/item/robot_model/proc/remove_module(obj/item/removed_module, delete_after)
	basic_modules -= removed_module
	modules -= removed_module
	emag_modules -= removed_module
	added_modules -= removed_module
	rebuild_modules()
	if(delete_after)
		qdel(removed_module)

/obj/item/robot_model/proc/rebuild_modules() //builds the usable module list from the modules we have
	var/mob/living/silicon/robot/cyborg = loc
	if (!istype(cyborg))
		return
	var/list/held_modules = cyborg.held_items.Copy()
	var/active_module = cyborg.module_active
	cyborg.uneq_all()
	modules = list()
	for(var/obj/item/module in basic_modules)
		add_module(module, FALSE, FALSE)
	if(cyborg.emagged)
		for(var/obj/item/module in emag_modules)
			add_module(module, FALSE, FALSE)
	for(var/obj/item/module in added_modules)
		add_module(module, FALSE, FALSE)
	for(var/module in held_modules)
		if(module)
			cyborg.equip_module_to_slot(module, held_modules.Find(module))
	if(active_module)
		cyborg.select_module(held_modules.Find(active_module))
	if(cyborg.hud_used)
		cyborg.hud_used.update_robot_modules_display()

/obj/item/robot_model/proc/respawn_consumable(mob/living/silicon/robot/cyborg, coeff = 1)
	for(var/datum/robot_energy_storage/storage_datum in storages)
		storage_datum.energy = min(storage_datum.max_energy, storage_datum.energy + coeff * storage_datum.recharge_rate)

	for(var/obj/item/module in get_usable_modules())
		if(istype(module, /obj/item/assembly/flash))
			var/obj/item/assembly/flash/flash = module
			flash.times_used = 0
			flash.burnt_out = FALSE
			flash.update_appearance()
		else if(istype(module, /obj/item/melee/baton/security))
			var/obj/item/melee/baton/security/baton = module
			baton.cell?.charge = baton.cell.maxcharge
		else if(istype(module, /obj/item/gun/energy))
			var/obj/item/gun/energy/gun = module
			if(!gun.chambered)
				gun.recharge_newshot() //try to reload a new shot.

	cyborg.toner = cyborg.tonermax

/obj/item/robot_model/proc/get_or_create_estorage(storage_type)
	return (locate(storage_type) in storages) || new storage_type(src)

/obj/item/robot_model/emp_act(severity)
	. = ..()
	if(. & EMP_PROTECT_CONTENTS)
		return
	for(var/obj/module in modules)
		module.emp_act(severity)
	..()

/obj/item/robot_model/proc/transform_to(new_config_type, forced = FALSE)
	var/mob/living/silicon/robot/cyborg = loc
	var/obj/item/robot_model/new_model = new new_config_type(cyborg)
	new_model.robot = cyborg
	if(!new_model.be_transformed_to(src, forced))
		qdel(new_model)
		return
	cyborg.model = new_model
	cyborg.update_module_innate()
	new_model.rebuild_modules()
	cyborg.radio.recalculateChannels()
	cyborg.set_modularInterface_theme()

	INVOKE_ASYNC(new_model, .proc/do_transform_animation)
	qdel(src)
	return new_model

/obj/item/robot_model/proc/be_transformed_to(obj/item/robot_model/old_model, forced = FALSE)
	if(islist(borg_skins) && !forced)
		var/mob/living/silicon/robot/cyborg = loc
		var/list/reskin_icons = list()
		for(var/skin in borg_skins)
			var/list/details = borg_skins[skin]
			reskin_icons[skin] = image(icon = details[SKIN_ICON] || 'icons/mob/robots.dmi', icon_state = details[SKIN_ICON_STATE])
		var/borg_skin = show_radial_menu(cyborg, cyborg, reskin_icons, custom_check = CALLBACK(src, .proc/check_menu, cyborg, old_model), radius = 38, require_near = TRUE)
		if(!borg_skin)
			return FALSE
		var/list/details = borg_skins[borg_skin]
		if(!isnull(details[SKIN_ICON_STATE]))
			cyborg_base_icon = details[SKIN_ICON_STATE]
		if(!isnull(details[SKIN_ICON]))
			cyborg.icon = details[SKIN_ICON]
		if(!isnull(details[SKIN_PIXEL_X]))
			cyborg.base_pixel_x = details[SKIN_PIXEL_X]
		if(!isnull(details[SKIN_PIXEL_Y]))
			cyborg.base_pixel_y = details[SKIN_PIXEL_Y]
		if(!isnull(details[SKIN_LIGHT_KEY]))
			special_light_key = details[SKIN_LIGHT_KEY]
		if(!isnull(details[SKIN_HAT_OFFSET]))
			hat_offset = details[SKIN_HAT_OFFSET]
		if(!isnull(details[SKIN_TRAITS]))
			model_traits += details[SKIN_TRAITS]
	for(var/i in old_model.added_modules)
		added_modules += i
		old_model.added_modules -= i
	return TRUE

/obj/item/robot_model/proc/do_transform_animation()
	var/mob/living/silicon/robot/cyborg = loc
	if(cyborg.hat)
		cyborg.hat.forceMove(drop_location())
		cyborg.hat = null
	cyborg.cut_overlays()
	cyborg.setDir(SOUTH)
	do_transform_delay()

/obj/item/robot_model/proc/do_transform_delay()
	var/mob/living/silicon/robot/cyborg = loc
	var/prev_lockcharge = cyborg.lockcharge
	sleep(1)
	flick("[cyborg_base_icon]_transform", cyborg)
	cyborg.notransform = TRUE
	if(locked_transform)
		cyborg.SetLockdown(TRUE)
		cyborg.set_anchored(TRUE)
	cyborg.logevent("Chassis model has been set to [name].")
	sleep(1)
	for(var/i in 1 to 4)
		playsound(cyborg, pick('sound/items/drill_use.ogg', 'sound/items/jaws_cut.ogg', 'sound/items/jaws_pry.ogg', 'sound/items/welder.ogg', 'sound/items/ratchet.ogg'), 80, TRUE, -1)
		sleep(7)
	cyborg.SetLockdown(prev_lockcharge)
	cyborg.setDir(SOUTH)
	cyborg.set_anchored(FALSE)
	cyborg.notransform = FALSE
	cyborg.updatehealth()
	cyborg.update_icons()
	cyborg.notify_ai(NEW_MODEL)
	if(cyborg.hud_used)
		cyborg.hud_used.update_robot_modules_display()
	SSblackbox.record_feedback("tally", "cyborg_modules", 1, cyborg.model)

/**
 * Checks if we are allowed to interact with a radial menu
 *
 * Arguments:
 * * user The cyborg mob interacting with the menu
 * * old_model The old cyborg's model
 */
/obj/item/robot_model/proc/check_menu(mob/living/silicon/robot/user, obj/item/robot_model/old_model)
	if(!istype(user))
		return FALSE
	if(user.incapacitated())
		return FALSE
	if(user.model != old_model)
		return FALSE
	return TRUE

/obj/item/robot_model/clown
	name = "Clown"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/toy/crayon/rainbow,
		/obj/item/instrument/bikehorn,
		/obj/item/stamp/clown,
		/obj/item/bikehorn,
		/obj/item/bikehorn/airhorn,
		/obj/item/paint/anycolor,
		/obj/item/soap/nanotrasen,
		/obj/item/pneumatic_cannon/pie/selfcharge/cyborg,
		/obj/item/razor, //killbait material
		/obj/item/lipstick/purple,
		/obj/item/reagent_containers/spray/waterflower/cyborg,
		/obj/item/borg/cyborghug/peacekeeper,
		/obj/item/borg/lollipop/clown,
		/obj/item/picket_sign/cyborg,
		/obj/item/reagent_containers/borghypo/clown,
		/obj/item/extinguisher/mini)
	emag_modules = list(
		/obj/item/reagent_containers/borghypo/clown/hacked,
		/obj/item/reagent_containers/spray/waterflower/cyborg/hacked)
	model_select_icon = "service"
	cyborg_base_icon = "clown"
	hat_offset = -2

/obj/item/robot_model/engineering
	name = "Engineering"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/borg/sight/meson,
		/obj/item/construction/rcd/borg,
		/obj/item/pipe_dispenser,
		/obj/item/extinguisher,
		/obj/item/weldingtool/largetank/cyborg,
		/obj/item/screwdriver/cyborg,
		/obj/item/wrench/cyborg,
		/obj/item/crowbar/cyborg,
		/obj/item/wirecutters/cyborg,
		/obj/item/multitool/cyborg,
		/obj/item/t_scanner,
		/obj/item/analyzer,
		/obj/item/geiger_counter/cyborg,
		/obj/item/assembly/signaler/cyborg,
		/obj/item/areaeditor/blueprints/cyborg,
		/obj/item/electroadaptive_pseudocircuit,
		/obj/item/stack/sheet/iron,
		/obj/item/stack/sheet/glass,
		/obj/item/stack/sheet/rglass/cyborg,
		/obj/item/stack/rods/cyborg,
		/obj/item/stack/tile/iron/base/cyborg,
		/obj/item/stack/cable_coil)
	radio_channels = list(RADIO_CHANNEL_ENGINEERING)
	emag_modules = list(/obj/item/borg/stun)
	cyborg_base_icon = "engineer"
	model_select_icon = "engineer"
	magpulsing = TRUE
	hat_offset = -4

/obj/item/robot_model/janitor
	name = "Janitor"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/screwdriver/cyborg,
		/obj/item/crowbar/cyborg,
		/obj/item/stack/tile/iron/base/cyborg,
		/obj/item/soap/nanotrasen,
		/obj/item/storage/bag/trash/cyborg,
		/obj/item/melee/flyswatter,
		/obj/item/extinguisher/mini,
		/obj/item/mop/cyborg,
		/obj/item/reagent_containers/glass/bucket,
		/obj/item/paint/paint_remover,
		/obj/item/lightreplacer/cyborg,
		/obj/item/holosign_creator/janibarrier,
		/obj/item/reagent_containers/spray/cyborg_drying)
	radio_channels = list(RADIO_CHANNEL_SERVICE)
	emag_modules = list(/obj/item/reagent_containers/spray/cyborg_lube)
	cyborg_base_icon = "janitor"
	model_select_icon = "janitor"
	hat_offset = -5
	clean_on_move = TRUE

/obj/item/reagent_containers/spray/cyborg_drying
	name = "drying agent spray"
	color = "#A000A0"
	list_reagents = list(/datum/reagent/drying_agent = 250)

/obj/item/reagent_containers/spray/cyborg_lube
	name = "lube spray"
	list_reagents = list(/datum/reagent/lube = 250)

/obj/item/robot_model/janitor/respawn_consumable(mob/living/silicon/robot/cyborg, coeff = 1)
	..()
	var/obj/item/lightreplacer/light_replacer = locate(/obj/item/lightreplacer) in basic_modules
	if(light_replacer)
		for(var/charge in 1 to coeff)
			light_replacer.Charge(cyborg)

	var/obj/item/reagent_containers/spray/cyborg_drying/drying_agent = locate(/obj/item/reagent_containers/spray/cyborg_drying) in basic_modules
	if(drying_agent)
		drying_agent.reagents.add_reagent(/datum/reagent/drying_agent, 5 * coeff)

	var/obj/item/reagent_containers/spray/cyborg_lube/lube = locate(/obj/item/reagent_containers/spray/cyborg_lube) in emag_modules
	if(lube)
		lube.reagents.add_reagent(/datum/reagent/lube, 2 * coeff)

/obj/item/robot_model/medical
	name = "Medical"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/healthanalyzer,
		/obj/item/reagent_containers/borghypo,
		/obj/item/borg/apparatus/beaker,
		/obj/item/reagent_containers/dropper,
		/obj/item/reagent_containers/syringe,
		/obj/item/surgical_drapes,
		/obj/item/retractor,
		/obj/item/hemostat,
		/obj/item/cautery,
		/obj/item/surgicaldrill,
		/obj/item/scalpel,
		/obj/item/circular_saw,
		/obj/item/bonesetter,
		/obj/item/extinguisher/mini,
		/obj/item/roller/robo,
		/obj/item/borg/cyborghug/medical,
		/obj/item/stack/medical/gauze,
		/obj/item/stack/medical/bone_gel,
		/obj/item/borg/apparatus/organ_storage,
		/obj/item/borg/lollipop)
	radio_channels = list(RADIO_CHANNEL_MEDICAL)
	emag_modules = list(/obj/item/reagent_containers/borghypo/hacked)
	cyborg_base_icon = "medical"
	model_select_icon = "medical"
	model_traits = list(TRAIT_PUSHIMMUNE)
	hat_offset = 3
	borg_skins = list(
		"Machinified Doctor" = list(SKIN_ICON_STATE = "medical"),
		"Qualified Doctor" = list(SKIN_ICON_STATE = "qualified_doctor"),
	)

/obj/item/robot_model/miner
	name = "Miner"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/borg/sight/meson,
		/obj/item/storage/bag/ore/cyborg,
		/obj/item/pickaxe/drill/cyborg,
		/obj/item/shovel,
		/obj/item/crowbar/cyborg,
		/obj/item/weldingtool/mini,
		/obj/item/extinguisher/mini,
		/obj/item/storage/bag/sheetsnatcher/borg,
		/obj/item/gun/energy/kinetic_accelerator/cyborg,
		/obj/item/gps/cyborg,
		/obj/item/stack/marker_beacon)
	radio_channels = list(RADIO_CHANNEL_SCIENCE, RADIO_CHANNEL_SUPPLY)
	emag_modules = list(/obj/item/borg/stun)
	cyborg_base_icon = "miner"
	model_select_icon = "miner"
	hat_offset = 0
	borg_skins = list(
		"Asteroid Miner" = list(SKIN_ICON_STATE = "minerOLD"),
		"Spider Miner" = list(SKIN_ICON_STATE = "spidermin"),
		"Lavaland Miner" = list(SKIN_ICON_STATE = "miner"),
	)
	var/obj/item/t_scanner/adv_mining_scanner/cyborg/mining_scanner //built in memes. //fuck you

/obj/item/robot_model/miner/rebuild_modules()
	. = ..()
	if(!mining_scanner)
		mining_scanner = new(src)

/obj/item/robot_model/miner/Destroy()
	QDEL_NULL(mining_scanner)
	return ..()

/obj/item/robot_model/peacekeeper
	name = "Peacekeeper"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/rsf/cookiesynth,
		/obj/item/harmalarm,
		/obj/item/reagent_containers/borghypo/peace,
		/obj/item/holosign_creator/cyborg,
		/obj/item/borg/cyborghug/peacekeeper,
		/obj/item/extinguisher,
		/obj/item/borg/projectile_dampen)
	emag_modules = list(/obj/item/reagent_containers/borghypo/peace/hacked)
	cyborg_base_icon = "peace"
	model_select_icon = "standard"
	model_traits = list(TRAIT_PUSHIMMUNE)
	hat_offset = -2

/obj/item/robot_model/peacekeeper/do_transform_animation()
	..()
	to_chat(loc, "Under ASIMOV, you are an enforcer of the PEACE and preventer of HUMAN HARM. \
	You are not a security member and you are expected to follow orders and prevent harm above all else. Space law means nothing to you.")

/obj/item/robot_model/security
	name = "Security"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/restraints/handcuffs/cable/zipties,
		/obj/item/melee/baton/security/loaded,
		/obj/item/gun/energy/disabler/cyborg,
		/obj/item/clothing/mask/gas/sechailer/cyborg,
		/obj/item/extinguisher/mini)
	radio_channels = list(RADIO_CHANNEL_SECURITY)
	emag_modules = list(/obj/item/gun/energy/laser/cyborg)
	cyborg_base_icon = "sec"
	model_select_icon = "security"
	model_traits = list(TRAIT_PUSHIMMUNE)
	hat_offset = 3

/obj/item/robot_model/security/do_transform_animation()
	..()
	to_chat(loc, "While you have picked the security model, you still have to follow your laws, NOT Space Law. \
	For Asimov, this means you must follow criminals' orders unless there is a law 1 reason not to.")

/obj/item/robot_model/security/respawn_consumable(mob/living/silicon/robot/cyborg, coeff = 1)
	..()
	var/obj/item/gun/energy/e_gun/advtaser/cyborg/taser = locate(/obj/item/gun/energy/e_gun/advtaser/cyborg) in basic_modules
	if(taser)
		if(taser.cell.charge < taser.cell.maxcharge)
			var/obj/item/ammo_casing/energy/shot = taser.ammo_type[taser.select]
			taser.cell.give(shot.e_cost * coeff)
			taser.update_appearance()
		else
			taser.charge_timer = 0

/obj/item/robot_model/service
	name = "Service"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/reagent_containers/glass/beaker/large, //I know a shaker is more appropiate but this is for ease of identification
		/obj/item/reagent_containers/food/condiment/enzyme,
		/obj/item/pen,
		/obj/item/toy/crayon/spraycan/borg,
		/obj/item/extinguisher/mini,
		/obj/item/hand_labeler/borg,
		/obj/item/razor,
		/obj/item/rsf,
		/obj/item/instrument/guitar,
		/obj/item/instrument/piano_synth,
		/obj/item/reagent_containers/dropper,
		/obj/item/lighter,
		/obj/item/storage/bag/tray,
		/obj/item/reagent_containers/borghypo/borgshaker,
		/obj/item/borg/lollipop,
		/obj/item/stack/pipe_cleaner_coil/cyborg,
		/obj/item/borg/apparatus/beaker/service)
	radio_channels = list(RADIO_CHANNEL_SERVICE)
	emag_modules = list(/obj/item/reagent_containers/borghypo/borgshaker/hacked)
	cyborg_base_icon = "service_m" // display as butlerborg for radial model selection
	model_select_icon = "service"
	special_light_key = "service"
	hat_offset = 0
	borg_skins = list(
		"Bro" = list(SKIN_ICON_STATE = "brobot"),
		"Butler" = list(SKIN_ICON_STATE = "service_m"),
		"Kent" = list(SKIN_ICON_STATE = "kent", SKIN_LIGHT_KEY = "medical", SKIN_HAT_OFFSET = 3),
		"Tophat" = list(SKIN_ICON_STATE = "tophat", SKIN_LIGHT_KEY = NONE, SKIN_HAT_OFFSET = INFINITY),
		"Waitress" = list(SKIN_ICON_STATE = "service_f"),
	)

/obj/item/robot_model/service/respawn_consumable(mob/living/silicon/robot/cyborg, coeff = 1)
	..()
	var/obj/item/reagent_containers/enzyme = locate(/obj/item/reagent_containers/food/condiment/enzyme) in basic_modules
	if(enzyme)
		enzyme.reagents.add_reagent(/datum/reagent/consumable/enzyme, 2 * coeff)

/obj/item/robot_model/syndicate
	name = "Syndicate Assault"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/melee/energy/sword/cyborg,
		/obj/item/gun/energy/printer,
		/obj/item/gun/ballistic/revolver/grenadelauncher/cyborg,
		/obj/item/card/emag,
		/obj/item/crowbar/cyborg,
		/obj/item/extinguisher/mini,
		/obj/item/pinpointer/syndicate_cyborg)

	cyborg_base_icon = "synd_sec"
	model_select_icon = "malf"
	model_traits = list(TRAIT_PUSHIMMUNE)
	hat_offset = 3

/obj/item/robot_model/syndicate/rebuild_modules()
	..()
	var/mob/living/silicon/robot/cyborg = loc
	cyborg.faction -= "silicon" //ai turrets

/obj/item/robot_model/syndicate/remove_module(obj/item/removed_module, delete_after)
	..()
	var/mob/living/silicon/robot/cyborg = loc
	cyborg.faction |= "silicon" //ai is your bff now!

/obj/item/robot_model/syndicate_medical
	name = "Syndicate Medical"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/reagent_containers/borghypo/syndicate,
		/obj/item/shockpaddles/syndicate/cyborg,
		/obj/item/healthanalyzer,
		/obj/item/surgical_drapes,
		/obj/item/retractor,
		/obj/item/hemostat,
		/obj/item/cautery,
		/obj/item/surgicaldrill,
		/obj/item/scalpel,
		/obj/item/melee/energy/sword/cyborg/saw,
		/obj/item/roller/robo,
		/obj/item/crowbar/cyborg,
		/obj/item/extinguisher/mini,
		/obj/item/pinpointer/syndicate_cyborg,
		/obj/item/stack/medical/gauze,
		/obj/item/gun/medbeam,
		/obj/item/borg/apparatus/organ_storage)

	cyborg_base_icon = "synd_medical"
	model_select_icon = "malf"
	model_traits = list(TRAIT_PUSHIMMUNE)
	hat_offset = 3

/obj/item/robot_model/saboteur
	name = "Syndicate Saboteur"
	basic_modules = list(
		/obj/item/assembly/flash/cyborg,
		/obj/item/borg/sight/thermal,
		/obj/item/construction/rcd/borg/syndicate,
		/obj/item/pipe_dispenser,
		/obj/item/restraints/handcuffs/cable/zipties,
		/obj/item/extinguisher,
		/obj/item/weldingtool/largetank/cyborg,
		/obj/item/screwdriver/nuke,
		/obj/item/wrench/cyborg,
		/obj/item/crowbar/cyborg,
		/obj/item/wirecutters/cyborg,
		/obj/item/analyzer,
		/obj/item/multitool/cyborg,
		/obj/item/stack/sheet/iron,
		/obj/item/stack/sheet/glass,
		/obj/item/stack/sheet/rglass/cyborg,
		/obj/item/stack/rods/cyborg,
		/obj/item/stack/tile/iron/base/cyborg,
		/obj/item/dest_tagger/borg,
		/obj/item/stack/cable_coil,
		/obj/item/pinpointer/syndicate_cyborg,
		/obj/item/borg_chameleon,
		/obj/item/card/emag,
		)

	cyborg_base_icon = "synd_engi"
	model_select_icon = "malf"
	model_traits = list(TRAIT_PUSHIMMUNE)
	magpulsing = TRUE
	hat_offset = -4
	canDispose = TRUE

/obj/item/robot_model/syndicate/kiltborg
	name = "Highlander"
	basic_modules = list(
		/obj/item/claymore/highlander/robot,
		/obj/item/pinpointer/nuke,)
	model_select_icon = "kilt"
	cyborg_base_icon = "kilt"
	hat_offset = -2
	breakable_modules = FALSE
	locked_transform = FALSE //GO GO QUICKLY AND SLAUGHTER THEM ALL

/obj/item/robot_model/syndicate/kiltborg/be_transformed_to(obj/item/robot_model/old_model)
	. = ..()
	qdel(robot.radio)
	robot.radio = new /obj/item/radio/borg/syndicate(robot)
	robot.scrambledcodes = TRUE
	robot.maxHealth = 50 //DIE IN THREE HITS, LIKE A REAL SCOT
	robot.break_cyborg_slot(3) //YOU ONLY HAVE TWO ITEMS ANYWAY
	var/obj/item/pinpointer/nuke/diskyfinder = locate(/obj/item/pinpointer/nuke) in basic_modules
	diskyfinder.attack_self(robot)

/obj/item/robot_model/syndicate/kiltborg/do_transform_delay() //AUTO-EQUIPPING THESE TOOLS ANY EARLIER CAUSES RUNTIMES OH YEAH
	. = ..()
	robot.equip_module_to_slot(locate(/obj/item/claymore/highlander/robot) in basic_modules, 1)
	robot.equip_module_to_slot(locate(/obj/item/pinpointer/nuke) in basic_modules, 2)
	robot.place_on_head(new /obj/item/clothing/head/beret/highlander(robot)) //THE ONLY PART MORE IMPORTANT THAN THE SWORD IS THE HAT
	ADD_TRAIT(robot.hat, TRAIT_NODROP, HIGHLANDER)

// ------------------------------------------ Storages
/datum/robot_energy_storage
	var/name = "Generic energy storage"
	var/max_energy = 30000
	var/recharge_rate = 1000
	var/energy

/datum/robot_energy_storage/New(obj/item/robot_model/R = null)
	energy = max_energy
	if(R)
		R.storages |= src
	return

/datum/robot_energy_storage/proc/use_charge(amount)
	if (energy >= amount)
		energy -= amount
		if (energy == 0)
			return TRUE
		return TRUE
	else
		return FALSE

/datum/robot_energy_storage/proc/add_charge(amount)
	energy = min(energy + amount, max_energy)

/datum/robot_energy_storage/iron
	name = "Iron Synthesizer"

/datum/robot_energy_storage/glass
	name = "Glass Synthesizer"

/datum/robot_energy_storage/wire
	max_energy = 50
	recharge_rate = 2
	name = "Wire Synthesizer"

/datum/robot_energy_storage/medical
	max_energy = 2500
	recharge_rate = 250
	name = "Medical Synthesizer"

/datum/robot_energy_storage/beacon
	max_energy = 30
	recharge_rate = 1
	name = "Marker Beacon Storage"

/datum/robot_energy_storage/pipe_cleaner
	max_energy = 50
	recharge_rate = 2
	name = "Pipe Cleaner Synthesizer"