Compare commits
41 Commits
2bc786503c
...
2e83236ee7
Author | SHA1 | Date |
---|---|---|
RedstonewolfX | 2e83236ee7 | |
MokaStitcher | 51bb80cb66 | |
RedstonewolfX | 9062925cf8 | |
RedstonewolfX | 696a52fe15 | |
RedstonewolfX | 4a27252231 | |
RedstonewolfX | 752bd4f017 | |
RedstonewolfX | c6c489b289 | |
RedstonewolfX | 21315a3664 | |
RedstonewolfX | 029dfa4b3b | |
flx-sta | 6cfc6f9341 | |
RedstonewolfX | 9c391f92bd | |
NightKev | fa6b754dec | |
MokaStitcher | 7ea608fb8a | |
Jannik Tappert | 808b15bd94 | |
RedstonewolfX | a9b6359e34 | |
RedstonewolfX | 0114d795a5 | |
RedstonewolfX | 8fdce2405c | |
RedstonewolfX | 64861447cb | |
Frederico Santos | 801b0a66f7 | |
Frederico Santos | 103c87ec3b | |
NightKev | 6373a86011 | |
NightKev | 2a2ef7d50c | |
RedstonewolfX | b71ddda191 | |
RedstonewolfX | 49036f9522 | |
RedstonewolfX | 3e65888ff1 | |
RedstonewolfX | d7263b6677 | |
RedstonewolfX | 3095ecebf8 | |
RedstonewolfX | f5b49d812d | |
RedstonewolfX | e9b06bdf1b | |
RedstonewolfX | 6225a020e0 | |
RedstonewolfX | 48fbc571cd | |
RedstonewolfX | 9baeac8d40 | |
RedstonewolfX | 1b558cd945 | |
RedstonewolfX | ae33a46945 | |
RedstonewolfX | e4c62e55cf | |
RedstonewolfX | 590f1a6833 | |
RedstonewolfX | 02e9273f97 | |
RedstonewolfX | f8478aa79b | |
RedstonewolfX | d1e14850ef | |
RedstonewolfX | 103ad807ee | |
RedstonewolfX | 0be82b32dd |
|
@ -1674,7 +1674,12 @@ export default class BattleScene extends SceneBase {
|
|||
this.scoreText.setVisible(this.gameMode.isDaily);
|
||||
}
|
||||
|
||||
updateAndShowText(duration: integer): void {
|
||||
/**
|
||||
* Displays the current luck value.
|
||||
* @param duration The time for this label to fade in, if it is not already visible.
|
||||
* @param isDaily If true, hides the label. (This is done because Luck does not apply in Daily Mode anymore)
|
||||
*/
|
||||
updateAndShowText(duration: number, isDaily?: boolean): void {
|
||||
const labels = [ this.luckLabelText, this.luckText ];
|
||||
labels.forEach(t => t.setAlpha(0));
|
||||
const luckValue = getPartyLuckValue(this.getParty());
|
||||
|
@ -1685,12 +1690,16 @@ export default class BattleScene extends SceneBase {
|
|||
this.luckText.setTint(0xffef5c, 0x47ff69, 0x6b6bff, 0xff6969);
|
||||
}
|
||||
this.luckLabelText.setX((this.game.canvas.width / 6) - 2 - (this.luckText.displayWidth + 2));
|
||||
if (isDaily) {
|
||||
// Hide luck label
|
||||
labels.forEach(t => t.setVisible(false));
|
||||
}
|
||||
this.tweens.add({
|
||||
targets: labels,
|
||||
duration: duration,
|
||||
alpha: 1,
|
||||
onComplete: () => {
|
||||
labels.forEach(t => t.setVisible(true));
|
||||
labels.forEach(t => t.setVisible(!isDaily));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1699,7 +1699,8 @@ const modifierPool: ModifierPool = {
|
|||
new WeightedModifierType(modifierTypes.FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24),
|
||||
new WeightedModifierType(modifierTypes.AMULET_COIN, skipInLastClassicWaveOrDefault(3)),
|
||||
new WeightedModifierType(modifierTypes.EVIOLITE, (party: Pokemon[]) => {
|
||||
if (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.EVIOLITE]) {
|
||||
const { gameMode, gameData } = party[0].scene;
|
||||
if (gameMode.isDaily || (!gameMode.isFreshStartChallenge() && gameData.isUnlocked(Unlockables.EVIOLITE))) {
|
||||
return party.some(p => ((p.getSpeciesForm(true).speciesId in pokemonEvolutions) || (p.isFusion() && (p.getFusionSpeciesForm(true).speciesId in pokemonEvolutions))) && !p.getHeldItems().some(i => i instanceof Modifiers.EvolutionStatBoosterModifier)) ? 10 : 0;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1777,7 +1778,7 @@ const modifierPool: ModifierPool = {
|
|||
new WeightedModifierType(modifierTypes.MULTI_LENS, 18),
|
||||
new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5),
|
||||
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24),
|
||||
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) ? 1 : 0, 1),
|
||||
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (party[0].scene.gameMode.isDaily || (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.isUnlocked(Unlockables.MINI_BLACK_HOLE))) ? 1 : 0, 1),
|
||||
].map(m => {
|
||||
m.setTier(ModifierTier.MASTER); return m;
|
||||
})
|
||||
|
@ -1969,9 +1970,13 @@ export function getModifierPoolForType(poolType: ModifierPoolType): ModifierPool
|
|||
}
|
||||
|
||||
const tierWeights = [ 768 / 1024, 195 / 1024, 48 / 1024, 12 / 1024, 1 / 1024 ];
|
||||
export const itemPoolChecks: Map<ModifierTypeKeys, boolean> = new Map();
|
||||
|
||||
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType, rerollCount: integer = 0) {
|
||||
const pool = getModifierPoolForType(poolType);
|
||||
itemPoolChecks.forEach((v, k) => {
|
||||
itemPoolChecks.set(k, false);
|
||||
});
|
||||
|
||||
const ignoredIndexes = {};
|
||||
const modifierTableData = {};
|
||||
|
@ -2008,6 +2013,9 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
|
|||
ignoredIndexes[t].push(i++);
|
||||
return total;
|
||||
}
|
||||
if (itemPoolChecks.has(modifierType.modifierType.id as ModifierTypeKeys)) {
|
||||
itemPoolChecks.set(modifierType.modifierType.id as ModifierTypeKeys, true);
|
||||
}
|
||||
thresholds.set(total, i++);
|
||||
return total;
|
||||
}, 0);
|
||||
|
@ -2410,8 +2418,16 @@ export class ModifierTypeOption {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the team's luck value.
|
||||
* @param party The player's party.
|
||||
* @returns A value between 0 and 14, or 0 if the player is in Daily Run mode.
|
||||
*/
|
||||
export function getPartyLuckValue(party: Pokemon[]): integer {
|
||||
const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? p.getLuck() : 0)
|
||||
if (party[0].scene.gameMode.isDaily) {
|
||||
return 0;
|
||||
}
|
||||
const luck = Phaser.Math.Clamp(party.map(p => p.isAllowedInBattle() ? (p.scene.gameMode.isDaily ? 0 : p.getLuck()) : 0)
|
||||
.reduce((total: integer, value: integer) => total += value, 0), 0, 14);
|
||||
return luck || 0;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { type PokeballCounts } from "./battle-scene";
|
|||
import { Gender } from "./data/gender";
|
||||
import { Variant } from "./data/variant";
|
||||
import { type ModifierOverride } from "./modifier/modifier-type";
|
||||
import { Unlockables } from "./system/unlockables";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
|
||||
|
@ -70,8 +71,10 @@ class DefaultOverrides {
|
|||
[PokeballType.MASTER_BALL]: 0,
|
||||
},
|
||||
};
|
||||
/** Forces an item to be UNLOCKED */
|
||||
readonly ITEM_UNLOCK_OVERRIDE: Unlockables[] = [];
|
||||
/** Set to `true` to show all tutorials */
|
||||
readonly BYPASS_TUTORIAL_SKIP: boolean = false;
|
||||
readonly BYPASS_TUTORIAL_SKIP_OVERRIDE: boolean = false;
|
||||
|
||||
// ----------------
|
||||
// PLAYER OVERRIDES
|
||||
|
|
|
@ -76,7 +76,8 @@ export class TitlePhase extends Phase {
|
|||
this.scene.ui.clearText();
|
||||
this.end();
|
||||
};
|
||||
if (this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) {
|
||||
const { gameData } = this.scene;
|
||||
if (gameData.isUnlocked(Unlockables.ENDLESS_MODE)) {
|
||||
const options: OptionSelectItem[] = [
|
||||
{
|
||||
label: GameMode.getModeName(GameModes.CLASSIC),
|
||||
|
@ -100,7 +101,7 @@ export class TitlePhase extends Phase {
|
|||
}
|
||||
}
|
||||
];
|
||||
if (this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) {
|
||||
if (gameData.isUnlocked(Unlockables.SPLICED_ENDLESS_MODE)) {
|
||||
options.push({
|
||||
label: GameMode.getModeName(GameModes.SPLICED_ENDLESS),
|
||||
handler: () => {
|
||||
|
@ -220,6 +221,7 @@ export class TitlePhase extends Phase {
|
|||
|
||||
const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier())
|
||||
.concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier()))
|
||||
.concat([modifierTypes.MAP().withIdFromFunc(modifierTypes.MAP).newModifier()])
|
||||
.concat(getDailyRunStarterModifiers(party))
|
||||
.filter((m) => m !== null);
|
||||
|
||||
|
|
|
@ -372,6 +372,18 @@ export class GameData {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an `Unlockable` has been unlocked.
|
||||
* @param unlockable The Unlockable to check
|
||||
* @returns `true` if the player has unlocked this `Unlockable` or an override has enabled it
|
||||
*/
|
||||
public isUnlocked(unlockable: Unlockables): boolean {
|
||||
if (Overrides.ITEM_UNLOCK_OVERRIDE.includes(unlockable)) {
|
||||
return true;
|
||||
}
|
||||
return this.unlocks[unlockable];
|
||||
}
|
||||
|
||||
public saveSystem(): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
this.scene.ui.savingIcon.show();
|
||||
|
|
|
@ -22,15 +22,25 @@ export function applySessionDataPatches(data: SessionSaveData) {
|
|||
} else if (m.className === "PokemonResetNegativeStatStageModifier") {
|
||||
m.className = "ResetNegativeStatStageModifier";
|
||||
} else if (m.className === "TempBattleStatBoosterModifier") {
|
||||
m.className = "TempStatStageBoosterModifier";
|
||||
m.typeId = "TEMP_STAT_STAGE_BOOSTER";
|
||||
// Dire Hit no longer a part of the TempBattleStatBoosterModifierTypeGenerator
|
||||
if (m.typeId !== "DIRE_HIT") {
|
||||
m.className = "TempStatStageBoosterModifier";
|
||||
m.typeId = "TEMP_STAT_STAGE_BOOSTER";
|
||||
|
||||
// Migration from TempBattleStat to Stat
|
||||
const newStat = m.typePregenArgs[0] + 1;
|
||||
m.typePregenArgs[0] = newStat;
|
||||
// Migration from TempBattleStat to Stat
|
||||
const newStat = m.typePregenArgs[0] + 1;
|
||||
m.typePregenArgs[0] = newStat;
|
||||
|
||||
// From [ stat, battlesLeft ] to [ stat, maxBattles, battleCount ]
|
||||
m.args = [ newStat, 5, m.args[1] ];
|
||||
} else {
|
||||
m.className = "TempCritBoosterModifier";
|
||||
m.typePregenArgs = [];
|
||||
|
||||
// From [ stat, battlesLeft ] to [ maxBattles, battleCount ]
|
||||
m.args = [ 5, m.args[1] ];
|
||||
}
|
||||
|
||||
// From [ stat, battlesLeft ] to [ stat, maxBattles, battleCount ]
|
||||
m.args = [ newStat, 5, m.args[1] ];
|
||||
} else if (m.className === "DoubleBattleChanceBoosterModifier" && m.args.length === 1) {
|
||||
let maxBattles: number;
|
||||
switch (m.typeId) {
|
||||
|
@ -73,7 +83,7 @@ export function applySystemDataPatches(data: SystemSaveData) {
|
|||
case "1.0.3":
|
||||
case "1.0.4":
|
||||
// --- LEGACY PATCHES ---
|
||||
if (data.starterData) {
|
||||
if (data.starterData && data.dexData) {
|
||||
// Migrate ability starter data if empty for caught species
|
||||
Object.keys(data.starterData).forEach(sd => {
|
||||
if (data.dexData[sd]?.caughtAttr && (data.starterData[sd] && !data.starterData[sd].abilityAttr)) {
|
||||
|
@ -104,12 +114,14 @@ export function applySystemDataPatches(data: SystemSaveData) {
|
|||
// --- PATCHES ---
|
||||
|
||||
// Fix Starter Data
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
if (data.starterData[starterId]?.abilityAttr) {
|
||||
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
|
||||
}
|
||||
if (data.dexData[starterId]?.caughtAttr) {
|
||||
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
|
||||
if (data.starterData && data.dexData) {
|
||||
for (const starterId of defaultStarterSpecies) {
|
||||
if (data.starterData[starterId]?.abilityAttr) {
|
||||
data.starterData[starterId].abilityAttr |= AbilityAttr.ABILITY_1;
|
||||
}
|
||||
if (data.dexData[starterId]?.caughtAttr) {
|
||||
data.dexData[starterId].caughtAttr |= DexAttr.FEMALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import { MapModifier } from "#app/modifier/modifier";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import GameManager from "./utils/gameManager";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { getPartyLuckValue, itemPoolChecks } from "#app/modifier/modifier-type";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
import Overrides from "#app/overrides";
|
||||
|
||||
//const TIMEOUT = 20 * 1000;
|
||||
|
||||
describe("Daily Mode", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
|
@ -28,5 +38,97 @@ describe("Daily Mode", () => {
|
|||
expect(pkm.level).toBe(20);
|
||||
expect(pkm.moveset.length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(game.scene.getModifiers(MapModifier).length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
//*
|
||||
// Need to figure out how to properly start a battle
|
||||
// Need to fix eviolite - test keeps insisting it is not in loot table, even though Mini Black Hole (which is using the exact same condition) is
|
||||
describe("Shop modifications", async () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override
|
||||
.startingWave(9)
|
||||
.startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather
|
||||
.battleType("single")
|
||||
.shinyLevel(true)
|
||||
.startingLevel(100) // Avoid levelling up
|
||||
.enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents()
|
||||
.disableTrainerWaves()
|
||||
.moveset([Moves.KOWTOW_CLEAVE])
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
itemPoolChecks.set("EVIOLITE", false);
|
||||
itemPoolChecks.set("MINI_BLACK_HOLE", false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
itemPoolChecks.clear();
|
||||
});
|
||||
|
||||
it("should not have Eviolite and Mini Black Hole available in Classic if not unlocked", async () => {
|
||||
await game.classicMode.runToSummon();
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeFalsy();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeFalsy();
|
||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.doKillOpponents();
|
||||
await game.phaseInterceptor.to(BattleEndPhase);
|
||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||
expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler);
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeFalsy();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it("should have Eviolite and Mini Black Hole available in Daily", async () => {
|
||||
await game.dailyMode.runToSummon();
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeFalsy();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeFalsy();
|
||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.doKillOpponents();
|
||||
await game.phaseInterceptor.to(BattleEndPhase);
|
||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||
expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler);
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("EVIOLITE")).toBeTruthy();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeDefined();
|
||||
expect(itemPoolChecks.get("MINI_BLACK_HOLE")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("should apply luck in Classic Mode", async () => {
|
||||
await game.classicMode.runToSummon();
|
||||
const party = game.scene.getParty();
|
||||
expect(Overrides.SHINY_OVERRIDE).toBeTruthy();
|
||||
expect(party[0]).toBeDefined();
|
||||
expect(party[0].getLuck()).toBeGreaterThan(0);
|
||||
expect(getPartyLuckValue(party)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should not apply luck in Daily Run", async () => {
|
||||
await game.dailyMode.runToSummon();
|
||||
const party = game.scene.getParty();
|
||||
expect(party[0]).toBeDefined();
|
||||
expect(party[0].getLuck()).toBeGreaterThan(0);
|
||||
expect(getPartyLuckValue(party)).toBe(0);
|
||||
});
|
||||
});
|
||||
//*/
|
||||
|
|
|
@ -2,6 +2,7 @@ import { GameMode, GameModes, getGameMode } from "#app/game-mode";
|
|||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as Utils from "../utils";
|
||||
import GameManager from "./utils/gameManager";
|
||||
|
||||
describe("game-mode", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
@ -12,6 +13,7 @@ describe("game-mode", () => {
|
|||
});
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
vi.clearAllMocks();
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { Species } from "#app/enums/species";
|
||||
import { GameModes } from "#app/game-mode";
|
||||
import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { Button } from "#enums/buttons";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import { MockClock } from "#test/utils/mocks/mockClock";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { Biome } from "#app/enums/biome";
|
||||
|
||||
describe("Reload", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
|
@ -50,6 +54,13 @@ describe("Reload", () => {
|
|||
game.move.select(Moves.KOWTOW_CLEAVE);
|
||||
await game.phaseInterceptor.to("DamagePhase");
|
||||
await game.doKillOpponents();
|
||||
game.onNextPrompt("SelectBiomePhase", Mode.OPTION_SELECT, () => {
|
||||
(game.scene.time as MockClock).overrideDelay = null;
|
||||
const optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
game.scene.time.delayedCall(1010, () => optionSelectUiHandler.processInput(Button.ACTION));
|
||||
game.endPhase();
|
||||
(game.scene.time as MockClock).overrideDelay = 1;
|
||||
});
|
||||
await game.toNextWave();
|
||||
expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase");
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { BerryType } from "#app/enums/berry-type";
|
|||
import { Button } from "#app/enums/buttons";
|
||||
import { Moves } from "#app/enums/moves";
|
||||
import { Species } from "#app/enums/species";
|
||||
import { itemPoolChecks } from "#app/modifier/modifier-type";
|
||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||
|
@ -94,3 +95,47 @@ describe("UI - Transfer Items", () => {
|
|||
await game.phaseInterceptor.to(SelectModifierPhase);
|
||||
}, 20000);
|
||||
});
|
||||
|
||||
describe.skip("Backup Test", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.battleType("single")
|
||||
.startingLevel(100)
|
||||
.startingWave(1)
|
||||
.startingHeldItems([
|
||||
{ name: "BERRY", count: 1, type: BerryType.SITRUS },
|
||||
{ name: "BERRY", count: 2, type: BerryType.APICOT },
|
||||
{ name: "BERRY", count: 2, type: BerryType.LUM },
|
||||
])
|
||||
.moveset([Moves.DRAGON_CLAW])
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyMoveset([Moves.SPLASH]);
|
||||
itemPoolChecks.set("EVIOLITE", false);
|
||||
itemPoolChecks.set("MINI_BLACK_HOLE", false);
|
||||
});
|
||||
it("should run", async () => {
|
||||
await game.classicMode.startBattle([Species.RAYQUAZA, Species.RAYQUAZA, Species.RAYQUAZA]);
|
||||
|
||||
game.move.select(Moves.DRAGON_CLAW);
|
||||
|
||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||
expect(game.scene.ui.getHandler()).toBeInstanceOf(ModifierSelectUiHandler);
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.to(BattleEndPhase);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,8 @@ import { ModifierOverride } from "#app/modifier/modifier-type";
|
|||
import Overrides from "#app/overrides";
|
||||
import { vi } from "vitest";
|
||||
import { GameManagerHelper } from "./gameManagerHelper";
|
||||
import { Unlockables } from "#app/system/unlockables";
|
||||
import { Variant } from "#app/data/variant";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
|
||||
|
@ -300,6 +302,17 @@ export class OverridesHelper extends GameManagerHelper {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives the player access to an Unlockable.
|
||||
* @param unlockable The Unlockable to enable.
|
||||
* @returns this
|
||||
*/
|
||||
enableUnlockable(unlockable: Unlockables[]) {
|
||||
vi.spyOn(Overrides, "ITEM_UNLOCK_OVERRIDE", "get").mockReturnValue(unlockable);
|
||||
this.log("Temporarily unlocked the following content: ", unlockable);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the items rolled at the end of a battle
|
||||
* @param items the items to be rolled
|
||||
|
@ -311,6 +324,25 @@ export class OverridesHelper extends GameManagerHelper {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override player shininess
|
||||
* @param shininess Whether the player's Pokemon should be shiny.
|
||||
*/
|
||||
shinyLevel(shininess: boolean): this {
|
||||
vi.spyOn(Overrides, "SHINY_OVERRIDE", "get").mockReturnValue(shininess);
|
||||
this.log(`Set player Pokemon as ${shininess ? "" : "not "}shiny!`);
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Override player shiny variant
|
||||
* @param variant The player's shiny variant.
|
||||
*/
|
||||
variantLevel(variant: Variant): this {
|
||||
vi.spyOn(Overrides, "VARIANT_OVERRIDE", "get").mockReturnValue(variant);
|
||||
this.log(`Set player Pokemon's shiny variant to ${variant}!`);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the enemy (Pokemon) to have the given amount of health segments
|
||||
* @param healthSegments the number of segments to give
|
||||
|
|
|
@ -43,6 +43,7 @@ import { UnavailablePhase } from "#app/phases/unavailable-phase";
|
|||
import { VictoryPhase } from "#app/phases/victory-phase";
|
||||
import { PartyHealPhase } from "#app/phases/party-heal-phase";
|
||||
import UI, { Mode } from "#app/ui/ui";
|
||||
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
|
||||
import {
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterOptionSelectedPhase,
|
||||
|
@ -122,6 +123,7 @@ export default class PhaseInterceptor {
|
|||
[EndEvolutionPhase, this.startPhase],
|
||||
[LevelCapPhase, this.startPhase],
|
||||
[AttemptRunPhase, this.startPhase],
|
||||
[SelectBiomePhase, this.startPhase],
|
||||
[MysteryEncounterPhase, this.startPhase],
|
||||
[MysteryEncounterOptionSelectedPhase, this.startPhase],
|
||||
[MysteryEncounterBattlePhase, this.startPhase],
|
||||
|
@ -346,7 +348,8 @@ export default class PhaseInterceptor {
|
|||
console.log("setMode", `${Mode[mode]} (=${mode})`, args);
|
||||
const ret = this.originalSetMode.apply(instance, [mode, ...args]);
|
||||
if (!this.phases[currentPhase.constructor.name]) {
|
||||
throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptor PHASES list`);
|
||||
throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptor PHASES list --- Add it to PHASES inside of /test/utils/phaseInterceptor.ts`);
|
||||
|
||||
}
|
||||
if (this.phases[currentPhase.constructor.name].endBySetMode) {
|
||||
this.inProgress?.callback();
|
||||
|
|
|
@ -74,11 +74,11 @@ const tutorialHandlers = {
|
|||
* @returns a promise with result `true` if the tutorial was run and finished, `false` otherwise
|
||||
*/
|
||||
export async function handleTutorial(scene: BattleScene, tutorial: Tutorial): Promise<boolean> {
|
||||
if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP) {
|
||||
if (!scene.enableTutorials && !Overrides.BYPASS_TUTORIAL_SKIP_OVERRIDE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP) {
|
||||
if (scene.gameData.getTutorialFlags()[tutorial] && !Overrides.BYPASS_TUTORIAL_SKIP_OVERRIDE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -344,6 +344,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
|||
super.clear();
|
||||
this.config = null;
|
||||
this.optionSelectContainer.setVisible(false);
|
||||
this.scrollCursor = 0;
|
||||
this.eraseCursor();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import BattleScene from "../battle-scene";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { Button } from "#enums/buttons";
|
||||
import i18next from "i18next";
|
||||
import { Achv, achvs, getAchievementDescription } from "../system/achv";
|
||||
import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "../system/voucher";
|
||||
import MessageUiHandler from "./message-ui-handler";
|
||||
import { addTextObject, TextStyle } from "./text";
|
||||
import { Mode } from "./ui";
|
||||
import { addWindow } from "./ui-theme";
|
||||
import { Achv, achvs, getAchievementDescription } from "#app/system/achv";
|
||||
import { Voucher, getVoucherTypeIcon, getVoucherTypeName, vouchers } from "#app/system/voucher";
|
||||
import MessageUiHandler from "#app/ui/message-ui-handler";
|
||||
import { addTextObject, TextStyle } from "#app/ui/text";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { addWindow } from "#app/ui/ui-theme";
|
||||
import { ScrollBar } from "#app/ui/scroll-bar";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
|
||||
enum Page {
|
||||
|
@ -49,6 +50,7 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
private vouchersTotal: number;
|
||||
private currentTotal: number;
|
||||
|
||||
private scrollBar: ScrollBar;
|
||||
private scrollCursor: number;
|
||||
private cursorObj: Phaser.GameObjects.NineSlice | null;
|
||||
private currentPage: Page;
|
||||
|
@ -91,7 +93,10 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
this.iconsBg = addWindow(this.scene, 0, this.headerBg.height, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - this.headerBg.height - 68);
|
||||
this.iconsBg.setOrigin(0, 0);
|
||||
|
||||
this.iconsContainer = this.scene.add.container(6, this.headerBg.height + 6);
|
||||
const yOffset = 6;
|
||||
this.scrollBar = new ScrollBar(this.scene, this.iconsBg.width - 9, this.iconsBg.y + yOffset, 4, this.iconsBg.height - yOffset * 2, this.ROWS);
|
||||
|
||||
this.iconsContainer = this.scene.add.container(5, this.headerBg.height + 8);
|
||||
|
||||
this.icons = [];
|
||||
|
||||
|
@ -148,6 +153,7 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
this.mainContainer.add(this.headerText);
|
||||
this.mainContainer.add(this.headerActionText);
|
||||
this.mainContainer.add(this.iconsBg);
|
||||
this.mainContainer.add(this.scrollBar);
|
||||
this.mainContainer.add(this.iconsContainer);
|
||||
this.mainContainer.add(titleBg);
|
||||
this.mainContainer.add(this.titleText);
|
||||
|
@ -162,6 +168,7 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
|
||||
this.currentPage = Page.ACHIEVEMENTS;
|
||||
this.setCursor(0);
|
||||
this.setScrollCursor(0);
|
||||
|
||||
this.mainContainer.setVisible(false);
|
||||
}
|
||||
|
@ -175,6 +182,8 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
this.mainContainer.setVisible(true);
|
||||
this.setCursor(0);
|
||||
this.setScrollCursor(0);
|
||||
this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS));
|
||||
this.scrollBar.setScrollCursor(0);
|
||||
|
||||
this.getUi().moveTo(this.mainContainer, this.getUi().length - 1);
|
||||
|
||||
|
@ -224,6 +233,8 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
this.updateAchvIcons();
|
||||
}
|
||||
this.setCursor(0, true);
|
||||
this.scrollBar.setTotalRows(Math.ceil(this.currentTotal / this.COLS));
|
||||
this.scrollBar.setScrollCursor(0);
|
||||
this.mainContainer.update();
|
||||
}
|
||||
if (button === Button.CANCEL) {
|
||||
|
@ -237,32 +248,44 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
if (this.cursor < this.COLS) {
|
||||
if (this.scrollCursor) {
|
||||
success = this.setScrollCursor(this.scrollCursor - 1);
|
||||
} else {
|
||||
// Wrap around to the last row
|
||||
success = this.setScrollCursor(Math.ceil(this.currentTotal / this.COLS) - this.ROWS);
|
||||
let newCursorIndex = this.cursor + (this.ROWS - 1) * this.COLS;
|
||||
if (newCursorIndex > this.currentTotal - this.scrollCursor * this.COLS -1) {
|
||||
newCursorIndex -= this.COLS;
|
||||
}
|
||||
success = success && this.setCursor(newCursorIndex);
|
||||
}
|
||||
} else {
|
||||
success = this.setCursor(this.cursor - this.COLS);
|
||||
}
|
||||
break;
|
||||
case Button.DOWN:
|
||||
const canMoveDown = (this.cursor + itemOffset) + this.COLS < this.currentTotal;
|
||||
const canMoveDown = itemOffset + 1 < this.currentTotal;
|
||||
if (rowIndex >= this.ROWS - 1) {
|
||||
if (this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS && canMoveDown) {
|
||||
// scroll down one row
|
||||
success = this.setScrollCursor(this.scrollCursor + 1);
|
||||
} else {
|
||||
// wrap back to the first row
|
||||
success = this.setScrollCursor(0) && this.setCursor(this.cursor % this.COLS);
|
||||
}
|
||||
} else if (canMoveDown) {
|
||||
success = this.setCursor(this.cursor + this.COLS);
|
||||
success = this.setCursor(Math.min(this.cursor + this.COLS, this.currentTotal - itemOffset - 1));
|
||||
}
|
||||
break;
|
||||
case Button.LEFT:
|
||||
if (!this.cursor && this.scrollCursor) {
|
||||
success = this.setScrollCursor(this.scrollCursor - 1) && this.setCursor(this.cursor + (this.COLS - 1));
|
||||
} else if (this.cursor) {
|
||||
if (this.cursor % this.COLS === 0) {
|
||||
success = this.setCursor(Math.min(this.cursor + this.COLS - 1, this.currentTotal - itemOffset - 1));
|
||||
} else {
|
||||
success = this.setCursor(this.cursor - 1);
|
||||
}
|
||||
break;
|
||||
case Button.RIGHT:
|
||||
if (this.cursor + 1 === this.ROWS * this.COLS && this.scrollCursor < Math.ceil(this.currentTotal / this.COLS) - this.ROWS) {
|
||||
success = this.setScrollCursor(this.scrollCursor + 1) && this.setCursor(this.cursor - (this.COLS - 1));
|
||||
} else if (this.cursor + itemOffset < this.currentTotal - 1) {
|
||||
if ((this.cursor + 1) % this.COLS === 0 || (this.cursor + itemOffset) === (this.currentTotal - 1)) {
|
||||
success = this.setCursor(this.cursor - this.cursor % this.COLS);
|
||||
} else {
|
||||
success = this.setCursor(this.cursor + 1);
|
||||
}
|
||||
break;
|
||||
|
@ -315,15 +338,22 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
}
|
||||
|
||||
this.scrollCursor = scrollCursor;
|
||||
this.scrollBar.setScrollCursor(this.scrollCursor);
|
||||
|
||||
// Cursor cannot go farther than the last element in the list
|
||||
const maxCursor = Math.min(this.cursor, this.currentTotal - this.scrollCursor * this.COLS - 1);
|
||||
if (maxCursor !== this.cursor) {
|
||||
this.setCursor(maxCursor);
|
||||
}
|
||||
|
||||
switch (this.currentPage) {
|
||||
case Page.ACHIEVEMENTS:
|
||||
this.updateAchvIcons();
|
||||
this.showAchv(achvs[Object.keys(achvs)[Math.min(this.cursor + this.scrollCursor * this.COLS, Object.values(achvs).length - 1)]]);
|
||||
this.showAchv(achvs[Object.keys(achvs)[this.cursor + this.scrollCursor * this.COLS]]);
|
||||
break;
|
||||
case Page.VOUCHERS:
|
||||
this.updateVoucherIcons();
|
||||
this.showVoucher(vouchers[Object.keys(vouchers)[Math.min(this.cursor + this.scrollCursor * this.COLS, Object.values(vouchers).length - 1)]]);
|
||||
this.showVoucher(vouchers[Object.keys(vouchers)[this.cursor + this.scrollCursor * this.COLS]]);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
|
@ -411,6 +441,7 @@ export default class AchvsUiHandler extends MessageUiHandler {
|
|||
super.clear();
|
||||
this.currentPage = Page.ACHIEVEMENTS;
|
||||
this.mainContainer.setVisible(false);
|
||||
this.setScrollCursor(0);
|
||||
this.eraseCursor();
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
|||
|
||||
/* Multiplies the appearance duration by the speed parameter so that it is always constant, and avoids "flashbangs" at game speed x5 */
|
||||
this.scene.showShopOverlay(750 * this.scene.gameSpeed);
|
||||
this.scene.updateAndShowText(750);
|
||||
this.scene.updateAndShowText(750, this.scene.gameMode.isDaily);
|
||||
this.scene.updateBiomeWaveText();
|
||||
this.scene.updateMoneyText();
|
||||
|
||||
|
|
|
@ -1,36 +1,65 @@
|
|||
/**
|
||||
* A vertical scrollbar element that resizes dynamically based on the current scrolling
|
||||
* and number of elements that can be shown on screen
|
||||
*/
|
||||
export class ScrollBar extends Phaser.GameObjects.Container {
|
||||
private bg: Phaser.GameObjects.Image;
|
||||
private bg: Phaser.GameObjects.NineSlice;
|
||||
private handleBody: Phaser.GameObjects.Rectangle;
|
||||
private handleBottom: Phaser.GameObjects.Image;
|
||||
private pages: number;
|
||||
private page: number;
|
||||
private handleBottom: Phaser.GameObjects.NineSlice;
|
||||
private currentRow: number;
|
||||
private totalRows: number;
|
||||
private maxRows: number;
|
||||
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, pages: number) {
|
||||
/**
|
||||
* @param scene the current scene
|
||||
* @param x the scrollbar's x position (origin: top left)
|
||||
* @param y the scrollbar's y position (origin: top left)
|
||||
* @param width the scrollbar's width
|
||||
* @param height the scrollbar's height
|
||||
* @param maxRows the maximum number of rows that can be shown at once
|
||||
*/
|
||||
constructor(scene: Phaser.Scene, x: number, y: number, width: number, height: number, maxRows: number) {
|
||||
super(scene, x, y);
|
||||
|
||||
this.bg = scene.add.image(0, 0, "scroll_bar");
|
||||
this.maxRows = maxRows;
|
||||
|
||||
const borderSize = 2;
|
||||
width = Math.max(width, 4);
|
||||
|
||||
this.bg = scene.add.nineslice(0, 0, "scroll_bar", undefined, width, height, borderSize, borderSize, borderSize, borderSize);
|
||||
this.bg.setOrigin(0, 0);
|
||||
this.add(this.bg);
|
||||
|
||||
this.handleBody = scene.add.rectangle(1, 1, 3, 4, 0xaaaaaa);
|
||||
this.handleBody = scene.add.rectangle(1, 1, width - 2, 4, 0xaaaaaa);
|
||||
this.handleBody.setOrigin(0, 0);
|
||||
this.add(this.handleBody);
|
||||
|
||||
this.handleBottom = scene.add.image(1, 1, "scroll_bar_handle");
|
||||
this.handleBottom = scene.add.nineslice(1, 1, "scroll_bar_handle", undefined, width - 2, 2, 2, 0, 0, 0);
|
||||
this.handleBottom.setOrigin(0, 0);
|
||||
this.add(this.handleBottom);
|
||||
}
|
||||
|
||||
setPage(page: number): void {
|
||||
this.page = page;
|
||||
this.handleBody.y = 1 + (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) / this.pages * page;
|
||||
/**
|
||||
* Set the current row that is displayed
|
||||
* Moves the bar handle up or down accordingly
|
||||
* @param scrollCursor how many times the view was scrolled down
|
||||
*/
|
||||
setScrollCursor(scrollCursor: number): void {
|
||||
this.currentRow = scrollCursor;
|
||||
this.handleBody.y = 1 + (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) / this.totalRows * this.currentRow;
|
||||
this.handleBottom.y = this.handleBody.y + this.handleBody.displayHeight;
|
||||
}
|
||||
|
||||
setPages(pages: number): void {
|
||||
this.pages = pages;
|
||||
this.handleBody.height = (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) * 9 / this.pages;
|
||||
/**
|
||||
* Set the total number of rows to display
|
||||
* If it's smaller than the maximum number of rows on screen the bar will get hidden
|
||||
* Otherwise the scrollbar handle gets resized based on the ratio to the maximum number of rows
|
||||
* @param rows how many rows of data there are in total
|
||||
*/
|
||||
setTotalRows(rows: number): void {
|
||||
this.totalRows = rows;
|
||||
this.handleBody.height = (this.bg.displayHeight - 1 - this.handleBottom.displayHeight) * this.maxRows / this.totalRows;
|
||||
|
||||
this.setVisible(this.pages > 9);
|
||||
this.setVisible(this.totalRows > this.maxRows);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import UiHandler from "../ui-handler";
|
||||
import BattleScene from "../../battle-scene";
|
||||
import {Mode} from "../ui";
|
||||
import {InterfaceConfig} from "../../inputs-controller";
|
||||
import {addWindow} from "../ui-theme";
|
||||
import {addTextObject, TextStyle} from "../text";
|
||||
import {getIconWithSettingName} from "#app/configs/inputs/configHandler";
|
||||
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
|
||||
import UiHandler from "#app/ui/ui-handler";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { InterfaceConfig } from "#app/inputs-controller";
|
||||
import { addWindow } from "#app/ui/ui-theme";
|
||||
import { addTextObject, TextStyle } from "#app/ui/text";
|
||||
import { ScrollBar } from "#app/ui/scroll-bar";
|
||||
import { getIconWithSettingName } from "#app/configs/inputs/configHandler";
|
||||
import NavigationMenu, { NavigationManager } from "#app/ui/settings/navigationMenu";
|
||||
import { Device } from "#enums/devices";
|
||||
import { Button } from "#enums/buttons";
|
||||
import i18next from "i18next";
|
||||
|
@ -19,7 +20,7 @@ export interface LayoutConfig {
|
|||
inputsIcons: InputsIcons;
|
||||
settingLabels: Phaser.GameObjects.Text[];
|
||||
optionValueLabels: Phaser.GameObjects.Text[][];
|
||||
optionCursors: integer[];
|
||||
optionCursors: number[];
|
||||
keys: string[];
|
||||
bindingSettings: Array<String>;
|
||||
}
|
||||
|
@ -31,8 +32,9 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
protected optionsContainer: Phaser.GameObjects.Container;
|
||||
protected navigationContainer: NavigationMenu;
|
||||
|
||||
protected scrollCursor: integer;
|
||||
protected optionCursors: integer[];
|
||||
protected scrollBar: ScrollBar;
|
||||
protected scrollCursor: number;
|
||||
protected optionCursors: number[];
|
||||
protected cursorObj: Phaser.GameObjects.NineSlice | null;
|
||||
|
||||
protected optionsBg: Phaser.GameObjects.NineSlice;
|
||||
|
@ -65,7 +67,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
protected device: Device;
|
||||
|
||||
abstract saveSettingToLocalStorage(setting, cursor): void;
|
||||
abstract setSetting(scene: BattleScene, setting, value: integer): boolean;
|
||||
abstract setSetting(scene: BattleScene, setting, value: number): boolean;
|
||||
|
||||
/**
|
||||
* Constructor for the AbstractSettingsUiHandler.
|
||||
|
@ -241,7 +243,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
|
||||
// Calculate the total available space for placing option labels next to their setting label
|
||||
// We reserve space for the setting label and then distribute the remaining space evenly
|
||||
const totalSpace = (300 - labelWidth) - totalWidth / 6;
|
||||
const totalSpace = (297 - labelWidth) - totalWidth / 6;
|
||||
// Calculate the spacing between options based on the available space divided by the number of gaps between labels
|
||||
const optionSpacing = Math.floor(totalSpace / (optionValueLabels[s].length - 1));
|
||||
|
||||
|
@ -269,6 +271,11 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
// Add the options container to the overall settings container to be displayed in the UI.
|
||||
this.settingsContainer.add(optionsContainer);
|
||||
}
|
||||
|
||||
// Add vertical scrollbar
|
||||
this.scrollBar = new ScrollBar(this.scene, this.optionsBg.width - 9, this.optionsBg.y + 5, 4, this.optionsBg.height - 11, this.rowsToDisplay);
|
||||
this.settingsContainer.add(this.scrollBar);
|
||||
|
||||
// Add the settings container to the UI.
|
||||
ui.add(this.settingsContainer);
|
||||
|
||||
|
@ -413,6 +420,8 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
this.optionCursors = layout.optionCursors;
|
||||
this.inputsIcons = layout.inputsIcons;
|
||||
this.bindingSettings = layout.bindingSettings;
|
||||
this.scrollBar.setTotalRows(layout.settingLabels.length);
|
||||
this.scrollBar.setScrollCursor(0);
|
||||
|
||||
// Return true indicating the layout was successfully applied.
|
||||
return true;
|
||||
|
@ -538,7 +547,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
* @param cursor - The cursor position to set.
|
||||
* @returns `true` if the cursor was set successfully.
|
||||
*/
|
||||
setCursor(cursor: integer): boolean {
|
||||
setCursor(cursor: number): boolean {
|
||||
const ret = super.setCursor(cursor);
|
||||
// If the optionsContainer is not initialized, return the result from the parent class directly.
|
||||
if (!this.optionsContainer) {
|
||||
|
@ -547,7 +556,8 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
|
||||
// Check if the cursor object exists, if not, create it.
|
||||
if (!this.cursorObj) {
|
||||
this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1);
|
||||
const cursorWidth = (this.scene.game.canvas.width / 6) - (this.scrollBar.visible? 16 : 10);
|
||||
this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1);
|
||||
this.cursorObj.setOrigin(0, 0); // Set the origin to the top-left corner.
|
||||
this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container.
|
||||
}
|
||||
|
@ -564,7 +574,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
* @param scrollCursor - The scroll cursor position to set.
|
||||
* @returns `true` if the scroll cursor was set successfully.
|
||||
*/
|
||||
setScrollCursor(scrollCursor: integer): boolean {
|
||||
setScrollCursor(scrollCursor: number): boolean {
|
||||
// Check if the new scroll position is the same as the current one; if so, do not update.
|
||||
if (scrollCursor === this.scrollCursor) {
|
||||
return false;
|
||||
|
@ -572,6 +582,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
|
||||
// Update the internal scroll cursor state
|
||||
this.scrollCursor = scrollCursor;
|
||||
this.scrollBar.setScrollCursor(this.scrollCursor);
|
||||
|
||||
// Apply the new scroll position to the settings UI.
|
||||
this.updateSettingsScroll();
|
||||
|
@ -590,7 +601,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler
|
|||
* @param save - Whether to save the setting to local storage.
|
||||
* @returns `true` if the option cursor was set successfully.
|
||||
*/
|
||||
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
|
||||
setOptionCursor(settingIndex: number, cursor: number, save?: boolean): boolean {
|
||||
// Retrieve the specific setting using the settingIndex from the settingDevice enumeration.
|
||||
const setting = this.setting[Object.keys(this.setting)[settingIndex]];
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import BattleScene from "../../battle-scene";
|
||||
import { hasTouchscreen, isMobile } from "../../touch-controls";
|
||||
import { TextStyle, addTextObject } from "../text";
|
||||
import { Mode } from "../ui";
|
||||
import UiHandler from "../ui-handler";
|
||||
import { addWindow } from "../ui-theme";
|
||||
import {Button} from "#enums/buttons";
|
||||
import {InputsIcons} from "#app/ui/settings/abstract-control-settings-ui-handler";
|
||||
import NavigationMenu, {NavigationManager} from "#app/ui/settings/navigationMenu";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import { hasTouchscreen, isMobile } from "#app/touch-controls";
|
||||
import { TextStyle, addTextObject } from "#app/ui/text";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import UiHandler from "#app/ui/ui-handler";
|
||||
import { addWindow } from "#app/ui/ui-theme";
|
||||
import { ScrollBar } from "#app/ui/scroll-bar";
|
||||
import { Button } from "#enums/buttons";
|
||||
import { InputsIcons } from "#app/ui/settings/abstract-control-settings-ui-handler";
|
||||
import NavigationMenu, { NavigationManager } from "#app/ui/settings/navigationMenu";
|
||||
import { Setting, SettingKeys, SettingType } from "#app/system/settings/settings";
|
||||
import i18next from "i18next";
|
||||
|
||||
|
@ -19,11 +20,12 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
private optionsContainer: Phaser.GameObjects.Container;
|
||||
private navigationContainer: NavigationMenu;
|
||||
|
||||
private scrollCursor: integer;
|
||||
private scrollCursor: number;
|
||||
private scrollBar: ScrollBar;
|
||||
|
||||
private optionsBg: Phaser.GameObjects.NineSlice;
|
||||
|
||||
private optionCursors: integer[];
|
||||
private optionCursors: number[];
|
||||
|
||||
private settingLabels: Phaser.GameObjects.Text[];
|
||||
private optionValueLabels: Phaser.GameObjects.Text[][];
|
||||
|
@ -117,7 +119,7 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
|
||||
const labelWidth = Math.max(78, this.settingLabels[s].displayWidth + 8);
|
||||
|
||||
const totalSpace = (300 - labelWidth) - totalWidth / 6;
|
||||
const totalSpace = (297 - labelWidth) - totalWidth / 6;
|
||||
const optionSpacing = Math.floor(totalSpace / (this.optionValueLabels[s].length - 1));
|
||||
|
||||
let xOffset = 0;
|
||||
|
@ -130,7 +132,11 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
|
||||
this.optionCursors = this.settings.map(setting => setting.default);
|
||||
|
||||
this.scrollBar = new ScrollBar(this.scene, this.optionsBg.width - 9, this.optionsBg.y + 5, 4, this.optionsBg.height - 11, this.rowsToDisplay);
|
||||
this.scrollBar.setTotalRows(this.settings.length);
|
||||
|
||||
this.settingsContainer.add(this.optionsBg);
|
||||
this.settingsContainer.add(this.scrollBar);
|
||||
this.settingsContainer.add(this.navigationContainer);
|
||||
this.settingsContainer.add(actionsBg);
|
||||
this.settingsContainer.add(this.optionsContainer);
|
||||
|
@ -186,6 +192,7 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
|
||||
this.settingsContainer.setVisible(true);
|
||||
this.setCursor(0);
|
||||
this.setScrollCursor(0);
|
||||
|
||||
this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
|
||||
|
||||
|
@ -301,11 +308,12 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
* @param cursor - The cursor position to set.
|
||||
* @returns `true` if the cursor was set successfully.
|
||||
*/
|
||||
setCursor(cursor: integer): boolean {
|
||||
setCursor(cursor: number): boolean {
|
||||
const ret = super.setCursor(cursor);
|
||||
|
||||
if (!this.cursorObj) {
|
||||
this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1);
|
||||
const cursorWidth = (this.scene.game.canvas.width / 6) - (this.scrollBar.visible? 16 : 10);
|
||||
this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, cursorWidth, 16, 1, 1, 1, 1);
|
||||
this.cursorObj.setOrigin(0, 0);
|
||||
this.optionsContainer.add(this.cursorObj);
|
||||
}
|
||||
|
@ -323,7 +331,7 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
* @param save - Whether to save the setting to local storage.
|
||||
* @returns `true` if the option cursor was set successfully.
|
||||
*/
|
||||
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
|
||||
setOptionCursor(settingIndex: number, cursor: number, save?: boolean): boolean {
|
||||
const setting = this.settings[settingIndex];
|
||||
|
||||
if (setting.key === SettingKeys.Touch_Controls && cursor && hasTouchscreen() && isMobile()) {
|
||||
|
@ -359,12 +367,13 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
* @param scrollCursor - The scroll cursor position to set.
|
||||
* @returns `true` if the scroll cursor was set successfully.
|
||||
*/
|
||||
setScrollCursor(scrollCursor: integer): boolean {
|
||||
setScrollCursor(scrollCursor: number): boolean {
|
||||
if (scrollCursor === this.scrollCursor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.scrollCursor = scrollCursor;
|
||||
this.scrollBar.setScrollCursor(this.scrollCursor);
|
||||
|
||||
this.updateSettingsScroll();
|
||||
|
||||
|
@ -394,6 +403,7 @@ export default class AbstractSettingsUiHandler extends UiHandler {
|
|||
clear() {
|
||||
super.clear();
|
||||
this.settingsContainer.setVisible(false);
|
||||
this.setScrollCursor(0);
|
||||
this.eraseCursor();
|
||||
this.getUi().bgmBar.toggleBgmBar(this.scene.showBgmBar);
|
||||
if (this.reloadRequired) {
|
||||
|
|
|
@ -627,7 +627,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
|
||||
const starterBoxContainer = this.scene.add.container(speciesContainerX + 6, 9); //115
|
||||
|
||||
this.starterSelectScrollBar = new ScrollBar(this.scene, 161, 12, 0);
|
||||
this.starterSelectScrollBar = new ScrollBar(this.scene, 161, 12, 5, starterContainerWindow.height - 6, 9);
|
||||
|
||||
starterBoxContainer.add(this.starterSelectScrollBar);
|
||||
|
||||
|
@ -2540,8 +2540,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
}
|
||||
});
|
||||
|
||||
this.starterSelectScrollBar.setPages(Math.max(Math.ceil(this.filteredStarterContainers.length / 9), 1));
|
||||
this.starterSelectScrollBar.setPage(0);
|
||||
this.starterSelectScrollBar.setTotalRows(Math.max(Math.ceil(this.filteredStarterContainers.length / 9), 1));
|
||||
this.starterSelectScrollBar.setScrollCursor(0);
|
||||
|
||||
// sort
|
||||
const sort = this.filterBar.getVals(DropDownColumn.SORT)[0];
|
||||
|
@ -2576,7 +2576,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
const onScreenFirstIndex = this.scrollCursor * maxColumns;
|
||||
const onScreenLastIndex = Math.min(this.filteredStarterContainers.length - 1, onScreenFirstIndex + maxRows * maxColumns -1);
|
||||
|
||||
this.starterSelectScrollBar.setPage(this.scrollCursor);
|
||||
this.starterSelectScrollBar.setScrollCursor(this.scrollCursor);
|
||||
|
||||
let pokerusCursorIndex = 0;
|
||||
this.filteredStarterContainers.forEach((container, i) => {
|
||||
|
|
Loading…
Reference in New Issue