code:
GLOBAL_LIST_INIT(arcade_prize_pool, list(
		/obj/item/storage/box/snappops = 2,
		/obj/item/toy/talking/ai = 2,
		/obj/item/toy/talking/codex_gigas = 2,
		/obj/item/clothing/under/syndicate/tacticool = 2,
		/obj/item/toy/sword = 2,
		/obj/item/toy/gun = 2,
		/obj/item/gun/ballistic/shotgun/toy/crossbow = 2,
		/obj/item/storage/box/fakesyndiesuit = 2,
		/obj/item/storage/crayons = 2,
		/obj/item/toy/spinningtoy = 2,
		/obj/item/toy/balloon/arrest = 2,
		/obj/item/toy/mecha/ripley = 1,
		/obj/item/toy/mecha/ripleymkii = 1,
		/obj/item/toy/mecha/hauler = 1,
		/obj/item/toy/mecha/clarke = 1,
		/obj/item/toy/mecha/odysseus = 1,
		/obj/item/toy/mecha/gygax = 1,
		/obj/item/toy/mecha/durand = 1,
		/obj/item/toy/mecha/savannahivanov = 1,
		/obj/item/toy/mecha/phazon = 1,
		/obj/item/toy/mecha/honk = 1,
		/obj/item/toy/mecha/darkgygax = 1,
		/obj/item/toy/mecha/mauler = 1,
		/obj/item/toy/mecha/darkhonk = 1,
		/obj/item/toy/mecha/deathripley = 1,
		/obj/item/toy/mecha/reticence = 1,
		/obj/item/toy/mecha/marauder = 1,
		/obj/item/toy/mecha/seraph = 1,
		/obj/item/toy/mecha/firefighter = 1,
		/obj/item/toy/cards/deck = 2,
		/obj/item/toy/nuke = 2,
		/obj/item/toy/minimeteor = 2,
		/obj/item/toy/redbutton = 2,
		/obj/item/toy/talking/owl = 2,
		/obj/item/toy/talking/griffin = 2,
		/obj/item/coin/antagtoken = 2,
		/obj/item/stack/tile/fakespace/loaded = 2,
		/obj/item/stack/tile/fakepit/loaded = 2,
		/obj/item/stack/tile/eighties/loaded = 2,
		/obj/item/toy/toy_xeno = 2,
		/obj/item/storage/box/actionfigure = 1,
		/obj/item/restraints/handcuffs/fake = 2,
		/obj/item/grenade/chem_grenade/glitter/pink = 1,
		/obj/item/grenade/chem_grenade/glitter/blue = 1,
		/obj/item/grenade/chem_grenade/glitter/white = 1,
		/obj/item/toy/eightball = 2,
		/obj/item/toy/windup_toolbox = 2,
		/obj/item/toy/clockwork_watch = 2,
		/obj/item/toy/toy_dagger = 2,
		/obj/item/extendohand/acme = 1,
		/obj/item/hot_potato/harmless/toy = 1,
		/obj/item/card/emagfake = 1,
		/obj/item/clothing/shoes/wheelys = 2,
		/obj/item/clothing/shoes/kindle_kicks = 2,
		/obj/item/toy/plush/goatplushie = 2,
		/obj/item/toy/plush/moth = 2,
		/obj/item/toy/plush/pkplush = 2,
		/obj/item/toy/plush/rouny = 2,
		/obj/item/storage/belt/military/snack = 2,
		/obj/item/toy/brokenradio = 2,
		/obj/item/toy/braintoy = 2,
		/obj/item/toy/eldritch_book = 2,
		/obj/item/storage/box/heretic_box = 1,
		/obj/item/toy/foamfinger = 2,
		/obj/item/clothing/glasses/trickblindfold = 2))

/obj/machinery/computer/arcade
	name = "random arcade"
	desc = "random arcade machine"
	icon_state = "arcade"
	icon_keyboard = null
	icon_screen = "invaders"
	light_color = LIGHT_COLOR_GREEN
	var/list/prize_override

/obj/machinery/computer/arcade/proc/Reset()
	return

/obj/machinery/computer/arcade/Initialize(mapload)
	. = ..()

	Reset()

/obj/machinery/computer/arcade/proc/prizevend(mob/user, prizes = 1)
	SEND_SIGNAL(src, COMSIG_ARCADE_PRIZEVEND, user, prizes)
	if(user.mind?.get_skill_level(/datum/skill/gaming) >= SKILL_LEVEL_LEGENDARY && HAS_TRAIT(user, TRAIT_GAMERGOD))
		visible_message("[user] inputs an intense cheat code!",\
		span_notice("You hear a flurry of buttons being pressed."))
		say("CODE ACTIVATED: EXTRA PRIZES.")
		prizes *= 2
	for(var/i = 0, i < prizes, i++)
		SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "arcade", /datum/mood_event/arcade)
		if(prob(0.0001)) //1 in a million
			new /obj/item/gun/energy/pulse/prize(src)
			visible_message(span_notice("[src] dispenses.. woah, a gun! Way past cool."), span_notice("You hear a chime and a shot."))
			user.client.give_award(/datum/award/achievement/misc/pulse, user)
			return

		var/prizeselect
		if(prize_override)
			prizeselect = pick_weight(prize_override)
		else
			prizeselect = pick_weight(GLOB.arcade_prize_pool)
		var/atom/movable/the_prize = new prizeselect(get_turf(src))
		playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
		visible_message(span_notice("[src] dispenses [the_prize]!"), span_notice("You hear a chime and a clunk."))

/obj/machinery/computer/arcade/emp_act(severity)
	. = ..()
	var/override = FALSE
	if(prize_override)
		override = TRUE

	if(machine_stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
		return

	var/empprize = null
	var/num_of_prizes = 0
	switch(severity)
		if(1)
			num_of_prizes = rand(1,4)
		if(2)
			num_of_prizes = rand(0,2)
	for(var/i = num_of_prizes; i > 0; i--)
		if(override)
			empprize = pick_weight(prize_override)
		else
			empprize = pick_weight(GLOB.arcade_prize_pool)
		new empprize(loc)
	explosion(src, devastation_range = -1, light_impact_range = 1+num_of_prizes, flame_range = 1+num_of_prizes)

/obj/machinery/computer/arcade/attackby(obj/item/O, mob/user, params)
	if(istype(O, /obj/item/stack/arcadeticket))
		var/obj/item/stack/arcadeticket/T = O
		var/amount = T.get_amount()
		if(amount <2)
			to_chat(user, span_warning("You need 2 tickets to claim a prize!"))
			return
		prizevend(user)
		T.pay_tickets()
		T.update_appearance()
		O = T
		to_chat(user, span_notice("You turn in 2 tickets to the [src] and claim a prize!"))
		return

// ** BATTLE ** //
/obj/machinery/computer/arcade/battle
	name = "arcade machine"
	desc = "Does not support Pinball."
	icon_state = "arcade"
	circuit = /obj/item/circuitboard/computer/arcade/battle

	var/enemy_name = "Space Villain"
	///Enemy health/attack points
	var/enemy_hp = 100
	var/enemy_mp = 40
	///Temporary message, for attack messages, etc
	var/temp = "

Winners don't use space drugs
"
	///the list of passive skill the enemy currently has. the actual passives are added in the enemy_setup() proc
	var/list/enemy_passive
	///if all the enemy's weakpoints have been triggered becomes TRUE
	var/finishing_move = FALSE
	///linked to passives, when it's equal or above the max_passive finishing move will become TRUE
	var/pissed_off = 0
	///the number of passives the enemy will start with
	var/max_passive = 3
	///weapon wielded by the enemy, the shotgun doesn't count.
	var/chosen_weapon

	///Player health
	var/player_hp = 85
	///player magic points
	var/player_mp = 20
	///used to remember the last three move of the player before this turn.
	var/list/last_three_move
	///if the enemy or player died. restart the game when TRUE
	var/gameover = FALSE
	///the player cannot make any move while this is set to TRUE. should only TRUE during enemy turns.
	var/blocked = FALSE
	///used to clear the enemy_action proc timer when the game is restarted
	var/timer_id
	///weapon used by the enemy, pure fluff.for certain actions
	var/list/weapons
	///unique to the emag mode, acts as a time limit where the player dies when it reaches 0.
	var/bomb_cooldown = 19

///creates the enemy base stats for a new round along with the enemy passives
/obj/machinery/computer/arcade/battle/proc/enemy_setup(player_skill)
	player_hp = 85
	player_mp = 20
	enemy_hp = 100
	enemy_mp = 40
	gameover = FALSE
	blocked = FALSE
	finishing_move = FALSE
	pissed_off = 0
	last_three_move = null

	enemy_passive = list("short_temper" = TRUE, "poisonous" = TRUE, "smart" = TRUE, "shotgun" = TRUE, "magical" = TRUE, "chonker" = TRUE)
	for(var/i = LAZYLEN(enemy_passive); i > max_passive; i--) //we'll remove passives from the list until we have the number of passive we want
		var/picked_passive = pick(enemy_passive)
		LAZYREMOVE(enemy_passive, picked_passive)

	if(LAZYACCESS(enemy_passive, "chonker"))
		enemy_hp += 20

	if(LAZYACCESS(enemy_passive, "shotgun"))
		chosen_weapon = "shotgun"
	else if(weapons)
		chosen_weapon = pick(weapons)
	else
		chosen_weapon = "null gun" //if the weapons list is somehow empty, shouldn't happen but runtimes are sneaky bastards.

	if(player_skill)
		player_hp += player_skill * 2

/obj/machinery/computer/arcade/battle/Reset()
	max_passive = 3
	var/name_action
	var/name_part1
	var/name_part2

	if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
		name_action = pick_list(ARCADE_FILE, "rpg_action_halloween")
		name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_halloween")
		name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_halloween")
		weapons = strings(ARCADE_FILE, "rpg_weapon_halloween")
	else if(SSevents.holidays && SSevents.holidays[CHRISTMAS])
		name_action = pick_list(ARCADE_FILE, "rpg_action_xmas")
		name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_xmas")
		name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_xmas")
		weapons = strings(ARCADE_FILE, "rpg_weapon_xmas")
	else if(SSevents.holidays && SSevents.holidays[VALENTINES])
		name_action = pick_list(ARCADE_FILE, "rpg_action_valentines")
		name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_valentines")
		name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_valentines")
		weapons = strings(ARCADE_FILE, "rpg_weapon_valentines")
	else
		name_action = pick_list(ARCADE_FILE, "rpg_action")
		name_part1 = pick_list(ARCADE_FILE, "rpg_adjective")
		name_part2 = pick_list(ARCADE_FILE, "rpg_enemy")
		weapons = strings(ARCADE_FILE, "rpg_weapon")

	enemy_name = ("The " + name_part1 + " " + name_part2)
	name = (name_action + " " + enemy_name)

	enemy_setup(0) //in the case it's reset we assume the player skill is 0 because the VOID isn't a gamer

/obj/machinery/computer/arcade/battle/ui_interact(mob/user)
	. = ..()
	screen_setup(user)

///sets up the main screen for the user
/obj/machinery/computer/arcade/battle/proc/screen_setup(mob/user)
	var/dat = "Close"
	dat += "
[enemy_name]
"

	dat += "[temp]"
	dat += "
Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]"

	if (gameover)
		dat += "New Game"
	else
		dat += "Light attack"
		dat += "Defend"
		dat += "Counter attack"
		dat += "Power attack"

	dat += ""
	if(user.client) //mainly here to avoid a runtime when the player gets gibbed when losing the emag mode.
		var/datum/browser/popup = new(user, "arcade", "Space Villain 2000")
		popup.set_content(dat)
		popup.open()

