code:
/obj/machinery/vending/engivend
	name = "\improper Engi-Vend"
	desc = "Spare tool vending. What? Did you expect some witty description?"
	icon_state = "engivend"
	icon_deny = "engivend-deny"
	req_access = list(ACCESS_ENGINE_EQUIP)
	products = list(
		/obj/item/clothing/glasses/meson/engine = 2,
		/obj/item/clothing/glasses/welding = 3,
		/obj/item/multitool = 4,
		/obj/item/grenade/chem_grenade/smart_metal_foam = 10,
		/obj/item/geiger_counter = 5,
		/obj/item/stock_parts/cell/high = 10,
		/obj/item/electronics/airlock = 10,
		/obj/item/electronics/apc = 10,
		/obj/item/electronics/airalarm = 10,
		/obj/item/electronics/firealarm = 10,
		/obj/item/electronics/firelock = 10
	)
	contraband = list(
		/obj/item/stock_parts/cell/potato = 3
	)
	premium = list(
		/obj/item/storage/belt/utility = 3,
		/obj/item/construction/rcd/loaded = 2,
		/obj/item/storage/box/smart_metal_foam = 1
	)
	refill_canister = /obj/item/vending_refill/engivend
	default_price = PAYCHECK_EASY
	extra_price = PAYCHECK_COMMAND * 1.5
	payment_department = ACCOUNT_ENG
	light_mask = "engivend-light-mask"

/obj/item/vending_refill/engivend
	machine_name = "Engi-Vend"
	icon_state = "refill_engi"


code:
/obj/machinery/vending/games
	name = "\improper Good Clean Fun"
	desc = "Vends things that the Captain and Head of Personnel are probably not going to appreciate you fiddling with instead of your job..."
	product_ads = "Escape to a fantasy world!;Fuel your gambling addiction!;Ruin your friendships!;Roll for initiative!;Elves and dwarves!;Paranoid computers!;Totally not satanic!;Fun times forever!"
	icon_state = "games"
	products = list(
		/obj/item/storage/crayons = 2,
		/obj/item/toy/cards/deck = 5,
		/obj/item/storage/dice = 10,
		/obj/item/toy/cards/deck/cas = 3,
		/obj/item/toy/cards/deck/cas/black = 3,
		/obj/item/toy/cards/deck/kotahi = 3,
		/obj/item/toy/cards/deck/tarot = 3,
		/obj/item/toy/cards/deck/wizoff = 3,
		/obj/item/hourglass = 2,
		/obj/item/instrument/piano_synth/headphones = 4,
		/obj/item/camera = 3,
		/obj/item/camera_film = 5,
		/obj/item/chisel = 3,
		/obj/item/stack/pipe_cleaner_coil/random = 10,
		/obj/item/cardpack/series_one = 10,
		/obj/item/cardpack/resin = 10,
		/obj/item/storage/card_binder = 10,
		/obj/item/skillchip/basketweaving=2,
		/obj/item/skillchip/bonsai=2,
		/obj/item/skillchip/wine_taster=2,
		/obj/item/skillchip/light_remover=2,
		/obj/item/skillchip/useless_adapter=5,
		/obj/item/dyespray=3,
		/obj/item/razor=3,
		/obj/item/canvas/nineteen_nineteen = 5,
		/obj/item/canvas/twentythree_nineteen = 5,
		/obj/item/canvas/twentythree_twentythree = 5
	)
	contraband = list(
		/obj/item/dice/fudge = 9,
		/obj/item/clothing/shoes/wheelys/skishoes=4,
		/obj/item/instrument/musicalmoth=1
	)
	premium = list(
		/obj/item/disk/holodisk = 5,
		/obj/item/toy/crayon/spraycan  = 3,
		/obj/item/canvas/twentyfour_twentyfour = 5,
		/obj/item/rcl = 2,
		/obj/item/airlock_painter = 1,
		/obj/item/melee/skateboard/pro = 3,
		/obj/item/clothing/shoes/wheelys/rollerskates= 3,
		/obj/item/melee/skateboard/hoverboard = 1,
		/obj/item/storage/box/tail_pin = 1,
		/obj/item/toy/intento = 3
	)
	refill_canister = /obj/item/vending_refill/games
	default_price = PAYCHECK_ASSISTANT
	extra_price = PAYCHECK_HARD * 1.25
	payment_department = ACCOUNT_SRV
	light_mask = "games-light-mask"

/obj/item/vending_refill/games
	machine_name = "\improper Good Clean Fun"
	icon_state = "refill_games"

repo_name: AnalWerewolf/sundown

license: mit


code:
/obj/machinery/vending/liberationstation
	name = "\improper Liberation Station"
	desc = "An overwhelming amount of <b>ancient patriotism</b> washes over you just by looking at the machine."
	icon_state = "liberationstation"
	product_slogans = "Liberation Station: Your one-stop shop for all things second amendment!;Be a patriot today, pick up a gun!;Quality weapons for cheap prices!;Better dead than red!"
	product_ads = "Float like an astronaut, sting like a bullet!;Express your second amendment today!;Guns don't kill people, but you can!;Who needs responsibilities when you have guns?"
	vend_reply = "Remember the name: Liberation Station!"
	products = list(
		/obj/item/food/burger/plain = 5, //O say can you see, by the dawn's early light
		/obj/item/food/burger/baseball = 3, //What so proudly we hailed at the twilight's last gleaming
		/obj/item/food/fries = 5, //Whose broad stripes and bright stars through the perilous fight
		/obj/item/reagent_containers/food/drinks/bottle/beer/light = 10, //O'er the ramparts we watched, were so gallantly streaming?
		/obj/item/gun/ballistic/automatic/pistol/deagle/gold = 2,
		/obj/item/gun/ballistic/automatic/pistol/deagle/camo = 2,
		/obj/item/gun/ballistic/automatic/pistol/m1911 = 2,
		/obj/item/gun/ballistic/automatic/proto/unrestricted = 2,
		/obj/item/gun/ballistic/shotgun/automatic/combat = 2,
		/obj/item/gun/ballistic/automatic/gyropistol = 1,
		/obj/item/gun/ballistic/shotgun = 2,
		/obj/item/gun/ballistic/automatic/ar = 2
	)
	premium = list(
		/obj/item/ammo_box/magazine/smgm9mm = 2,
		/obj/item/ammo_box/magazine/m50 = 4,
		/obj/item/ammo_box/magazine/m45 = 2,
		/obj/item/ammo_box/magazine/m75 = 2,
		/obj/item/food/cheesyfries = 5,
		/obj/item/food/burger/baconburger = 5 //Premium burgers for the premium section
		)
	contraband = list(
		/obj/item/clothing/under/misc/patriotsuit = 3,
		/obj/item/bedsheet/patriot = 5,
		/obj/item/food/burger/superbite = 3
	) //U S A
	armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 50)
	resistance_flags = FIRE_PROOF
	default_price = PAYCHECK_HARD * 2.5
	extra_price = PAYCHECK_COMMAND * 2.5
	payment_department = ACCOUNT_SEC
	light_mask = "liberation-light-mask"


code:
/obj/machinery/vending/toyliberationstation
	name = "\improper Syndicate Donksoft Toy Vendor"
	desc = "An ages 8 and up approved vendor that dispenses toys. If you were to find the right wires, you can unlock the adult mode setting!"
	icon_state = "syndi"
	product_slogans = "Get your cool toys today!;Trigger a valid hunter today!;Quality toy weapons for cheap prices!;Give them to HoPs for all access!;Give them to HoS to get permabrigged!"
	product_ads = "Feel robust with your toys!;Express your inner child today!;Toy weapons don't kill people, but valid hunters do!;Who needs responsibilities when you have toy weapons?;Make your next murder FUN!"
	vend_reply = "Come back for more!"
	circuit = /obj/item/circuitboard/machine/vending/syndicatedonksofttoyvendor
	products = list(
		/obj/item/gun/ballistic/automatic/toy/unrestricted = 10,
		/obj/item/gun/ballistic/automatic/pistol/toy = 10,
		/obj/item/gun/ballistic/shotgun/toy/unrestricted = 10,
		/obj/item/toy/sword = 10,
		/obj/item/ammo_box/foambox = 20,
		/obj/item/toy/foamblade = 10,
		/obj/item/toy/balloon/syndicate = 10,
		/obj/item/clothing/suit/syndicatefake = 5,
		/obj/item/clothing/head/syndicatefake = 5 //OPS IN DORMS oh wait it's just an assistant
	)
	contraband = list(
		/obj/item/gun/ballistic/shotgun/toy/crossbow = 10,   //Congrats, you unlocked the +18 setting!
		/obj/item/gun/ballistic/automatic/c20r/toy/unrestricted/riot = 10,
		/obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted/riot = 10,
		/obj/item/ammo_box/foambox/riot = 20,
		/obj/item/toy/katana = 10,
		/obj/item/dualsaber/toy = 5,
		/obj/item/toy/cards/deck/syndicate = 10 //Gambling and it hurts, making it a +18 item
	)
	armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 50)
	resistance_flags = FIRE_PROOF
	refill_canister = /obj/item/vending_refill/donksoft
	default_price = PAYCHECK_HARD
	extra_price = PAYCHECK_COMMAND
	payment_department = ACCOUNT_SRV
	light_mask = "donksoft-light-mask"


code:
/obj/machinery/vending/magivend
	name = "\improper MagiVend"
	desc = "A magic vending machine."
	icon_state = "MagiVend"
	product_slogans = "Sling spells the proper way with MagiVend!;Be your own Houdini! Use MagiVend!"
	vend_reply = "Have an enchanted evening!"
	product_ads = "FJKLFJSD;AJKFLBJAKL;1234 LOONIES LOL!;>MFW;Kill them fuckers!;GET DAT FUKKEN DISK;HONK!;EI NATH;Destroy the station!;Admin conspiracies since forever!;Space-time bending hardware!"
	products = list(
		/obj/item/clothing/head/wizard = 1,
		/obj/item/clothing/suit/wizrobe = 1,
		/obj/item/clothing/head/wizard/red = 1,
		/obj/item/clothing/suit/wizrobe/red = 1,
		/obj/item/clothing/head/wizard/yellow = 1,
		/obj/item/clothing/suit/wizrobe/yellow = 1,
		/obj/item/clothing/shoes/sandal/magic = 1,
		/obj/item/staff = 2
	)
	contraband = list(/obj/item/reagent_containers/glass/bottle/wizarditis = 1) //No one can get to the machine to hack it anyways; for the lulz - Microwave
	armor = list(MELEE = 100, BULLET = 100, LASER = 100, ENERGY = 100, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 50)
	resistance_flags = FIRE_PROOF
	default_price = 0 //Just in case, since it's primary use is storage.
	extra_price = PAYCHECK_COMMAND
	payment_department = ACCOUNT_SRV
	light_mask = "magivend-light-mask"


code:
/obj/machinery/vending/medical
	name = "\improper NanoMed Plus"
	desc = "Medical drug dispenser."
	icon_state = "med"
	icon_deny = "med-deny"
	product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!"
	req_access = list(ACCESS_MEDICAL)
	products = list(
		/obj/item/stack/medical/gauze = 8,
		/obj/item/reagent_containers/syringe = 12,
		/obj/item/reagent_containers/dropper = 3,
		/obj/item/healthanalyzer = 4,
		/obj/item/wrench/medical = 1,
		/obj/item/stack/sticky_tape/surgical = 3,
		/obj/item/healthanalyzer/wound = 4,
		/obj/item/stack/medical/ointment = 2,
		/obj/item/stack/medical/suture = 2,
		/obj/item/stack/medical/bone_gel/four = 4
	)
	contraband = list(
		/obj/item/storage/box/gum/happiness = 3,
		/obj/item/storage/box/hug/medical = 1
	)
	premium = list(
		/obj/item/reagent_containers/hypospray/medipen = 3,
		/obj/item/storage/belt/medical = 3,
		/obj/item/sensor_device = 2,
		/obj/item/pinpointer/crew = 2,
		/obj/item/storage/firstaid/advanced = 2,
		/obj/item/shears = 1,
		/obj/item/storage/organbox = 1
	)
	refill_canister = /obj/item/vending_refill/medical
	default_price = PAYCHECK_EASY
	extra_price = PAYCHECK_COMMAND
	payment_department = ACCOUNT_MED
	light_mask = "med-light-mask"

/obj/item/vending_refill/medical
	machine_name = "NanoMed Plus"
	icon_state = "refill_medical"

/obj/machinery/vending/medical/syndicate_access
	name = "\improper SyndiMed Plus"
	req_access = list(ACCESS_SYNDICATE)

//Created out of a necessity to get these dumb chems out of the medical tools vendor.
/obj/machinery/vending/drugs
	name = "\improper NanoDrug Plus"
	desc = "Medical drugs dispenser."
	icon_state = "drug"
	icon_deny = "drug-deny"
	product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!"
	req_access = list(ACCESS_MEDICAL)
	products = list(/obj/item/reagent_containers/pill/patch/libital = 5,
					/obj/item/reagent_containers/pill/patch/aiuri = 5,
					/obj/item/reagent_containers/syringe/convermol = 2,
					/obj/item/reagent_containers/pill/insulin = 5,
					/obj/item/reagent_containers/glass/bottle/multiver = 2,
					/obj/item/reagent_containers/glass/bottle/syriniver = 2,
					/obj/item/reagent_containers/glass/bottle/epinephrine = 3,
					/obj/item/reagent_containers/glass/bottle/morphine = 4,
					/obj/item/reagent_containers/glass/bottle/potass_iodide = 1,
					/obj/item/reagent_containers/glass/bottle/salglu_solution = 3,
					/obj/item/reagent_containers/glass/bottle/toxin = 3,
					/obj/item/reagent_containers/syringe/antiviral = 6,
					/obj/item/reagent_containers/medigel/libital = 2,
					/obj/item/reagent_containers/medigel/aiuri = 2,
					/obj/item/reagent_containers/medigel/sterilizine = 1)
	contraband = list(/obj/item/reagent_containers/pill/tox = 3,
					/obj/item/reagent_containers/pill/morphine = 4,
					/obj/item/reagent_containers/pill/multiver = 6)
	premium = list(/obj/item/reagent_containers/medigel/synthflesh = 2,
					/obj/item/storage/pill_bottle/psicodine = 2)
	default_price = 50
	extra_price = 100
	payment_department = ACCOUNT_MED
	refill_canister = /obj/item/vending_refill/drugs

