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 += "

[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) - - 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 = "

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_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!

" - 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/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 ( +
+ + + {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 + + + + +
+ ); +};