/obj/machinery/computer/arcade/battle/Topic(href, href_list)
	if(..())
		return
	var/gamerSkill = 0
	if(usr?.mind)
		gamerSkill = usr.mind.get_skill_level(/datum/skill/gaming)

	if (!blocked && !gameover)
		var/attackamt = rand(5,7) + rand(0, gamerSkill)

		if(finishing_move) //time to bonk that fucker,cuban pete will sometime survive a finishing move.
			attackamt *= 100

		//light attack suck absolute ass but it doesn't cost any MP so it's pretty good to finish an enemy off
		if (href_list["attack"])
			temp = "

you do quick jab for [attackamt] of damage!
"
			enemy_hp -= attackamt
			arcade_action(usr,"attack",attackamt)

		//defend lets you gain back MP and take less damage from non magical attack.
		else if(href_list["defend"])
			temp = "

you take a defensive stance and gain back 10 mp!
"
			player_mp += 10
			arcade_action(usr,"defend",attackamt)
			playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)

		//mainly used to counter short temper and their absurd damage, will deal twice the damage the player took of a non magical attack.
		else if(href_list["counter_attack"] && player_mp >= 10)
			temp = "

you prepare yourself to counter the next attack!
"
			player_mp -= 10
			arcade_action(usr,"counter_attack",attackamt)
			playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)

		else if(href_list["counter_attack"] && player_mp < 10)
			temp = "

you don't have the mp necessary to counter attack and defend yourself instead
"
			player_mp += 10
			arcade_action(usr,"defend",attackamt)
			playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)

		//power attack deals twice the amount of damage but is really expensive MP wise, mainly used with combos to get weakpoints.
		else if (href_list["power_attack"] && player_mp >= 20)
			temp = "

You attack [enemy_name] with all your might for [attackamt * 2] damage!
"
			enemy_hp -= attackamt * 2
			player_mp -= 20
			arcade_action(usr,"power_attack",attackamt)

		else if(href_list["power_attack"] && player_mp < 20)
			temp = "

You don't have the mp necessary for a power attack and settle for a light attack!
"
			enemy_hp -= attackamt
			arcade_action(usr,"attack",attackamt)

	if (href_list["close"])
		usr.unset_machine()
		usr << browse(null, "window=arcade")

	else if (href_list["newgame"]) //Reset everything
		temp = "

New Round
"

		if(obj_flags & EMAGGED)
			Reset()
			obj_flags &= ~EMAGGED

		enemy_setup(gamerSkill)
		screen_setup(usr)

	add_fingerprint(usr)
	return

///happens after a player action and before the enemy turn. the enemy turn will be cancelled if there's a gameover.
/obj/machinery/computer/arcade/battle/proc/arcade_action(mob/user,player_stance,attackamt)
	screen_setup(user)
	blocked = TRUE
	if(player_stance == "attack" || player_stance == "power_attack")
		if(attackamt > 40)
			playsound(src, 'sound/arcade/boom.ogg', 50, TRUE, extrarange = -3)
		else
			playsound(src, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)

	timer_id = addtimer(CALLBACK(src, .proc/enemy_action,player_stance,user),1 SECONDS,TIMER_STOPPABLE)
	gameover_check(user)

///the enemy turn, the enemy's action entirely depend on their current passive and a teensy tiny bit of randomness
/obj/machinery/computer/arcade/battle/proc/enemy_action(player_stance,mob/user)
	var/list/list_temp = list()

	switch(LAZYLEN(last_three_move)) //we keep the last three action of the player in a list here
		if(0 to 2)
			LAZYADD(last_three_move, player_stance)
		if(3)
			for(var/i in 1 to 2)
				last_three_move[i] = last_three_move[i + 1]
			last_three_move[3] = player_stance

		if(4 to INFINITY)
			last_three_move = null //this shouldn't even happen but we empty the list if it somehow goes above 3

	var/enemy_stance
	var/attack_amount = rand(8,10) //making the attack amount not vary too much so that it's easier to see if the enemy has a shotgun

	if(player_stance == "defend")
		attack_amount -= 5

	//if emagged, cuban pete will set up a bomb acting up as a timer. when it reaches 0 the player fucking dies
	if(obj_flags & EMAGGED)
		switch(bomb_cooldown--)
			if(18)
				list_temp += "

[enemy_name] takes two valve tank and links them together, what's he planning?
"
			if(15)
				list_temp += "

[enemy_name] adds a remote control to the tan- ho god is that a bomb?
"
			if(12)
				list_temp += "

[enemy_name] throws the bomb next to you, you'r too scared to pick it up.
"
			if(6)
				list_temp += "

[enemy_name]'s hand brushes the remote linked to the bomb, your heart skipped a beat.
"
			if(2)
				list_temp += "

[enemy_name] is going to press the button! It's now or never!
"
			if(0)
				player_hp -= attack_amount * 1000 //hey it's a maxcap we might as well go all in

	//yeah I used the shotgun as a passive, you know why? because the shotgun gives +5 attack which is pretty good
	if(LAZYACCESS(enemy_passive, "shotgun"))
		if(weakpoint_check("shotgun","defend","defend","power_attack"))
			list_temp += "

You manage to disarm [enemy_name] with a surprise power attack and shoot him with his shotgun until it runs out of ammo!
 "
			enemy_hp -= 10
			chosen_weapon = "empty shotgun"
		else
			attack_amount += 5

	//heccing chonker passive, only gives more HP at the start of a new game but has one of the hardest weakpoint to trigger.
	if(LAZYACCESS(enemy_passive, "chonker"))
		if(weakpoint_check("chonker","power_attack","power_attack","power_attack"))
			list_temp += "

After a lot of power attacks you manage to tip over [enemy_name] as they fall over their enormous weight
 "
			enemy_hp -= 30

	//smart passive trait, mainly works in tandem with other traits, makes the enemy unable to be counter_attacked
	if(LAZYACCESS(enemy_passive, "smart"))
		if(weakpoint_check("smart","defend","defend","attack"))
			list_temp += "

[enemy_name] is confused by your illogical strategy!
 "
			attack_amount -= 5

		else if(attack_amount >= player_hp)
			player_hp -= attack_amount
			list_temp += "

[enemy_name] figures out you are really close to death and finishes you off with their [chosen_weapon]!
"
			enemy_stance = "attack"

		else if(player_stance == "counter_attack")
			list_temp += "

[enemy_name] is not taking your bait.
 "
			if(LAZYACCESS(enemy_passive, "short_temper"))
				list_temp += "However controlling their hatred of you still takes a toll on their mental and physical health!"
				enemy_hp -= 5
				enemy_mp -= 5
			enemy_stance = "defensive"

	//short temper passive trait, gets easily baited into being counter attacked but will bypass your counter when low on HP
	if(LAZYACCESS(enemy_passive, "short_temper"))
		if(weakpoint_check("short_temper","counter_attack","counter_attack","counter_attack"))
			list_temp += "

[enemy_name] is getting frustrated at all your counter attacks and throws a tantrum!
"
			enemy_hp -= attack_amount

		else if(player_stance == "counter_attack")
			if(!(LAZYACCESS(enemy_passive, "smart")) && enemy_hp > 30)
				list_temp += "

[enemy_name] took the bait and allowed you to counter attack for [attack_amount * 2] damage!
"
				player_hp -= attack_amount
				enemy_hp -= attack_amount * 2
				enemy_stance = "attack"

			else if(enemy_hp <= 30) //will break through the counter when low enough on HP even when smart.
				list_temp += "

[enemy_name] is getting tired of your tricks and breaks through your counter with their [chosen_weapon]!
"
				player_hp -= attack_amount
				enemy_stance = "attack"

		else if(!enemy_stance)
			var/added_temp

			if(rand())
				added_temp = "you for [attack_amount + 5] damage!"
				player_hp -= attack_amount + 5
				enemy_stance = "attack"
			else
				added_temp = "the wall, breaking their skull in the process and losing [attack_amount] hp!" //[enemy_name] you have a literal dent in your skull
				enemy_hp -= attack_amount
				enemy_stance = "attack"

			list_temp += "

[enemy_name] grits their teeth and charge right into [added_temp]
"

	//in the case none of the previous passive triggered, Mainly here to set an enemy stance for passives that needs it like the magical passive.
	if(!enemy_stance)
		enemy_stance = pick("attack","defensive")
		if(enemy_stance == "attack")
			player_hp -= attack_amount
			list_temp += "

[enemy_name] attacks you for [attack_amount] points of damage with their [chosen_weapon]
"
			if(player_stance == "counter_attack")
				enemy_hp -= attack_amount * 2
				list_temp += "

You counter [enemy_name]'s attack and deal [attack_amount * 2] points of damage!
"

		if(enemy_stance == "defensive" && enemy_mp < 15)
			list_temp += "

[enemy_name] take some time to get some mp back!
 "
			enemy_mp += attack_amount

		else if (enemy_stance == "defensive" && enemy_mp >= 15 && !(LAZYACCESS(enemy_passive, "magical")))
			list_temp += "

[enemy_name] quickly heal themselves for 5 hp!
 "
			enemy_mp -= 15
			enemy_hp += 5

	//magical passive trait, recharges MP nearly every turn it's not blasting you with magic.
	if(LAZYACCESS(enemy_passive, "magical"))
		if(player_mp >= 50)
			list_temp += "

the huge amount of magical energy you have acumulated throws [enemy_name] off balance!
"
			enemy_mp = 0
			LAZYREMOVE(enemy_passive, "magical")
			pissed_off++

		else if(LAZYACCESS(enemy_passive, "smart") && player_stance == "counter_attack" && enemy_mp >= 20)
			list_temp += "

[enemy_name] blasts you with magic from afar for 10 points of damage before you can counter!
"
			player_hp -= 10
			enemy_mp -= 20

		else if(enemy_hp >= 20 && enemy_mp >= 40 && enemy_stance == "defensive")
			list_temp += "

[enemy_name] Blasts you with magic from afar!
"
			enemy_mp -= 40
			player_hp -= 30
			enemy_stance = "attack"

		else if(enemy_hp < 20 && enemy_mp >= 20 && enemy_stance == "defensive") //it's a pretty expensive spell so they can't spam it that much
			list_temp += "

[enemy_name] heal themselves with magic and gain back 20 hp!
"
			enemy_hp += 20
			enemy_mp -= 30
		else
			list_temp += "

[enemy_name]'s magical nature lets them get some mp back!
"
			enemy_mp += attack_amount

	//poisonous passive trait, while it's less damage added than the shotgun it acts up even when the enemy doesn't attack at all.
	if(LAZYACCESS(enemy_passive, "poisonous"))
		if(weakpoint_check("poisonous","attack","attack","attack"))
			list_temp += "

your flurry of attack throws back the poisonnous gas at [enemy_name] and makes them choke on it!
 "
			enemy_hp -= 5
		else
			list_temp += "