/obj/item/vending_refill/drugs
	machine_name = "NanoDrug Plus"
	icon_state = "refill_medical"


code:
/obj/machinery/vending/wallmed
	name = "\improper NanoMed"
	desc = "Wall-mounted Medical Equipment dispenser."
	icon_state = "wallmed"
	icon_deny = "wallmed-deny"
	density = FALSE
	products = list(
		/obj/item/reagent_containers/syringe = 3,
		/obj/item/reagent_containers/pill/patch/libital = 5,
		/obj/item/reagent_containers/pill/patch/aiuri = 5,
		/obj/item/reagent_containers/pill/multiver = 2,
		/obj/item/reagent_containers/medigel/libital = 2,
		/obj/item/reagent_containers/medigel/aiuri = 2,
		/obj/item/reagent_containers/medigel/sterilizine = 1,
		/obj/item/healthanalyzer/wound = 2,
		/obj/item/stack/medical/bone_gel/four = 2
	)
	contraband = list(
		/obj/item/reagent_containers/pill/tox = 2,
		/obj/item/reagent_containers/pill/morphine = 2,
		/obj/item/storage/box/gum/happiness = 1
	)
	refill_canister = /obj/item/vending_refill/wallmed
	default_price = PAYCHECK_HARD //Double the medical price due to being meant for public consumption, not player specfic
	extra_price = PAYCHECK_HARD * 1.5
	payment_department = ACCOUNT_MED
	tiltable = FALSE
	light_mask = "wallmed-light-mask"

/obj/machinery/vending/wallmed/directional/north
	dir = SOUTH
	pixel_y = 32

/obj/machinery/vending/wallmed/directional/south
	dir = NORTH
	pixel_y = -32

/obj/machinery/vending/wallmed/directional/east
	dir = WEST
	pixel_x = 32

/obj/machinery/vending/wallmed/directional/west
	dir = EAST
	pixel_x = -32

/obj/item/vending_refill/wallmed
	machine_name = "NanoMed"
	icon_state = "refill_medical"


code:
/obj/machinery/vending/hydroseeds
	name = "\improper MegaSeed Servitor"
	desc = "When you need seeds fast!"
	product_slogans = "THIS'S WHERE TH' SEEDS LIVE! GIT YOU SOME!;Hands down the best seed selection on the station!;Also certain mushroom varieties available, more for experts! Get certified today!"
	product_ads = "We like plants!;Grow some crops!;Grow, baby, growww!;Aw h'yeah son!"
	icon_state = "seeds"
	light_mask = "seeds-light-mask"
	products = list(
		/obj/item/seeds/aloe = 3,
		/obj/item/seeds/ambrosia = 3,
		/obj/item/seeds/apple = 3,
		/obj/item/seeds/banana = 3,
		/obj/item/seeds/berry = 3,
		/obj/item/seeds/cabbage = 3,
		/obj/item/seeds/carrot = 3,
		/obj/item/seeds/cherry = 3,
		/obj/item/seeds/chanter = 3,
		/obj/item/seeds/chili = 3,
		/obj/item/seeds/cocoapod = 3,
		/obj/item/seeds/coffee = 3,
		/obj/item/seeds/cotton = 3,
		/obj/item/seeds/corn = 3,
		/obj/item/seeds/eggplant = 3,
		/obj/item/seeds/garlic = 3,
		/obj/item/seeds/grape = 3,
		/obj/item/seeds/grass = 3,
		/obj/item/seeds/korta_nut = 3,
		/obj/item/seeds/lemon = 3,
		/obj/item/seeds/lime = 3,
		/obj/item/seeds/onion = 3,
		/obj/item/seeds/orange = 3,
		/obj/item/seeds/peas = 3,
		/obj/item/seeds/pineapple = 3,
		/obj/item/seeds/potato = 3,
		/obj/item/seeds/poppy = 3,
		/obj/item/seeds/pumpkin = 3,
		/obj/item/seeds/wheat/rice = 3,
		/obj/item/seeds/rose = 3,
		/obj/item/seeds/soya = 3,
		/obj/item/seeds/sugarcane = 3,
		/obj/item/seeds/sunflower = 3,
		/obj/item/seeds/tea = 3,
		/obj/item/seeds/tobacco = 3,
		/obj/item/seeds/tomato = 3,
		/obj/item/seeds/tower = 3,
		/obj/item/seeds/watermelon = 3,
		/obj/item/seeds/wheat = 3,
		/obj/item/seeds/whitebeet = 3
	)
	contraband = list(
		/obj/item/seeds/amanita = 2,
		/obj/item/seeds/glowshroom = 2,
		/obj/item/seeds/liberty = 2,
		/obj/item/seeds/nettle = 2,
		/obj/item/seeds/plump = 2,
		/obj/item/seeds/reishi = 2,
		/obj/item/seeds/cannabis = 3,
		/obj/item/seeds/starthistle = 2,
		/obj/item/seeds/random = 2
	)
	premium = list(
		/obj/item/reagent_containers/spray/waterflower = 1
	)
	refill_canister = /obj/item/vending_refill/hydroseeds
	default_price = PAYCHECK_PRISONER
	extra_price = PAYCHECK_ASSISTANT
	payment_department = ACCOUNT_SRV

/obj/item/vending_refill/hydroseeds
	machine_name = "MegaSeed Servitor"
	icon_state = "refill_plant"


code:
/obj/machinery/vending/modularpc
	name = "\improper Deluxe Silicate Selections"
	desc = "All the parts you need to build your own custom pc."
	icon_state = "modularpc"
	icon_deny = "modularpc-deny"
	light_mask = "modular-light-mask"
	product_ads = "Get your gamer gear!;The best GPUs for all of your space-crypto needs!;The most robust cooling!;The finest RGB in space!"
	vend_reply = "Game on!"
	products = list(
		/obj/item/modular_computer/laptop = 4,
		/obj/item/modular_computer/tablet = 4,
		/obj/item/computer_hardware/hard_drive = 4,
		/obj/item/computer_hardware/hard_drive/small = 4,
		/obj/item/computer_hardware/network_card = 8,
		/obj/item/computer_hardware/hard_drive/portable = 8,
		/obj/item/computer_hardware/battery = 8,
		/obj/item/stock_parts/cell/computer = 8,
		/obj/item/computer_hardware/processor_unit = 4,
		/obj/item/computer_hardware/processor_unit/small = 4,
		/obj/item/computer_hardware/sensorpackage = 4
	)
	premium = list(
		/obj/item/computer_hardware/card_slot = 2,
		/obj/item/computer_hardware/ai_slot = 2,
		/obj/item/computer_hardware/printer/mini = 2,
		/obj/item/computer_hardware/recharger/apc_recharger = 2,
		/obj/item/paicard = 2,
		/obj/item/computer_hardware/radio_card = 1
	)
	refill_canister = /obj/item/vending_refill/modularpc
	default_price = PAYCHECK_MEDIUM
	extra_price = PAYCHECK_HARD
	payment_department = ACCOUNT_SCI

/obj/item/vending_refill/modularpc
	machine_name = "Deluxe Silicate Selections"
	icon_state = "refill_engi"


code:
/obj/machinery/vending/hydronutrients
	name = "\improper NutriMax"
	desc = "A plant nutrients vendor."
	product_slogans = "Aren't you glad you don't have to fertilize the natural way?;Now with 50% less stink!;Plants are people too!"
	product_ads = "We like plants!;Don't you want some?;The greenest thumbs ever.;We like big plants.;Soft soil..."
	icon_state = "nutri"
	icon_deny = "nutri-deny"
	light_mask = "nutri-light-mask"
	products = list(
		/obj/item/reagent_containers/glass/bottle/nutrient/ez = 30,
		/obj/item/reagent_containers/glass/bottle/nutrient/l4z = 20,
		/obj/item/reagent_containers/glass/bottle/nutrient/rh = 10,
		/obj/item/reagent_containers/spray/pestspray = 20,
		/obj/item/reagent_containers/syringe = 5,
		/obj/item/storage/bag/plants = 5,
		/obj/item/cultivator = 3,
		/obj/item/shovel/spade = 3,
		/obj/item/secateurs = 3,
		/obj/item/plant_analyzer = 4
	)
	contraband = list(
		/obj/item/reagent_containers/glass/bottle/ammonia = 10,
		/obj/item/reagent_containers/glass/bottle/diethylamine = 5
	)
	refill_canister = /obj/item/vending_refill/hydronutrients
	default_price = PAYCHECK_ASSISTANT * 0.8
	extra_price = PAYCHECK_HARD * 0.8
	payment_department = ACCOUNT_SRV

/obj/item/vending_refill/hydronutrients
	machine_name = "NutriMax"
	icon_state = "refill_plant"


code:
//This one's from bay12
/obj/machinery/vending/plasmaresearch
	name = "\improper Bombuddy 3000"
	desc = "All the fine parts you need in one vending machine!"
	products = list(
		/obj/item/clothing/under/rank/rnd/scientist = 6,
		/obj/item/clothing/suit/bio_suit = 6,
		/obj/item/clothing/head/bio_hood = 6,
		/obj/item/transfer_valve = 6,
		/obj/item/assembly/timer = 6,
		/obj/item/assembly/signaler = 6,
		/obj/item/assembly/prox_sensor = 6,
		/obj/item/assembly/igniter = 6
	)
	contraband = list(/obj/item/assembly/health = 3)
	default_price = PAYCHECK_ASSISTANT
	extra_price = PAYCHECK_ASSISTANT
	payment_department = ACCOUNT_SCI


code:
//This one's from bay12
/obj/machinery/vending/robotics
	name = "\improper Robotech Deluxe"
	desc = "All the tools you need to create your own robot army."
	icon_state = "robotics"
	icon_deny = "robotics-deny"
	light_mask = "robotics-light-mask"
	req_access = list(ACCESS_ROBOTICS)
	products = list(
		/obj/item/clothing/suit/toggle/labcoat = 4,
		/obj/item/clothing/under/rank/rnd/roboticist = 4,
		/obj/item/stack/cable_coil = 4,
		/obj/item/assembly/flash/handheld = 4,
		/obj/item/stock_parts/cell/high = 12,
		/obj/item/assembly/prox_sensor = 3,
		/obj/item/assembly/signaler = 3,
		/obj/item/healthanalyzer = 3,
		/obj/item/scalpel = 2,
		/obj/item/circular_saw = 2,
		/obj/item/bonesetter = 2,
		/obj/item/tank/internals/anesthetic = 2,
		/obj/item/clothing/mask/breath/medical = 5,
		/obj/item/screwdriver = 5,
		/obj/item/crowbar = 5
	)
	refill_canister = /obj/item/vending_refill/robotics
	default_price = PAYCHECK_HARD
	payment_department = ACCOUNT_SCI

/obj/item/vending_refill/robotics
	machine_name = "Robotech Deluxe"
	icon_state = "refill_engi"


code:
/obj/machinery/vending/security
	name = "\improper SecTech"
	desc = "A security equipment vendor."
	product_ads = "Crack communist skulls!;Beat some heads in!;Don't forget - harm is good!;Your weapons are right here.;Handcuffs!;Freeze, scumbag!;Don't tase me bro!;Tase them, bro.;Why not have a donut?"
	icon_state = "sec"
	icon_deny = "sec-deny"
	light_mask = "sec-light-mask"
	req_access = list(ACCESS_SECURITY)
	products = list(
		/obj/item/restraints/handcuffs = 8,
		/obj/item/restraints/handcuffs/cable/zipties = 10,
		/obj/item/grenade/flashbang = 4,
		/obj/item/assembly/flash/handheld = 5,
		/obj/item/food/donut = 12,
		/obj/item/storage/box/evidence = 6,
		/obj/item/flashlight/seclite = 4,
		/obj/item/restraints/legcuffs/bola/energy = 7
	)
	contraband = list(
		/obj/item/clothing/glasses/sunglasses = 2,
		/obj/item/storage/fancy/donut_box = 2
	)
	premium = list(
		/obj/item/storage/belt/security/webbing = 5,
		/obj/item/coin/antagtoken = 1,
		/obj/item/clothing/head/helmet/blueshirt = 1,
		/obj/item/clothing/suit/armor/vest/blueshirt = 1,
		/obj/item/clothing/gloves/tackler = 5,
		/obj/item/grenade/stingbang = 1,
		/obj/item/watertank/pepperspray = 2
	)
	refill_canister = /obj/item/vending_refill/security
	default_price = PAYCHECK_MEDIUM
	extra_price = PAYCHECK_HARD * 1.5
	payment_department = ACCOUNT_SEC

/obj/machinery/vending/security/pre_throw(obj/item/I)
	if(istype(I, /obj/item/grenade))
		var/obj/item/grenade/G = I
		G.arm_grenade()
	else if(istype(I, /obj/item/flashlight))
		var/obj/item/flashlight/F = I
		F.on = TRUE
		F.update_brightness()

/obj/item/vending_refill/security
	icon_state = "refill_sec"


code:
/obj/machinery/vending/snack
	name = "\improper Getmore Chocolate Corp"
	desc = "A snack machine courtesy of the Getmore Chocolate Corporation, based out of Mars."
	product_slogans = "Try our new nougat bar!;Twice the calories for half the price!"
	product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!"
	icon_state = "snack"
	light_mask = "snack-light-mask"
	products = list(
		/obj/item/food/spacetwinkie = 6,
		/obj/item/food/cheesiehonkers = 6,
		/obj/item/food/candy = 6,
		/obj/item/food/chips = 6,
		/obj/item/food/chips/shrimp = 6,
		/obj/item/food/sosjerky = 6,
		/obj/item/food/chips = 6,
		/obj/item/food/sosjerky = 6,
		/obj/item/food/no_raisin = 6,
		/obj/item/food/peanuts = 6,
		/obj/item/food/peanuts/random = 3,
		/obj/item/food/cnds = 6,
		/obj/item/food/cnds/random = 3,
		/obj/item/reagent_containers/food/drinks/dry_ramen = 3,
		/obj/item/storage/box/gum = 3,
		/obj/item/food/energybar = 6
	)
	contraband = list(
		/obj/item/food/syndicake = 6,
		/obj/item/food/peanuts/ban_appeal = 3,
		/obj/item/food/candy/bronx = 1
	)
	refill_canister = /obj/item/vending_refill/snack
	canload_access_list = list(ACCESS_KITCHEN)
	default_price = PAYCHECK_ASSISTANT * 0.6
	extra_price = PAYCHECK_EASY
	payment_department = ACCOUNT_SRV
	input_display_header = "Chef's Food Selection"

