diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 84d5964064f..55ff05d7726 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -6,11 +6,13 @@ on: - main - beta - release + - 'hotfix*' pull_request: branches: - main - beta - release + - 'hotfix*' merge_group: types: [checks_requested] diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index a5e8f96961e..ae23a515c4f 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -6,11 +6,13 @@ on: - main - beta - release + - 'hotfix*' pull_request: branches: - main - beta - release + - 'hotfix*' merge_group: types: [checks_requested] diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2b9f6dc9c0d..748072c536f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,11 +6,13 @@ on: - main - beta - release + - 'hotfix*' pull_request: branches: - main - beta - release + - 'hotfix*' merge_group: types: [checks_requested] workflow_dispatch: diff --git a/package.json b/package.json index 74e98e37f89..9648361341a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.10.1", + "version": "1.10.2", "type": "module", "scripts": { "start": "vite", diff --git a/public/images/ui/champion_ribbon.png b/public/images/ui/champion_ribbon.png index b188f4c92d2..a19bb01279b 100644 Binary files a/public/images/ui/champion_ribbon.png and b/public/images/ui/champion_ribbon.png differ diff --git a/public/images/ui/champion_ribbon_emerald.png b/public/images/ui/champion_ribbon_emerald.png index 4b0523f7f64..29a9503059d 100644 Binary files a/public/images/ui/champion_ribbon_emerald.png and b/public/images/ui/champion_ribbon_emerald.png differ diff --git a/public/images/ui/legacy/champion_ribbon.png b/public/images/ui/legacy/champion_ribbon.png index b188f4c92d2..a19bb01279b 100644 Binary files a/public/images/ui/legacy/champion_ribbon.png and b/public/images/ui/legacy/champion_ribbon.png differ diff --git a/public/images/ui/legacy/champion_ribbon_emerald.png b/public/images/ui/legacy/champion_ribbon_emerald.png index 4b0523f7f64..29a9503059d 100644 Binary files a/public/images/ui/legacy/champion_ribbon_emerald.png and b/public/images/ui/legacy/champion_ribbon_emerald.png differ diff --git a/public/locales b/public/locales index 58fa5f9b6e9..a73ea68fdda 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 58fa5f9b6e94469017bfbe69bef992ed48ef5343 +Subproject commit a73ea68fdda09bb5018f524cbe6b7e73a3ddf4e0 diff --git a/src/constants.ts b/src/constants.ts index 589a091153c..17cf08aa7e2 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -53,12 +53,6 @@ export const defaultStarterSpecies: SpeciesId[] = [ SpeciesId.QUAXLY, ]; -export const defaultStarterSpeciesAndEvolutions: SpeciesId[] = defaultStarterSpecies.flatMap(id => [ - id, - (id + 1) as SpeciesId, - (id + 2) as SpeciesId, -]); - export const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary /** diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index b03cc5b951a..cd02455af0f 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -937,7 +937,7 @@ class StealthRockTag extends DamagingTrapTag { protected override getTriggerMessage(pokemon: Pokemon): string { return i18next.t("arenaTag:stealthRockActivateTrap", { - pokemonName: getPokemonNameWithAffix(pokemon), + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), }); } diff --git a/src/data/balance/egg-moves.ts b/src/data/balance/egg-moves.ts index 2d2e981219d..5dd7bcbd9e2 100644 --- a/src/data/balance/egg-moves.ts +++ b/src/data/balance/egg-moves.ts @@ -187,7 +187,7 @@ export const speciesEggMoves = { [SpeciesId.WYNAUT]: [ MoveId.RECOVER, MoveId.SHED_TAIL, MoveId.TAUNT, MoveId.COMEUPPANCE ], [SpeciesId.SNORUNT]: [ MoveId.SPARKLY_SWIRL, MoveId.NASTY_PLOT, MoveId.EARTH_POWER, MoveId.BLOOD_MOON ], [SpeciesId.SPHEAL]: [ MoveId.FLIP_TURN, MoveId.FREEZE_DRY, MoveId.SLACK_OFF, MoveId.STEAM_ERUPTION ], - [SpeciesId.CLAMPERL]: [ MoveId.SHELL_SIDE_ARM, MoveId.BOUNCY_BUBBLE, MoveId.FREEZE_DRY, MoveId.STEAM_ERUPTION ], + [SpeciesId.CLAMPERL]: [ MoveId.SHELL_SIDE_ARM, MoveId.SNIPE_SHOT, MoveId.GIGA_DRAIN, MoveId.BOUNCY_BUBBLE ], [SpeciesId.RELICANTH]: [ MoveId.DRAGON_DANCE, MoveId.SHORE_UP, MoveId.WAVE_CRASH, MoveId.DIAMOND_STORM ], [SpeciesId.LUVDISC]: [ MoveId.BATON_PASS, MoveId.HEART_SWAP, MoveId.GLITZY_GLOW, MoveId.REVIVAL_BLESSING ], [SpeciesId.BAGON]: [ MoveId.HEADLONG_RUSH, MoveId.FIRE_LASH, MoveId.DRAGON_DANCE, MoveId.DRAGON_DARTS ], diff --git a/src/data/balance/passives.ts b/src/data/balance/passives.ts index 0e34917fd80..1297ad71c36 100644 --- a/src/data/balance/passives.ts +++ b/src/data/balance/passives.ts @@ -402,7 +402,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = { [SpeciesId.SPHEAL]: { 0: AbilityId.UNAWARE }, [SpeciesId.SEALEO]: { 0: AbilityId.UNAWARE }, [SpeciesId.WALREIN]: { 0: AbilityId.UNAWARE }, - [SpeciesId.CLAMPERL]: { 0: AbilityId.DAUNTLESS_SHIELD }, + [SpeciesId.CLAMPERL]: { 0: AbilityId.OVERCOAT }, [SpeciesId.GOREBYSS]: { 0: AbilityId.ARENA_TRAP }, [SpeciesId.HUNTAIL]: { 0: AbilityId.ARENA_TRAP }, [SpeciesId.RELICANTH]: { 0: AbilityId.PRIMORDIAL_SEA }, diff --git a/src/data/balance/pokemon-evolutions.ts b/src/data/balance/pokemon-evolutions.ts index d42bce041c2..bf90ebb7edc 100644 --- a/src/data/balance/pokemon-evolutions.ts +++ b/src/data/balance/pokemon-evolutions.ts @@ -1,3 +1,4 @@ +import { defaultStarterSpecies } from "#app/constants"; import { globalScene } from "#app/global-scene"; import { speciesStarterCosts } from "#balance/starters"; import { allMoves } from "#data/data-lists"; @@ -1883,6 +1884,15 @@ export function initPokemonPrevolutions(): void { // TODO: This may cause funny business for double starters such as Pichu/Pikachu export const pokemonStarters: PokemonPrevolutions = {}; +/** + * The default species and all their evolutions + */ +export const defaultStarterSpeciesAndEvolutions: SpeciesId[] = defaultStarterSpecies.flatMap(id => { + const stage2ids = pokemonEvolutions[id]?.map(e => e.speciesId) ?? []; + const stage3ids = stage2ids.flatMap(s2id => pokemonEvolutions[s2id]?.map(e => e.speciesId) ?? []); + return [id, ...stage2ids, ...stage3ids]; +}); + export function initPokemonStarters(): void { const starterKeys = Object.keys(pokemonPrevolutions); starterKeys.forEach(pk => { diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 3c282e7640e..01e6fa78ffc 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -1,6 +1,6 @@ import type { FixedBattleConfig } from "#app/battle"; import { getRandomTrainerFunc } from "#app/battle"; -import { defaultStarterSpeciesAndEvolutions } from "#app/constants"; +import { defaultStarterSpeciesAndEvolutions } from "#balance/pokemon-evolutions"; import { speciesStarterCosts } from "#balance/starters"; import type { PokemonSpecies } from "#data/pokemon-species"; import { AbilityAttr } from "#enums/ability-attr"; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index dc9f2306101..4a744d6e9c3 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -2325,6 +2325,13 @@ export class HealOnAllyAttr extends HealAttr { // Don't trigger if not targeting an ally return target === user.getAlly() && super.canApply(user, target, _move, _args); } + + override apply(user: Pokemon, target: Pokemon, _move: Move, _args: any[]): boolean { + if (user.isOpponent(target)) { + return false; + } + return super.apply(user, target, _move, _args); + } } /** @@ -3270,7 +3277,6 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { ) ) - user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn}) user.pushMoveHistory({move: move.id, targets: [target.getBattlerIndex()], result: MoveResult.OTHER, useMode, turn: globalScene.currentBattle.turn}) // Queue up an attack on the given slot. globalScene.arena.positionalTagManager.addTag({ diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 196ca873f4e..358bba92a09 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -237,7 +237,7 @@ export const BerriesAboundEncounter: MysteryEncounter = MysteryEncounterBuilder. const config = globalScene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; config.pokemonConfigs![0].tags = [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON]; config.pokemonConfigs![0].mysteryEncounterBattleEffects = (pokemon: Pokemon) => { - queueEncounterMessage(`${namespace}:option.2.boss_enraged`); + queueEncounterMessage(`${namespace}:option.2.bossEnraged`); globalScene.phaseManager.unshiftNew( "StatStageChangePhase", pokemon.getBattlerIndex(), diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index af576ffd8be..ed588ea2884 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -249,7 +249,7 @@ async function tryApplyDigRewardItems() { await showEncounterText( i18next.t("battle:rewardGainCount", { modifierName: leftovers.name, - count: 2, + count: 1, }), null, undefined, diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 064ad57cfb3..fd8551f2289 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -795,7 +795,7 @@ export class PokemonSpecies extends PokemonSpeciesForm implements Localizable { return Gender.GENDERLESS; } - if (randSeedFloat() <= this.malePercent) { + if (randSeedFloat() * 100 <= this.malePercent) { return Gender.MALE; } return Gender.FEMALE; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e73d1cdf273..f02c9a1f30c 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -454,7 +454,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { getNameToRender(useIllusion = true) { const illusion = this.summonData.illusion; const name = useIllusion ? (illusion?.name ?? this.name) : this.name; - const nickname: string | undefined = useIllusion ? illusion?.nickname : this.nickname; + const nickname: string | undefined = useIllusion ? (illusion?.nickname ?? this.nickname) : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); // TODO: Remove `atob` and `escape`... eventually... @@ -1781,7 +1781,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container { * @returns Whether this Pokemon is currently fused with another species. */ isFusion(useIllusion = false): boolean { - return useIllusion ? !!this.summonData.illusion?.fusionSpecies : !!this.fusionSpecies; + return !!(useIllusion ? (this.summonData.illusion?.fusionSpecies ?? this.fusionSpecies) : this.fusionSpecies); } /** diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index 05c890136ee..06a02af38b0 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -177,7 +177,7 @@ export class SelectModifierPhase extends BattlePhase { this.openModifierMenu(modifierType, cost, modifierSelectCallback); } } else { - this.applyModifier(modifierType.newModifier()!); + this.applyModifier(modifierType.newModifier()!, cost); } return cost === -1; } diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index d3c67f66ec9..446fa37ec4a 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -563,7 +563,7 @@ export class PartyUiHandler extends MessageUiHandler { const ui = this.getUi(); const option = this.options[this.optionsCursor]; - if (option === PartyOption.TRANSFER) { + if (this.transferMode && option === PartyOption.TRANSFER) { return this.processTransferOption(); } @@ -1021,7 +1021,8 @@ export class PartyUiHandler extends MessageUiHandler { } // Toggle item transfer mode to discard items or vice versa - if (this.cursor === 7) { + // Prevent changing mode, when currently transfering an item + if (this.cursor === 7 && !this.transferMode) { switch (this.partyUiMode) { case PartyUiMode.DISCARD: this.partyUiMode = PartyUiMode.MODIFIER_TRANSFER; @@ -1609,7 +1610,7 @@ export class PartyUiHandler extends MessageUiHandler { const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM]; optionName = `${modifier.active ? i18next.t("partyUiHandler:deactivate") : i18next.t("partyUiHandler:activate")} ${modifier.type.name}`; } else if (option === PartyOption.UNPAUSE_EVOLUTION) { - optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:unpausedEvolution") : i18next.t("partyUiHandler:pauseEvolution")}`; + optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:unpauseEvolution") : i18next.t("partyUiHandler:pauseEvolution")}`; } else { if (this.localizedOptions.includes(option)) { optionName = i18next.t(`partyUiHandler:${toCamelCase(PartyOption[option])}`); diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index e9f9c5a0038..97cd3016479 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -181,7 +181,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler { ui.setOverlayMode( UiMode.CONFIRM, () => { - globalScene.gameData.tryClearSession(cursor).then(response => { + globalScene.gameData.deleteSession(cursor).then(response => { if (response[0] === false) { globalScene.reset(true); } else { diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 25d5277b4c2..a29a65aca80 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -2095,27 +2095,21 @@ export class StarterSelectUiHandler extends MessageUiHandler { const passiveAttr = starterData.passiveAttr; if (passiveAttr & PassiveAttr.UNLOCKED) { // this is for enabling and disabling the passive - if (!(passiveAttr & PassiveAttr.ENABLED)) { - options.push({ - label: i18next.t("starterSelectUiHandler:enablePassive"), - handler: () => { - starterData.passiveAttr |= PassiveAttr.ENABLED; - ui.setMode(UiMode.STARTER_SELECT); - this.setSpeciesDetails(this.lastSpecies); - return true; - }, - }); - } else { - options.push({ - label: i18next.t("starterSelectUiHandler:disablePassive"), - handler: () => { - starterData.passiveAttr ^= PassiveAttr.ENABLED; - ui.setMode(UiMode.STARTER_SELECT); - this.setSpeciesDetails(this.lastSpecies); - return true; - }, - }); - } + const label = i18next.t( + passiveAttr & PassiveAttr.ENABLED + ? "starterSelectUiHandler:disablePassive" + : "starterSelectUiHandler:enablePassive", + ); + options.push({ + label, + handler: () => { + starterData.passiveAttr ^= PassiveAttr.ENABLED; + persistentStarterData.passiveAttr ^= PassiveAttr.ENABLED; + ui.setMode(UiMode.STARTER_SELECT); + this.setSpeciesDetails(this.lastSpecies); + return true; + }, + }); } // if container.favorite is false, show the favorite option const isFavorite = starterAttributes?.favorite ?? false; @@ -4618,6 +4612,8 @@ export class StarterSelectUiHandler extends MessageUiHandler { clear(): void { super.clear(); + saveStarterPreferences(this.originalStarterPreferences); + this.clearStarterPreferences(); this.cursor = -1; this.hideInstructions(); diff --git a/test/items/leftovers.test.ts b/test/items/leftovers.test.ts index 6ae4094799b..484843b81ff 100644 --- a/test/items/leftovers.test.ts +++ b/test/items/leftovers.test.ts @@ -2,7 +2,6 @@ import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import { DamageAnimPhase } from "#phases/damage-anim-phase"; -import { TurnEndPhase } from "#phases/turn-end-phase"; import { GameManager } from "#test/test-utils/game-manager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -54,7 +53,7 @@ describe("Items - Leftovers", () => { const leadHpAfterDamage = leadPokemon.hp; // Check if leftovers heal us - await game.phaseInterceptor.to(TurnEndPhase); + await game.phaseInterceptor.to("PokemonHealPhase"); expect(leadPokemon.hp).toBeGreaterThan(leadHpAfterDamage); }); }); diff --git a/test/moves/entry-hazards.test.ts b/test/moves/entry-hazards.test.ts index c4dead1bb67..af8145183a3 100644 --- a/test/moves/entry-hazards.test.ts +++ b/test/moves/entry-hazards.test.ts @@ -200,7 +200,7 @@ describe("Moves - Entry Hazards", () => { expect(enemy).toHaveTakenDamage(enemy.getMaxHp() * 0.125 * multi); expect(game.textInterceptor.logs).toContain( i18next.t("arenaTag:stealthRockActivateTrap", { - pokemonName: getPokemonNameWithAffix(enemy), + pokemonNameWithAffix: getPokemonNameWithAffix(enemy), }), ); }); diff --git a/test/moves/pollen-puff.test.ts b/test/moves/pollen-puff.test.ts index 76732a39c43..02772055f1f 100644 --- a/test/moves/pollen-puff.test.ts +++ b/test/moves/pollen-puff.test.ts @@ -61,4 +61,16 @@ describe("Moves - Pollen Puff", () => { expect(target.battleData.hitCount).toBe(2); }); + + // Regression test for pollen puff healing an enemy after dealing damage + it("should not heal an enemy after dealing damage", async () => { + await game.classicMode.startBattle([SpeciesId.FEEBAS]); + const target = game.field.getEnemyPokemon(); + game.move.use(MoveId.POLLEN_PUFF); + + await game.phaseInterceptor.to("BerryPhase", false); + + expect(target.hp).not.toBe(target.getMaxHp()); + expect(game.phaseInterceptor.log).not.toContain("PokemonHealPhase"); + }); }); diff --git a/test/test-utils/phase-interceptor.ts b/test/test-utils/phase-interceptor.ts index 0d357a75557..996f00806c6 100644 --- a/test/test-utils/phase-interceptor.ts +++ b/test/test-utils/phase-interceptor.ts @@ -37,6 +37,7 @@ import { NewBiomeEncounterPhase } from "#phases/new-biome-encounter-phase"; import { NextEncounterPhase } from "#phases/next-encounter-phase"; import { PartyExpPhase } from "#phases/party-exp-phase"; import { PartyHealPhase } from "#phases/party-heal-phase"; +import { PokemonHealPhase } from "#phases/pokemon-heal-phase"; import { PokemonTransformPhase } from "#phases/pokemon-transform-phase"; import { PositionalTagPhase } from "#phases/positional-tag-phase"; import { PostGameOverPhase } from "#phases/post-game-over-phase"; @@ -181,6 +182,7 @@ export class PhaseInterceptor { UnlockPhase, PostGameOverPhase, RevivalBlessingPhase, + PokemonHealPhase, ]; private endBySetMode = [ diff --git a/test/ui/item-manage-button.test.ts b/test/ui/item-manage-button.test.ts index a7ea76918a5..c28cd9e802e 100644 --- a/test/ui/item-manage-button.test.ts +++ b/test/ui/item-manage-button.test.ts @@ -6,7 +6,7 @@ import { UiMode } from "#enums/ui-mode"; import type { Pokemon } from "#field/pokemon"; import { GameManager } from "#test/test-utils/game-manager"; import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; -import type { PartyUiHandler } from "#ui/party-ui-handler"; +import { type PartyUiHandler, PartyUiMode } from "#ui/party-ui-handler"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -169,4 +169,51 @@ describe("UI - Transfer Items", () => { expect(pokemon.getHeldItems().map(h => h.stackCount)).toEqual([2, 2]); } }); + + // TODO: This test breaks when running all tests on github. Fix this once hotfix period is over. + it.todo("should not allow changing to discard mode when transfering items", async () => { + let handler: PartyUiHandler | undefined; + + const { resolve, promise } = Promise.withResolvers(); + + game.onNextPrompt("SelectModifierPhase", UiMode.MODIFIER_SELECT, async () => { + await new Promise(r => setTimeout(r, 100)); + const modifierHandler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + + modifierHandler.processInput(Button.DOWN); + modifierHandler.setCursor(1); + modifierHandler.processInput(Button.ACTION); + }); + + game.onNextPrompt("SelectModifierPhase", UiMode.PARTY, async () => { + await new Promise(r => setTimeout(r, 100)); + handler = game.scene.ui.getHandler() as PartyUiHandler; + + handler.setCursor(0); + handler.processInput(Button.ACTION); + + await new Promise(r => setTimeout(r, 100)); + handler.processInput(Button.ACTION); + + resolve(); + }); + + await promise; + expect(handler).toBeDefined(); + if (handler) { + const partyMode = handler["partyUiMode"]; + expect(partyMode).toBe(PartyUiMode.MODIFIER_TRANSFER); + + handler.setCursor(7); + handler.processInput(Button.ACTION); + // Should not change mode to discard + expect(handler["partyUiMode"]).toBe(PartyUiMode.MODIFIER_TRANSFER); + + handler.processInput(Button.CANCEL); + handler.setCursor(7); + handler.processInput(Button.ACTION); + // Should change mode to discard + expect(handler["partyUiMode"]).toBe(PartyUiMode.DISCARD); + } + }); });