the stinky breath of [enemy_name] hurts you for 3 hp!
 "
			player_hp -= 3

	//if all passive's weakpoint have been triggered, set finishing_move to TRUE
	if(pissed_off >= max_passive && !finishing_move)
		list_temp += "

You have weakened [enemy_name] enough for them to show their weak point, you will do 10 times as much damage with your next attack!
 "
		finishing_move = TRUE

	playsound(src, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)

	temp = list_temp.Join()
	gameover_check(user)
	screen_setup(user)
	blocked = FALSE

/obj/machinery/computer/arcade/battle/proc/gameover_check(mob/user)
	var/xp_gained = 0
	if(enemy_hp <= 0)
		if(!gameover)
			if(timer_id)
				deltimer(timer_id)
				timer_id = null
			if(player_hp <= 0)
				player_hp = 1 //let's just pretend the enemy didn't kill you so not both the player and enemy look dead.
			gameover = TRUE
			blocked = FALSE
			temp = "

[enemy_name] has fallen! Rejoice!
"
			playsound(loc, 'sound/arcade/win.ogg', 50, TRUE)

			if(obj_flags & EMAGGED)
				new /obj/effect/spawner/newbomb/timer(loc)
				new /obj/item/clothing/head/collectable/petehat(loc)
				message_admins("[ADMIN_LOOKUPFLW(usr)] has outbombed Cuban Pete and been awarded a bomb.")
				log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.")
				Reset()
				obj_flags &= ~EMAGGED
				xp_gained += 100
			else
				prizevend(user)
				xp_gained += 50
			SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("win", (obj_flags & EMAGGED ? "emagged":"normal")))

	else if(player_hp <= 0)
		if(timer_id)
			deltimer(timer_id)
			timer_id = null
		gameover = TRUE
		temp = "

You have been crushed! GAME OVER
"
		playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE)
		xp_gained += 10//pity points
		if(obj_flags & EMAGGED)
			var/mob/living/living_user = user
			if (istype(living_user))
				living_user.gib()
		SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "hp", (obj_flags & EMAGGED ? "emagged":"normal")))

	if(gameover)
		user?.mind?.adjust_experience(/datum/skill/gaming, xp_gained+1)//always gain at least 1 point of XP

///used to check if the last three move of the player are the one we want in the right order and if the passive's weakpoint has been triggered yet
/obj/machinery/computer/arcade/battle/proc/weakpoint_check(passive,first_move,second_move,third_move)
	if(LAZYLEN(last_three_move) < 3)
		return FALSE

	if(last_three_move[1] == first_move && last_three_move[2] == second_move && last_three_move[3] == third_move && LAZYACCESS(enemy_passive, passive))
		LAZYREMOVE(enemy_passive, passive)
		pissed_off++
		return TRUE
	else
		return FALSE

/obj/machinery/computer/arcade/battle/Destroy()
	enemy_passive = null
	weapons = null
	last_three_move = null
	return ..() //well boys we did it, lists are no more

/obj/machinery/computer/arcade/battle/examine_more(mob/user)
	var/list/msg = list(span_notice("You notice some writing scribbled on the side of [src]..."))
	msg += "\t[span_info("smart -> defend, defend, light attack")]"
	msg += "\t[span_info("shotgun -> defend, defend, power attack")]"
	msg += "\t[span_info("short temper -> counter, counter, counter")]"
	msg += "\t[span_info("poisonous -> light attack, light attack, light attack")]"
	msg += "\t[span_info("chonker -> power attack, power attack, power attack")]"
	msg += "\t[span_info("magical -> defend until outmagiced")]"
	return msg

/obj/machinery/computer/arcade/battle/emag_act(mob/user)
	if(obj_flags & EMAGGED)
		return

	to_chat(user, span_warning("A mesmerizing Rhumba beat starts playing from the arcade machine's speakers!"))
	temp = "

If you die in the game, you die for real!
"
	max_passive = 6
	bomb_cooldown = 18
	var/gamerSkill = 0
	if(usr?.mind)
		gamerSkill = usr.mind.get_skill_level(/datum/skill/gaming)
	enemy_setup(gamerSkill)
	enemy_hp += 100 //extra HP just to make cuban pete even more bullshit
	player_hp += 30 //the player will also get a few extra HP in order to have a fucking chance

	screen_setup(user)
	gameover = FALSE

	obj_flags |= EMAGGED

	enemy_name = "Cuban Pete"
	name = "Outbomb Cuban Pete"

	updateUsrDialog()

// ** AMPUTATION ** //

/obj/machinery/computer/arcade/amputation
	name = "Mediborg's Amputation Adventure"
	desc = "A picture of a blood-soaked medical cyborg flashes on the screen. The mediborg has a speech bubble that says, \"Put your hand in the machine if you aren't a coward!\""
	icon_state = "arcade"
	circuit = /obj/item/circuitboard/computer/arcade/amputation

/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, list/modifiers)
	. = ..()
	if(!iscarbon(user))
		return
	var/mob/living/carbon/c_user = user
	if(!c_user.get_bodypart(BODY_ZONE_L_ARM) && !c_user.get_bodypart(BODY_ZONE_R_ARM))
		return
	to_chat(c_user, span_warning("You move your hand towards the machine, and begin to hesitate as a bloodied guillotine emerges from inside of it..."))
	if(do_after(c_user, 50, target = src))
		to_chat(c_user, span_userdanger("The guillotine drops on your arm, and the machine sucks it in!"))
		playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
		var/which_hand = BODY_ZONE_L_ARM
		if(!(c_user.active_hand_index % 2))
			which_hand = BODY_ZONE_R_ARM
		var/obj/item/bodypart/chopchop = c_user.get_bodypart(which_hand)
		chopchop.dismember()
		qdel(chopchop)
		user.mind?.adjust_experience(/datum/skill/gaming, 100)
		playsound(loc, 'sound/arcade/win.ogg', 50, TRUE)
		prizevend(user, rand(3,5))
	else
		to_chat(c_user, span_notice("You (wisely) decide against putting your hand in the machine."))

/obj/machinery/computer/arcade/amputation/festive //dispenses wrapped gifts instead of arcade prizes, also known as the ancap christmas tree
	name = "Mediborg's Festive Amputation Adventure"
	desc = "A picture of a blood-soaked medical cyborg wearing a Santa hat flashes on the screen. The mediborg has a speech bubble that says, \"Put your hand in the machine if you aren't a coward!\""
	prize_override = list(/obj/item/a_gift/anything = 1)


code:
// *** THE ORION TRAIL ** //

#define ORION_TRAIL_WINTURN 9

//defines in machines.dm

///assoc list, [datum singleton] = weight
GLOBAL_LIST_INIT(orion_events, generate_orion_events())

/proc/generate_orion_events()
	. = list()
	for(var/path in subtypesof(/datum/orion_event))
		var/datum/orion_event/new_event = new path(src)
		.[new_event] = new_event.weight

/obj/machinery/computer/arcade/orion_trail
	name = "The Orion Trail"
	desc = "Learn how our ancestors got to Orion, and have fun in the process!"
	icon_state = "arcade"
	circuit = /obj/item/circuitboard/computer/arcade/orion_trail
	var/busy = FALSE //prevent clickspam that allowed people to ~speedrun~ the game.
	var/engine = 0
	var/hull = 0
	var/electronics = 0
	var/food = 80
	var/fuel = 60
	var/turns = 4
	var/alive = ORION_STARTING_CREW_COUNT
	var/datum/orion_event/event = null
	var/reason
	var/list/settlers = list("Harry","Larry","Bob")
	var/list/settlermoods = list()
	var/list/events
	//actual amount of lings on board
	var/lings_aboard = 0
	//if the game should pretend there are lings on board.
	var/lings_suspected = FALSE
	var/spaceport_raided = FALSE
	var/gameStatus = ORION_STATUS_START

	var/obj/item/radio/radio
	var/list/gamers = list()
	var/killed_crew = 0

/obj/machinery/computer/arcade/orion_trail/Initialize(mapload)
	. = ..()
	radio = new /obj/item/radio(src)
	radio.listening = 0
	setup_events()

/obj/machinery/computer/arcade/orion_trail/proc/setup_events()
	events = GLOB.orion_events

/obj/machinery/computer/arcade/orion_trail/Destroy()
	QDEL_NULL(radio)
	events = null
	return ..()

/obj/machinery/computer/arcade/orion_trail/kobayashi
	name = "Kobayashi Maru control computer"
	desc = "A test for cadets."
	icon = 'icons/obj/machines/particle_accelerator.dmi'
	icon_state = "control_boxp"
	//kobatashi has a smaller list of events, so we copy from the global list and cut whatever isn't here
	var/list/event_whitelist = list(
		/datum/orion_event/raiders,
		/datum/orion_event/flux,
		/datum/orion_event/illness,
		/datum/orion_event/engine_part,
		/datum/orion_event/electronic_part,
		/datum/orion_event/hull_part,
		/datum/orion_event/space_port,
		/datum/orion_event/space_port/tau_ceti,
		/datum/orion_event/space_port_raid,
		/datum/orion_event/black_hole,
		/datum/orion_event/black_hole_death,
	)
	prize_override = list(/obj/item/paper/fluff/holodeck/trek_diploma = 1)
	settlers = list("Kirk","Worf","Gene")

/obj/machinery/computer/arcade/orion_trail/kobayashi/setup_events()
	events = GLOB.orion_events.Copy()
	for(var/datum/orion_event/event as anything in events)
		if(!(event.type in event_whitelist))
			events.Remove(event)

/obj/machinery/computer/arcade/orion_trail/proc/newgame()
	// Set names of settlers in crew
	var/mob/living/carbon/player = usr
	var/player_crew_name = player.first_name()
	settlers = list()
	for(var/i in 1 to ORION_STARTING_CREW_COUNT - 1) //one reserved to be YOU
		add_crewmember(update = FALSE)
	add_crewmember("[player_crew_name]")
	// Re-set items to defaults
	engine = 1
	hull = 1
	electronics = 1
	food = 80
	fuel = 60
	alive = ORION_STARTING_CREW_COUNT
	turns = 1
	event = null
	gameStatus = ORION_STATUS_NORMAL
	lings_aboard = 0
	lings_suspected = FALSE
	killed_crew = 0

	//spaceport junk
	spaceport_raided = FALSE

/obj/machinery/computer/arcade/orion_trail/proc/report_player(mob/gamer)
	if(gamers[gamer] == ORION_GAMER_GIVE_UP)
		return // enough harassing them

	if(gamers[gamer] == ORION_GAMER_PAMPHLET)
		say("WARNING: Continued antisocial behavior detected: Dispensing self-help literature.")
		new /obj/item/paper/pamphlet/violent_video_games(drop_location())
		gamers[gamer]--
		return

	if(!(gamer in gamers))
		gamers[gamer] = 0

	gamers[gamer]++ // How many times the player has 'prestiged' (massacred their crew)

	if(gamers[gamer] > ORION_GAMER_REPORT_THRESHOLD && prob(20 * gamers[gamer]))

		radio.set_frequency(FREQ_SECURITY)
		radio.talk_into(src, "SECURITY ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please watch for violent behavior.", FREQ_SECURITY)

		radio.set_frequency(FREQ_MEDICAL)
		radio.talk_into(src, "PSYCH ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please schedule psych evaluation.", FREQ_MEDICAL)

		gamers[gamer] = ORION_GAMER_PAMPHLET //next report send a pamph

		gamer.client.give_award(/datum/award/achievement/misc/gamer, gamer) // PSYCH REPORT NOTE: patient kept rambling about how they did it for an "achievement", recommend continued holding for observation
		gamer.mind?.adjust_experience(/datum/skill/gaming, 50) // cheevos make u better

		if(!isnull(GLOB.data_core.general))
			for(var/datum/data/record/insanity_records in GLOB.data_core.general)
				if(insanity_records.fields["name"] == gamer.name)
					insanity_records.fields["m_stat"] = "*Unstable*"
					return

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