/obj/item/vending_refill/snack
	machine_name = "Getmore Chocolate Corp"

/obj/machinery/vending/snack/blue
	icon_state = "snackblue"

/obj/machinery/vending/snack/orange
	icon_state = "snackorange"

/obj/machinery/vending/snack/green
	icon_state = "snackgreen"

/obj/machinery/vending/snack/teal
	icon_state = "snackteal"


code:
/obj/machinery/vending/sovietsoda
	name = "\improper BODA"
	desc = "Old sweet water vending machine."
	icon_state = "sovietsoda"
	light_mask = "soviet-light-mask"
	product_ads = "For Tsar and Country.;Have you fulfilled your nutrition quota today?;Very nice!;We are simple people, for this is all we eat.;If there is a person, there is a problem. If there is no person, then there is no problem."
	products = list(
		/obj/item/reagent_containers/food/drinks/drinkingglass/filled/soda = 30
	)
	contraband = list(
		/obj/item/reagent_containers/food/drinks/drinkingglass/filled/cola = 20
	)
	resistance_flags = FIRE_PROOF
	refill_canister = /obj/item/vending_refill/sovietsoda
	default_price = 1
	extra_price = PAYCHECK_ASSISTANT //One credit for every state of FREEDOM
	payment_department = NO_FREEBIES
	light_color = COLOR_PALE_ORANGE

/obj/item/vending_refill/sovietsoda
	machine_name = "BODA"
	icon_state = "refill_cola"


code:
/obj/machinery/vending/sustenance
	name = "\improper Sustenance Vendor"
	desc = "A vending machine which vends food, as required by section 47-C of the NT's Prisoner Ethical Treatment Agreement."
	product_slogans = "Enjoy your meal.;Enough calories to support strenuous labor."
	product_ads = "Sufficiently healthy.;Efficiently produced tofu!;Mmm! So good!;Have a meal.;You need food to live!;Even prisoners deserve their daily bread!;Have some more candy corn!;Try our new ice cups!"
	light_mask = "snack-light-mask"
	icon_state = "sustenance"
	products = list(
		/obj/item/food/tofu/prison = 24,
		/obj/item/food/breadslice/moldy = 15,
		/obj/item/reagent_containers/food/drinks/ice/prison = 12,
		/obj/item/food/candy_corn/prison = 6,
		/obj/item/kitchen/spoon/plastic = 6,
	)
	contraband = list(
		/obj/item/knife = 6,
		/obj/item/kitchen/spoon = 6,
		/obj/item/reagent_containers/food/drinks/coffee = 12,
		/obj/item/tank/internals/emergency_oxygen = 6,
		/obj/item/clothing/mask/breath = 6,
	)

	refill_canister = /obj/item/vending_refill/sustenance
	default_price = PAYCHECK_PRISONER
	extra_price = PAYCHECK_PRISONER * 0.6
	payment_department = NO_FREEBIES

/obj/item/vending_refill/sustenance
	machine_name = "Sustenance Vendor"
	icon_state = "refill_snack"


code:
/obj/machinery/vending/donksofttoyvendor
	name = "\improper Donksoft Toy Vendor"
	desc = "Ages 8 and up approved vendor that dispenses toys."
	icon_state = "nt-donk"
	product_slogans = "Get your cool toys today!;Trigger a valid hunter today!;Quality toy weapons for cheap prices!;Give them to HoPs for all access!;Give them to HoS to get permabrigged!"
	product_ads = "Feel robust with your toys!;Express your inner child today!;Toy weapons don't kill people, but valid hunters do!;Who needs responsibilities when you have toy weapons?;Make your next murder FUN!"
	vend_reply = "Come back for more!"
	light_mask = "donksoft-light-mask"
	circuit = /obj/item/circuitboard/machine/vending/donksofttoyvendor
	products = list(
		/obj/item/gun/ballistic/automatic/toy/unrestricted = 10,
		/obj/item/gun/ballistic/automatic/pistol/toy = 10,
		/obj/item/gun/ballistic/shotgun/toy/unrestricted = 10,
		/obj/item/toy/sword = 10,
		/obj/item/ammo_box/foambox = 20,
		/obj/item/toy/foamblade = 10,
		/obj/item/toy/balloon/syndicate = 10,
		/obj/item/clothing/suit/syndicatefake = 5,
		/obj/item/clothing/head/syndicatefake = 5
	)
	contraband = list(
		/obj/item/gun/ballistic/shotgun/toy/crossbow = 10,
		/obj/item/gun/ballistic/automatic/c20r/toy/unrestricted = 10,
		/obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted = 10,
		/obj/item/toy/katana = 10,
		/obj/item/dualsaber/toy = 5
	)
	refill_canister = /obj/item/vending_refill/donksoft
	default_price = PAYCHECK_ASSISTANT
	extra_price = PAYCHECK_HARD
	payment_department = ACCOUNT_SRV

/obj/item/vending_refill/donksoft
	machine_name = "Donksoft Toy Vendor"
	icon_state = "refill_donksoft"


code:
/obj/item/vending_refill/wardrobe
	icon_state = "refill_clothes"

/obj/machinery/vending/wardrobe
	default_price = PAYCHECK_ASSISTANT
	extra_price = PAYCHECK_HARD
	payment_department = NO_FREEBIES
	input_display_header = "Returned Clothing"
	light_mask = "wardrobe-light-mask"

/obj/machinery/vending/wardrobe/canLoadItem(obj/item/I,mob/user)
	return (I.type in products)

/obj/machinery/vending/wardrobe/sec_wardrobe
	name = "\improper SecDrobe"
	desc = "A vending machine for security and security-related clothing!"
	icon_state = "secdrobe"
	product_ads = "Beat perps in style!;It's red so you can't see the blood!;You have the right to be fashionable!;Now you can be the fashion police you always wanted to be!"
	vend_reply = "Thank you for using the SecDrobe!"
	products = list(/obj/item/clothing/suit/hooded/wintercoat/security = 3,
					/obj/item/storage/backpack/security = 3,
					/obj/item/storage/backpack/satchel/sec = 3,
					/obj/item/storage/backpack/duffelbag/sec = 3,
					/obj/item/clothing/under/rank/security/officer = 3,
					/obj/item/clothing/shoes/jackboots = 3,
					/obj/item/clothing/head/beret/sec = 3,
					/obj/item/clothing/head/soft/sec = 3,
					/obj/item/clothing/mask/bandana/red = 3,
					/obj/item/clothing/gloves/color/black = 3,
					/obj/item/clothing/under/rank/security/officer/skirt = 3,
					/obj/item/clothing/under/rank/security/officer/grey = 3,
					/obj/item/clothing/under/pants/khaki = 3,
					/obj/item/clothing/under/rank/security/officer/blueshirt = 3)
	premium = list(/obj/item/clothing/under/rank/security/officer/formal = 3,
					/obj/item/clothing/suit/security/officer = 3,
					/obj/item/clothing/head/beret/sec/navyofficer = 3)
	refill_canister = /obj/item/vending_refill/wardrobe/sec_wardrobe
	payment_department = ACCOUNT_SEC
	light_color = COLOR_MOSTLY_PURE_RED

/obj/item/vending_refill/wardrobe/sec_wardrobe
	machine_name = "SecDrobe"

/obj/machinery/vending/wardrobe/medi_wardrobe
	name = "\improper MediDrobe"
	desc = "A vending machine rumoured to be capable of dispensing clothing for medical personnel."
	icon_state = "medidrobe"
	product_ads = "Make those blood stains look fashionable!!"
	vend_reply = "Thank you for using the MediDrobe!"
	products = list(/obj/item/clothing/accessory/pocketprotector = 4,
					/obj/item/storage/backpack/duffelbag/med = 4,
					/obj/item/storage/backpack/medic = 4,
					/obj/item/storage/backpack/satchel/med = 4,
					/obj/item/clothing/suit/hooded/wintercoat/medical = 4,
					/obj/item/clothing/suit/hooded/wintercoat/medical/paramedic = 4,
					/obj/item/clothing/under/rank/medical/paramedic = 4,
					/obj/item/clothing/under/rank/medical/paramedic/skirt = 4,
					/obj/item/clothing/under/rank/medical/doctor/nurse = 4,
					/obj/item/clothing/head/nursehat = 4,
					/obj/item/clothing/head/beret/medical = 4,
					/obj/item/clothing/under/rank/medical/doctor/skirt= 4,
					/obj/item/clothing/under/rank/medical/doctor/blue = 4,
					/obj/item/clothing/under/rank/medical/doctor/green = 4,
					/obj/item/clothing/under/rank/medical/doctor/purple = 4,
					/obj/item/clothing/under/rank/medical/doctor = 4,
					/obj/item/clothing/suit/toggle/labcoat = 4,
					/obj/item/clothing/suit/toggle/labcoat/paramedic = 4,
					/obj/item/clothing/shoes/sneakers/white = 4,
					/obj/item/clothing/head/beret/medical/paramedic = 4,
					/obj/item/clothing/head/soft/paramedic = 4,
					/obj/item/clothing/suit/apron/surgical = 4,
					/obj/item/clothing/mask/surgical = 4)
	refill_canister = /obj/item/vending_refill/wardrobe/medi_wardrobe
	payment_department = ACCOUNT_MED

/obj/item/vending_refill/wardrobe/medi_wardrobe
	machine_name = "MediDrobe"

/obj/machinery/vending/wardrobe/engi_wardrobe
	name = "EngiDrobe"
	desc = "A vending machine renowned for vending industrial grade clothing."
	icon_state = "engidrobe"
	product_ads = "Guaranteed to protect your feet from industrial accidents!;Afraid of radiation? Then wear yellow!"
	vend_reply = "Thank you for using the EngiDrobe!"
	products = list(/obj/item/clothing/accessory/pocketprotector = 3,
					/obj/item/storage/backpack/duffelbag/engineering = 3,
					/obj/item/storage/backpack/industrial = 3,
					/obj/item/storage/backpack/satchel/eng = 3,
					/obj/item/clothing/suit/hooded/wintercoat/engineering = 3,
					/obj/item/clothing/under/rank/engineering/engineer = 3,
					/obj/item/clothing/under/rank/engineering/engineer/skirt = 3,
					/obj/item/clothing/under/rank/engineering/engineer/hazard = 3,
					/obj/item/clothing/suit/hazardvest = 3,
					/obj/item/clothing/shoes/workboots = 3,
					/obj/item/clothing/head/beret/engi = 3,
					/obj/item/clothing/head/hardhat = 3,
					/obj/item/clothing/head/hardhat/weldhat = 3)
	refill_canister = /obj/item/vending_refill/wardrobe/engi_wardrobe
	payment_department = ACCOUNT_ENG
	light_color = COLOR_VIVID_YELLOW

/obj/item/vending_refill/wardrobe/engi_wardrobe
	machine_name = "EngiDrobe"

/obj/machinery/vending/wardrobe/atmos_wardrobe
	name = "AtmosDrobe"
	desc = "This relatively unknown vending machine delivers clothing for Atmospherics Technicians, an equally unknown job."
	icon_state = "atmosdrobe"
	product_ads = "Get your inflammable clothing right here!!!"
	vend_reply = "Thank you for using the AtmosDrobe!"
	products = list(/obj/item/clothing/accessory/pocketprotector = 2,
					/obj/item/storage/backpack/duffelbag/engineering = 2,
					/obj/item/storage/backpack/satchel/eng = 2,
					/obj/item/storage/backpack/industrial = 2,
					/obj/item/clothing/suit/hooded/wintercoat/engineering/atmos = 3,
					/obj/item/clothing/under/rank/engineering/atmospheric_technician = 3,
					/obj/item/clothing/under/rank/engineering/atmospheric_technician/skirt = 3,
					/obj/item/clothing/head/beret/atmos = 3,
					/obj/item/clothing/shoes/sneakers/black = 3)
	refill_canister = /obj/item/vending_refill/wardrobe/atmos_wardrobe
	payment_department = ACCOUNT_ENG
	light_color = COLOR_VIVID_YELLOW

/obj/item/vending_refill/wardrobe/atmos_wardrobe
	machine_name = "AtmosDrobe"

/obj/machinery/vending/wardrobe/cargo_wardrobe
	name = "CargoDrobe"
	desc = "A highly advanced vending machine for buying cargo related clothing for free."
	icon_state = "cargodrobe"
	product_ads = "Upgraded Assistant Style! Pick yours today!;These shorts are comfy and easy to wear, get yours now!"
	vend_reply = "Thank you for using the CargoDrobe!"
	products = list(
		/obj/item/storage/bag/mail = 3,
		/obj/item/clothing/suit/hooded/wintercoat/cargo = 3,
		/obj/item/clothing/under/rank/cargo/tech = 3,
		/obj/item/clothing/under/rank/cargo/tech/skirt = 3,
		/obj/item/clothing/shoes/sneakers/black = 3,
		/obj/item/clothing/gloves/fingerless = 3,
		/obj/item/clothing/head/beret/cargo = 3,
		/obj/item/clothing/head/soft = 3,
		/obj/item/radio/headset/headset_cargo = 3
	)
	premium = list(
		/obj/item/clothing/under/rank/cargo/miner = 3,
		/obj/item/clothing/head/mailman = 1,
		/obj/item/clothing/under/misc/mailman = 1
	)
	refill_canister = /obj/item/vending_refill/wardrobe/cargo_wardrobe
	payment_department = ACCOUNT_CAR

