diff --git a/code/__DEFINES/arcades.dm b/code/__DEFINES/arcades.dm
new file mode 100644
index 00000000000..fac3e8091d3
--- /dev/null
+++ b/code/__DEFINES/arcades.dm
@@ -0,0 +1,15 @@
+///Goes in the Weapon slot.
+#define WEAPON_SLOT "Weapon"
+///Goes in the Armor slot.
+#define ARMOR_SLOT "Armor"
+
+//Worlds found in Battle Arcade
+#define BATTLE_WORLD_ONE "The Plains"
+#define BATTLE_WORLD_TWO "The Forest"
+#define BATTLE_WORLD_THREE "The Mountains"
+#define BATTLE_WORLD_FOUR "The Desert"
+#define BATTLE_WORLD_FIVE "The Swamp"
+#define BATTLE_WORLD_SIX "The Ocean"
+#define BATTLE_WORLD_SEVEN "The Sky"
+#define BATTLE_WORLD_EIGHT "The Moon"
+#define BATTLE_WORLD_NINE "The Void"
diff --git a/code/_globalvars/arcade.dm b/code/_globalvars/arcade.dm
new file mode 100644
index 00000000000..4143ce5fa76
--- /dev/null
+++ b/code/_globalvars/arcade.dm
@@ -0,0 +1,73 @@
+///List of all things that can be dispensed by an arcade cabinet and the weight of them being picked.
+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/spinningtoy/dark_matter = 1,
+ /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/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/toy/plush/abductor = 2,
+ /obj/item/toy/plush/abductor/agent = 2,
+ /obj/item/toy/plush/shark = 2,
+ /obj/item/storage/belt/military/snack/full = 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/item/clothing/mask/party_horn = 2,
+ /obj/item/storage/box/party_poppers = 2,
+))
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index ac2b669762c..fe28c7f3b00 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -281,7 +281,7 @@ SUBSYSTEM_DEF(ticker)
log_world("Game start took [(world.timeofday - init_start)/10]s")
INVOKE_ASYNC(SSdbcore, TYPE_PROC_REF(/datum/controller/subsystem/dbcore,SetRoundStart))
- to_chat(world, span_notice("Welcome to [station_name()], enjoy your stay!"))
+ to_chat(world, span_notice(span_bold("Welcome to [station_name()], enjoy your stay!")))
SEND_SOUND(world, sound(SSstation.announcer.get_rand_welcome_sound()))
current_state = GAME_STATE_PLAYING
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index edf2cd21534..6e3a41937b7 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -662,7 +662,7 @@
update_last_used(user)
. = ..()
-/obj/machinery/ui_act(action, list/params)
+/obj/machinery/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
add_fingerprint(usr)
update_last_used(usr)
if(HAS_AI_ACCESS(usr) && !GLOB.cameranet.checkTurfVis(get_turf(src))) //We check if they're an AI specifically here, so borgs can still access off-camera stuff.
diff --git a/code/game/machinery/computer/arcade/_arcade.dm b/code/game/machinery/computer/arcade/_arcade.dm
new file mode 100644
index 00000000000..1bfd43cdb69
--- /dev/null
+++ b/code/game/machinery/computer/arcade/_arcade.dm
@@ -0,0 +1,104 @@
+/obj/machinery/computer/arcade
+ name = "\proper the arcade cabinet which shouldn't exist"
+ desc = "This arcade cabinet has no games installed, and in fact, should not exist. \
+ Report the location of this machine to your local diety."
+ icon_state = "arcade"
+ icon_keyboard = null
+ icon_screen = "invaders"
+ light_color = LIGHT_COLOR_GREEN
+ interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_REQUIRES_LITERACY
+
+ ///If set, will dispense these as prizes instead of the default GLOB.arcade_prize_pool
+ ///Like prize pool, it must be a list of the prize and the weight of being selected.
+ var/list/prize_override
+
+/obj/machinery/computer/arcade/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
+ . = ..()
+ if(. & ITEM_INTERACT_ANY_BLOCKER)
+ return .
+
+ if(istype(tool, /obj/item/stack/arcadeticket))
+ var/obj/item/stack/arcadeticket/tickets = tool
+ if(!tickets.use(2))
+ balloon_alert(user, "need 2 tickets!")
+ return ITEM_INTERACT_BLOCKING
+
+ prizevend(user)
+ balloon_alert(user, "prize claimed")
+ return ITEM_INTERACT_SUCCESS
+
+ if(istype(tool, /obj/item/key/displaycase) || istype(tool, /obj/item/access_key))
+ var/static/list/radial_menu_options
+ if(!radial_menu_options)
+ radial_menu_options = list(
+ "Reset Cabinet" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_reset"),
+ "Cancel" = image(icon = 'icons/hud/radial.dmi', icon_state = "radial_close"),
+ )
+ var/radial_reset_menu = show_radial_menu(user, src, radial_menu_options, require_near = TRUE)
+ if(radial_reset_menu != "Reset Cabinet")
+ return ITEM_INTERACT_BLOCKING
+ playsound(loc, 'sound/items/rattling_keys.ogg', 25, TRUE)
+ if(!do_after(user, 10 SECONDS, src))
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "cabinet reset")
+ reset_cabinet(user)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/computer/arcade/screwdriver_act(mob/living/user, obj/item/I)
+ //you can't stop playing when you start.
+ if(obj_flags & EMAGGED)
+ return ITEM_INTERACT_BLOCKING
+ return ..()
+
+///Performs a factory reset of the cabinet and wipes all its stats.
+/obj/machinery/computer/arcade/proc/reset_cabinet(mob/living/user)
+ SHOULD_CALL_PARENT(TRUE)
+ obj_flags &= ~EMAGGED
+ SStgui.update_uis(src)
+
+/obj/machinery/computer/arcade/emp_act(severity)
+ . = ..()
+ 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(prize_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)
+
+///Dispenses the proper prizes and gives them a positive mood event. If valid, has a small chance to give a pulse rifle.
+/obj/machinery/computer/arcade/proc/prizevend(mob/living/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 in 1 to prizes)
+ user.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)
+ continue
+
+ 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."))
+
+
diff --git a/code/game/machinery/computer/arcade/amputation.dm b/code/game/machinery/computer/arcade/amputation.dm
new file mode 100644
index 00000000000..84a02af387a
--- /dev/null
+++ b/code/game/machinery/computer/arcade/amputation.dm
@@ -0,0 +1,47 @@
+/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
+ interaction_flags_machine = NONE //borgs can't play, but the illiterate can.
+
+/obj/machinery/computer/arcade/amputation/attack_tk(mob/user)
+ return //that's a pretty damn big guillotine
+
+/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, list/modifiers)
+ . = ..()
+ if(!iscarbon(user))
+ return
+ to_chat(user, span_warning("You move your hand towards the machine, and begin to hesitate as a bloodied guillotine emerges from inside of it..."))
+ user.played_game()
+ var/obj/item/bodypart/chopchop = user.get_active_hand()
+ if(do_after(user, 5 SECONDS, target = src, extra_checks = CALLBACK(src, PROC_REF(do_they_still_have_that_hand), user, chopchop)))
+ playsound(src, 'sound/weapons/slice.ogg', 25, TRUE, -1)
+ to_chat(user, span_userdanger("The guillotine drops on your arm, and the machine sucks it in!"))
+ chopchop.dismember()
+ qdel(chopchop)
+ user.mind?.adjust_experience(/datum/skill/gaming, 100)
+ user.won_game()
+ playsound(src, 'sound/arcade/win.ogg', 50, TRUE)
+ new /obj/item/stack/arcadeticket((get_turf(src)), rand(6,10))
+ to_chat(user, span_notice("[src] dispenses a handful of tickets!"))
+ return
+ if(!do_they_still_have_that_hand(user, chopchop))
+ to_chat(user, span_warning("The guillotine drops, but your hand seems to be gone already!"))
+ playsound(src, 'sound/weapons/slice.ogg', 25, TRUE, -1)
+ else
+ to_chat(user, span_notice("You (wisely) decide against putting your hand in the machine."))
+ user.lost_game()
+
+///Makes sure the user still has their starting hand, preventing the user from pulling the arm out and still getting prizes.
+/obj/machinery/computer/arcade/amputation/proc/do_they_still_have_that_hand(mob/user, obj/item/bodypart/chopchop)
+ if(QDELETED(chopchop) || chopchop.owner != user)
+ return FALSE
+ return TRUE
+
+///Dispenses wrapped gifts instead of arcade prizes, also known as the ancap christmas tree
+/obj/machinery/computer/arcade/amputation/festive
+ 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/gift/anything = 1)
diff --git a/code/game/machinery/computer/arcade/arcade.dm b/code/game/machinery/computer/arcade/arcade.dm
deleted file mode 100644
index 91a10673dd6..00000000000
--- a/code/game/machinery/computer/arcade/arcade.dm
+++ /dev/null
@@ -1,701 +0,0 @@
-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/spinningtoy/dark_matter = 1,
- /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/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/toy/plush/abductor = 2,
- /obj/item/toy/plush/abductor/agent = 2,
- /obj/item/toy/plush/shark = 2,
- /obj/item/storage/belt/military/snack/full = 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/item/clothing/mask/party_horn = 2,
- /obj/item/storage/box/party_poppers = 2))
-
-/obj/machinery/computer/arcade
- name = "\proper the arcade cabinet which shouldn't exist"
- desc = "This arcade cabinet has no games installed, and in fact, should not exist. \
- Report the location of this machine to your local diety."
- icon_state = "arcade"
- icon_keyboard = null
- icon_screen = "invaders"
- light_color = LIGHT_COLOR_GREEN
- interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON
- 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/living/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 in 1 to prizes)
- user.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/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking)
- . = ..()
- if(. & ITEM_INTERACT_ANY_BLOCKER)
- return .
- if(!istype(tool, /obj/item/stack/arcadeticket))
- return .
-
- var/obj/item/stack/arcadeticket/tickets = tool
- if(!tickets.use(2))
- balloon_alert(user, "need 2 tickets!")
- return ITEM_INTERACT_BLOCKING
-
- prizevend(user)
- balloon_alert(user, "prize claimed")
- return ITEM_INTERACT_SUCCESS
-
-// ** BATTLE ** //
-/obj/machinery/computer/arcade/battle
- name = "arcade machine"
- desc = "Does not support Pinball."
- icon_state = "arcade"
- circuit = /obj/item/circuitboard/computer/arcade/battle
-
- interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_SET_MACHINE // we don't need to be literate to play video games fam
-
- 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(check_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(check_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(check_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 += "
"
- 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)
-
- usr.played_game()
-
- 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 = "
"
-
- 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_REF(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!
"
- 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_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]
[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/plasma(loc, /obj/item/assembly/timer)
- new /obj/item/clothing/head/collectable/petehat(loc)
- message_admins("[ADMIN_LOOKUPFLW(usr)] has outbombed Cuban Pete and been awarded a bomb.")
- usr.log_message("outbombed Cuban Pete and has been awarded a bomb.", LOG_GAME)
- Reset()
- obj_flags &= ~EMAGGED
- xp_gained += 100
- else
- new /obj/item/stack/arcadeticket((get_turf(src)), 2)
- to_chat(user, span_notice("[src] dispenses 2 tickets!"))
- xp_gained += 50
- SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("win", (obj_flags & EMAGGED ? "emagged":"normal")))
- user.won_game()
-
- 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.investigate_log("has been gibbed by an emagged Orion Trail game.", INVESTIGATE_DEATHS)
- living_user.gib(DROP_ALL_REMAINS)
- SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "hp", (obj_flags & EMAGGED ? "emagged":"normal")))
- user.lost_game()
-
- 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)
- . = ..()
- . += span_notice("You notice some writing scribbled on the side of [src]...")
- . += "\t[span_info("smart -> defend, defend, light attack")]"
- . += "\t[span_info("shotgun -> defend, defend, power attack")]"
- . += "\t[span_info("short temper -> counter, counter, counter")]"
- . += "\t[span_info("poisonous -> light attack, light attack, light attack")]"
- . += "\t[span_info("chonker -> power attack, power attack, power attack")]"
- . += "\t[span_info("magical -> defend until outmagiced")]"
- return .
-
-/obj/machinery/computer/arcade/battle/emag_act(mob/user, obj/item/card/emag/emag_card)
- if(obj_flags & EMAGGED)
- return FALSE
-
- balloon_alert(user, "hard mode enabled")
- 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(user?.mind)
- gamerSkill = user.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()
- return TRUE
-
-// ** 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_tk(mob/user)
- return //that's a pretty damn big guillotine
-
-/obj/machinery/computer/arcade/amputation/attack_hand(mob/user, list/modifiers)
- . = ..()
- if(!iscarbon(user))
- return
- to_chat(user, span_warning("You move your hand towards the machine, and begin to hesitate as a bloodied guillotine emerges from inside of it..."))
- user.played_game()
- var/obj/item/bodypart/chopchop = user.get_active_hand()
- if(do_after(user, 5 SECONDS, target = src, extra_checks = CALLBACK(src, PROC_REF(do_they_still_have_that_hand), user, chopchop)))
- playsound(src, 'sound/weapons/slice.ogg', 25, TRUE, -1)
- to_chat(user, span_userdanger("The guillotine drops on your arm, and the machine sucks it in!"))
- chopchop.dismember()
- qdel(chopchop)
- user.mind?.adjust_experience(/datum/skill/gaming, 100)
- user.won_game()
- playsound(src, 'sound/arcade/win.ogg', 50, TRUE)
- new /obj/item/stack/arcadeticket((get_turf(src)), rand(6,10))
- to_chat(user, span_notice("[src] dispenses a handful of tickets!"))
- return
- else if(!do_they_still_have_that_hand(user, chopchop))
- to_chat(user, span_warning("The guillotine drops, but your hand seems to be gone already!"))
- playsound(src, 'sound/weapons/slice.ogg', 25, TRUE, -1)
- else
- to_chat(user, span_notice("You (wisely) decide against putting your hand in the machine."))
- user.lost_game()
-
-///Makes sure the user still has their starting hand.
-/obj/machinery/computer/arcade/amputation/proc/do_they_still_have_that_hand(mob/user, obj/item/bodypart/chopchop)
- if(QDELETED(chopchop) || chopchop.owner != user) //No pulling your arm out of the machine!
- return FALSE
- return TRUE
-
-
-/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/gift/anything = 1)
diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm
new file mode 100644
index 00000000000..5586f3f8b22
--- /dev/null
+++ b/code/game/machinery/computer/arcade/battle.dm
@@ -0,0 +1,555 @@
+///How many enemies needs to be defeated until the 'Boss' of the stage appears.
+#define WORLD_ENEMY_BOSS 2
+///The default amount of EXP you gain from killing an enemy, modifiers stacked on top of this.
+#define DEFAULT_EXP_GAIN 50
+///The default cost to purchase an item. Sleeping at the Inn is half of this.
+#define DEFAULT_ITEM_PRICE 30
+
+///The max HP the player can have at any time.
+#define PLAYER_MAX_HP 100
+///The max MP the player can have at any time.
+#define PLAYER_MAX_MP 50
+///The default cost of a spell, in MP. Defending will instead restore this amount.
+#define SPELL_MP_COST 10
+
+///The player is currently in the Shop.
+#define UI_PANEL_SHOP "Shop"
+///The player is currently in the World Map.
+#define UI_PANEL_WORLD_MAP "World Map"
+///The player is currently in Batle.
+#define UI_PANEL_BATTLE "Battle"
+///The player is currently between battles.
+#define UI_PANEL_BETWEEN_FIGHTS "Between Battle"
+///The player is currently Game Overed.
+#define UI_PANEL_GAMEOVER "Game Over"
+
+///The player is set to counterattack the enemy's next move.
+#define BATTLE_ATTACK_FLAG_COUNTERATTACK (1<<0)
+///The player is set to defend against the enemy's next move.
+#define BATTLE_ATTACK_FLAG_DEFEND (1<<1)
+
+///The player is trying to Attack the Enemy.
+#define BATTLE_ARCADE_PLAYER_ATTACK "Attack"
+///The player is trying to Attack the Enemy with an MP boost.
+#define BATTLE_ARCADE_PLAYER_HEAVY_ATTACK "Heavy Attack"
+///The player is setting themselves to counterattack a potential incoming Enemy attack.
+#define BATTLE_ARCADE_PLAYER_COUNTERATTACK "Counterattack"
+///The player is defending against the Enemy and restoring MP.
+#define BATTLE_ARCADE_PLAYER_DEFEND "Defend"
+
+/obj/machinery/computer/arcade/battle
+ name = "battle arcade"
+ desc = "Explore vast worlds and conquer."
+ icon_state = "arcade"
+ icon_screen = "fighters"
+ circuit = /obj/item/circuitboard/computer/arcade/battle
+
+ ///List of all battle arcade gear that is available in the shop in game.
+ var/static/list/battle_arcade_gear_list
+ ///List of all worlds in the game.
+ var/static/list/all_worlds = list(
+ BATTLE_WORLD_ONE = 1,
+ BATTLE_WORLD_TWO = 1.25,
+ BATTLE_WORLD_THREE = 1.5,
+ BATTLE_WORLD_FOUR = 1.75,
+ BATTLE_WORLD_FIVE = 2,
+ BATTLE_WORLD_SIX = 2.25,
+ BATTLE_WORLD_SEVEN = 2.5,
+ BATTLE_WORLD_EIGHT = 2.75,
+ BATTLE_WORLD_NINE = 3,
+ )
+ var/static/list/all_attack_types = list(
+ BATTLE_ARCADE_PLAYER_ATTACK = "Attack the enemy in a default attack at no MP cost.",
+ BATTLE_ARCADE_PLAYER_HEAVY_ATTACK = "Attack the enemy with the power of Magic, costing MP for additional damage.",
+ BATTLE_ARCADE_PLAYER_COUNTERATTACK = "Use magic to prepare a counterattack of your enemy, allowing you to deal extra damage if you succeed.",
+ BATTLE_ARCADE_PLAYER_DEFEND = "Defend from the next incoming attack, lowing the amount of damage you take while restoring some HP and MP.",
+ )
+ ///The world we're currently in.
+ var/player_current_world = BATTLE_WORLD_ONE
+ ///The latest world the player has unlocked, granting access to all worlds below this.
+ var/latest_unlocked_world = BATTLE_WORLD_ONE
+ ///How many enemies we've defeated in a row, used to tell when we need to spawn the boss in.
+ var/enemies_defeated
+ ///The current panel the player is viewieng in the UI.
+ var/ui_panel = UI_PANEL_WORLD_MAP
+
+ /** PLAYER INFORMATION */
+
+ ///Boolean on whether it's the player's time to do their turn.
+ var/player_turn = TRUE
+ ///How much money the player has, used in the Inn. Starts with the default price for a single item.
+ var/player_gold = DEFAULT_ITEM_PRICE
+ ///The current amount of HP the player has.
+ var/player_current_hp = PLAYER_MAX_HP
+ ///The current amount of MP the player has.
+ var/player_current_mp = PLAYER_MAX_MP
+ ///Assoc list of gear the player has equipped.
+ var/list/datum/battle_arcade_gear/equipped_gear = list(
+ WEAPON_SLOT = null,
+ ARMOR_SLOT = null,
+ )
+
+ /** CURRENT ENEMY INFORMATION */
+
+ ///A feedback message displayed in the UI during combat sequences.
+ var/feedback_message
+ ///Determines which boss image to use on the UI.
+ var/enemy_icon_id = 1
+ ///The enemy's name
+ var/enemy_name
+ ///How much HP the current enemy has.
+ var/enemy_max_hp
+ ///How much HP the current enemy has.
+ var/enemy_hp
+ ///How much MP the current enemy has.
+ var/enemy_mp
+ ///How much gold the enemy will drop, randomized on new opponent.
+ var/enemy_gold_reward
+ ///unique to the emag mode, acts as a time limit where the player dies when it reaches 0.
+ var/bomb_cooldown = 19
+
+/obj/machinery/computer/arcade/battle/Initialize(mapload, obj/item/circuitboard/C)
+ . = ..()
+ if(isnull(battle_arcade_gear_list))
+ var/list/all_gear = list()
+ for(var/datum/battle_arcade_gear/template as anything in subtypesof(/datum/battle_arcade_gear))
+ all_gear[template::name] = new template
+ battle_arcade_gear_list = all_gear
+
+/obj/machinery/computer/arcade/battle/emag_act(mob/user, obj/item/card/emag/emag_card)
+ if(obj_flags & EMAGGED)
+ return FALSE
+ obj_flags |= EMAGGED
+ balloon_alert(user, "hard mode enabled")
+ to_chat(user, span_warning("A mesmerizing Rhumba beat starts playing from the arcade machine's speakers!"))
+ setup_new_opponent()
+ feedback_message = "If you die in the game, you die for real!"
+ SStgui.update_uis(src)
+ return TRUE
+
+/obj/machinery/computer/arcade/battle/reset_cabinet(mob/living/user)
+ enemy_name = null
+ player_turn = initial(player_turn)
+ feedback_message = initial(feedback_message)
+ player_current_world = initial(player_current_world)
+ latest_unlocked_world = initial(latest_unlocked_world)
+ enemies_defeated = initial(enemies_defeated)
+ player_gold = initial(player_gold)
+ player_current_hp = initial(player_current_hp)
+ player_current_mp = initial(player_current_mp)
+ ui_panel = initial(ui_panel)
+ bomb_cooldown = initial(bomb_cooldown)
+ equipped_gear = list(WEAPON_SLOT = null, ARMOR_SLOT = null)
+ return ..()
+
+///Sets up a new opponent depending on what stage they are at.
+/obj/machinery/computer/arcade/battle/proc/setup_new_opponent(enemy_gets_first_move = FALSE)
+ var/name_adjective
+ var/new_name
+
+ if(check_holidays(HALLOWEEN))
+ name_adjective = pick_list(ARCADE_FILE, "rpg_adjective_halloween")
+ new_name = pick_list(ARCADE_FILE, "rpg_enemy_halloween")
+ else if(check_holidays(CHRISTMAS))
+ name_adjective = pick_list(ARCADE_FILE, "rpg_adjective_xmas")
+ new_name = pick_list(ARCADE_FILE, "rpg_enemy_xmas")
+ else if(check_holidays(VALENTINES))
+ name_adjective = pick_list(ARCADE_FILE, "rpg_adjective_valentines")
+ new_name = pick_list(ARCADE_FILE, "rpg_enemy_valentines")
+ else
+ name_adjective = pick_list(ARCADE_FILE, "rpg_adjective")
+ new_name = pick_list(ARCADE_FILE, "rpg_enemy")
+
+ enemy_hp = round(rand(90, 125) * all_worlds[player_current_world], 1)
+ enemy_mp = round(rand(20, 30) * all_worlds[player_current_world], 1)
+ enemy_gold_reward = rand((DEFAULT_ITEM_PRICE / 2), DEFAULT_ITEM_PRICE)
+
+ // there's only one boss in each stage (except the last)
+ if((player_current_world == latest_unlocked_world) && enemies_defeated == WORLD_ENEMY_BOSS)
+ enemy_mp *= 1.25
+ enemy_hp *= 1.25
+ enemy_gold_reward *= 1.5
+ name_adjective = "Big Boss"
+
+ enemy_icon_id = rand(1,6)
+ enemy_name = "The [name_adjective] [new_name]"
+ feedback_message = "New game started against [enemy_name]"
+
+ if(obj_flags & EMAGGED)
+ enemy_name = "Cuban Pete"
+ enemy_hp += 100 //extra HP just to make cuban pete even more bullshit
+
+ //set max HP to reference later
+ enemy_max_hp = enemy_hp
+ //set the player to fight now.
+ ui_panel = UI_PANEL_BATTLE
+
+ if(enemy_gets_first_move)
+ perform_enemy_turn()
+
+/**
+ * on_battle_win
+ *
+ * Called when the player wins a level, this handles giving EXP, loot, tickets, etc.
+ * It also handles clearing the enemy out for the next one, and unlocking new worlds.
+ * We stop at BATTLE_WORLD_NINE because it is the last stage, and has infinite bosses.
+ */
+/obj/machinery/computer/arcade/battle/proc/on_battle_win(mob/user)
+ enemy_name = null
+ feedback_message = null
+ player_turn = TRUE
+ if(player_current_world == latest_unlocked_world)
+ if(enemies_defeated == WORLD_ENEMY_BOSS)
+ enemies_defeated = 0
+ //the last stage doesn't have a next one to move onto.
+ if(latest_unlocked_world != BATTLE_WORLD_NINE)
+ var/current_world = all_worlds.Find(latest_unlocked_world)
+ latest_unlocked_world = all_worlds[current_world + 1]
+ ui_panel = UI_PANEL_WORLD_MAP
+ say("New world unlocked, [latest_unlocked_world]!")
+ enemies_defeated++
+ if(obj_flags & EMAGGED)
+ obj_flags &= ~EMAGGED
+ bomb_cooldown = initial(bomb_cooldown)
+ new /obj/effect/spawner/newbomb/plasma(loc, /obj/item/assembly/timer)
+ new /obj/item/clothing/head/collectable/petehat(loc)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] has outbombed Cuban Pete and been awarded a bomb.")
+ usr.log_message("outbombed Cuban Pete and has been awarded a bomb.", LOG_GAME)
+ else
+ to_chat(user, span_notice("[src] dispenses 2 tickets!"))
+ new /obj/item/stack/arcadeticket((get_turf(src)), 2)
+ player_gold += enemy_gold_reward
+ if(user)
+ var/exp_gained = DEFAULT_EXP_GAIN * all_worlds[player_current_world]
+ user.mind?.adjust_experience(/datum/skill/gaming, exp_gained)
+ user.won_game()
+ SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("win", (obj_flags & EMAGGED ? "emagged":"normal")))
+ playsound(loc, 'sound/arcade/win.ogg', 40)
+ if(ui_panel != UI_PANEL_WORLD_MAP) //we havent been booted to world map, we're still going.
+ ui_panel = UI_PANEL_BETWEEN_FIGHTS
+
+///Called when a mob loses at the battle arcade.
+/obj/machinery/computer/arcade/battle/proc/lose_game(mob/user)
+ if(obj_flags & EMAGGED)
+ var/mob/living/living_user = user
+ if(istype(living_user))
+ living_user.investigate_log("has been gibbed by an emagged Orion Trail game.", INVESTIGATE_DEATHS)
+ living_user.gib(DROP_ALL_REMAINS)
+ user.lost_game()
+ SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "hp", (obj_flags & EMAGGED ? "emagged":"normal")))
+ SStgui.update_uis(src)
+
+///Called when the enemy attacks you.
+/obj/machinery/computer/arcade/battle/proc/user_take_damage(mob/user, base_damage_taken)
+ var/datum/battle_arcade_gear/armor = equipped_gear[ARMOR_SLOT]
+ var/damage_taken = (base_damage_taken * all_worlds[player_current_world]) / (!isnull(armor) ? armor.bonus_modifier : 1)
+ player_current_hp -= round(max(0, damage_taken), 1)
+ if(player_current_hp <= 0)
+ ui_panel = UI_PANEL_GAMEOVER
+ feedback_message = "GAME OVER."
+ say("You have been crushed! GAME OVER.")
+ playsound(loc, 'sound/arcade/lose.ogg', 40, TRUE)
+ lose_game(user)
+ else
+ feedback_message = "User took [damage_taken] damage!"
+ playsound(loc, 'sound/arcade/hit.ogg', 40, TRUE, extrarange = -3)
+ SStgui.update_uis(src)
+
+///Called when you attack the enemy.
+/obj/machinery/computer/arcade/battle/proc/process_player_attack(mob/user, attack_type)
+ var/damage_dealt
+ switch(attack_type)
+ if(BATTLE_ARCADE_PLAYER_ATTACK)
+ var/datum/battle_arcade_gear/weapon = equipped_gear[WEAPON_SLOT]
+ damage_dealt = (rand(5, 15) * (!isnull(weapon) ? weapon.bonus_modifier : 1))
+ if(BATTLE_ARCADE_PLAYER_HEAVY_ATTACK)
+ var/datum/battle_arcade_gear/weapon = equipped_gear[WEAPON_SLOT]
+ damage_dealt = (rand(15, 25) * (!isnull(weapon) ? weapon.bonus_modifier : 1))
+ if(BATTLE_ARCADE_PLAYER_COUNTERATTACK)
+ feedback_message = "User prepares to counterattack!"
+ process_enemy_turn(user, defending_flags = BATTLE_ATTACK_FLAG_COUNTERATTACK)
+ playsound(loc, 'sound/arcade/mana.ogg', 40, TRUE, extrarange = -3)
+ if(BATTLE_ARCADE_PLAYER_DEFEND)
+ feedback_message = "User pulls up their shield!"
+ process_enemy_turn(user, defending_flags = BATTLE_ATTACK_FLAG_DEFEND)
+ playsound(loc, 'sound/arcade/mana.ogg', 40, TRUE, extrarange = -3)
+
+ if(!damage_dealt)
+ return
+ enemy_hp -= round(max(0, damage_dealt), 1)
+ feedback_message = "[enemy_name] took [damage_dealt] damage!"
+ playsound(loc, 'sound/arcade/hit.ogg', 40, TRUE, extrarange = -3)
+ process_enemy_turn(user)
+
+///Called when you successfully counterattack the enemy.
+/obj/machinery/computer/arcade/battle/proc/successful_counterattack()
+ var/datum/battle_arcade_gear/weapon = equipped_gear[WEAPON_SLOT]
+ var/damage_dealt = (rand(20, 30) * (!isnull(weapon) ? weapon.bonus_modifier : 1))
+ enemy_hp -= round(max(0, damage_dealt), 1)
+ feedback_message = "User counterattacked for [damage_dealt] damage!"
+ playsound(loc, 'sound/arcade/boom.ogg', 40, TRUE, extrarange = -3)
+ SStgui.update_uis(src)
+
+///Handles the delay between the user's and enemy's turns to process what's going on.
+/obj/machinery/computer/arcade/battle/proc/process_enemy_turn(mob/user, defending_flags = NONE)
+ if(enemy_hp <= 0)
+ return on_battle_win(user)
+ //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)
+ bomb_cooldown--
+ switch(bomb_cooldown)
+ if(18)
+ feedback_message = "[enemy_name] takes two valve tank and links them together, what's he planning?"
+ if(15)
+ feedback_message = "[enemy_name] adds a remote control to the tan- ho god is that a bomb?"
+ if(12)
+ feedback_message = "[enemy_name] throws the bomb next to you, you'r too scared to pick it up."
+ if(6)
+ feedback_message = "[enemy_name]'s hand brushes the remote linked to the bomb, your heart skipped a beat."
+ if(2)
+ feedback_message = "[enemy_name] is going to press the button! It's now or never!"
+ if(0)
+ player_current_hp = 0 //instant death
+ addtimer(CALLBACK(src, PROC_REF(perform_enemy_turn), user, defending_flags), 1 SECONDS)
+
+/**
+ * perform_enemy_turn
+ *
+ * Actually performs the enemy's turn.
+ * We first roll to see if the enemy should use magic. As their HP goes lower, the chances of self healing goes higher, but
+ * if they lack the MP, then it's rolling to steal MP from the player.
+ * After, we will roll to see if the player counterattacks the enemy (if set), otherwise we will attack normally.
+ */
+/obj/machinery/computer/arcade/battle/proc/perform_enemy_turn(mob/user, defending_flags = NONE)
+ player_turn = TRUE
+ var/chance_to_magic = round(max((-(enemy_hp - enemy_max_hp) / 2), 75), 1)
+ if((enemy_hp != enemy_max_hp) && prob(chance_to_magic))
+ if(enemy_mp >= 10)
+ var/healed_amount = rand(10, 20)
+ enemy_hp = round(min(enemy_max_hp, enemy_hp + healed_amount), 1)
+ enemy_mp -= round(max(0, 10), 1)
+ feedback_message = "[enemy_name] healed for [healed_amount] health points!"
+ playsound(loc, 'sound/arcade/heal.ogg', 40, TRUE, extrarange = -3)
+ SStgui.update_uis(src)
+ return
+ if(player_current_mp >= 5) //minimum to steal
+ var/healed_amount = rand(5, 10)
+ player_current_mp -= round(max(0, healed_amount), 1)
+ enemy_mp += healed_amount
+ feedback_message = "[enemy_name] stole [healed_amount] MP from you!"
+ playsound(loc, 'sound/arcade/steal.ogg', 40, TRUE)
+ SStgui.update_uis(src)
+ return
+ //we couldn't heal ourselves or steal MP, we'll just attack instead.
+ var/skill_level = user?.mind?.get_skill_level(/datum/skill/gaming) || 1
+ var/chance_at_counterattack = 40 + (skill_level * 5) //at level 1 this is 45, at legendary this is 75
+ var/damage_dealt = (defending_flags & BATTLE_ATTACK_FLAG_DEFEND) ? rand(5, 10) : rand(15, 20)
+ if((defending_flags & BATTLE_ATTACK_FLAG_COUNTERATTACK) && prob(chance_at_counterattack))
+ return successful_counterattack()
+ return user_take_damage(user, damage_dealt)
+
+/obj/machinery/computer/arcade/battle/ui_data(mob/user)
+ var/list/data = ..()
+
+ data["feedback_message"] = feedback_message
+ data["shop_items"] = list()
+ for(var/gear_name in battle_arcade_gear_list)
+ var/datum/battle_arcade_gear/gear = battle_arcade_gear_list[gear_name]
+ if(latest_unlocked_world != gear.world_available)
+ continue
+ data["shop_items"] += list(gear_name)
+ data["ui_panel"] = ui_panel
+ data["player_current_world"] = player_current_world
+ data["unlocked_world_modifier"] = all_worlds[latest_unlocked_world]
+ data["latest_unlocked_world_position"] = all_worlds.Find(latest_unlocked_world)
+ data["player_gold"] = player_gold
+ data["player_current_hp"] = player_current_hp
+ data["player_current_mp"] = player_current_mp
+ data["enemy_icon_id"] = "boss[enemy_icon_id].gif"
+ data["enemy_name"] = enemy_name
+ data["enemy_max_hp"] = enemy_max_hp
+ data["enemy_hp"] = enemy_hp
+ data["enemy_mp"] = enemy_mp
+
+ data["equipped_gear"] = list()
+ for(var/gear_slot as anything in equipped_gear)
+ var/datum/battle_arcade_gear/user_gear = equipped_gear[gear_slot]
+ if(!istype(user_gear))
+ continue
+ data["equipped_gear"] += list(list(
+ "name" = user_gear.name,
+ "slot" = gear_slot,
+ ))
+
+ return data
+
+/obj/machinery/computer/arcade/battle/ui_static_data(mob/user)
+ var/list/data = ..()
+
+ data["all_worlds"] = list()
+ for(var/individual_world in all_worlds)
+ UNTYPED_LIST_ADD(data["all_worlds"], individual_world)
+ data["attack_types"] = list()
+ for(var/individual_attack_type in all_attack_types)
+ UNTYPED_LIST_ADD(data["attack_types"], list("name" = individual_attack_type, "tooltip" = all_attack_types[individual_attack_type]))
+ data["cost_of_items"] = DEFAULT_ITEM_PRICE
+ data["max_hp"] = PLAYER_MAX_HP
+ data["max_mp"] = PLAYER_MAX_MP
+
+ return data
+
+/obj/machinery/computer/arcade/battle/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/simple/arcade),
+ )
+
+/obj/machinery/computer/arcade/battle/ui_interact(mob/user, datum/tgui/ui)
+ . = ..()
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "BattleArcade", "Battle Arcade")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/obj/machinery/computer/arcade/battle/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ var/mob/living/gamer = ui.user
+ if(!istype(gamer))
+ return
+
+ switch(ui_panel)
+ if(UI_PANEL_GAMEOVER)
+ switch(action)
+ if("restart")
+ reset_cabinet()
+ return TRUE
+ if(UI_PANEL_SHOP)
+ switch(action)
+ if("sleep")
+ if(player_gold < DEFAULT_ITEM_PRICE / 2)
+ say("You don't have enough gold to rest!")
+ return TRUE
+ player_gold -= DEFAULT_ITEM_PRICE / 2
+ playsound(loc, 'sound/mecha/skyfall_power_up.ogg', 40)
+ player_current_hp = PLAYER_MAX_HP
+ player_current_mp = PLAYER_MAX_MP
+ return TRUE
+ if("buy_item")
+ var/datum/battle_arcade_gear/gear = battle_arcade_gear_list[params["purchasing_item"]]
+ if(latest_unlocked_world != gear.world_available || equipped_gear[gear.slot] == gear)
+ say("That item is not in stock.")
+ return TRUE
+ if(player_gold < (DEFAULT_ITEM_PRICE * all_worlds[latest_unlocked_world]))
+ say("You don't have enough gold to buy that!")
+ return TRUE
+ player_gold -= DEFAULT_ITEM_PRICE * all_worlds[latest_unlocked_world]
+ equipped_gear[gear.slot] = gear
+ return TRUE
+ if("leave")
+ ui_panel = UI_PANEL_WORLD_MAP
+ return TRUE
+ if(UI_PANEL_WORLD_MAP)
+ switch(action)
+ if("start_fight")
+ var/world_travelling = all_worlds.Find(params["selected_arena"])
+ var/max_unlocked_worlds = all_worlds.Find(latest_unlocked_world)
+ if(world_travelling > max_unlocked_worlds)
+ say("That world is not unlocked yet!")
+ return TRUE
+ player_current_world = all_worlds[world_travelling]
+ setup_new_opponent()
+ return TRUE
+ if("enter_inn")
+ ui_panel = UI_PANEL_SHOP
+ return TRUE
+ if(UI_PANEL_BETWEEN_FIGHTS)
+ switch(action)
+ if("continue_without_rest")
+ setup_new_opponent()
+ return TRUE
+ if("continue_with_rest")
+ if(prob(60))
+ playsound(loc, 'sound/mecha/skyfall_power_up.ogg', 40)
+ player_current_hp = PLAYER_MAX_HP
+ player_current_mp = PLAYER_MAX_MP
+ else
+ playsound(loc, 'sound/machines/defib_zap.ogg', 40)
+ if(prob(40))
+ //You got robbed, and now have to go to your next fight.
+ player_gold /= 2
+ else
+ //You got ambushed, the enemy gets the first hit.
+ setup_new_opponent(enemy_gets_first_move = TRUE)
+ return TRUE
+ setup_new_opponent()
+ return TRUE
+ if("abandon_quest")
+ if(player_current_world == latest_unlocked_world)
+ enemies_defeated = 0
+ ui_panel = UI_PANEL_WORLD_MAP
+ return TRUE
+ if(UI_PANEL_BATTLE)
+ if(!player_turn)
+ return TRUE
+ player_turn = FALSE
+ switch(action)
+ if(BATTLE_ARCADE_PLAYER_ATTACK)
+ process_player_attack(gamer, BATTLE_ARCADE_PLAYER_ATTACK)
+ return TRUE
+ if(BATTLE_ARCADE_PLAYER_HEAVY_ATTACK)
+ if(player_current_mp < SPELL_MP_COST)
+ say("You don't have enough MP to counterattack!")
+ player_turn = TRUE
+ return TRUE
+ player_current_mp -= SPELL_MP_COST
+ process_player_attack(gamer, BATTLE_ARCADE_PLAYER_HEAVY_ATTACK)
+ return TRUE
+ if(BATTLE_ARCADE_PLAYER_COUNTERATTACK)
+ if(player_current_mp < SPELL_MP_COST)
+ say("You don't have enough MP to counterattack!")
+ player_turn = TRUE
+ return TRUE
+ player_current_mp -= SPELL_MP_COST
+ process_player_attack(gamer, BATTLE_ARCADE_PLAYER_COUNTERATTACK)
+ return TRUE
+ if(BATTLE_ARCADE_PLAYER_DEFEND)
+ player_current_hp = round(min(player_current_hp + (SPELL_MP_COST / 2), PLAYER_MAX_HP), 1)
+ player_current_mp = round(min(player_current_mp + SPELL_MP_COST, PLAYER_MAX_MP), 1)
+ process_player_attack(gamer, BATTLE_ARCADE_PLAYER_DEFEND)
+ return TRUE
+ if("flee")
+ //you can't outrun the cuban pete
+ if(obj_flags & EMAGGED)
+ lose_game(gamer)
+ return
+ player_turn = TRUE
+ ui_panel = UI_PANEL_WORLD_MAP
+ player_gold /= 2
+ return TRUE
+ //they pressed something but it wasn't in the menu, we'll be nice and give them back their turn anyway.
+ player_turn = TRUE
+
+#undef WORLD_ENEMY_BOSS
+#undef DEFAULT_EXP_GAIN
+#undef DEFAULT_ITEM_PRICE
+
+#undef PLAYER_MAX_HP
+#undef PLAYER_MAX_MP
+#undef SPELL_MP_COST
+
+#undef UI_PANEL_SHOP
+#undef UI_PANEL_WORLD_MAP
+#undef UI_PANEL_BATTLE
+#undef UI_PANEL_BETWEEN_FIGHTS
+#undef UI_PANEL_GAMEOVER
+
+#undef BATTLE_ATTACK_FLAG_COUNTERATTACK
+#undef BATTLE_ATTACK_FLAG_DEFEND
+
+#undef BATTLE_ARCADE_PLAYER_ATTACK
+#undef BATTLE_ARCADE_PLAYER_HEAVY_ATTACK
+#undef BATTLE_ARCADE_PLAYER_COUNTERATTACK
+#undef BATTLE_ARCADE_PLAYER_DEFEND
diff --git a/code/game/machinery/computer/arcade/battle_gear.dm b/code/game/machinery/computer/arcade/battle_gear.dm
new file mode 100644
index 00000000000..7a91df0e058
--- /dev/null
+++ b/code/game/machinery/computer/arcade/battle_gear.dm
@@ -0,0 +1,113 @@
+/datum/battle_arcade_gear
+ ///The name of the gear, used in shops.
+ var/name = "Gear"
+ ///The slot this gear fits into
+ var/slot = WEAPON_SLOT
+ ///The world the player has to be at in order to buy this item.
+ var/world_available
+ ///The stat given by the gear
+ var/bonus_modifier
+
+/datum/battle_arcade_gear/tier_1
+ world_available = BATTLE_WORLD_ONE
+
+/datum/battle_arcade_gear/tier_1/weapon
+ name = "Sword"
+ slot = WEAPON_SLOT
+ bonus_modifier = 1.5
+
+/datum/battle_arcade_gear/tier_1/armor
+ name = "Leather Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 1.5
+
+/datum/battle_arcade_gear/tier_2
+ world_available = BATTLE_WORLD_TWO
+
+/datum/battle_arcade_gear/tier_2/weapon
+ name = "Axe"
+ slot = WEAPON_SLOT
+ bonus_modifier = 1.75
+
+/datum/battle_arcade_gear/tier_2/armor
+ name = "Chainmail"
+ slot = ARMOR_SLOT
+ bonus_modifier = 1.75
+
+/datum/battle_arcade_gear/tier_3
+ world_available = BATTLE_WORLD_THREE
+
+/datum/battle_arcade_gear/tier_3/weapon
+ name = "Mace"
+ slot = WEAPON_SLOT
+ bonus_modifier = 2
+
+/datum/battle_arcade_gear/tier_3/armor
+ name = "Plate Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 2
+
+/datum/battle_arcade_gear/tier_4
+ world_available = BATTLE_WORLD_FOUR
+
+/datum/battle_arcade_gear/tier_4/weapon
+ name = "Greatsword"
+ slot = WEAPON_SLOT
+ bonus_modifier = 2.5
+
+/datum/battle_arcade_gear/tier_4/armor
+ name = "Full Plate Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 2.5
+
+/datum/battle_arcade_gear/tier_5
+ world_available = BATTLE_WORLD_FIVE
+
+/datum/battle_arcade_gear/tier_5/weapon
+ name = "Halberd"
+ slot = WEAPON_SLOT
+ bonus_modifier = 3
+
+/datum/battle_arcade_gear/tier_5/armor
+ name = "Dragon Scale Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 3
+
+/datum/battle_arcade_gear/tier_6
+ world_available = BATTLE_WORLD_SIX
+
+/datum/battle_arcade_gear/tier_6/weapon
+ name = "Warhammer"
+ slot = WEAPON_SLOT
+ bonus_modifier = 3.5
+
+/datum/battle_arcade_gear/tier_6/armor
+ name = "Adamantine Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 3.5
+
+/datum/battle_arcade_gear/tier_7
+ world_available = BATTLE_WORLD_SEVEN
+
+/datum/battle_arcade_gear/tier_7/weapon
+ name = "Excalibur"
+ slot = WEAPON_SLOT
+ bonus_modifier = 4
+
+/datum/battle_arcade_gear/tier_7/armor
+ name = "Celestial Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 4
+
+/datum/battle_arcade_gear/tier_8
+ world_available = BATTLE_WORLD_EIGHT
+
+/datum/battle_arcade_gear/tier_8/weapon
+ name = "Mjolnir"
+ slot = WEAPON_SLOT
+ bonus_modifier = 5
+
+/datum/battle_arcade_gear/tier_8/armor
+ name = "Ethereal Armor"
+ slot = ARMOR_SLOT
+ bonus_modifier = 5
diff --git a/code/game/machinery/computer/arcade/orion.dm b/code/game/machinery/computer/arcade/orion.dm
index 945cbb5593a..85bebddd25c 100644
--- a/code/game/machinery/computer/arcade/orion.dm
+++ b/code/game/machinery/computer/arcade/orion.dm
@@ -1,23 +1,13 @@
-// *** 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
+
+ ///List of all orion events, created on Initialize.
+ var/static/list/orion_events
var/busy = FALSE //prevent clickspam that allowed people to ~speedrun~ the game.
var/engine = 0
var/hull = 0
@@ -44,12 +34,18 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
/obj/machinery/computer/arcade/orion_trail/Initialize(mapload)
. = ..()
+ if(isnull(orion_events))
+ var/list/events = list()
+ for(var/path in subtypesof(/datum/orion_event))
+ var/datum/orion_event/new_event = new path(src)
+ events[new_event] = new_event.weight
+ orion_events = events
radio = new /obj/item/radio(src)
radio.set_listening(FALSE)
setup_events()
/obj/machinery/computer/arcade/orion_trail/proc/setup_events()
- events = GLOB.orion_events
+ events = orion_events.Copy()
/obj/machinery/computer/arcade/orion_trail/Destroy()
QDEL_NULL(radio)
@@ -81,7 +77,7 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
settlers = list("Kirk","Worf","Gene")
/obj/machinery/computer/arcade/orion_trail/kobayashi/setup_events()
- events = GLOB.orion_events.Copy()
+ events = orion_events.Copy()
for(var/datum/orion_event/event as anything in events)
if(!(event.type in event_whitelist))
events.Remove(event)
@@ -196,8 +192,6 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
. = TRUE
-
-
var/gamer_skill_level = 0
var/gamer_skill = 0
var/gamer_skill_rands = 0
@@ -316,7 +310,6 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
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
@@ -502,62 +495,56 @@ GLOBAL_LIST_INIT(orion_events, generate_orion_events())
obj_flags |= EMAGGED
return TRUE
-/mob/living/basic/trooper/syndicate/ranged/smg/orion
- name = "spaceport security"
- desc = "Premier corporate security forces for all spaceports found along the Orion Trail."
- faction = list(FACTION_ORION)
- loot = list()
-
+///A minibomb achieved from winning at emagged Orion.
/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/toys/toy.dmi'
icon_state = "ship"
w_class = WEIGHT_CLASS_SMALL
- var/active = 0 //if the ship is on
+ ///Boolean on whether the ship is active, setting itself off for destruction.
+ var/active = 0
/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
+ if(active)
. += span_notice("There's a little switch on the bottom. It's flipped up.")
+ return
+ . += span_notice("There's a little switch on the bottom. It's flipped down.")
-/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
+/obj/item/orion_ship/attack_self(mob/user)
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(2 SECONDS)
- visible_message(span_warning("[src] begins to vibrate..."))
- say("Uh, Port? Having some issues with our reactor, could you check it out? Over.")
- sleep(3 SECONDS)
- say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-")
- playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, TRUE)
- sleep(0.36 SECONDS)
- visible_message(span_userdanger("[src] explodes!"))
- explosion(src, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 8, flame_range = 16)
- qdel(src)
+ active = TRUE
+ addtimer(CALLBACK(src, PROC_REF(commit_explosion)), 1 SECONDS)
-/obj/singularity/orion
- move_self = FALSE
+///After some dialogue (which doubles as the timer until explosion), causes a minibomb-level explosion.
+/obj/item/orion_ship/proc/commit_explosion(dialogue_level = 0)
+ var/time_for_next_level
+ switch(dialogue_level)
+ if(0)
+ say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.")
+ time_for_next_level = 2 SECONDS
+ if(1)
+ say("Uh, Port? Having some issues with our reactor, could you check it out? Over.")
+ time_for_next_level = 3 SECONDS
+ if(2)
+ say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-")
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, TRUE)
+ time_for_next_level = 0.36 SECONDS
+ if(3 to INFINITY)
+ visible_message(span_userdanger("[src] explodes!"))
+ explosion(src, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 8, flame_range = 16)
+ qdel(src)
+ return
-/obj/singularity/orion/Initialize(mapload)
- . = ..()
-
- var/datum/component/singularity/singularity = singularity_component.resolve()
- singularity?.grav_pull = 1
-
-/obj/singularity/orion/process(seconds_per_tick)
- if(SPT_PROB(0.5, seconds_per_tick))
- mezzer()
+ if(time_for_next_level)
+ dialogue_level++
+ addtimer(CALLBACK(src, PROC_REF(commit_explosion), dialogue_level), time_for_next_level)
#undef ORION_TRAIL_WINTURN
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 5449d0bcf06..012e8e8a544 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -451,8 +451,8 @@
ui.open()
/obj/item/key/displaycase
- name = "display case key"
- desc = "The key to the curator's display cases."
+ name = "curator key"
+ desc = "The key to the curator's display cases and arcade cabinets."
/obj/item/showpiece_dummy
name = "holographic replica"
diff --git a/code/modules/admin/sql_message_system.dm b/code/modules/admin/sql_message_system.dm
index 27ef1232224..0cbcbb77903 100644
--- a/code/modules/admin/sql_message_system.dm
+++ b/code/modules/admin/sql_message_system.dm
@@ -752,18 +752,4 @@
notesfile.cd = "/"
notesfile.dir.Remove(ckey)
-/*alternatively this proc can be run once to pass through every note and attempt to convert it before deleting the file, if done then AUTOCONVERT_NOTES should be turned off
-this proc can take several minutes to execute fully if converting and cause DD to hang if converting a lot of notes; it's not advised to do so while a server is live
-/proc/mass_convert_notes()
- to_chat(world, "Beginning mass note conversion", confidential = TRUE)
- var/savefile/notesfile = new(NOTESFILE)
- if(!notesfile)
- log_game("Error: Cannot access [NOTESFILE]")
- return
- notesfile.cd = "/"
- for(var/ckey in notesfile.dir)
- convert_notes_sql(ckey)
- to_chat(world, "Deleting NOTESFILE", confidential = TRUE)
- fdel(NOTESFILE)
- to_chat(world, "Finished mass note conversion, remember to turn off AUTOCONVERT_NOTES", confidential = TRUE)*/
#undef NOTESFILE
diff --git a/code/modules/admin/verbs/server.dm b/code/modules/admin/verbs/server.dm
index d049372e6a9..ee7d638960a 100644
--- a/code/modules/admin/verbs/server.dm
+++ b/code/modules/admin/verbs/server.dm
@@ -120,7 +120,7 @@
return TRUE
SSticker.start_immediately = FALSE
SSticker.SetTimeLeft(1800)
- to_chat(world, "The game will start in 180 seconds.")
+ to_chat(world, span_infoplain(span_bold("The game will start in 180 seconds.")))
SEND_SOUND(world, sound('sound/ai/default/attention.ogg'))
message_admins("[usr.key] has cancelled immediate game start. Game will start in 180 seconds.")
log_admin("[usr.key] has cancelled immediate game start.")
@@ -174,9 +174,9 @@
var/alai = CONFIG_GET(flag/allow_ai)
CONFIG_SET(flag/allow_ai, !alai)
if (alai)
- to_chat(world, "The AI job is no longer chooseable.", confidential = TRUE)
+ to_chat(world, span_bold("The AI job is no longer chooseable."), confidential = TRUE)
else
- to_chat(world, "The AI job is chooseable now.", confidential = TRUE)
+ to_chat(world, span_bold("The AI job is chooseable now."), confidential = TRUE)
log_admin("[key_name(usr)] toggled AI allowed.")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
@@ -236,10 +236,10 @@
SSticker.SetTimeLeft(newtime)
SSticker.start_immediately = FALSE
if(newtime < 0)
- to_chat(world, "The game start has been delayed.", confidential = TRUE)
+ to_chat(world, span_infoplain(span_bold("The game start has been delayed.")), confidential = TRUE)
log_admin("[key_name(usr)] delayed the round start.")
else
- to_chat(world, "The game will start in [DisplayTimeText(newtime)].", confidential = TRUE)
+ to_chat(world, span_infoplain(span_bold("The game will start in [DisplayTimeText(newtime)].")), confidential = TRUE)
SEND_SOUND(world, sound('sound/ai/default/attention.ogg'))
log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].")
BLACKBOX_LOG_ADMIN_VERB("Delay Game Start")
@@ -274,9 +274,9 @@
var/new_guest_ban = !CONFIG_GET(flag/guest_ban)
CONFIG_SET(flag/guest_ban, new_guest_ban)
if (new_guest_ban)
- to_chat(world, "Guests may no longer enter the game.", confidential = TRUE)
+ to_chat(world, span_bold("Guests may no longer enter the game."), confidential = TRUE)
else
- to_chat(world, "Guests may now enter the game.", confidential = TRUE)
+ to_chat(world, span_bold("Guests may now enter the game."), confidential = TRUE)
log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
message_admins(span_adminnotice("[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed."))
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
diff --git a/code/modules/asset_cache/assets/arcade.dm b/code/modules/asset_cache/assets/arcade.dm
index bcd23919f97..338b891190c 100644
--- a/code/modules/asset_cache/assets/arcade.dm
+++ b/code/modules/asset_cache/assets/arcade.dm
@@ -1,5 +1,7 @@
/datum/asset/simple/arcade
assets = list(
+ "shopkeeper.png" = 'icons/ui_icons/arcade/shopkeeper.png',
+ "fireplace.png" = 'icons/ui_icons/arcade/fireplace.png',
"boss1.gif" = 'icons/ui_icons/arcade/boss1.gif',
"boss2.gif" = 'icons/ui_icons/arcade/boss2.gif',
"boss3.gif" = 'icons/ui_icons/arcade/boss3.gif',
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 1ba96157dc8..684721af5ee 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -263,7 +263,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(!GLOB.admin_datums[ckey])
var/list/autoadmin_ranks = ranks_from_rank_name(CONFIG_GET(string/autoadmin_rank))
if (autoadmin_ranks.len == 0)
- to_chat(world, "Autoadmin rank not found")
+ to_chat(GLOB.admins, "Autoadmin rank not found")
else
new /datum/admins(autoadmin_ranks, ckey)
if(CONFIG_GET(flag/enable_localhost_rank) && !connecting_admin)
@@ -1165,7 +1165,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return 0
if(!isnum(player_age) || player_age < 0)
- return 0
+ return 0
if(!isnum(days_needed))
return 0
diff --git a/code/modules/mob/living/basic/trooper/syndicate.dm b/code/modules/mob/living/basic/trooper/syndicate.dm
index c4d1bbd3630..8f8d564693b 100644
--- a/code/modules/mob/living/basic/trooper/syndicate.dm
+++ b/code/modules/mob/living/basic/trooper/syndicate.dm
@@ -154,6 +154,13 @@
ranged_cooldown = 3 SECONDS
r_hand = /obj/item/gun/ballistic/automatic/c20r
+///Spawns from an emagged orion trail machine set to kill the player.
+/mob/living/basic/trooper/syndicate/ranged/smg/orion
+ name = "spaceport security"
+ desc = "Premier corporate security forces for all spaceports found along the Orion Trail."
+ faction = list(FACTION_ORION)
+ loot = list()
+
/mob/living/basic/trooper/syndicate/ranged/smg/pilot //caravan ambush ruin
name = "Syndicate Salvage Pilot"
loot = list(/obj/effect/mob_spawn/corpse/human/syndicatepilot)
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index 5b712d52da2..d808ea0b009 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -482,3 +482,16 @@
/// Special singularity that spawns for shuttle events only
/obj/singularity/shuttle_event
anchored = FALSE
+
+/// Special singularity spawned by being sucked into a black hole during emagged orion trail.
+/obj/singularity/orion
+ move_self = FALSE
+
+/obj/singularity/orion/Initialize(mapload)
+ . = ..()
+ var/datum/component/singularity/singularity = singularity_component.resolve()
+ singularity?.grav_pull = 1
+
+/obj/singularity/orion/process(seconds_per_tick)
+ if(SPT_PROB(0.5, seconds_per_tick))
+ mezzer()
diff --git a/code/modules/recycling/disposal/holder.dm b/code/modules/recycling/disposal/holder.dm
index 2d964d0f8fb..fff78f74bef 100644
--- a/code/modules/recycling/disposal/holder.dm
+++ b/code/modules/recycling/disposal/holder.dm
@@ -172,7 +172,7 @@
for(var/obj/structure/disposalpipe/P in T)
if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir
if(QDELING(P))
- to_chat(world, "DEBUG -- [src] here, new pipe is being thanos'd")
+ CRASH("Pipe is being deleted while being used by a disposal holder at ([P.x], [P.y], [P.z]")
return P
// if no matching pipe, return null
return null
diff --git a/icons/hud/radial.dmi b/icons/hud/radial.dmi
index 90813b4d7d2..e4a1693fb57 100644
Binary files a/icons/hud/radial.dmi and b/icons/hud/radial.dmi differ
diff --git a/icons/obj/machines/computer.dmi b/icons/obj/machines/computer.dmi
index f8fb31c9ba0..9fcc46bf9b4 100644
Binary files a/icons/obj/machines/computer.dmi and b/icons/obj/machines/computer.dmi differ
diff --git a/icons/ui_icons/arcade/fireplace.png b/icons/ui_icons/arcade/fireplace.png
new file mode 100644
index 00000000000..080cadbebf0
Binary files /dev/null and b/icons/ui_icons/arcade/fireplace.png differ
diff --git a/icons/ui_icons/arcade/shopkeeper.png b/icons/ui_icons/arcade/shopkeeper.png
new file mode 100644
index 00000000000..e5eca14d856
Binary files /dev/null and b/icons/ui_icons/arcade/shopkeeper.png differ
diff --git a/strings/arcade.json b/strings/arcade.json
index 584cbc6decd..a97a3d75549 100644
--- a/strings/arcade.json
+++ b/strings/arcade.json
@@ -79,64 +79,6 @@
"Yuletide"
],
- "rpg_action": [
- "Annihilate",
- "Ban",
- "Defeat",
- "Destroy",
- "Mutilate",
- "Nuke",
- "Own",
- "Perma",
- "Pwn",
- "Pulverize",
- "Robust",
- "Save",
- "Stop",
- "Strike",
- "Valid",
- "Yeet"
- ],
-
- "rpg_action_valentines": [
- "Bewitch",
- "Crush",
- "Cuddle",
- "Divorce",
- "ERP",
- "Romance",
- "Seduce",
- "Smooch",
- "Titillate"
- ],
-
- "rpg_action_halloween": [
- "Amputate",
- "Behead",
- "Beware",
- "Butcher",
- "Dismember",
- "Exorcise",
- "Frighten",
- "Horrify",
- "Scare",
- "Scythe",
- "Slay",
- "Spook",
- "Vanquish"
- ],
-
- "rpg_action_xmas": [
- "Freeze",
- "Jingle",
- "Nutcrack",
- "Re-gift",
- "Snowplow",
- "Stuff",
- "Unwrap",
- "Wassail"
- ],
-
"rpg_enemy": [
"Administrator",
"Bloopers",
@@ -217,66 +159,6 @@
"Stalking Stocking",
"Yeti",
"Yuki-Onna"
- ],
-
- "rpg_weapon": [
- "baseball bat",
- "bike horn",
- "broken bottle",
- "chef's knife",
- "circular saw",
- "claymore",
- "cleaver",
- "crowbar",
- "curator's whip",
- "fire axe",
- "fire extinguisher",
- "full oxygen tank",
- "glass shard",
- "liz o' nine tails",
- "null rod",
- "potted plant",
- "rolling pin",
- "scalpel",
- "screwdriver",
- "stunprod",
- "toolbox",
- "welder"
- ],
-
- "rpg_weapon_valentines": [
- "bouquet of deathnettles",
- "candy-heart shotgun",
- "cupid's arrow",
- "declaration of anguished feelings",
- "razor-sharp breakup letter",
- "still-beating heart",
- "thorny rose",
- "toolbox of chocolates"
- ],
-
- "rpg_weapon_halloween": [
- "chainsaw",
- "heavy gravestone",
- "lit jack-o-lantern",
- "machete",
- "silver dagger",
- "skeleton femur",
- "spectral blade",
- "wooden stake"
- ],
-
- "rpg_weapon_xmas": [
- "bike horn playing carols",
- "blunt fruitcake",
- "box cutter",
- "Christmas-light garrotte",
- "festivus polearm",
- "rejected present",
- "sharpened icicle",
- "sprig of mistletoe",
- "thrown snowball",
- "whole Christmas tree"
]
}
diff --git a/tgstation.dme b/tgstation.dme
index 6c37b73b583..a1c100997c4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -42,6 +42,7 @@
#include "code\__DEFINES\antagonists.dm"
#include "code\__DEFINES\apc_defines.dm"
#include "code\__DEFINES\appearance.dm"
+#include "code\__DEFINES\arcades.dm"
#include "code\__DEFINES\area_editor.dm"
#include "code\__DEFINES\art.dm"
#include "code\__DEFINES\assemblies.dm"
@@ -506,6 +507,7 @@
#include "code\__HELPERS\sorts\TimSort.dm"
#include "code\_globalvars\_regexes.dm"
#include "code\_globalvars\admin.dm"
+#include "code\_globalvars\arcade.dm"
#include "code\_globalvars\bitfields.dm"
#include "code\_globalvars\colorvars.dm"
#include "code\_globalvars\configuration.dm"
@@ -2037,7 +2039,10 @@
#include "code\game\machinery\computer\telescreen.dm"
#include "code\game\machinery\computer\terminal.dm"
#include "code\game\machinery\computer\warrant.dm"
-#include "code\game\machinery\computer\arcade\arcade.dm"
+#include "code\game\machinery\computer\arcade\_arcade.dm"
+#include "code\game\machinery\computer\arcade\amputation.dm"
+#include "code\game\machinery\computer\arcade\battle.dm"
+#include "code\game\machinery\computer\arcade\battle_gear.dm"
#include "code\game\machinery\computer\arcade\orion.dm"
#include "code\game\machinery\computer\arcade\orion_event.dm"
#include "code\game\machinery\computer\atmos_computers\__identifiers.dm"
diff --git a/tgui/packages/tgui/interfaces/BattleArcade.tsx b/tgui/packages/tgui/interfaces/BattleArcade.tsx
new file mode 100644
index 00000000000..a400628575f
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/BattleArcade.tsx
@@ -0,0 +1,243 @@
+import { resolveAsset } from '../assets';
+import { useBackend } from '../backend';
+import { Box, Button, Image, Section } from '../components';
+import { Window } from '../layouts';
+
+type Data = {
+ all_worlds: string[];
+ attack_types: AttackTypeData[];
+ max_hp: number;
+ max_mp: number;
+ ui_panel: string;
+ feedback_message: string;
+ player_current_world: string;
+ unlocked_world_modifier: number;
+ latest_unlocked_world_position: number;
+ player_gold: number;
+ player_current_hp: number;
+ player_current_mp: number;
+ enemy_icon_id: string;
+ enemy_name: string;
+ enemy_max_hp: number;
+ enemy_hp: number;
+ enemy_mp: number;
+ cost_of_items: number;
+ equipped_gear: EquippedGear[];
+ shop_items: string[];
+};
+
+type AttackTypeData = {
+ name: string;
+ tooltip: string;
+};
+
+type EquippedGear = {
+ name: string;
+ slot: string;
+};
+
+export const BattleArcade = (props) => {
+ const { act, data } = useBackend();
+ const {
+ ui_panel,
+ player_current_hp,
+ player_current_mp,
+ max_hp,
+ max_mp,
+ player_gold,
+ equipped_gear = [],
+ } = data;
+ return (
+
+
+
+ Player stats HP:{' '}
+
+ {player_current_hp}/{max_hp}
+ {' '}
+ | MP:{' '}
+
+ {player_current_mp}/{max_mp}
+ {' '}
+ | {player_gold || 0}G
+ {!equipped_gear.length && 'No gear equipped!'}
+ {equipped_gear.map((gear, index) => (
+ <>
+ {gear.slot}: {gear.name}
+ >
+ ))}
+
+ {ui_panel === 'Shop' ? (
+
+ ) : ui_panel === 'World Map' ? (
+
+ ) : ui_panel === 'Battle' ? (
+
+ ) : ui_panel === 'Between Battle' ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
+
+const ShopPanel = (props) => {
+ const { act, data } = useBackend();
+ const { shop_items, cost_of_items, unlocked_world_modifier } = data;
+ return (
+
+ Welcome to the Inn!
+
+
+ Feel free to browse our wares, or take a nap. I'll be here to
+ ensure you'll get a good night's rest without worry of
+ ambushing or robbery.
+
+ {shop_items.map((item, index) => (
+
+ ))}
+
+
+
+ );
+};
+
+const WorldMapPanel = (props) => {
+ const { act, data } = useBackend();
+ const { all_worlds, latest_unlocked_world_position } = data;
+ return (
+
+
+ WORLD MAP{' '}
+
+
+
+ The further down you go, the harder the enemies will be, but the more
+ loot you will get.
+
+
+ {all_worlds.map((world, index) => (
+
+ ))}
+
+ );
+};
+
+const BattlePanel = (props) => {
+ const { act, data } = useBackend();
+ const {
+ attack_types,
+ enemy_icon_id,
+ enemy_name,
+ enemy_max_hp,
+ enemy_hp,
+ enemy_mp,
+ feedback_message,
+ } = data;
+ return (
+
+ {feedback_message && {feedback_message}}
+
+ {enemy_name}'s HP: {enemy_hp}/{enemy_max_hp}
+
+
+ {enemy_name}'s MP: {enemy_mp}
+
+
+ {attack_types.map((attack, index) => (
+
+ ))}
+ act('flee')}
+ >
+ Flee (Lose half your Gold)
+
+
+ );
+};
+
+const BetweenBattlePanel = (props) => {
+ const { act, data } = useBackend();
+ return (
+
+
+
+ As night sets, you can choose to rest. This will restore your health and
+ mana to full before the next battle, but you may also be ambushed,
+ forcing you into the next fight without any additional healing.
+
+
+
+
+
+ );
+};
+
+const GameOverPanel = (props) => {
+ const { act, data } = useBackend();
+ return (
+
+
+ Game Over
+
+
+
+
+
+ );
+};