/obj/machinery/computer/arcade/orion_trail/ui_assets(mob/user)
	return list(
		get_asset_datum(/datum/asset/spritesheet/moods),
	)

/obj/machinery/computer/arcade/orion_trail/ui_data(mob/user)
	var/list/data = list()
	data["gamestatus"] = gameStatus

	data["engine"] = engine
	data["turns"] = turns
	data["hull"] = hull
	data["electronics"] = electronics
	data["food"] = food
	data["fuel"] = fuel
	data["lings_suspected"] = lings_suspected

	data["eventname"] = event?.name
	data["eventtext"] = event?.text
	data["buttons"] = event?.event_responses

	data["spaceport_raided"] = spaceport_raided

	data["reason"] = reason

	return data

/obj/machinery/computer/arcade/orion_trail/ui_static_data(mob/user)
	var/list/static_data = list()
	static_data["gamename"] = name
	static_data["emagged"] = obj_flags & EMAGGED
	static_data["settlers"] = settlers
	static_data["settlermoods"] = settlermoods
	return static_data

/obj/machinery/computer/arcade/orion_trail/ui_act(action, list/params)
	. = ..()
	if(.)
		return

	var/mob/living/carbon/gamer = usr
	if(!istype(gamer))
		return

	. = TRUE

	var/gamer_skill_level = 0
	var/gamer_skill = 0
	var/gamer_skill_rands = 0

	if(gamer?.mind)
		gamer_skill_level = gamer.mind.get_skill_level(/datum/skill/gaming)
		gamer_skill = gamer.mind.get_skill_modifier(/datum/skill/gaming, SKILL_PROBS_MODIFIER)
		gamer_skill_rands = gamer.mind.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER)

	var/xp_gained = 0

	if(event)
		event.response(src, action)
		if(!settlers.len || food <= 0 || fuel <= 0)
			set_game_over(gamer)
			return
		new_settler_mood() //events shake people up a bit and can also change food
		update_static_data(usr)
		return TRUE
	switch(action)
		if("start_game")
			if(gameStatus != ORION_STATUS_START)
				return
			newgame()
		if("instructions")
			if(gameStatus != ORION_STATUS_START)
				return
			gameStatus = ORION_STATUS_INSTRUCTIONS
		if("back_to_menu") //back to the main menu
			gameStatus = ORION_STATUS_START
			if(gameStatus == ORION_STATUS_GAMEOVER)
				event = null
				reason = null
				food = 80
				fuel = 60
				settlers = list("Harry","Larry","Bob")
		if("continue")
			if (gameStatus == ORION_STATUS_START)
				return
			if(turns >= ORION_TRAIL_WINTURN)
				win(gamer)
				xp_gained += 34
				return
			gamer.mind.adjust_experience(/datum/skill/gaming, xp_gained+1)
			food -= (alive+lings_aboard)*2
			fuel -= 5
			turns += 1
			//out of supplies, die
			if(food <= 0 || fuel <= 0)
				set_game_over(gamer)
			if(turns == 2 && prob(30-gamer_skill)) //asteroids part of the trip
				encounter_event(/datum/orion_event/hull_part, gamer, gamer_skill, gamer_skill_level, gamer_skill_rands)
				return
			if(turns == 4) //halfway mark
				encounter_event(/datum/orion_event/space_port/tau_ceti, gamer, gamer_skill, gamer_skill_level, gamer_skill_rands)
				return
			if(turns == 7) //black hole part of the trip
				encounter_event(/datum/orion_event/black_hole, gamer, gamer_skill, gamer_skill_level, gamer_skill_rands)
				return
			//an uneventful (get it) turn
			if(prob(25 + gamer_skill))
				return
			encounter_event(null, gamer, gamer_skill, gamer_skill_level)
			if(lings_aboard && (istype(event, /datum/orion_event/changeling_infiltration) || prob(45 + gamer_skill)))
				//upgrade infiltration/whatever else we got to attack right away
				encounter_event(/datum/orion_event/changeling_attack, gamer, gamer_skill, gamer_skill_level, gamer_skill_rands)
		if("random_kill")
			execute_crewmember(gamer)
		if("target_kill")
			execute_crewmember(gamer, params["who"])
		//Spaceport specific interactions
		if("buycrew") //buy a crewmember
			if(!spaceport_raided && food >= 10 && fuel >= 10 && gameStatus == ORION_STATUS_MARKET)
				if(params["odd"])
					//find some silly crewmember name
					add_crewmember(pick(GLOB.commando_names + GLOB.nightmare_names + GLOB.ai_names + GLOB.clown_names + GLOB.mime_names + GLOB.plasmaman_names + GLOB.ethereal_names + GLOB.carp_names))
				else
					add_crewmember()
				fuel -= ORION_BUY_CREW_PRICE
				food -= ORION_BUY_CREW_PRICE
				killed_crew-- // I mean not really but you know
		if("sellcrew") //sell a crewmember
			if(!spaceport_raided && settlers.len > 1 && gameStatus == ORION_STATUS_MARKET)
				remove_crewmember()
				fuel += ORION_SELL_CREW_PRICE
				food += ORION_SELL_CREW_PRICE
		if("leave_spaceport")
			if(gameStatus != ORION_STATUS_MARKET) //Can't leave a place you aren't in
				return
			gameStatus = ORION_STATUS_NORMAL
			spaceport_raided = FALSE
		if("raid_spaceport")
			if(gameStatus != ORION_STATUS_MARKET)
				return
			spaceport_raided = TRUE
			encounter_event(/datum/orion_event/space_port_raid, gamer, gamer_skill, gamer_skill_level, gamer_skill_rands)
		if("buyparts")
			if(!spaceport_raided && fuel > ORION_TRADE_RATE && gameStatus == ORION_STATUS_MARKET)
				switch(params["part"])
					if(ORION_BUY_ENGINE_PARTS)
						engine++
					if(ORION_BUY_ELECTRONICS)
						hull++
					if(ORION_BUY_HULL_PARTS)
						electronics++
				fuel -= ORION_TRADE_RATE
		if("trade")
			if(!spaceport_raided && gameStatus == ORION_STATUS_MARKET)
				switch(params["what"])
					if(ORION_I_WANT_FUEL)
						if(fuel > ORION_TRADE_RATE)
							fuel -= ORION_TRADE_RATE
							food += ORION_TRADE_RATE
					if(ORION_I_WANT_FOOD)
						if(food > ORION_TRADE_RATE)
							fuel += ORION_TRADE_RATE
							food -= ORION_TRADE_RATE
	add_fingerprint(gamer)

/**
 * pickweights a new event, sets event var as it. it then preps the event if it needs it
 *
 * giving a path argument will instead find that instanced datum instead of pickweighting. Used in events that follow from events.
 * Arguments:
 * * path: if we want a specific event, this is the path of the wanted one
 * * gamer: person using the arcade, used in emag effects
 * * gamer_skill: gaming skill of the player
 * * gamer_skill_level: gaming skill level of the player
 * * gamer_skill_rands: See above but for random chances, you can just look at gaming skill to see how it chalks that up
 */
/obj/machinery/computer/arcade/orion_trail/proc/encounter_event(path, gamer, gamer_skill, gamer_skill_level, gamer_skill_rands)
	if(!path)
		event = pick_weight_allow_zero(events)
	else
		for(var/datum/orion_event/instance as anything in events)
			if(instance.type == path)
				event = instance
				break
	if(!event)
		CRASH("Woah, hey! we could not find the specified event \"[path]\"! Add it to the events list, numb nuts!")
	event.on_select(src, gamer_skill, gamer_skill_level, gamer_skill_rands)
	if(obj_flags & EMAGGED)
		event.emag_effect(src, gamer)

/obj/machinery/computer/arcade/orion_trail/proc/set_game_over(user, given_reason)
	gameStatus = ORION_STATUS_GAMEOVER
	event = null
	reason = given_reason || death_reason(user)

/obj/machinery/computer/arcade/orion_trail/proc/death_reason(mob/living/carbon/gamer)
	var/reason
	if(!settlers.len)
		reason = "Your entire crew died, and your ship joins the fleet of ghost-ships littering the galaxy."
	else
		if(food <= 0)
			reason = "You ran out of food and starved."
			if(obj_flags & EMAGGED)
				gamer.set_nutrition(0) //yeah you pretty hongry
				to_chat(gamer, span_userdanger("Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor."))
		if(fuel <= 0)
			reason = "You ran out of fuel, and drift, slowly, into a star."
			if(obj_flags & EMAGGED)
				gamer.adjust_fire_stacks(5)
				gamer.IgniteMob() //flew into a star, so you're on fire
				to_chat(gamer, span_userdanger("You feel an immense wave of heat emanate from the arcade machine. Your skin bursts into flames."))

	if(obj_flags & EMAGGED)
		to_chat(gamer, span_userdanger("You're never going to make it to Orion..."))
		gamer.death()
		obj_flags &= ~EMAGGED //removes the emagged status after you lose
		gameStatus = ORION_STATUS_START
		name = "The Orion Trail"
		desc = "Learn how our ancestors got to Orion, and have fun in the process!"

	gamer?.mind?.adjust_experience(/datum/skill/gaming, 10)//learning from your mistakes is the first rule of roguelikes
	return reason

//Add Random/Specific crewmember
/obj/machinery/computer/arcade/orion_trail/proc/add_crewmember(specific = "", update = TRUE)
	var/newcrew = ""
	if(specific)
		newcrew = specific
	else
		if(prob(50))
			newcrew = pick(GLOB.first_names_male)
		else
			newcrew = pick(GLOB.first_names_female)
	if(newcrew)
		settlers += newcrew
		alive++
	if(update)
		new_settler_mood()//new faces!
		update_static_data(usr)
	return newcrew

//Remove Random/Specific crewmember
/obj/machinery/computer/arcade/orion_trail/proc/remove_crewmember(specific = "", dont_remove = "", update = TRUE)
	var/list/safe_to_remove = settlers
	var/removed = ""
	if(dont_remove)
		safe_to_remove -= dont_remove
	if(specific && specific != dont_remove)
		safe_to_remove = list(specific)
	removed = pick(safe_to_remove)

	if(removed)
		if(lings_aboard && prob(40*lings_aboard)) //if there are 2 lings you're twice as likely to get one, obviously
			lings_aboard = max(0,--lings_aboard)
		settlers -= removed
		alive--
	if(update)
		new_settler_mood()//bro, i...
		update_static_data(usr)
	return removed

/**
 * Crewmember executed code, targeted when there are no lings and untargeted when there are some
 * If there was no suspected lings (aka random shots) this is just murder and counts towards killed crew
 *
 * Arguments:
 * * gamer: carbon that may need emag effects applied
 */