/obj/item/vending_refill/wardrobe/cargo_wardrobe
	machine_name = "CargoDrobe"

/obj/machinery/vending/wardrobe/robo_wardrobe
	name = "RoboDrobe"
	desc = "A vending machine designed to dispense clothing known only to roboticists."
	icon_state = "robodrobe"
	product_ads = "You turn me TRUE, use defines!;0110001101101100011011110111010001101000011001010111001101101000011001010111001001100101"
	vend_reply = "Thank you for using the RoboDrobe!"
	products = list(/obj/item/clothing/glasses/hud/diagnostic = 2,
					/obj/item/clothing/under/rank/rnd/roboticist = 2,
					/obj/item/clothing/under/rank/rnd/roboticist/skirt = 2,
					/obj/item/clothing/suit/toggle/labcoat/roboticist = 2,
					/obj/item/clothing/suit/hooded/wintercoat/science/robotics = 3,
					/obj/item/clothing/shoes/sneakers/black = 2,
					/obj/item/clothing/gloves/fingerless = 2,
					/obj/item/clothing/head/soft/black = 2,
					/obj/item/clothing/mask/bandana/skull = 2)
	contraband = list(/obj/item/clothing/suit/hooded/techpriest = 2,
					/obj/item/organ/tongue/robot = 2)
	refill_canister = /obj/item/vending_refill/wardrobe/robo_wardrobe
	extra_price = PAYCHECK_HARD * 1.2
	payment_department = ACCOUNT_SCI
/obj/item/vending_refill/wardrobe/robo_wardrobe
	machine_name = "RoboDrobe"

/obj/machinery/vending/wardrobe/science_wardrobe
	name = "SciDrobe"
	desc = "A simple vending machine suitable to dispense well tailored science clothing. Endorsed by Space Cubans."
	icon_state = "scidrobe"
	product_ads = "Longing for the smell of plasma burnt flesh? Buy your science clothing now!;Made with 10% Auxetics, so you don't have to worry about losing your arm!"
	vend_reply = "Thank you for using the SciDrobe!"
	products = list(/obj/item/clothing/accessory/pocketprotector = 3,
					/obj/item/storage/backpack/science = 3,
					/obj/item/storage/backpack/satchel/science = 3,
					/obj/item/storage/backpack/duffelbag/science = 3,
					/obj/item/clothing/head/beret/science = 3,
					/obj/item/clothing/head/beret/science/fancy = 3,
					/obj/item/clothing/suit/hooded/wintercoat/science = 3,
					/obj/item/clothing/under/rank/rnd/scientist = 3,
					/obj/item/clothing/under/rank/rnd/scientist/skirt = 3,
					/obj/item/clothing/suit/toggle/labcoat/science = 3,
					/obj/item/clothing/shoes/sneakers/white = 3,
					/obj/item/radio/headset/headset_sci = 3,
					/obj/item/clothing/mask/gas = 3)
	refill_canister = /obj/item/vending_refill/wardrobe/science_wardrobe
	payment_department = ACCOUNT_SCI
/obj/item/vending_refill/wardrobe/science_wardrobe
	machine_name = "SciDrobe"

/obj/machinery/vending/wardrobe/hydro_wardrobe
	name = "Hydrobe"
	desc = "A machine with a catchy name. It dispenses botany related clothing and gear."
	icon_state = "hydrobe"
	product_ads = "Do you love soil? Then buy our clothes!;Get outfits to match your green thumb here!"
	vend_reply = "Thank you for using the Hydrobe!"
	products = list(/obj/item/storage/backpack/botany = 2,
					/obj/item/storage/backpack/satchel/hyd = 2,
					/obj/item/storage/backpack/duffelbag/hydroponics = 2,
					/obj/item/clothing/suit/hooded/wintercoat/hydro = 2,
					/obj/item/clothing/suit/apron = 2,
					/obj/item/clothing/suit/apron/overalls = 3,
					/obj/item/clothing/suit/apron/waders = 3,
					/obj/item/clothing/under/rank/civilian/hydroponics = 3,
					/obj/item/clothing/under/rank/civilian/hydroponics/skirt = 3,
					/obj/item/clothing/mask/bandana = 3,
					/obj/item/clothing/accessory/armband/hydro = 3)
	refill_canister = /obj/item/vending_refill/wardrobe/hydro_wardrobe
	payment_department = ACCOUNT_SRV
	light_color = LIGHT_COLOR_ELECTRIC_GREEN

/obj/item/vending_refill/wardrobe/hydro_wardrobe
	machine_name = "HyDrobe"

/obj/machinery/vending/wardrobe/curator_wardrobe
	name = "CuraDrobe"
	desc = "A lowstock vendor only capable of vending clothing for curators and librarians."
	icon_state = "curadrobe"
	product_ads = "Glasses for your eyes and literature for your soul, Curadrobe has it all!; Impress & enthrall your library guests with Curadrobe's extended line of pens!"
	vend_reply = "Thank you for using the CuraDrobe!"
	products = list(/obj/item/pen = 4,
					/obj/item/pen/red = 2,
					/obj/item/pen/blue = 2,
					/obj/item/pen/fourcolor = 1,
					/obj/item/pen/fountain = 2,
					/obj/item/clothing/accessory/pocketprotector = 2,
					/obj/item/clothing/under/rank/civilian/curator = 1,
					/obj/item/clothing/under/rank/civilian/curator/skirt = 1,
					/obj/item/clothing/under/rank/captain/suit = 1,
					/obj/item/clothing/under/rank/captain/suit/skirt = 1,
					/obj/item/clothing/under/rank/civilian/head_of_personnel/suit = 1,
					/obj/item/clothing/under/rank/civilian/head_of_personnel/suit/skirt = 1,
					/obj/item/storage/backpack/satchel/explorer = 1,
					/obj/item/clothing/glasses/regular = 2,
					/obj/item/clothing/glasses/regular/jamjar = 1,
					/obj/item/storage/bag/books = 1)
	refill_canister = /obj/item/vending_refill/wardrobe/curator_wardrobe
	payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/curator_wardrobe
	machine_name = "CuraDrobe"

/obj/machinery/vending/wardrobe/bar_wardrobe
	name = "BarDrobe"
	desc = "A stylish vendor to dispense the most stylish bar clothing!"
	icon_state = "bardrobe"
	product_ads = "Guaranteed to prevent stains from spilled drinks!"
	vend_reply = "Thank you for using the BarDrobe!"
	products = list(/obj/item/clothing/head/that = 2,
					/obj/item/radio/headset/headset_srv = 2,
					/obj/item/clothing/under/suit/sl = 2,
					/obj/item/clothing/under/rank/civilian/bartender = 2,
					/obj/item/clothing/under/rank/civilian/bartender/purple = 2,
					/obj/item/clothing/under/rank/civilian/bartender/skirt = 2,
					/obj/item/clothing/accessory/waistcoat = 2,
					/obj/item/clothing/suit/apron/purple_bartender = 2,
					/obj/item/clothing/head/soft/black = 2,
					/obj/item/clothing/shoes/sneakers/black = 2,
					/obj/item/reagent_containers/glass/rag = 2,
					/obj/item/storage/box/beanbag = 1,
					/obj/item/clothing/suit/armor/vest/alt = 1,
					/obj/item/circuitboard/machine/dish_drive = 1,
					/obj/item/clothing/glasses/sunglasses/reagent = 1,
					/obj/item/clothing/neck/petcollar = 1,
					/obj/item/storage/belt/bandolier = 1,
					/obj/item/storage/dice/hazard = 1,
					/obj/item/storage/bag/money = 2)
	premium = list(/obj/item/storage/box/dishdrive = 1)
	refill_canister = /obj/item/vending_refill/wardrobe/bar_wardrobe
	payment_department = ACCOUNT_SRV
	extra_price = PAYCHECK_HARD
/obj/item/vending_refill/wardrobe/bar_wardrobe
	machine_name = "BarDrobe"

/obj/machinery/vending/wardrobe/chef_wardrobe
	name = "ChefDrobe"
	desc = "This vending machine might not dispense meat, but it certainly dispenses chef related clothing."
	icon_state = "chefdrobe"
	product_ads = "Our clothes are guaranteed to protect you from food splatters!"
	vend_reply = "Thank you for using the ChefDrobe!"
	products = list(/obj/item/clothing/under/suit/waiter = 2,
					/obj/item/radio/headset/headset_srv = 2,
					/obj/item/clothing/accessory/waistcoat = 2,
					/obj/item/clothing/suit/apron/chef = 3,
					/obj/item/clothing/head/soft/mime = 2,
					/obj/item/storage/box/mousetraps = 2,
					/obj/item/circuitboard/machine/dish_drive = 1,
					/obj/item/clothing/suit/toggle/chef = 1,
					/obj/item/clothing/under/rank/civilian/chef = 1,
					/obj/item/clothing/under/rank/civilian/chef/skirt = 2,
					/obj/item/clothing/head/chefhat = 1,
					/obj/item/clothing/under/rank/civilian/cookjorts = 2,
					/obj/item/clothing/shoes/cookflops = 2,
					/obj/item/reagent_containers/glass/rag = 1,
					/obj/item/clothing/suit/hooded/wintercoat = 2)
	refill_canister = /obj/item/vending_refill/wardrobe/chef_wardrobe
	payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/chef_wardrobe
	machine_name = "ChefDrobe"

/obj/machinery/vending/wardrobe/jani_wardrobe
	name = "JaniDrobe"
	desc = "A self cleaning vending machine capable of dispensing clothing for janitors."
	icon_state = "janidrobe"
	product_ads = "Come and get your janitorial clothing, now endorsed by lizard janitors everywhere!"
	vend_reply = "Thank you for using the JaniDrobe!"
	products = list(/obj/item/clothing/under/rank/civilian/janitor = 2,
					/obj/item/cartridge/janitor = 2,
					/obj/item/clothing/under/rank/civilian/janitor/skirt = 2,
					/obj/item/clothing/suit/hooded/wintercoat/janitor = 2,
					/obj/item/clothing/gloves/color/black = 2,
					/obj/item/clothing/head/soft/purple = 2,
					/obj/item/pushbroom = 2,
					/obj/item/paint/paint_remover = 2,
					/obj/item/melee/flyswatter = 2,
					/obj/item/flashlight = 2,
					/obj/item/clothing/suit/caution = 6,
					/obj/item/holosign_creator = 2,
					/obj/item/lightreplacer = 2,
					/obj/item/soap/nanotrasen = 2,
					/obj/item/storage/bag/trash = 2,
					/obj/item/clothing/shoes/galoshes = 2,
					/obj/item/watertank/janitor = 1,
					/obj/item/storage/belt/janitor = 2)
	refill_canister = /obj/item/vending_refill/wardrobe/jani_wardrobe
	default_price = PAYCHECK_EASY
	extra_price = PAYCHECK_HARD * 0.8
	payment_department = ACCOUNT_SRV
	light_color = COLOR_STRONG_MAGENTA

/obj/item/vending_refill/wardrobe/jani_wardrobe
	machine_name = "JaniDrobe"

/obj/machinery/vending/wardrobe/law_wardrobe
	name = "LawDrobe"
	desc = "Objection! This wardrobe dispenses the rule of law... and lawyer clothing."
	icon_state = "lawdrobe"
	product_ads = "OBJECTION! Get the rule of law for yourself!"
	vend_reply = "Thank you for using the LawDrobe!"
	products = list(/obj/item/clothing/under/rank/civilian/lawyer/bluesuit = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/bluesuit/skirt = 1,
					/obj/item/clothing/suit/toggle/lawyer = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/purpsuit = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/purpsuit/skirt = 1,
					/obj/item/clothing/suit/toggle/lawyer/purple = 1,
					/obj/item/clothing/under/suit/black = 1,
					/obj/item/clothing/under/suit/black/skirt = 1,
					/obj/item/clothing/suit/toggle/lawyer/black = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/female = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/female/skirt = 1,
					/obj/item/clothing/under/suit/black_really = 1,
					/obj/item/clothing/under/suit/black_really/skirt = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/blue = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/blue/skirt = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/red = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/red/skirt = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/black = 1,
					/obj/item/clothing/under/rank/civilian/lawyer/black/skirt = 1,
					/obj/item/clothing/shoes/laceup = 2,
					/obj/item/clothing/accessory/lawyers_badge = 2)
	refill_canister = /obj/item/vending_refill/wardrobe/law_wardrobe
	payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/law_wardrobe
	machine_name = "LawDrobe"

/obj/machinery/vending/wardrobe/chap_wardrobe
	name = "DeusVend"
	desc = "God wills your purchase."
	icon_state = "chapdrobe"
	product_ads = "Are you being bothered by cultists or pesky revenants? Then come and dress like the holy man!;Clothes for men of the cloth!"
	vend_reply = "Thank you for using the DeusVend!"
	products = list(/obj/item/choice_beacon/holy = 1,
					/obj/item/storage/backpack/cultpack = 1,
					/obj/item/clothing/accessory/pocketprotector/cosmetology = 1,
					/obj/item/clothing/under/rank/civilian/chaplain = 1,
					/obj/item/clothing/under/rank/civilian/chaplain/skirt = 2,
					/obj/item/clothing/shoes/sneakers/black = 1,
					/obj/item/clothing/suit/chaplainsuit/nun = 1,
					/obj/item/clothing/head/nun_hood = 1,
					/obj/item/clothing/suit/chaplainsuit/holidaypriest = 1,
					/obj/item/clothing/suit/hooded/chaplainsuit/monkhabit = 1,
					/obj/item/storage/fancy/candle_box = 2,
					/obj/item/clothing/head/kippah = 3,
					/obj/item/clothing/suit/chaplainsuit/whiterobe = 1,
					/obj/item/clothing/head/taqiyahwhite = 1,
					/obj/item/clothing/head/taqiyahred = 3,
					/obj/item/clothing/suit/chaplainsuit/monkrobeeast = 1,
					/obj/item/clothing/head/beanie/rasta = 1)
	contraband = list(/obj/item/toy/plush/ratplush = 1,
					/obj/item/toy/plush/narplush = 1,
					/obj/item/clothing/head/medievaljewhat = 3,
					/obj/item/clothing/suit/chaplainsuit/clownpriest = 1,
					/obj/item/clothing/head/clownmitre = 1)
	premium = list(/obj/item/clothing/suit/chaplainsuit/bishoprobe = 1,
					/obj/item/clothing/head/bishopmitre = 1)
	refill_canister = /obj/item/vending_refill/wardrobe/chap_wardrobe
	payment_department = ACCOUNT_SRV
/obj/item/vending_refill/wardrobe/chap_wardrobe
	machine_name = "DeusVend"

/obj/machinery/vending/wardrobe/chem_wardrobe
	name = "ChemDrobe"
	desc = "A vending machine for dispensing chemistry related clothing."
	icon_state = "chemdrobe"
	product_ads = "Our clothes are 0.5% more resistant to acid spills! Get yours now!"
	vend_reply = "Thank you for using the ChemDrobe!"
	products = list(/obj/item/clothing/under/rank/medical/chemist = 2,
					/obj/item/clothing/under/rank/medical/chemist/skirt = 2,
					/obj/item/clothing/head/beret/medical = 2,
					/obj/item/clothing/shoes/sneakers/white = 2,
					/obj/item/clothing/suit/toggle/labcoat/chemist = 2,
					/obj/item/clothing/suit/hooded/wintercoat/medical/chemistry = 2,
					/obj/item/storage/backpack/chemistry = 2,
					/obj/item/storage/backpack/satchel/chem = 2,
					/obj/item/storage/backpack/duffelbag/chemistry = 2,
					/obj/item/storage/bag/chemistry = 2,
					/obj/item/ph_booklet = 3)
	contraband = list(/obj/item/reagent_containers/spray/syndicate = 2)
	refill_canister = /obj/item/vending_refill/wardrobe/chem_wardrobe
	payment_department = ACCOUNT_MED
/obj/item/vending_refill/wardrobe/chem_wardrobe
	machine_name = "ChemDrobe"

/obj/machinery/vending/wardrobe/gene_wardrobe
	name = "GeneDrobe"
	desc = "A machine for dispensing clothing related to genetics."
	icon_state = "genedrobe"
	product_ads = "Perfect for the mad scientist in you!"
	vend_reply = "Thank you for using the GeneDrobe!"
	products = list(
		/obj/item/clothing/under/rank/rnd/geneticist = 2,
		/obj/item/clothing/under/rank/rnd/geneticist/skirt = 2,
		/obj/item/clothing/shoes/sneakers/white = 2,
		/obj/item/clothing/suit/toggle/labcoat/genetics = 2,
		/obj/item/clothing/suit/hooded/wintercoat/science/genetics = 2,
		/obj/item/storage/backpack/genetics = 2,
		/obj/item/storage/backpack/satchel/gen = 2,
		/obj/item/storage/backpack/duffelbag/genetics = 2
	)
	refill_canister = /obj/item/vending_refill/wardrobe/gene_wardrobe
	payment_department = ACCOUNT_SCI
/obj/item/vending_refill/wardrobe/gene_wardrobe
	machine_name = "GeneDrobe"

/obj/machinery/vending/wardrobe/viro_wardrobe
	name = "ViroDrobe"
	desc = "An unsterilized machine for dispending virology related clothing."
	icon_state = "virodrobe"
	product_ads = " Viruses getting you down? Then upgrade to sterilized clothing today!"
	vend_reply = "Thank you for using the ViroDrobe"
	products = list(/obj/item/clothing/under/rank/medical/virologist = 2,
					/obj/item/clothing/under/rank/medical/virologist/skirt = 2,
					/obj/item/clothing/head/beret/medical = 2,
					/obj/item/clothing/shoes/sneakers/white = 2,
					/obj/item/clothing/suit/toggle/labcoat/virologist = 2,
					/obj/item/clothing/suit/hooded/wintercoat/medical/viro = 2,
					/obj/item/clothing/mask/surgical = 2,
					/obj/item/storage/backpack/virology = 2,
					/obj/item/storage/backpack/satchel/vir = 2,
					/obj/item/storage/backpack/duffelbag/virology = 2)
	refill_canister = /obj/item/vending_refill/wardrobe/viro_wardrobe
	payment_department = ACCOUNT_MED
/obj/item/vending_refill/wardrobe/viro_wardrobe
	machine_name = "ViroDrobe"

/obj/machinery/vending/wardrobe/det_wardrobe
	name = "\improper DetDrobe"
	desc = "A machine for all your detective needs, as long as you need clothes."
	icon_state = "detdrobe"
	product_ads = "Apply your brilliant deductive methods in style!"
	vend_reply = "Thank you for using the DetDrobe!"
	products = list(/obj/item/clothing/under/rank/security/detective = 2,
					/obj/item/clothing/under/rank/security/detective/skirt = 2,
					/obj/item/clothing/shoes/sneakers/brown = 2,
					/obj/item/clothing/suit/det_suit = 2,
					/obj/item/clothing/head/fedora/det_hat = 2,
					/obj/item/clothing/under/rank/security/detective/grey = 2,
					/obj/item/clothing/under/rank/security/detective/grey/skirt = 2,
					/obj/item/clothing/accessory/waistcoat = 2,
					/obj/item/clothing/shoes/laceup = 2,
					/obj/item/clothing/suit/det_suit/grey = 1,
					/obj/item/clothing/suit/det_suit/noir = 1,
					/obj/item/clothing/head/fedora = 2,
					/obj/item/clothing/gloves/color/black = 2,
					/obj/item/clothing/gloves/color/latex = 2,
					/obj/item/clothing/under/rank/security/detective/disco = 1,
					/obj/item/clothing/suit/det_suit/disco = 1,
					/obj/item/clothing/shoes/discoshoes = 1,
					/obj/item/clothing/neck/tie/disco = 1,
					/obj/item/clothing/under/rank/security/detective/kim = 1,
					/obj/item/clothing/suit/det_suit/kim = 1,
					/obj/item/clothing/shoes/kim = 1,
					/obj/item/clothing/gloves/kim = 1,
					/obj/item/clothing/glasses/regular/kim = 1,
					/obj/item/reagent_containers/food/drinks/flask/det = 2,
					/obj/item/storage/fancy/cigarettes = 5)
	premium = list(/obj/item/clothing/head/flatcap = 1)
	refill_canister = /obj/item/vending_refill/wardrobe/det_wardrobe
	extra_price = PAYCHECK_COMMAND * 1.75
	payment_department = ACCOUNT_SEC

/obj/item/vending_refill/wardrobe/det_wardrobe
	machine_name = "DetDrobe"


code:
/obj/machinery/vending/tool
	name = "\improper YouTool"
	desc = "Tools for tools."
	icon_state = "tool"
	icon_deny = "tool-deny"
	light_mask = "tool-light-mask"
	products = list(
		/obj/item/stack/cable_coil = 10,
		/obj/item/crowbar = 5,
		/obj/item/weldingtool = 3,
		/obj/item/wirecutters = 5,
		/obj/item/wrench = 5,
		/obj/item/analyzer = 5,
		/obj/item/t_scanner = 5,
		/obj/item/screwdriver = 5,
		/obj/item/flashlight/glowstick = 3,
		/obj/item/flashlight/glowstick/red = 3,
		/obj/item/flashlight = 5,
		/obj/item/clothing/ears/earmuffs = 1
	)
	contraband = list(
		/obj/item/clothing/gloves/color/fyellow = 2
	)
	premium = list(
		/obj/item/storage/belt/utility = 2,
		/obj/item/multitool = 2,
		/obj/item/weldingtool/hugetank = 2,
		/obj/item/clothing/head/welding = 2,
		/obj/item/clothing/gloves/color/yellow = 1
	)
	refill_canister = /obj/item/vending_refill/youtool
	default_price = PAYCHECK_ASSISTANT
	extra_price = PAYCHECK_COMMAND * 1.5
	payment_department = ACCOUNT_ENG

/obj/item/vending_refill/youtool
	machine_name = "YouTool"
	icon_state = "refill_engi"


code:
/**
 * # Binary Decimal Component
 *
 * Has a bit array on one side and a decimal number on the other.
 */
/obj/item/circuit_component/binary_decimal
	display_name = "Decimal - Bit Array"
	desc = "Splits a decimal number into an array of binary digits and vicecersa."
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

	/// One number
	var/datum/port/output/number

	/// Many binary digits
	var/list/datum/port/bit_array = list()

	var/array_size = 0

	var/default_array_size = 8

	var/min_size = 1 //Who in their right mind would use a 1-bit array for circuits anyway?
	var/max_size = MAX_BITFIELD_SIZE

	ui_buttons = list(
		"plus" = "increase",
		"minus" = "decrease"
	)

/obj/item/circuit_component/binary_decimal/save_data_to_list(list/component_data)
	. = ..()
	component_data["array_size"] = array_size

/obj/item/circuit_component/binary_decimal/load_data_from_list(list/component_data)
	set_array_size(component_data["array_size"])
	return ..()

/obj/item/circuit_component/binary_decimal/proc/set_array_size(new_size)
	if(new_size <= 0)
		for(var/datum/port/port in bit_array)
			remove_bit_port(port)
		bit_array = list()
		array_size = 0
		return

	while(array_size > new_size)
		var/index = length(bit_array)
		var/datum/port/output = bit_array[index]
		bit_array -= output
		remove_bit_port(output)
		array_size--

	while(array_size < new_size)
		array_size++
		var/index = length(bit_array)
		bit_array += add_bit_port(index)

/obj/item/circuit_component/binary_decimal/proc/add_bit_port(index)
	return

/obj/item/circuit_component/binary_decimal/proc/remove_bit_port(datum/port/to_remove)
	return

/obj/item/circuit_component/binary_decimal/populate_ports()
	set_array_size(default_array_size)

// Increase or decrease the array size
/obj/item/circuit_component/binary_decimal/ui_perform_action(mob/user, action)
	switch(action)
		if("increase")
			set_array_size(min(array_size + 1, max_size))
		if("decrease")
			set_array_size(max(array_size - 1, min_size))


code:
/**
 * # Compare Component
 *
 * Abstract component to build conditional components
 */
/obj/item/circuit_component/compare
	display_name = "Compare"

	/// The amount of input ports to have
	var/input_port_amount = 4

	/// The trigger for the true/false signals
	var/datum/port/input/compare

	/// Signals sent on compare
	var/datum/port/output/true
	var/datum/port/output/false

	/// The result from the output
	var/datum/port/output/result

	var/list/datum/port/input/compare_ports = list()

/obj/item/circuit_component/compare/populate_ports()
	for(var/port_id in 1 to input_port_amount)
		var/letter = ascii2text(text2ascii("A") + (port_id-1))
		compare_ports += add_input_port(letter, PORT_TYPE_ANY)

	populate_custom_ports()
	compare = add_input_port("Compare", PORT_TYPE_SIGNAL)

	true = add_output_port("True", PORT_TYPE_SIGNAL)
	false = add_output_port("False", PORT_TYPE_SIGNAL)
	result = add_output_port("Result", PORT_TYPE_NUMBER)

/**
 * Used by derivatives to load their own ports in for custom use.
 */
/obj/item/circuit_component/compare/proc/populate_custom_ports()
	return

/obj/item/circuit_component/compare/input_received(datum/port/input/port)

	var/logic_result = do_comparisons(compare_ports)
	if(COMPONENT_TRIGGERED_BY(compare, port))
		if(logic_result)
			true.set_output(COMPONENT_SIGNAL)
		else
			false.set_output(COMPONENT_SIGNAL)
	result.set_output(logic_result)

/// Do the comparisons and return a result
/obj/item/circuit_component/compare/proc/do_comparisons(list/ports)
	return FALSE


code:
/**
 * # Module Component
 *
 * A component that has an input, output
 */
/obj/item/circuit_component/module
	display_name = "Module"
	desc = "A component that has other components within it, acting like a function. Use it in your hand to control the amount of input and output ports it has, as well as being able to access the integrated circuit contained inside."

	var/obj/item/integrated_circuit/module/internal_circuit

	var/obj/item/circuit_component/module_input/input_component
	var/obj/item/circuit_component/module_output/output_component

	/// Linked ports that follow a `first_port = second_port` keyed structure.
	var/list/linked_ports = list()

	var/port_limit = 10

	ui_buttons = list(
		"edit" = "action"
	)

/obj/item/integrated_circuit/module
	var/obj/item/circuit_component/module/attached_module

/obj/item/integrated_circuit/module/ui_host(mob/user)
	if(attached_module)
		return attached_module.ui_host()
	return ..()

/obj/item/integrated_circuit/module/set_display_name(new_name)
	. = ..()
	attached_module.display_name = new_name
	attached_module.name = "module ([new_name])"

/obj/item/integrated_circuit/module/load_component(type)
	if(!attached_module)
		return ..()

	if(ispath(type, /obj/item/circuit_component/module_input))
		return attached_module.input_component

	if(ispath(type, /obj/item/circuit_component/module_output))
		return attached_module.output_component

	return ..()

/obj/item/integrated_circuit/module/add_component(obj/item/circuit_component/to_add, mob/living/user)
	if(to_add.circuit_flags & CIRCUIT_FLAG_REFUSE_MODULE)
		balloon_alert(user, "doesn't fit into module!")
		return
	. = ..()
	if(attached_module)
		attached_module.circuit_size += to_add.circuit_size

/obj/item/integrated_circuit/module/remove_component(obj/item/circuit_component/to_remove)
	if(attached_module)
		attached_module.circuit_size -= to_remove.circuit_size
	return ..()

/obj/item/integrated_circuit/module/Destroy()
	attached_module = null
	return ..()

/obj/item/circuit_component/module_input
	display_name = "Input"
	desc = "A component that receives data from the module it is attached to"

	removable = FALSE

	/// The currently attached module
	var/obj/item/circuit_component/module/attached_module

/obj/item/circuit_component/module_input/Destroy()
	attached_module = null
	return ..()

/obj/item/circuit_component/module_output
	display_name = "Output"
	desc = "A component that outputs data to the module it is attached to."

	removable = FALSE

	/// The currently attached module
	var/obj/item/circuit_component/module/attached_module