/obj/machinery/computer/arcade/orion_trail/proc/execute_crewmember(mob/living/carbon/gamer, target)
	var/sheriff = remove_crewmember(target) //I shot the sheriff
	if(target)
		killed_crew += 1 //if there was no suspected lings, this is just plain murder
	playsound(loc,'sound/weapons/gun/pistol/shot.ogg', 100, TRUE)
	if(!settlers.len || !alive)
		say("The last crewmember [sheriff], shot themselves, GAME OVER!")
		if(obj_flags & EMAGGED)
			gamer.death()
		set_game_over(gamer, "Your last pioneer committed suicide.")
		if(killed_crew >= ORION_STARTING_CREW_COUNT)
			gamer.mind?.adjust_experience(/datum/skill/gaming, -15)//no cheating by spamming game overs
			report_player(gamer)
	else if(obj_flags & EMAGGED)
		if(findtext(gamer.name, sheriff))
			say("The crew of the ship chose to kill [gamer]!")
			gamer.death()

/**
 * Creates a new mood icon for each settler
 *
 * Things that effect mood:
 * * Pioneer count
 * * Low food
 * * Low parts
 * * Sometimes they're just a bit happier or sadder
 * Arguments:
 * * None!
 */
/obj/machinery/computer/arcade/orion_trail/proc/new_settler_mood()
	settlermoods.Cut()
	for(var/i in 1 to settlers.len)
		var/food_mood = food >= 15
		var/supply_mood = -2
		if(hull)
			supply_mood++
		if(electronics)
			supply_mood++
		if(engine)
			supply_mood++
		var/changing_mood = 0
		if(prob(60)) //sometimes they just feel better or worse
			changing_mood = rand(-1,1)
		settlermoods[settlers[i]] += max(settlers.len + food_mood + supply_mood + changing_mood, 1)
		if(lings_suspected) //lings ruin any good mood
			settlermoods[settlers[i]] = min(settlermoods[settlers[i]], 3)

/obj/machinery/computer/arcade/orion_trail/proc/win(mob/user)
	gameStatus = ORION_STATUS_START
	say("Congratulations, you made it to Orion!")
	if(obj_flags & EMAGGED)
		new /obj/item/orion_ship(loc)
		message_admins("[ADMIN_LOOKUPFLW(usr)] made it to Orion on an emagged machine and got an explosive toy ship.")
		log_game("[key_name(usr)] made it to Orion on an emagged machine and got an explosive toy ship.")
	else
		prizevend(user)
	obj_flags &= ~EMAGGED
	name = initial(name)
	desc = initial(desc)

/obj/machinery/computer/arcade/orion_trail/emag_act(mob/user)
	if(obj_flags & EMAGGED)
		return
	to_chat(user, span_notice("You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Realism Mode."))
	name = "The Orion Trail: Realism Edition"
	desc = "Learn how our ancestors got to Orion, and try not to die in the process!"
	newgame()
	obj_flags |= EMAGGED

/mob/living/simple_animal/hostile/syndicate/ranged/smg/orion
	name = "spaceport security"
	desc = "Premier corporate security forces for all spaceports found along the Orion Trail."
	faction = list("orion")
	loot = list()
	del_on_death = TRUE

/obj/item/orion_ship
	name = "model settler ship"
	desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..."
	icon = 'icons/obj/toy.dmi'
	icon_state = "ship"
	w_class = WEIGHT_CLASS_SMALL
	var/active = 0 //if the ship is on

/obj/item/orion_ship/examine(mob/user)
	. = ..()
	if(!(in_range(user, src)))
		return
	if(!active)
		. += span_notice("There's a little switch on the bottom. It's flipped down.")
	else
		. += span_notice("There's a little switch on the bottom. It's flipped up.")

/obj/item/orion_ship/attack_self(mob/user) //Minibomb-level explosion. Should probably be more because of how hard it is to survive the machine! Also, just over a 5-second fuse
	if(active)
		return

	log_bomber(usr, "primed an explosive", src, "for detonation")

	to_chat(user, span_warning("You flip the switch on the underside of [src]."))
	active = 1
	visible_message(span_notice("[src] softly beeps and whirs to life!"))
	playsound(loc, 'sound/machines/defib_SaftyOn.ogg', 25, TRUE)
	say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.")
	sleep(20)
	visible_message(span_warning("[src] begins to vibrate..."))
	say("Uh, Port? Having some issues with our reactor, could you check it out? Over.")
	sleep(30)
	say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-")
	playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, TRUE)
	sleep(3.6)
	visible_message(span_userdanger("[src] explodes!"))
	explosion(src, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 8, flame_range = 16)
	qdel(src)

#undef ORION_TRAIL_WINTURN

repo_name: AnalWerewolf/sundown

license: mit


code:
//yep you guessed it back at it again with another datum singleton.
/datum/orion_event
	var/name = "this displays the events name"
	var/text = "this displays a blurb about the event"
	///pickweight to show up. will still be in the events pool if added to the events list but not RANDOM, only triggered.
	var/weight = 0
	///buttons to pick in response to the event. Don't worry, orion js will handle the rest
	var/list/event_responses = list()
	///default emag effect of events is to play an audible message and sound
	var/emag_message
	var/emag_sound
	///gaming skill level of the player
	var/gamer_skill_level = 0
	///gaming skill of the player
	var/gamer_skill = 0
	///some other metric that makes it easier to do randoms with skill testing, god really 3 vars guys
	var/gamer_skill_rands = 0

/**
 * What happens when this event is selected to trigger, sets vars. also can set some event pre-encounter randomization
 *
 * Arguments:
 * * game: arcade machine calling this
 * * gamer_skill: gaming skill of the player
 * * gamer_skill_level: gaming skill level of the player
 * * gamer_skill_rands: See above but it's another metric, you can just look at gaming skill to see how it chalks that up
 */
/datum/orion_event/proc/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	SHOULD_CALL_PARENT(TRUE)
	src.gamer_skill_level = gamer_skill_level
	src.gamer_skill = gamer_skill
	src.gamer_skill_rands = gamer_skill_rands

/**
 * What happens when you respond to this event by choosing one of the buttons
 *
 * Arguments:
 * * game: arcade machine calling this
 * * choice: name of the button you pressed.
 */
/datum/orion_event/proc/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	game.event = null

/**
 * Some effect that happens to the carbon when this event triggers on an emagged arcade machine.
 *
 * By default, it just sends an audible message and a sound, both vars on the orion datum
 * Arguments:
 * * game: arcade machine calling this
 * * gamer: victim to apply effects to
 * * gamer_skill: skill of the gamer, because gamers gods can lessen the effects of the emagged machine
 * * gamer_skill_level: skill level of the gamer, another way to measure emag downside avoidance
 */
/datum/orion_event/proc/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	if(emag_message)
		game.audible_message(emag_message)
	if(emag_sound)
		playsound(game, emag_sound, 100, TRUE)

#define BUTTON_FIX_ENGINE "Fix Engine"
#define BUTTON_WAIT "Wait"

///Engine Breakdown - spend one engine part or wait 3 days (harmless emag effect)
/datum/orion_event/engine_part
	name = "Engine Breakdown"
	text = "Oh no! The engine has broken down! \
	You can repair it with an engine part, or you \
	can make repairs for 3 days."
	emag_message = "You hear some large object lurch to a halt right behind you! When you go to look, nothing's there..."
	emag_sound = 'sound/effects/creak1.ogg'
	weight = 2
	event_responses = list()

/datum/orion_event/engine_part/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	..()
	if(game.engine >= 1)
		event_responses += BUTTON_FIX_ENGINE
	event_responses += BUTTON_WAIT

/datum/orion_event/engine_part/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	if(choice == BUTTON_FIX_ENGINE)
		game.engine = max(0, --game.engine)
	else
		game.food -= (game.alive + game.lings_aboard) * ORION_LONG_DELAY
	event_responses.Cut()
	..()

#define BUTTON_REPAIR_ELECTRONICS "Repair Electronics"

///Malfunction - spend one engine part or wait 3 days (emag effect randomizes some stats)
/datum/orion_event/electronic_part
	name = "Malfunction"
	text = "The ship's systems are malfunctioning! \
	You can replace the broken electronics with spares, \
	or you can spend 3 days troubleshooting the AI."
	weight = 2
	//set by select
	event_responses = list()

/datum/orion_event/electronic_part/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	..()
	if(game.electronics >= 1)
		event_responses += BUTTON_REPAIR_ELECTRONICS
	event_responses += BUTTON_WAIT

/datum/orion_event/electronic_part/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	if(choice == BUTTON_REPAIR_ELECTRONICS)
		game.electronics = max(0, game.electronics - 1)
	else
		game.food -= (game.alive + game.lings_aboard) * ORION_LONG_DELAY
	event_responses.Cut()
	..()

/datum/orion_event/electronic_part/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	playsound(game, 'sound/effects/empulse.ogg', 50, TRUE)
	game.visible_message(span_danger("[src] malfunctions, randomizing in-game stats!"))
	var/oldfood = game.food
	var/oldfuel = game.fuel
	game.food = rand(10,80) / rand(1,2)
	game.fuel = rand(10,60) / rand(1,2)
	if(game.electronics)
		addtimer(CALLBACK(game, .proc/revert_random, game, oldfood, oldfuel), 1 SECONDS)

/datum/orion_event/electronic_part/proc/revert_random(obj/machinery/computer/arcade/orion_trail/game, oldfood, oldfuel)
	if(oldfuel > game.fuel && oldfood > game.food)
		game.audible_message(span_danger("[src] lets out a somehow reassuring chime."))
	else if(oldfuel < game.fuel || oldfood < game.food)
		game.audible_message(span_danger("[src] lets out a somehow ominous chime."))
	game.food = oldfood
	game.fuel = oldfuel
	playsound(game, 'sound/machines/chime.ogg', 50, TRUE)

#define BUTTON_RESTORE_HULL "Restore Hull"

///Collision - spend one engine part or wait 3 days (has a nasty emag effect)
/datum/orion_event/hull_part
	name = "Collision"
	text = "Something hit us! Looks like there's some hull damage. \
	You can repair the damage with hull plates, or you can spend \
	the next 3 days welding scrap together."
	weight = 2
	event_responses = list()

/datum/orion_event/hull_part/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	..()
	if(game.hull >= 1)
		event_responses += BUTTON_RESTORE_HULL
	event_responses += BUTTON_WAIT

/datum/orion_event/hull_part/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	if(choice == BUTTON_RESTORE_HULL)
		game.hull = max(0, game.hull - 1)
	else
		game.food -= (game.alive + game.lings_aboard) * ORION_LONG_DELAY
	event_responses.Cut()
	..()

/datum/orion_event/hull_part/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	if(prob(10+gamer_skill))
		game.say("Something slams into the floor around [src] - luckily, it didn't get through!")
		playsound(game, 'sound/effects/bang.ogg', 50, TRUE)
		return
	playsound(game, 'sound/effects/bang.ogg', 100, TRUE)
	for(var/turf/open/floor/smashed in orange(1, src))
		smashed.ScrapeAway()
	game.say("Something slams into the floor around [src], exposing it to space!")
	if(game.hull)
		addtimer(CALLBACK(game, .proc/fix_floor, game), 1 SECONDS)