/obj/item/circuit_component/module_output/pre_input_received(datum/port/input/port)
	if(!port)
		return
	// We don't check the parent here because frankly, we don't care. We only sync our input with the module's output
	var/datum/port/output/port_to_update = attached_module.linked_ports[port]
	if(!port_to_update)
		CRASH("[port.type] doesn't have a linked port in [type]!")

	port_to_update.set_output(port.value)

/obj/item/circuit_component/module/pre_input_received(datum/port/input/port)
	if(!port)
		return
	var/datum/port/output/port_to_update = linked_ports[port]
	if(!port_to_update)
		CRASH("[port.type] doesn't have a linked port in [type]!")

	port_to_update.set_output(port.value)

/obj/item/circuit_component/module_output/Destroy()
	attached_module = null
	return ..()

/obj/item/circuit_component/module/Initialize(mapload)
	. = ..()
	internal_circuit = new(src)
	internal_circuit.attached_module = src

	input_component = new(internal_circuit)
	input_component.attached_module = src
	internal_circuit.add_component(input_component)
	input_component.rel_x = 0
	input_component.rel_y = 200

	output_component = new(internal_circuit)
	output_component.attached_module = src
	internal_circuit.add_component(output_component)
	output_component.rel_x = 400
	output_component.rel_y = 200

/obj/item/circuit_component/module/save_data_to_list(list/component_data)
	. = ..()
	component_data["integrated_circuit"] = internal_circuit.convert_to_json()

	var/list/input_data = list()
	for(var/datum/port/input/input_port as anything in input_ports)
		input_data += list(list(
			"name" = input_port.name,
			"type" = input_port.datatype,
		))

	var/list/output_data = list()
	for(var/datum/port/output/output_port as anything in output_ports)
		output_data += list(list(
			"name" = output_port.name,
			"type" = output_port.datatype,
		))

	component_data["input_ports"] = input_data
	component_data["output_ports"] = output_data

/obj/item/circuit_component/module/load_data_from_list(list/component_data)
	. = ..()

	var/list/input_ports = component_data["input_ports"]
	for(var/list/port_data as anything in input_ports)
		add_and_link_input_port(port_data["name"], port_data["type"])

	var/list/output_ports = component_data["output_ports"]
	for(var/list/port_data as anything in output_ports)
		add_and_link_output_port(port_data["name"], port_data["type"])

	if(component_data["integrated_circuit"])
		internal_circuit.load_circuit_data(component_data["integrated_circuit"])

/obj/item/circuit_component/module/proc/add_and_link_input_port(name, type)
	var/datum/port/new_port = add_input_port(name, type)
	linked_ports[new_port] = input_component.add_output_port(name, type)

/obj/item/circuit_component/module/proc/add_and_link_output_port(name, type)
	var/datum/port/new_port = output_component.add_input_port(name, type)
	linked_ports[new_port] = add_output_port(name, type)

/obj/item/circuit_component/module/add_to(obj/item/integrated_circuit/added_to)
	. = ..()
	RegisterSignal(added_to, COMSIG_CIRCUIT_SET_CELL, .proc/handle_set_cell)
	RegisterSignal(added_to, COMSIG_CIRCUIT_SET_ON, .proc/handle_set_on)
	RegisterSignal(added_to, COMSIG_CIRCUIT_SET_SHELL, .proc/handle_set_shell)
	internal_circuit.set_cell(added_to.cell)
	internal_circuit.set_shell(added_to.shell)
	internal_circuit.set_on(added_to.on)

/obj/item/circuit_component/module/removed_from(obj/item/integrated_circuit/removed_from)
	internal_circuit.set_cell(null)
	internal_circuit.set_on(FALSE)
	internal_circuit.remove_current_shell()
	UnregisterSignal(removed_from, list(
		COMSIG_CIRCUIT_SET_CELL,
		COMSIG_CIRCUIT_SET_ON,
		COMSIG_CIRCUIT_SET_SHELL,
	))
	return ..()

/obj/item/circuit_component/module/proc/handle_set_cell(datum/source, obj/item/stock_parts/cell/cell)
	SIGNAL_HANDLER
	internal_circuit.set_cell(cell)

/obj/item/circuit_component/module/proc/handle_set_on(datum/source, new_value)
	SIGNAL_HANDLER
	internal_circuit.set_on(new_value)

/obj/item/circuit_component/module/proc/handle_set_shell(datum/source, atom/movable/new_shell)
	SIGNAL_HANDLER
	internal_circuit.set_shell(new_shell)

/obj/item/circuit_component/module/Destroy()
	QDEL_NULL(input_component)
	QDEL_NULL(output_component)
	QDEL_NULL(internal_circuit)
	linked_ports = null
	return ..()

/obj/item/circuit_component/module/ui_data(mob/user)
	. = list()
	.["input_ports"] = list()
	for(var/datum/port/input/input_port as anything in input_ports)
		.["input_ports"] += list(list(
			"name" = input_port.name,
			"type" = input_port.datatype,
		))

	.["output_ports"] = list()
	for(var/datum/port/output/output_port as anything in output_ports)
		.["output_ports"] += list(list(
			"name" = output_port.name,
			"type" = output_port.datatype,
		))

/obj/item/circuit_component/module/ui_static_data(mob/user)
	. = list()
	.["global_port_types"] = GLOB.wiremod_basic_types

/obj/item/circuit_component/module/attackby(obj/item/I, mob/living/user, params)
	if(istype(I, /obj/item/circuit_component))
		internal_circuit.attackby(I, user, params)
		return
	return ..()

#define WITHIN_RANGE(id, table) (id >= 1 && id <= length(table))

/obj/item/circuit_component/module/ui_act(action, list/params)
	. = ..()
	if(.)
		return

	switch(action)
		if("open_internal_circuit")
			internal_circuit.interact(usr)
			. = TRUE
		if("add_input_port")
			if(length(input_ports) > port_limit)
				return
			add_and_link_input_port("Input Port", PORT_TYPE_ANY)
			. = TRUE
		if("remove_input_port")
			var/port_id = text2num(params["port_id"])
			if(!WITHIN_RANGE(port_id, input_ports))
				return
			var/datum/port/removed_port = input_ports[port_id]
			linked_ports -= removed_port
			remove_input_port(removed_port)
			input_component.remove_output_port(input_component.output_ports[port_id])
			. = TRUE
		if("add_output_port")
			if(length(output_ports) > port_limit)
				return
			add_and_link_output_port("Output Port", PORT_TYPE_ANY)
			. = TRUE
		if("remove_output_port")
			var/port_id = text2num(params["port_id"])
			if(!WITHIN_RANGE(port_id, output_ports))
				return

			var/datum/port/removed_port = output_component.input_ports[port_id]
			linked_ports -= removed_port
			remove_output_port(output_ports[port_id])
			output_component.remove_input_port(removed_port)
			. = TRUE
		if("set_port_name", "set_port_type")
			var/port_id = text2num(params["port_id"])
			var/is_input = params["is_input"]

			var/list/ports_to_use
			var/list/internal_ports_to_use
			if(is_input)
				ports_to_use = input_ports
				internal_ports_to_use = input_component.output_ports
			else
				ports_to_use = output_ports
				internal_ports_to_use = output_component.input_ports

			if(!WITHIN_RANGE(port_id, ports_to_use))
				return

			var/datum/port/component_port = ports_to_use[port_id]
			var/datum/port/internal_component_port = internal_ports_to_use[port_id]

			if(action == "set_port_type")
				var/type = params["port_type"]
				if(!(type in GLOB.wiremod_basic_types))
					return
				component_port.set_datatype(type)
				internal_component_port.set_datatype(type)
			else
				var/port_name = params["port_name"]
				if(!port_name)
					return
				port_name = strip_html(port_name, PORT_MAX_NAME_LENGTH)
				component_port.name = port_name
				internal_component_port.name = port_name
			. = TRUE

	if(.)
		SStgui.update_uis(internal_circuit)

#undef WITHIN_RANGE

/obj/item/circuit_component/module/ui_perform_action(mob/user, action)
	interact(user)

/obj/item/circuit_component/module/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "CircuitModule", name)
		ui.open()
		ui.set_autoupdate(FALSE)


code:
/**
 * # Variable Component
 *
 * Abstract component for handling variables
 */
/obj/item/circuit_component/variable
	display_name = "Variable Getter"
	desc = "A component that gets a variable globally on the circuit."

	/// Variable name
	var/datum/port/input/option/variable_name

	var/datum/circuit_variable/current_variable
	circuit_size = 0

/obj/item/circuit_component/variable/populate_options()
	variable_name = add_option_port("Variable", null)

/obj/item/circuit_component/variable/add_to(obj/item/integrated_circuit/added_to)
	. = ..()
	variable_name.possible_options = added_to.circuit_variables

/obj/item/circuit_component/variable/removed_from(obj/item/integrated_circuit/removed_from)
	variable_name.possible_options = null
	return ..()

/obj/item/circuit_component/variable/pre_input_received(datum/port/input/port)
	if(!parent)
		return

	var/variable_string = variable_name.value
	if(!variable_string)
		remove_current_variable()
		return

	var/datum/circuit_variable/variable = parent.circuit_variables[variable_string]
	if(!variable)
		remove_current_variable()
		return

	set_current_variable(variable)

/obj/item/circuit_component/variable/proc/remove_current_variable()
	SIGNAL_HANDLER
	if(current_variable)
		current_variable.remove_listener(src)
		UnregisterSignal(current_variable, COMSIG_PARENT_QDELETING)
		current_variable = null

/obj/item/circuit_component/variable/proc/set_current_variable(datum/circuit_variable/variable)
	if(variable == current_variable)
		return

	remove_current_variable()
	current_variable = variable
	current_variable.add_listener(src)
	RegisterSignal(current_variable, COMSIG_PARENT_QDELETING, .proc/remove_current_variable)


code:
/**
 * # Light Component
 *
 * Emits a light of a specific brightness and colour. Requires a shell.
 */
/obj/item/circuit_component/light
	display_name = "Light"
	desc = "A component that emits a light of a specific brightness and colour. Requires a shell."

	/// The colours of the light
	var/datum/port/input/red
	var/datum/port/input/green
	var/datum/port/input/blue

	/// The brightness
	var/datum/port/input/brightness

	/// Whether the light is on or not
	var/datum/port/input/on

	var/max_power = 5
	var/min_lightness = 0.4
	var/shell_light_color

/obj/item/circuit_component/light/get_ui_notices()
	. = ..()
	. += create_ui_notice("Maximum Brightness: [max_power]", "orange", "lightbulb")

/obj/item/circuit_component/light/populate_ports()
	red = add_input_port("Red", PORT_TYPE_NUMBER)
	green = add_input_port("Green", PORT_TYPE_NUMBER)
	blue = add_input_port("Blue", PORT_TYPE_NUMBER)
	brightness = add_input_port("Brightness", PORT_TYPE_NUMBER)

	on = add_input_port("On", PORT_TYPE_NUMBER)

/obj/item/circuit_component/light/register_shell(atom/movable/shell)
	. = ..()
	trigger_component()

/obj/item/circuit_component/light/unregister_shell(atom/movable/shell)
	shell.set_light_on(FALSE)
	return ..()

/obj/item/circuit_component/light/pre_input_received(datum/port/input/port)
	brightness.set_value(clamp(brightness.value || 0, 0, max_power))
	red.set_value(clamp(red.value, 0, 255))
	blue.set_value(clamp(blue.value, 0, 255))
	green.set_value(clamp(green.value, 0, 255))
	var/list/hsl = rgb2hsl(red.value || 0, green.value || 0, blue.value || 0)
	var/list/light_col = hsl2rgb(hsl[1], hsl[2], max(min_lightness, hsl[3]))
	shell_light_color = rgb(light_col[1], light_col[2], light_col[3])

/obj/item/circuit_component/light/input_received(datum/port/input/port)
	if(parent.shell)
		set_atom_light(parent.shell)

/obj/item/circuit_component/light/proc/set_atom_light(atom/movable/target_atom)
	// Clamp anyways just for safety
	var/bright_val = min(max(brightness.value || 0, 0), max_power)

	target_atom.set_light_power(bright_val)
	target_atom.set_light_range(bright_val)
	target_atom.set_light_color(shell_light_color)
	target_atom.set_light_on(!!on.value)


code:
/**
 * # Man-Machine Interface Component
 *
 * Allows an MMI to be inserted into a shell, allowing it to be linked up. Requires a shell.
 */
/obj/item/circuit_component/mmi
	display_name = "Man-Machine Interface"
	desc = "A component that allows MMI to enter shells to send output signals."
	circuit_flags = CIRCUIT_FLAG_REFUSE_MODULE

	/// The message to send to the MMI in the shell.
	var/datum/port/input/message
	/// Sends the current MMI a message
	var/datum/port/input/send
	/// Ejects the current MMI
	var/datum/port/input/eject

	/// Called when the MMI tries moving north
	var/datum/port/output/north
	/// Called when the MMI tries moving east
	var/datum/port/output/east
	/// Called when the MMI tries moving south
	var/datum/port/output/south
	/// Called when the MMI tries moving west
	var/datum/port/output/west

	/// Returns what the MMI last clicked on.
	var/datum/port/output/clicked_atom
	/// Called when the MMI clicks.
	var/datum/port/output/attack
	/// Called when the MMI right clicks.
	var/datum/port/output/secondary_attack

	/// The current MMI card
	var/obj/item/mmi/brain

	/// Maximum length of the message that can be sent to the MMI
	var/max_length = 300

/obj/item/circuit_component/mmi/populate_ports()
	message = add_input_port("Message", PORT_TYPE_STRING)
	send = add_input_port("Send Message", PORT_TYPE_SIGNAL)
	eject = add_input_port("Eject", PORT_TYPE_SIGNAL)

	north = add_output_port("North", PORT_TYPE_SIGNAL)
	east = add_output_port("East", PORT_TYPE_SIGNAL)
	south = add_output_port("South", PORT_TYPE_SIGNAL)
	west = add_output_port("West", PORT_TYPE_SIGNAL)

	attack = add_output_port("Attack", PORT_TYPE_SIGNAL)
	secondary_attack = add_output_port("Secondary Attack", PORT_TYPE_SIGNAL)
	clicked_atom = add_output_port("Target Entity", PORT_TYPE_ATOM)