/datum/orion_event/hull_part/proc/fix_floor(obj/machinery/computer/arcade/orion_trail/game)
	game.say("A new floor suddenly appears around [src]. What the hell?")
	playsound(game, 'sound/weapons/genhit.ogg', 100, TRUE)
	for(var/turf/open/space/fixed in orange(1, src))
		fixed.PlaceOnTop(/turf/open/floor/plating)

#define BUTTON_EXPLORE_SHIP "Explore Ship"
#define BUTTON_LEAVE_THE_DERELICT "Leave the Derelict"

/datum/orion_event/old_ship
	name = "Derelict Ship"
	text = "Your crew spots an old ship floating through space. \
	It might have some supplies, but then again it looks rather unsafe."
	weight = 2
	event_responses = list(BUTTON_EXPLORE_SHIP, BUTTON_LEAVE_THE_DERELICT)

/datum/orion_event/old_ship/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	if(choice == BUTTON_LEAVE_THE_DERELICT)
		return ..()
	game.encounter_event(/datum/orion_event/exploring_derelict)

/datum/orion_event/old_ship/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	return //do nothing because this leads into an event where we actually will do something.

#define BUTTON_WELCOME_ABOARD "Welcome aboard."
#define BUTTON_WHERE_DID_YOU_GO "Where did you go?!"
#define BUTTON_A_GOOD_FIND "A good find."
#define BUTTON_CONTINUE_TRAVELS "Continue travels."

/datum/orion_event/exploring_derelict
	name = "Derelict Exploration"
	weight = 2
	//set by on_select
	event_responses = list()

/datum/orion_event/exploring_derelict/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	..()
	switch(rand(100))
		if(0 to 14)
			var/rescued = game.add_crewmember()
			var/oldfood = rand(1,7)
			var/oldfuel = rand(4,10)
			game.food += oldfood
			game.fuel += oldfuel
			text = "As you look through it you find some supplies and a living person! \
			[rescued] was rescued from the abandoned ship! You also found [oldfood] Food and [oldfuel] Fuel."
			event_responses += BUTTON_WELCOME_ABOARD
		if(15 to 35)
			var/lostfuel = rand(4,7)
			var/deadname = game.remove_crewmember()
			game.fuel -= lostfuel
			text = "[deadname] was lost deep in the wreckage, and your own vessel lost [lostfuel] Fuel maneuvering to the the abandoned ship."
			event_responses += BUTTON_WHERE_DID_YOU_GO
		if(36 to 65)
			var/oldfood = rand(5,11)
			game.food += oldfood
			game.engine++
			text = "You found [oldfood] Food and some parts amongst the wreck."
			event_responses += BUTTON_A_GOOD_FIND
		else
			text = "As you look through the wreck you cannot find much of use."
			event_responses += BUTTON_CONTINUE_TRAVELS

/datum/orion_event/exploring_derelict/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	event_responses.Cut() //so they don't pile up between games
	..()

#define BUTTON_CONTINUE "Continue"

/datum/orion_event/raiders
	name = "Raiders"
	weight = 3
	event_responses = list(BUTTON_CONTINUE)

/datum/orion_event/raiders/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	..()
	text = "Raiders have come aboard your ship! "
	if(prob(50))
		var/sfood = rand(1,10)
		var/sfuel = rand(1,10)
		game.food -= sfood
		game.fuel -= sfuel
		text += "They have stolen [sfood] Food and [sfuel] Fuel."
	else if(prob(10))
		var/deadname = game.remove_crewmember()
		text += "[deadname] tried to fight back, but was killed."
	else
		text += "Fortunately, you fended them off without any trouble."

/datum/orion_event/raiders/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	if(prob(50-gamer_skill))
		to_chat(usr, span_userdanger("You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?"))
		gamer.hallucination += 30
	else
		to_chat(usr, span_userdanger("Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there..."))
		gamer.take_bodypart_damage(30)
		playsound(game, 'sound/weapons/genhit2.ogg', 100, TRUE)

/datum/orion_event/illness
	name = "Space Illness"
	//needs to specify who died, set by select
	weight = 3
	event_responses = list(BUTTON_CONTINUE)

/datum/orion_event/illness/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	..()
	var/deadname = game.remove_crewmember()
	text = "A deadly illness has been contracted! [deadname] was killed by the disease."

/datum/orion_event/illness/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	var/maxSeverity = 3
	if(gamer_skill_level >= SKILL_LEVEL_EXPERT)
		maxSeverity = 2 //part of gitting gud is rng mitigation
	var/severity = rand(1,maxSeverity) //pray to RNGesus. PRAY, PIGS
	if(severity == 1)
		to_chat(gamer, span_userdanger("You suddenly feel slightly nauseated.") )
		gamer.adjust_disgust(50)
	if(severity == 2)
		to_chat(usr, span_userdanger("You suddenly feel extremely nauseated and hunch over until it passes."))
		gamer.adjust_disgust(110)
		gamer.Stun(60)
	if(severity >= 3) //you didn't pray hard enough
		to_chat(gamer, span_warning("An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit."))
		gamer.adjust_disgust(150) //max this bitch out so they barf a lot
		gamer.Stun(100)

#define BUTTON_KEEP_SPEED "Keep Speed"
#define BUTTON_SLOW_DOWN "Slow Down"

/datum/orion_event/flux
	name = "Flux"
	text = "This region of space is highly turbulent. If we go \
	slowly we may avoid more damage, but if we keep our speed we won't waste supplies."
	weight = 1
	event_responses = list(BUTTON_KEEP_SPEED, BUTTON_SLOW_DOWN)

/datum/orion_event/flux/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	if(choice == BUTTON_KEEP_SPEED)
		if(prob(25))
			return ..()
		game.encounter_event(/datum/orion_event/engine_part)
	else //Slow Down response
		game.food -= (game.alive+game.lings_aboard) * ORION_SHORT_DELAY
		game.fuel -= 5
		..()

/datum/orion_event/flux/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	if(prob(25 + gamer_skill))//withstand the wind with your GAMER SKILL
		to_chat(gamer, span_userdanger("A violent gale blows past you, and you barely manage to stay standing!"))
		return
	gamer.Paralyze(60)
	game.say("A sudden gust of powerful wind slams [gamer] into the floor!")
	gamer.take_bodypart_damage(25)
	playsound(game, 'sound/weapons/genhit.ogg', 100, TRUE)

/datum/orion_event/changeling_infiltration
	name = "Changeling Infiltration"
	weight = 3
	event_responses = list(BUTTON_CONTINUE)

/datum/orion_event/changeling_infiltration/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	. = ..()
	text = "Strange reports warn of changelings infiltrating crews on trips to Orion..."
	if(game.settlers.len <= 2)
		text += " But your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..."
		if(prob(10)) // "likely", I didn't say it was guaranteed! //worst outcome too because changeling controls don't even enable
			game.lings_aboard = min(++game.lings_aboard,2)
	else
		if(game.lings_aboard) //less likely to stack lings
			if(prob(20))
				game.lings_aboard = min(++game.lings_aboard,2)
		else if(prob(70))
			game.lings_aboard = min(++game.lings_aboard,2)

		text += " You have reasonable suspicion there may be a changeling. Some menu options have been changed."
		game.lings_suspected = TRUE

/datum/orion_event/changeling_attack
	name = "Changeling Attack"
	event_responses = list(BUTTON_CONTINUE)

/datum/orion_event/changeling_attack/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	. = ..()
	text = ""
	if(game.lings_aboard <= 0) //shouldn't trigger, but hey.
		text = "Haha, fooled you, there are no changelings on board! (You should report this to a coder :S )"
		return
	var/ling1 = game.remove_crewmember()
	var/ling2 = ""
	if(game.lings_aboard >= 2)
		ling2 = game.remove_crewmember()
	if(ling2)
		text = " [ling1] and [ling2]'s arms twist and contort into grotesque blades!"
	else
		text = " [ling1]'s arm twists and contorts into a grotesque blade!"

	var/chance2attack = game.alive*20
	if(!prob(chance2attack))
		text += " [pick("Sensing unfavorable odds", "After a failed attack", "Suddenly breaking nerve")], \
		the changeling[ling2 ? "s":""] vanish[ling2 ? "" : "es"] into space through the airlocks! You're safe... for now."
		if(ling2)
			game.lings_aboard = max(0,game.lings_aboard-2)
		else
			game.lings_aboard = max(0,game.lings_aboard - 2)
		return

	var/chancetokill = 30*game.lings_aboard-(5*game.alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance
	if(prob(chancetokill))
		var/deadguy = game.remove_crewmember()
		var/murder_text = pick(
			"The changeling[ling2 ? "s" : ""] bring[ling2 ? "" : "s"] down [deadguy] and disembowel[ling2 ? "" : "s"] them in a spray of gore!", \
			"[ling2 ? pick(ling1, ling2) : ling1] corners [deadguy] and impales them through the stomach!", \
			"[ling2 ? pick(ling1, ling2) : ling1] decapitates [deadguy] in a single cleaving arc!")
		text += " [murder_text]"
	else
		text += " You valiantly fight off the changeling[ling2 ? "s":""]!"
		if(ling2)
			game.food += 30
			game.lings_aboard = max(0, game.lings_aboard - 2)
		else
			game.food += 15
			game.lings_aboard = max(0, game.lings_aboard - 1)
		text += " Well, it's perfectly good food... \
			You cut the changeling[ling2 ? "s" : ""] into meat, gaining [ling2 ? "30" : "15"] Food!"

#define BUTTON_SPEED_PAST "Speed Past"
#define BUTTON_GO_AROUND "Go Around"

///Black Hole - final  (emag can spawn singulo, see death event)
/datum/orion_event/black_hole
	name = "Looming Black Hole"
	text = "Sensors indicate that a black hole's gravitational field is \
	affecting the region of space we were headed through. We could stay \
	of course, but risk of being overcome by its gravity, or we could \
	change course to go around, which will take longer."
	event_responses = list(BUTTON_SPEED_PAST, BUTTON_GO_AROUND)

/datum/orion_event/black_hole/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	if(choice == BUTTON_GO_AROUND)
		game.food -= (game.alive + game.lings_aboard) * ORION_LONG_DELAY
		game.fuel -= 15
		game.turns += 1
		return ..()
	if(prob(75-gamer_skill))
		game.encounter_event(/datum/orion_event/black_hole_death)
		return
	game.turns += 1
	..()

#define BUTTON_OH "Oh..."

///You died to a black hole, have some fluff text
/datum/orion_event/black_hole_death
	name = "Event Horizon"
	text = "As you jet the shuttle forward, you realize you underestimated the \
	pull of the black hole. Try as you may, you cannot escape its stellar force. \
	It isn't long before you pass the event horizon, and you close your eyes, readying \
	to be torn apart as your ship begins to buckle under the pull."
	event_responses = list(BUTTON_OH)

/datum/orion_event/black_hole_death/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	game.set_game_over(usr, "You were swept away into the black hole.")
	..()

/datum/orion_event/black_hole_death/emag_effect(obj/machinery/computer/arcade/orion_trail/game, mob/living/carbon/gamer)
	if(game.obj_flags & EMAGGED)
		playsound(game.loc, 'sound/effects/supermatter.ogg', 100, TRUE)
		game.say("A miniature black hole suddenly appears in front of [game], devouring [gamer] alive!")
		gamer.Stun(200, ignore_canstun = TRUE) //you can't run :^)
		var/black_hole = new /obj/singularity/academy(gamer.loc)
		addtimer(CALLBACK(game, /atom/movable/proc/say, "[black_hole] winks out, just as suddenly as it appeared."), 50)
		QDEL_IN(black_hole, 5 SECONDS)

#define BUTTON_DOCK "Dock"

///You found a space port!
/datum/orion_event/space_port
	name = "Space Port"
	var/normal_arrival = "You have spotted a small pocket of civilization \
	along the Orion Trail. A friendly hailing from the nearby space port \
	assures that you can dock to rest and prepare for the travels ahead."
	weight = 2
	event_responses = list(BUTTON_DOCK)

/datum/orion_event/space_port/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	. = ..()
	//If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port)
	if(game.fuel > 20 && game.food > 20) //but you don't need one
		text = normal_arrival
		return
	text = "The workers at the Port are shocked at the state of \
	your pioneers. With supplies that low, they take pity on you and \
	your crew, generously giving you some free supplies! "

	var/pity_fuel = 10
	var/pity_food = 10
	var/freecrew = prob(10)
	if(prob(30))
		pity_fuel = 25
		pity_fuel = 25
	if(freecrew)
		game.add_crewmember()
	text += "(+[pity_food] fuel, +[pity_fuel] food)"
	if(freecrew)
		text += " A worker at the Port is inspired by your determination, and joins your crew!"
	game.fuel += pity_fuel
	game.food += pity_food

/datum/orion_event/space_port/response(obj/machinery/computer/arcade/orion_trail/game, choice)
	game.gameStatus = ORION_STATUS_MARKET
	..()

///You found the midway mark!
/datum/orion_event/space_port/tau_ceti
	name = "Tau Ceti Beta"
	normal_arrival = "You have reached the halfway point in your journey, the largest space port \
	along the trail: Tau Ceti Beta. It bustles with activity and life. It gives you hope \
	of finding your future at Orion."
	//triggered by getting halfway
	weight = 0

///You raided a space port!
/datum/orion_event/space_port_raid
	name = "Space Port Raid"

	event_responses = list(BUTTON_CONTINUE)

/datum/orion_event/space_port_raid/on_select(obj/machinery/computer/arcade/orion_trail/game, gamer_skill, gamer_skill_level, gamer_skill_rands)
	. = ..()
	var/success = min(15 * game.alive + gamer_skill,100) //default crew (4) have a 60% chance
	game.spaceport_raided = TRUE

	var/fuel = 0
	var/food = 0
	if(prob(success))
		fuel = rand(5 + gamer_skill_rands,15 + gamer_skill_rands)
		food = rand(5 + gamer_skill_rands,15 + gamer_skill_rands)
		text = "You successfully raided the spaceport! You gained [fuel] Fuel and [food] Food! (+[fuel] fuel, +[food] food)"
		usr?.mind?.adjust_experience(/datum/skill/gaming, 10)
	else
		fuel = rand(-5,-15)
		food = rand(-5,-15)
		text = "You failed to raid the spaceport! You lost [fuel*-1] Fuel and [food*-1] Food in your scramble to escape! ([fuel] fuel, [food] food)"

		//your chance of lose a crewmember is 1/2 your chance of success
		//this makes higher % failures hurt more, don't get cocky space cowboy!
		if(prob(success*5))
			var/lost_crew = game.remove_crewmember()
			if(!game.settlers.len)
				game.set_game_over(usr, "You were gunned down by space port security.")
				return ..()
			text = "You failed to raid the spaceport! You lost [fuel*-1] Fuel and [food*-1] Food, AND [lost_crew] in your scramble to escape! ([fuel]fuel, [food] food, -Crew)"
			if(game.obj_flags & EMAGGED)
				game.say("WEEWOO! WEEWOO! Spaceport security en route!")
				playsound(game, 'sound/items/weeoo1.ogg', 100, FALSE)
				for(var/i, i<=3, i++)
					var/mob/living/simple_animal/hostile/syndicate/ranged/smg/orion/spaceport_security = new(get_turf(src))
					spaceport_security.GiveTarget(usr)
	game.fuel += fuel
	game.food += food


code:
#define ARENA_RED_TEAM "red"
#define ARENA_GREEN_TEAM "green"
#define ARENA_DEFAULT_ID "arena_default"
#define ARENA_CORNER_A "cornerA"
#define ARENA_CORNER_B "cornerB"

/// Arena related landmarks
/obj/effect/landmark/arena
	name = "arena landmark"
	var/landmark_tag
	var/arena_id = ARENA_DEFAULT_ID

/obj/effect/landmark/arena/start
	name = "arena corner A"
	landmark_tag = ARENA_CORNER_A

/obj/effect/landmark/arena/end
	name = "arena corner B"
	landmark_tag = ARENA_CORNER_B

/// Controller for admin event arenas
/obj/machinery/computer/arena
	name = "arena controller"
	/// Arena ID
	var/arena_id = ARENA_DEFAULT_ID
	/// Enables/disables spawning
	var/ready_to_spawn = FALSE
	/// Assoc list of map templates indexed by user friendly names
	var/static/list/arena_templates = list()
	/// Were the config directory arenas loaded
	var/static/default_arenas_loaded = FALSE
	/// Name of currently loaded template
	var/current_arena_template = "None"
	// What turf arena clears to
	var/empty_turf_type = /turf/open/indestructible
	// List of team ids
	var/list/teams = list(ARENA_RED_TEAM,ARENA_GREEN_TEAM)
	/// List of hud instances indedxed by team id
	var/static/list/team_huds = list()
	/// List of hud colors indexed by team id
	var/static/list/team_colors = list(ARENA_RED_TEAM = "red", ARENA_GREEN_TEAM = "green")
	// Team hud index in GLOB.huds indexed by team id
	var/static/list/team_hud_index = list()

	/// List of ckeys indexed by team id
	var/list/team_keys = list()
	/// List of outfit datums/types indexed by team id, can be empty
	var/list/outfits = list()
	/// Default team outfit if `outfits[team]` is empty
	var/default_outfit = /datum/outfit/job/assistant

	/// Is the arena template loading in
	var/loading = FALSE

	//How long between admin pressing start and doors opening
	var/start_delay = 30 SECONDS
	//Value for the countdown
	var/start_time
	var/list/countdowns = list() //List of countdown effects ticking down to start

	//Sound played when the fight starts.
	var/start_sound = 'sound/items/airhorn2.ogg'
	var/start_sound_volume = 50

/obj/machinery/computer/arena/Initialize(mapload, obj/item/circuitboard/C)
	. = ..()
	LoadDefaultArenas()
	GenerateAntagHuds()

/obj/machinery/computer/arena/proc/GenerateAntagHuds()
	for(var/team in teams)
		var/datum/atom_hud/antag/teamhud = team_huds[team]
		if(!teamhud) //These will be shared between arenas because this stuff is expensive and cross arena fighting is not a thing anyway
			teamhud = new
			teamhud.icon_color = team_colors[team]
			GLOB.huds += teamhud
			team_huds[team] = teamhud
			team_hud_index[team] = length(GLOB.huds)

/**
 * Loads the arenas from config directory.
 * THESE ARE FULLY CACHED FOR QUICK SWITCHING SO KEEP TRACK OF THE AMOUNT
 */
/obj/machinery/computer/arena/proc/LoadDefaultArenas()
	if(default_arenas_loaded)
		return
	var/arena_dir = "[global.config.directory]/arenas/"
	var/list/default_arenas = flist(arena_dir)
	for(var/arena_file in default_arenas)
		var/simple_name = replacetext(replacetext(arena_file,arena_dir,""),".dmm","")
		INVOKE_ASYNC(src, .proc/add_new_arena_template, null, arena_dir + arena_file, simple_name)

/obj/machinery/computer/arena/proc/get_landmark_turf(landmark_tag)
	for(var/obj/effect/landmark/arena/L in GLOB.landmarks_list)
		if(L.arena_id == arena_id && L.landmark_tag == landmark_tag && isturf(L.loc))
			return L.loc

/obj/machinery/computer/arena/proc/get_load_point()
	var/turf/A = get_landmark_turf(ARENA_CORNER_A)
	var/turf/B = get_landmark_turf(ARENA_CORNER_B)
	return locate(min(A.x,B.x),min(A.y,B.y),A.z)

/obj/machinery/computer/arena/proc/get_arena_turfs()
	var/lp = get_load_point()
	var/turf/A = get_landmark_turf(ARENA_CORNER_A)
	var/turf/B = get_landmark_turf(ARENA_CORNER_B)
	var/turf/hp = locate(max(A.x,B.x),max(A.y,B.y),A.z)
	return block(lp,hp)

/obj/machinery/computer/arena/proc/clear_arena()
	for(var/turf/T in get_arena_turfs())
		T.empty(turf_type = /turf/open/indestructible)
	current_arena_template = "None"

/obj/machinery/computer/arena/proc/load_arena(arena_template,mob/user)
	if(loading)
		return
	var/datum/map_template/M = arena_templates[arena_template]
	if(!M)
		to_chat(user,span_warning("No such arena"))
		return
	clear_arena() //Clear current arena
	var/turf/A = get_landmark_turf(ARENA_CORNER_A)
	var/turf/B = get_landmark_turf(ARENA_CORNER_B)
	var/wh = abs(A.x - B.x) + 1
	var/hz = abs(A.y - B.y) + 1
	if(M.width > wh || M.height > hz)
		to_chat(user,span_warning("Arena template is too big for the current arena!"))
		return
	loading = TRUE
	var/bd = M.load(get_load_point())
	if(bd)
		current_arena_template = arena_template
	loading = FALSE

	message_admins("[key_name_admin(user)] loaded [arena_template] event arena for [arena_id] arena.")
	log_admin("[key_name(user)] loaded [arena_template] event arena for [arena_id] arena.")

/obj/machinery/computer/arena/proc/add_new_arena_template(user,fname,friendly_name)
	if(!fname)
		fname = input(user, "Upload dmm file to use as arena template","Upload Map Template") as null|file
	if(!fname)
		return
	if(!friendly_name)
		friendly_name = "[fname]" //Could ask the user for friendly name here

	var/datum/map_template/T = new(fname,friendly_name,TRUE)
	if(!T.cached_map || T.cached_map.check_for_errors())
		to_chat(user,"Map failed to parse check for errors.")
		return

	arena_templates[T.name] = T
	message_admins("[key_name_admin(user)] uploaded new event arena: [friendly_name].")
	log_admin("[key_name(user)] uploaded new event arena: [friendly_name].")

/obj/machinery/computer/arena/proc/load_team(user,team)
	var/rawteam = stripped_multiline_input(user,"Enter team list (ckeys separated by newline)")
	for(var/i in splittext(rawteam,"\n"))
		var/key = ckey(i)
		if(!i)
			continue
		add_team_member(user,team,key)

/obj/machinery/computer/arena/proc/add_team_member(mob/user,team,key)
	if(!key)
		var/list/keys = list()
		for(var/mob/M in GLOB.player_list)
			keys += M.client
		var/client/selection = input("Please, select a player!", "Team member", null, null) as null|anything in sort_key(keys)
		//Could be freeform if you want to add disconnected i guess
		if(!selection)
			return
		key = selection.ckey
	if(!team_keys[team])
		team_keys[team] = list(key)
	else
		team_keys[team] |= key
	to_chat(user,"[key] added to [team] team.")

/obj/machinery/computer/arena/proc/remove_member(mob/user,ckey,team)
	team_keys[team] -= ckey
	to_chat(user,"[ckey] removed from [team] team.")

/obj/machinery/computer/arena/proc/spawn_member(obj/machinery/arena_spawn/spawnpoint,ckey,team)
	var/mob/oldbody = get_mob_by_key(ckey)
	if(!isobserver(oldbody))
		return
	var/mob/living/carbon/human/M = new/mob/living/carbon/human(get_turf(spawnpoint))
	oldbody.client.prefs.safe_transfer_prefs_to(M, is_antag = TRUE)
	M.set_species(/datum/species/human) // Could use setting per team
	M.equipOutfit(outfits[team] ? outfits[team] : default_outfit)
	M.faction += team //In case anyone wants to add team based stuff to arena special effects
	M.key = ckey

	var/datum/atom_hud/antag/team_hud = team_huds[team]
	team_hud.join_hud(M)
	set_antag_hud(M,"arena",team_hud_index[team])

/obj/machinery/computer/arena/proc/change_outfit(mob/user,team)
	outfits[team] = user.client.robust_dress_shop()

/obj/machinery/computer/arena/proc/toggle_spawn(mob/user)
	ready_to_spawn = !ready_to_spawn
	to_chat(user,"You [ready_to_spawn ? "enable" : "disable"] the spawners.")
	log_admin("[key_name(user)] toggled event arena spawning for [arena_id] arena.")
	// Could use update_appearance on spawnpoints here to show they're on
	if(ready_to_spawn)
		for(var/mob/M in all_contestants())
			to_chat(M,span_userdanger("Arena you're signed up for is ready!"))

/obj/machinery/computer/arena/proc/all_contestants()
	. = list()
	for(var/team in team_keys)
		for(var/key in team_keys[team])
			var/mob/M = get_mob_by_key(key)
			if(M)
				. += M

/obj/machinery/computer/arena/proc/reset_arena()
	clear_arena()
	set_doors(closed = TRUE)

/obj/machinery/computer/arena/proc/get_spawn(team)
	for(var/obj/machinery/arena_spawn/A in GLOB.machines)
		if(A.arena_id == arena_id && A.team == team)
			return A

/obj/machinery/computer/arena/proc/start_match(mob/user)
	//TODO: Check if everyone is spawned in, if not ask for confirmation.
	var/timetext = DisplayTimeText(start_delay)
	to_chat(user,span_notice("The match will start in [timetext]."))
	for(var/mob/M in all_contestants())
		to_chat(M,span_userdanger("The gates will open in [timetext]!"))
	start_time = world.time + start_delay
	addtimer(CALLBACK(src,.proc/begin),start_delay)
	for(var/team in teams)
		var/obj/machinery/arena_spawn/team_spawn = get_spawn(team)
		var/obj/effect/countdown/arena/A = new(team_spawn)
		A.start()
		countdowns += A

/obj/machinery/computer/arena/proc/begin()
	ready_to_spawn = FALSE
	set_doors(closed = FALSE)
	if(start_sound)
		for(var/team in teams)
			var/obj/machinery/arena_spawn/A = get_spawn(team)
			playsound(A,start_sound, start_sound_volume)
	for(var/mob/M in all_contestants())
		to_chat(M,span_userdanger("START!"))
	//Clean up the countdowns
	QDEL_LIST(countdowns)
	start_time = null
	updateUsrDialog()

/obj/machinery/computer/arena/proc/set_doors(closed = FALSE)
	for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these
		if(D.id != arena_id)
			continue
		if(closed)
			INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/close)
		else
			INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open)