/obj/item/circuit_component/mmi/Destroy()
	remove_current_brain()
	return ..()

/obj/item/circuit_component/mmi/input_received(datum/port/input/port)

	if(!brain)
		return

	if(COMPONENT_TRIGGERED_BY(eject, port))
		remove_current_brain()
	if(COMPONENT_TRIGGERED_BY(send, port))
		if(!message.value)
			return

		var/msg_str = copytext(html_encode(message.value), 1, max_length)

		var/mob/living/target = brain.brainmob
		if(!target)
			return

		to_chat(target, "[span_bold("You hear a message in your ear: ")][msg_str]")

/obj/item/circuit_component/mmi/register_shell(atom/movable/shell)
	. = ..()
	RegisterSignal(shell, COMSIG_PARENT_ATTACKBY, .proc/handle_attack_by)

/obj/item/circuit_component/mmi/unregister_shell(atom/movable/shell)
	UnregisterSignal(shell, COMSIG_PARENT_ATTACKBY)
	remove_current_brain()
	return ..()

/obj/item/circuit_component/mmi/proc/handle_attack_by(atom/movable/shell, obj/item/item, mob/living/attacker)
	SIGNAL_HANDLER
	if(istype(item, /obj/item/mmi))
		var/obj/item/mmi/target_mmi = item
		if(!target_mmi.brainmob)
			return
		add_mmi(item)
		return COMPONENT_NO_AFTERATTACK

/obj/item/circuit_component/mmi/proc/add_mmi(obj/item/mmi/to_add)
	remove_current_brain()

	to_add.forceMove(src)
	if(to_add.brainmob)
		update_mmi_mob(to_add, null, to_add.brainmob)
	brain = to_add
	RegisterSignal(to_add, COMSIG_PARENT_QDELETING, .proc/remove_current_brain)
	RegisterSignal(to_add, COMSIG_MOVABLE_MOVED, .proc/mmi_moved)

/obj/item/circuit_component/mmi/proc/mmi_moved(atom/movable/mmi)
	SIGNAL_HANDLER

	if(mmi.loc != src)
		remove_current_brain()

/obj/item/circuit_component/mmi/proc/remove_current_brain()
	SIGNAL_HANDLER
	if(!brain)
		return

	if(brain.brainmob)
		update_mmi_mob(brain, brain.brainmob)
	UnregisterSignal(brain, list(
		COMSIG_PARENT_QDELETING,
		COMSIG_MOVABLE_MOVED
	))
	if(brain.loc == src)
		brain.forceMove(drop_location())
	brain = null

/obj/item/circuit_component/mmi/proc/update_mmi_mob(datum/source, mob/living/old_mmi, mob/living/new_mmi)
	SIGNAL_HANDLER
	if(old_mmi)
		old_mmi.remote_control = null
		UnregisterSignal(old_mmi, COMSIG_MOB_CLICKON)
	if(new_mmi)
		new_mmi.remote_control = src
		RegisterSignal(new_mmi, COMSIG_MOB_CLICKON, .proc/handle_mmi_attack)

/obj/item/circuit_component/mmi/relaymove(mob/living/user, direct)
	if(user != brain.brainmob)
		return ..()

	if(direct & NORTH)
		north.set_output(COMPONENT_SIGNAL)
	if(direct & WEST)
		west.set_output(COMPONENT_SIGNAL)
	if(direct & EAST)
		east.set_output(COMPONENT_SIGNAL)
	if(direct & SOUTH)
		south.set_output(COMPONENT_SIGNAL)

	return TRUE

/obj/item/circuit_component/mmi/proc/handle_mmi_attack(mob/living/source, atom/target, list/modifiers)
	SIGNAL_HANDLER
	if(modifiers[RIGHT_CLICK])
		clicked_atom.set_output(target)
		secondary_attack.set_output(COMPONENT_SIGNAL)
		. = COMSIG_MOB_CANCEL_CLICKON
	else if(modifiers[LEFT_CLICK] && !modifiers[SHIFT_CLICK] && !modifiers[ALT_CLICK] && !modifiers[CTRL_CLICK])
		clicked_atom.set_output(target)
		attack.set_output(COMPONENT_SIGNAL)
		. = COMSIG_MOB_CANCEL_CLICKON

/obj/item/circuit_component/mmi/add_to(obj/item/integrated_circuit/add_to)
	. = ..()
	if(HAS_TRAIT(add_to, TRAIT_COMPONENT_MMI))
		return FALSE
	ADD_TRAIT(add_to, TRAIT_COMPONENT_MMI, src)

/obj/item/circuit_component/mmi/removed_from(obj/item/integrated_circuit/removed_from)
	REMOVE_TRAIT(removed_from, TRAIT_COMPONENT_MMI, src)
	remove_current_brain()
	return ..()


code:
/**
 * # Pathfinding component
 *
 * Calcualtes a path, returns a list of entities. Each entity is the next step in the path. Can be used with the direction component to move.
 */
/obj/item/circuit_component/pathfind
	display_name = "Pathfinder"
	desc = "When triggered, the next step to the target's location as an entity. This can be used with the direction component and the drone shell to make it move on its own. The Id Card input port is for considering ID access when pathing, it does not give the shell actual access."
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

	var/datum/port/input/input_X
	var/datum/port/input/input_Y
	var/datum/port/input/id_card

	var/datum/port/output/output
	var/datum/port/output/finished
	var/datum/port/output/failed
	var/datum/port/output/reason_failed

	var/list/path
	var/turf/old_dest
	var/turf/next_turf

	// Cooldown to limit how frequently we can path to the same location.
	var/same_path_cooldown = 5 SECONDS
	var/different_path_cooldown = 30 SECONDS

	var/max_range = 60

/obj/item/circuit_component/pathfind/get_ui_notices()
	. = ..()
	// Not necessary to show the same path cooldown, since it doesn't change much for the player
	. += create_ui_notice("Pathfinding Cooldown: [DisplayTimeText(different_path_cooldown)]", "orange", "stopwatch")
	. += create_ui_notice("Maximum Range: [max_range] tiles", "orange", "info")

/obj/item/circuit_component/pathfind/populate_ports()
	input_X = add_input_port("Target X", PORT_TYPE_NUMBER, trigger = null)
	input_Y = add_input_port("Target Y", PORT_TYPE_NUMBER, trigger = null)
	id_card = add_input_port("ID Card", PORT_TYPE_ATOM, trigger = null)

	output = add_output_port("Next step", PORT_TYPE_ATOM)
	finished = add_output_port("Arrived to destination", PORT_TYPE_SIGNAL)
	failed = add_output_port("Failed", PORT_TYPE_SIGNAL)
	reason_failed = add_output_port("Fail reason", PORT_TYPE_STRING)

/obj/item/circuit_component/pathfind/input_received(datum/port/input/port)
	INVOKE_ASYNC(src, .proc/perform_pathfinding, port)

/obj/item/circuit_component/pathfind/proc/perform_pathfinding(datum/port/input/port)
	var/target_X = input_X.value
	if(isnull(target_X))
		return

	var/target_Y = input_Y.value
	if(isnull(target_Y))
		return

	var/atom/path_id = id_card.value
	if(path_id && !istype(path_id, /obj/item/card/id))
		path_id = null
		failed.set_output(COMPONENT_SIGNAL)
		reason_failed.set_output("Object marked is not an ID! Using no ID instead.")

	// Get both the current turf and the destination's turf
	var/turf/current_turf = get_turf(src)
	var/turf/destination = locate(target_X, target_Y, current_turf?.z)

	// We're already here! No need to do anything.
	if(current_turf == destination)
		finished.set_output(COMPONENT_SIGNAL)
		old_dest = null
		TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)
		next_turf = null
		return

	// If we're going to the same place and the cooldown hasn't subsided, we're probably on the same path as before
	if (destination == old_dest && TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME))

		// Check if the current turf is the same as the current turf we're supposed to be in. If so, then we set the next step as the next turf on the list
		if(current_turf == next_turf)
			popleft(path)
			next_turf = get_turf(path[1])
			output.set_output(next_turf)

			// Restart the cooldown since we don't need a new path ( TIMER_COOLDOWN_START might restart the timer by itself and i dont need to call TIMER_COOLDOWN_END, but better safe than sorry )
			TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)
			TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME, same_path_cooldown)

	else // Either we're not going to the same place or the cooldown is over. Either way, we need a new path

		if(destination != old_dest && TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_PATHFIND_DIF))
			failed.set_output(COMPONENT_SIGNAL)
			reason_failed.set_output("Cooldown still active!")
			return

		TIMER_COOLDOWN_END(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME)

		old_dest = destination
		path = get_path_to(src, destination, max_range, id=path_id)
		if(length(path) == 0 || !path)// Check if we can even path there
			next_turf = null
			failed.set_output(COMPONENT_SIGNAL)
			reason_failed.set_output("Can't go there!")
			return
		else
			TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_PATHFIND_DIF, different_path_cooldown)
			next_turf = get_turf(path[1])
			output.set_output(next_turf)
		TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_PATHFIND_SAME, same_path_cooldown)


code:
/**
 * # Printer Component
 *
 * Allows for text strings to be printed on a paper. Requires a shell.
 */
/obj/item/circuit_component/printer
	display_name = "Printer"
	desc = "A component that prints a string input on a paper. Requires a shell and paper. \
		Attack with paper to load them in the circuit. Use in hand to dump the bottom-most paper."
	circuit_flags = CIRCUIT_FLAG_REFUSE_MODULE

	/// Prints stuff on the leftmost paper in the loaded_papers list.
	var/datum/port/input/print
	/// The selected font-family used when printing text on paper
	var/datum/port/input/option/typeface
	/// The RGB values of the color used when printing text on paper
	var/datum/port/input/text_color_red
	var/datum/port/input/text_color_green
	var/datum/port/input/text_color_blue
	/// Used to eject the leftmost paper on the loaded_papers list.
	var/datum/port/input/eject
	/// The signature that'll replace any %s and %sign used when printing text on paper.
	var/datum/port/input/signature

	/// The list of papers currently loaded on the component
	var/list/obj/item/paper/loaded_papers
	/// The maximum paper capacity of the component
	var/max_paper_capacity = 10

/obj/item/circuit_component/printer/populate_ports()
	print = add_input_port("Print", PORT_TYPE_STRING, trigger = .proc/print_on_paper)
	text_color_red = add_input_port("Color (Red)", PORT_TYPE_NUMBER, trigger = null, default = 0)
	text_color_green = add_input_port("Color (Green)", PORT_TYPE_NUMBER, trigger = null, default = 0)
	text_color_blue = add_input_port("Color (Blue)", PORT_TYPE_NUMBER, trigger = null, default = 0)
	signature = add_input_port("Signature", PORT_TYPE_STRING, trigger = null, default = "signature")
	eject = add_input_port("Eject", PORT_TYPE_SIGNAL, trigger = .proc/eject_paper, order = 2)

/obj/item/circuit_component/printer/populate_options()
	var/static/typeface_options = list(
		PRINTER_FONT,
		PEN_FONT,
		FOUNTAIN_PEN_FONT,
		CRAYON_FONT,
		"Impact",
		"Webdings",
	)
	typeface = add_option_port("Typeface", typeface_options, trigger = null)

/obj/item/circuit_component/printer/Destroy()
	QDEL_LIST(loaded_papers)
	return ..()

/obj/item/circuit_component/printer/add_to(obj/item/integrated_circuit/add_to)
	. = ..()
	if(HAS_TRAIT(add_to, TRAIT_COMPONENT_PRINTER))
		return FALSE
	ADD_TRAIT(add_to, TRAIT_COMPONENT_PRINTER, src)

/obj/item/circuit_component/printer/removed_from(obj/item/integrated_circuit/removed_from)
	REMOVE_TRAIT(removed_from, TRAIT_COMPONENT_PRINTER, src)
	return ..()

/obj/item/circuit_component/printer/register_shell(atom/movable/shell)
	RegisterSignal(shell, COMSIG_PARENT_ATTACKBY_SECONDARY, .proc/handle_secondary_attackby)
	RegisterSignal(shell, COMSIG_PARENT_EXAMINE, .proc/on_examine)

/obj/item/circuit_component/printer/unregister_shell(atom/movable/shell)
	UnregisterSignal(shell, list(COMSIG_PARENT_ATTACKBY_SECONDARY, COMSIG_PARENT_EXAMINE))

/obj/item/circuit_component/printer/get_ui_notices()
	. = ..()
	. += create_ui_notice("Papers Stored: [length(loaded_papers)]/[max_paper_capacity]", "orange", "info")

///Allows for paper to be loaded while inside the shell.
/obj/item/circuit_component/printer/proc/handle_secondary_attackby(atom/movable/shell, obj/item/item, mob/living/attacker)
	SIGNAL_HANDLER
	if(istype(item, /obj/item/paper))
		load_paper(item, attacker)
		return COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN

/obj/item/circuit_component/printer/proc/on_examine(datum/source, mob/user, list/examine_list)
	SIGNAL_HANDLER
	examine_list += span_notice("It's a printer component installed in. Right-click with paper to reload it.")

/obj/item/circuit_component/printer/attackby(obj/item/item, mob/living/user, params)
	if(istype(item, /obj/item/paper))
		load_paper(item, user)
	else
		return ..()

/obj/item/circuit_component/printer/proc/load_paper(obj/item/paper/paper, mob/living/user)
	if(length(loaded_papers) >= max_paper_capacity)
		to_chat(user, span_warning("[src] can't hold any more paper."))
	else if(user.transferItemToLoc(paper, src))
		LAZYADD(loaded_papers, paper)
		to_chat(user, span_notice("You load [paper] in [src]."))
	else
		to_chat(user, span_warning("[paper] seems to be stuck to your hand."))

/obj/item/circuit_component/printer/attack_self(mob/living/user)
	. = ..()
	var/obj/item/paper/paper = loaded_papers?[1]
	if(paper)
		user.put_in_hands(paper)
		to_chat(user, span_notice("You remove [paper] from [src]."))