/obj/machinery/computer/arena/Topic(href, href_list)
	if(..())
		return
	var/mob/user = usr

	if(!user.client.holder) // Should it require specific perm ?
		return

	if(href_list["upload"])
		add_new_arena_template(user)
	if(href_list["change_arena"])
		load_arena(href_list["change_arena"],user)
	if(href_list["toggle_spawn"])
		toggle_spawn(user)
	if(href_list["start"])
		start_match(user)
	if(href_list["team_action"])
		var/team = href_list["team"]
		switch(href_list["team_action"])
			if("addmember")
				add_team_member(user,team)
			if("loadteam")
				load_team(user,team)
			if("outfit")
				change_outfit(user,team)
	if(href_list["special"])
		switch(href_list["special"])
			if("reset")
				reset_arena()
			//Just example in case you want to add more
			if("randomarena")
				load_random_arena(user)
			if("spawntrophy")
				trophy_for_last_man_standing(user)
	if(href_list["member_action"])
		var/ckey = href_list["ckey"]
		var/team = href_list["team"]
		switch(href_list["member_action"])
			if("remove")
				remove_member(user,ckey,team)
	updateUsrDialog()

// Special functions

/obj/machinery/computer/arena/proc/load_random_arena(mob/user)
	if(!length(arena_templates))
		to_chat(user,span_warning("No arenas present"))
		return
	var/picked = pick(arena_templates)
	load_arena(picked,user)

/obj/machinery/computer/arena/proc/trophy_for_last_man_standing()
	var/arena_turfs = get_arena_turfs()
	for(var/mob/living/L in GLOB.mob_living_list)
		if(L.stat != DEAD && (get_turf(L) in arena_turfs))
			var/obj/item/reagent_containers/food/drinks/trophy/gold_cup/G = new(get_turf(L))
			G.name = "[L.real_name]'s Trophy"

/obj/machinery/computer/arena/ui_interact(mob/user)
	. = ..()
	var/list/dat = list()
	dat += "
Spawning is currently [ready_to_spawn ? "enabled" : "disabled"] Toggle
"
	dat += "
[start_time ? "Stop countdown" : "Start!"]
"
	for(var/team in teams)
		dat += "
[capitalize(team)] team:
"
		dat += "
"
		for(var/ckey in team_keys[team])
			var/player_status = "Not Present"
			var/mob/M = get_mob_by_key(ckey)
			if(M)
				//Should define waiting room upper/lower corner and check if they're there instead of generic live/dead check
				if(isobserver(M))
					player_status = "Ghosted"
				else
					player_status = M.stat == DEAD ? "Dead" : "Alive"
				dat += "
[ckey] - [player_status] - "
				dat += "FLW"
				dat += "Remove"
				//Add more per player features here
				dat += "
"
		dat += "
"
		dat += "
 Team Outfit : [outfits[team] ? outfits[team] : default_outfit]
"
		dat += "Load team"
		dat += "Add member"
		dat += "Change Outfit"
		//Add more per team features here

	dat += "Current arena: [current_arena_template]"
	dat += "
Arena List:
"
	for(var/A in arena_templates)
		dat += "[A]
"
	dat += "
"
	dat += "Upload new arena
"
	dat += "
"
	//Special actions
	dat += "Reset Arena.
"
	dat += "Load random arena.
"
	dat += "Spawn trophies for survivors.
"

	var/datum/browser/popup = new(user, "arena controller", "Arena Controller", 500, 600)
	popup.set_content(dat.Join())
	popup.open()

/// Arena spawnpoint
/obj/machinery/arena_spawn
	name = "Arena Spawnpoint"
	icon = 'icons/obj/device.dmi'
	icon_state = "syndbeacon"
	resistance_flags = INDESTRUCTIBLE
	/// In case we have multiple arena controllers at once.
	var/arena_id = ARENA_DEFAULT_ID
	/// Team ID
	var/team = "default"
	/// only exist to cut down on glob.machines lookups, do not modify
	var/obj/machinery/computer/arena/_controller

/obj/machinery/arena_spawn/red
	name = "Red Team Spawnpoint"
	color = "red"
	team = ARENA_RED_TEAM

/obj/machinery/arena_spawn/green
	name = "Green Team Spawnpoint"
	color = "green"
	team = ARENA_GREEN_TEAM

/obj/machinery/arena_spawn/proc/get_controller()
	if(_controller && !QDELETED(_controller) && _controller.arena_id == arena_id)
		return _controller
	for(var/obj/machinery/computer/arena/A in GLOB.machines)
		if(A.arena_id == arena_id)
			_controller = A
			return _controller

/obj/machinery/arena_spawn/attack_ghost(mob/user)
	var/obj/machinery/computer/arena/C = get_controller()
	if(!C) //Unlinked spawn
		return
	if(C.ready_to_spawn)
		var/list/allowed_keys = C.team_keys[team]
		if(!(user.ckey in allowed_keys))
			to_chat(user,span_warning("You're not on the team list."))
			return
		C.spawn_member(src,user.ckey,team)

#undef ARENA_GREEN_TEAM
#undef ARENA_RED_TEAM
#undef ARENA_DEFAULT_ID
#undef ARENA_CORNER_A
#undef ARENA_CORNER_B


code:
/obj/machinery/computer/atmos_alert
	name = "atmospheric alert console"
	desc = "Used to monitor the station's air alarms."
	circuit = /obj/item/circuitboard/computer/atmos_alert
	icon_screen = "alert:0"
	icon_keyboard = "atmos_key"
	light_color = LIGHT_COLOR_CYAN
	var/list/priority_alarms = list()
	var/list/minor_alarms = list()
	var/receive_frequency = FREQ_ATMOS_ALARMS
	var/datum/radio_frequency/radio_connection

/obj/machinery/computer/atmos_alert/Initialize(mapload)
	. = ..()
	set_frequency(receive_frequency)

/obj/machinery/computer/atmos_alert/Destroy()
	SSradio.remove_object(src, receive_frequency)
	return ..()

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

/obj/machinery/computer/atmos_alert/ui_data(mob/user)
	var/list/data = list()

	data["priority"] = list()
	for(var/zone in priority_alarms)
		data["priority"] += zone
	data["minor"] = list()
	for(var/zone in minor_alarms)
		data["minor"] += zone

	return data

/obj/machinery/computer/atmos_alert/ui_act(action, params)
	. = ..()
	if(.)
		return

	switch(action)
		if("clear")
			var/zone = params["zone"]
			if(zone in priority_alarms)
				to_chat(usr, span_notice("Priority alarm for [zone] cleared."))
				priority_alarms -= zone
				. = TRUE
			if(zone in minor_alarms)
				to_chat(usr, span_notice("Minor alarm for [zone] cleared."))
				minor_alarms -= zone
				. = TRUE
	update_appearance()

/obj/machinery/computer/atmos_alert/proc/set_frequency(new_frequency)
	SSradio.remove_object(src, receive_frequency)
	receive_frequency = new_frequency
	radio_connection = SSradio.add_object(src, receive_frequency, RADIO_ATMOSIA)

/obj/machinery/computer/atmos_alert/receive_signal(datum/signal/signal)
	if(!signal)
		return

	var/zone = signal.data["zone"]
	var/severity = signal.data["alert"]

	if(!zone || !severity)
		return

	minor_alarms -= zone
	priority_alarms -= zone
	if(severity == "severe")
		priority_alarms += zone
	else if (severity == "minor")
		minor_alarms += zone
	update_appearance()
	return

/obj/machinery/computer/atmos_alert/update_overlays()
	. = ..()
	if(machine_stat & (NOPOWER|BROKEN))
		return
	if(priority_alarms.len)
		. += "alert:2"
		return
	if(minor_alarms.len)
		. += "alert:1"