/obj/item/circuit_component/printer/Exited(atom/movable/movable)
	. = ..()
	if(movable in loaded_papers)
		LAZYREMOVE(loaded_papers, movable)

/obj/item/circuit_component/printer/pre_input_received(datum/port/input/port)
	if(port != print)
		return
	text_color_red.set_value(clamp(text_color_red.value, 0, 255))
	text_color_green.set_value(clamp(text_color_green.value, 0, 255))
	text_color_blue.set_value(clamp(text_color_blue.value, 0, 255))
	signature.set_value(reject_bad_text(signature.value, MAX_NAME_LEN, FALSE) || "signature")

/obj/item/circuit_component/printer/proc/print_on_paper(datum/port/input/port)
	if(!print.value)
		return
	var/obj/item/paper/paper = loaded_papers?[1]
	if(!paper)
		return
	paper.add_info(print.value, rgb(text_color_red, text_color_green, text_color_blue), typeface.value, signature.value)
	log_paper("Printer component writing to paper [paper.name]. [parent.get_creator()].")

/obj/item/circuit_component/printer/proc/eject_paper(datum/port/input/port, list/return_values)
	var/obj/item/paper/paper = loaded_papers?[1]
	if(!paper)
		return
	playsound(src, "sound/machines/dotprinter.ogg", 30, TRUE)
	if(isliving(parent?.shell?.loc))
		var/mob/living/living_loc = parent.shell.loc
		living_loc.put_in_hands(paper)
	else
		paper.forceMove(drop_location())


code:
/**
 * # Pull Component
 *
 * Tells the shell to start pulling on a designated atom. Only works on movable shells.
 */
/obj/item/circuit_component/pull
	display_name = "Start Pulling"
	desc = "A component that can force the shell to pull entities. Only works for drone shells."

	/// Frequency input
	var/datum/port/input/target
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

/obj/item/circuit_component/pull/populate_ports()
	target = add_input_port("Target", PORT_TYPE_ATOM)

/obj/item/circuit_component/pull/input_received(datum/port/input/port)
	var/atom/target_atom = target.value
	if(!target_atom)
		return

	var/mob/shell = parent.shell
	if(!istype(shell) || get_dist(shell, target_atom) > 1 || shell.z != target_atom.z)
		return

	INVOKE_ASYNC(shell, /atom/movable.proc/start_pulling, target_atom)


code:
#define COMP_RADIO_PUBLIC "public"
#define COMP_RADIO_PRIVATE "private"

/**
 * # Radio Component
 *
 * Listens out for signals on the designated frequencies and sends signals on designated frequencies
 */
/obj/item/circuit_component/radio
	display_name = "Radio"
	desc = "A component that can listen and send frequencies. If set to private, the component will only receive signals from other components attached to circuitboards with the same owner id."

	/// The publicity options. Controls whether it's public or private.
	var/datum/port/input/option/public_options

	/// Frequency input
	var/datum/port/input/freq
	/// Signal input
	var/datum/port/input/code

	/// Current frequency value
	var/current_freq = DEFAULT_SIGNALER_CODE

	var/datum/radio_frequency/radio_connection

/obj/item/circuit_component/radio/populate_options()
	var/static/component_options = list(
		COMP_RADIO_PUBLIC,
		COMP_RADIO_PRIVATE,
	)
	public_options = add_option_port("Encryption Options", component_options)

/obj/item/circuit_component/radio/populate_ports()
	freq = add_input_port("Frequency", PORT_TYPE_NUMBER, default = FREQ_SIGNALER)
	code = add_input_port("Code", PORT_TYPE_NUMBER, default = DEFAULT_SIGNALER_CODE)
	trigger_component()
	// These are cleaned up on the parent
	trigger_input = add_input_port("Send", PORT_TYPE_SIGNAL)
	trigger_output = add_output_port("Received", PORT_TYPE_SIGNAL)

/obj/item/circuit_component/radio/Destroy()
	SSradio.remove_object(src, current_freq)
	return ..()

/obj/item/circuit_component/radio/pre_input_received(datum/port/input/port)
	freq.set_value(sanitize_frequency(freq.value, TRUE))

/obj/item/circuit_component/radio/input_received(datum/port/input/port)
	INVOKE_ASYNC(src, .proc/handle_radio_input, port)

/obj/item/circuit_component/radio/proc/handle_radio_input(datum/port/input/port)
	var/frequency = freq.value

	if(frequency != current_freq)
		SSradio.remove_object(src, current_freq)
		radio_connection = SSradio.add_object(src, frequency, RADIO_SIGNALER)
		current_freq = frequency

	if(COMPONENT_TRIGGERED_BY(trigger_input, port))
		var/datum/signal/signal = new(list("code" = round(code.value) || 0, "key" = parent?.owner_id))
		radio_connection.post_signal(src, signal)

/obj/item/circuit_component/radio/receive_signal(datum/signal/signal)
	. = FALSE
	if(!signal)
		return
	if(signal.data["code"] != round(code.value || 0))
		return

	if(public_options.value == COMP_RADIO_PRIVATE && parent?.owner_id != signal.data["key"])
		return

	trigger_output.set_output(COMPONENT_SIGNAL)

#undef COMP_RADIO_PUBLIC
#undef COMP_RADIO_PRIVATE


code:
/**
 * # Sound Emitter Component
 *
 * A component that emits a sound when it receives an input.
 */
/obj/item/circuit_component/soundemitter
	display_name = "Sound Emitter"
	desc = "A component that emits a sound when it receives an input. The frequency is a multiplier which determines the speed at which the sound is played"
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

	/// Sound to play
	var/datum/port/input/option/sound_file

	/// Volume of the sound when played
	var/datum/port/input/volume

	/// Frequency of the sound when played
	var/datum/port/input/frequency

	/// The cooldown for this component of how often it can play sounds.
	var/sound_cooldown = 2 SECONDS

	var/list/options_map

/obj/item/circuit_component/soundemitter/get_ui_notices()
	. = ..()
	. += create_ui_notice("Sound Cooldown: [DisplayTimeText(sound_cooldown)]", "orange", "stopwatch")

/obj/item/circuit_component/soundemitter/populate_ports()
	volume = add_input_port("Volume", PORT_TYPE_NUMBER, default = 35)
	frequency = add_input_port("Frequency", PORT_TYPE_NUMBER, default = 0)

/obj/item/circuit_component/soundemitter/populate_options()
	var/static/component_options = list(
		"Buzz" = 'sound/machines/buzz-sigh.ogg',
		"Buzz Twice" = 'sound/machines/buzz-two.ogg',
		"Chime" = 'sound/machines/chime.ogg',
		"Honk" = 'sound/items/bikehorn.ogg',
		"Ping" = 'sound/machines/ping.ogg',
		"Sad Trombone" = 'sound/misc/sadtrombone.ogg',
		"Warn" = 'sound/machines/warning-buzzer.ogg',
		"Slow Clap" = 'sound/machines/slowclap.ogg',
	)
	sound_file = add_option_port("Sound Option", component_options)
	options_map = component_options

/obj/item/circuit_component/soundemitter/pre_input_received(datum/port/input/port)
	volume.set_value(clamp(volume.value, 0, 100))
	frequency.set_value(clamp(frequency.value, -100, 100))

/obj/item/circuit_component/soundemitter/input_received(datum/port/input/port)
	if(TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_SOUNDEMITTER))
		return

	var/sound_to_play = options_map[sound_file.value]
	if(!sound_to_play)
		return

	playsound(src, sound_to_play, volume.value, frequency != 0, frequency = frequency.value)

	TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_SOUNDEMITTER, sound_cooldown)


code:
/**
 * # Speech Component
 *
 * Sends a message. Requires a shell.
 */
/obj/item/circuit_component/speech
	display_name = "Speech"
	desc = "A component that sends a message. Requires a shell."
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

	/// The message to send
	var/datum/port/input/message

	/// The cooldown for this component of how often it can send speech messages.
	var/speech_cooldown = 1 SECONDS

/obj/item/circuit_component/speech/get_ui_notices()
	. = ..()
	. += create_ui_notice("Speech Cooldown: [DisplayTimeText(speech_cooldown)]", "orange", "stopwatch")

/obj/item/circuit_component/speech/populate_ports()
	message = add_input_port("Message", PORT_TYPE_STRING, trigger = null)

/obj/item/circuit_component/speech/input_received(datum/port/input/port)

	if(TIMER_COOLDOWN_CHECK(parent, COOLDOWN_CIRCUIT_SPEECH))
		return

	if(message.value)
		var/atom/movable/shell = parent.shell
		// Prevents appear as the individual component if there is a shell.
		if(shell)
			shell.say(message.value)
		else
			say(message.value)
		TIMER_COOLDOWN_START(parent, COOLDOWN_CIRCUIT_SPEECH, speech_cooldown)


code:
#define COMP_ANIMATE_ATOM "Object"
#define COMP_ANIMATE_FILTER "Filter"

/obj/item/circuit_component/begin_animation
	display_name = "Begin Animation"
	desc = "Begins an animation on the target. Create animation steps by chaining \"Animation Step\" components off of the \"Perform Animation\" port."
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_INSTANT|CIRCUIT_FLAG_ADMIN

	/// Whether we are animating an atom or a filter on the atom
	var/datum/port/input/option/atom_or_filter

	/// The filter to animate
	var/datum/port/input/filter_target
	/// The target to animate
	var/datum/port/input/target
	/// The amount of times this animation will loop
	var/datum/port/input/animation_loops
	/// Used to determine if the animation runs in parallel or not.
	var/datum/port/input/parallel
	/// Called to stop all animations on this object
	var/datum/port/input/stop_all_animations
	/// Called when performing the animation.
	var/datum/port/output/animate_event

/obj/item/circuit_component/begin_animation/populate_options()
	var/static/list/component_options = list(
		COMP_ANIMATE_ATOM,
		COMP_ANIMATE_FILTER,
	)

	atom_or_filter = add_option_port("Target Options", component_options)

/obj/item/circuit_component/begin_animation/populate_ports()
	target = add_input_port("Target", PORT_TYPE_ATOM)
	parallel = add_input_port("Parallel", PORT_TYPE_NUMBER, default = 1)
	animation_loops = add_input_port("Loops", PORT_TYPE_NUMBER)
	stop_all_animations = add_input_port("Stop All Animations", PORT_TYPE_SIGNAL, trigger = .proc/stop_animations)
	animate_event = add_output_port("Perform Animation", PORT_TYPE_INSTANT_SIGNAL)

/obj/item/circuit_component/begin_animation/pre_input_received(datum/port/input/port)
	if(port == atom_or_filter)
		if(filter_target)
			remove_input_port(filter_target)
			filter_target = null

		if(atom_or_filter.value == COMP_ANIMATE_FILTER)
			filter_target = add_input_port("Filter Name", PORT_TYPE_STRING, order = 0.5)

/obj/item/circuit_component/begin_animation/proc/stop_animations(datum/port/input/port)
	CIRCUIT_TRIGGER
	if(!target.value)
		return
	animate(target.value, null)

/obj/item/circuit_component/begin_animation/input_received(datum/port/input/port, list/return_values)
	if(!target.value)
		return

	SScircuit_component.queue_instant_run()
	animate_event.set_output(COMPONENT_SIGNAL)
	var/list/result = SScircuit_component.execute_instant_run()

	if(!result || !result["animation_steps"])
		return

	var/atom/target_atom = target.value
	if(!isatom(target_atom))
		return

	var/target_for_animation = target_atom
	if(atom_or_filter.value == COMP_ANIMATE_FILTER)
		target_for_animation = target_atom.get_filter(filter_target.value)

	if(!target_for_animation)
		return

	if(!isatom(target_atom))
		return
	target_atom.datum_flags |= DF_VAR_EDITED

	var/extra_flags = NONE
	if(parallel.value)
		extra_flags |= ANIMATION_PARALLEL

	var/list/first_step = popleft(result["animation_steps"])
	animate(target_for_animation, time = first_step["time"], first_step["vars"], loop = animation_loops.value, easing = first_step["easing"], flags = first_step["flags"]|extra_flags)
	for(var/list/step as anything in result["animation_steps"])
		animate(time = step["time"], step["vars"], easing = step["easing"], flags = step["flags"])
		if(atom_or_filter.value == COMP_ANIMATE_FILTER)
			var/list/filter_params = target_atom.filter_data[filter_target.value]
			for(var/param in step["vars"])
				if(filter_params.Find(param))
					filter_params[param] = step["vars"][param]

/obj/item/circuit_component/animation_step
	display_name = "Animation Step"
	desc = "Perform a single animation step. The input of this component should be connected, directly or indirectly, to the \"Perform Animation\" port of a \"Begin Animation\" component."

	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL|CIRCUIT_FLAG_ADMIN

	var/datum/port/input/animation_variables

	var/datum/port/input/animation_time
	var/datum/port/input/animation_easing
	var/datum/port/input/animation_flags

/obj/item/circuit_component/animation_step/populate_ports()
	animation_variables = add_input_port("Variables", PORT_TYPE_ASSOC_LIST(PORT_TYPE_STRING, PORT_TYPE_ANY), order = 1.66)
	animation_time = add_input_port("Time", PORT_TYPE_NUMBER, order = 1.66)
	animation_easing = add_input_port("Easing", PORT_TYPE_NUMBER, order = 1.66)
	animation_flags = add_input_port("Flags", PORT_TYPE_NUMBER, order = 1.66)

/obj/item/circuit_component/animation_step/input_received(datum/port/input/port, list/return_values)
	if(!return_values)
		return

	if(!return_values["animation_steps"])
		return_values["animation_steps"] = list()

	var/list/variables = animation_variables.value

	if(!variables)
		return

	var/list/step = list(
		"vars" = variables.Copy(),
		"time" = animation_time.value * (1 SECONDS),
		"easing" = animation_easing.value,
		"flags" = animation_flags.value
	)

	return_values["animation_steps"] += list(step)

#undef COMP_ANIMATE_ATOM
#undef COMP_ANIMATE_FILTER