Compare commits
No commits in common. "cacc75d5f52bc1c6dd52d9c4e20415f3f7b7a5dc" and "990e9ba658b3bfc802248320ab6c7f53148f9e24" have entirely different histories.
cacc75d5f5
...
990e9ba658
2
.github/workflows/eslint.yml
vendored
@ -11,8 +11,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main # Trigger on pull request events targeting the main branch
|
- main # Trigger on pull request events targeting the main branch
|
||||||
- beta # Trigger on pull request events targeting the beta branch
|
- beta # Trigger on pull request events targeting the beta branch
|
||||||
merge_group:
|
|
||||||
types: [checks_requested]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-linters: # Define a job named "run-linters"
|
run-linters: # Define a job named "run-linters"
|
||||||
|
2
.github/workflows/github-pages.yml
vendored
@ -8,8 +8,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- beta
|
- beta
|
||||||
merge_group:
|
|
||||||
types: [checks_requested]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pages:
|
pages:
|
||||||
|
2
.github/workflows/tests.yml
vendored
@ -11,8 +11,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main # Trigger on pull request events targeting the main branch
|
- main # Trigger on pull request events targeting the main branch
|
||||||
- beta # Trigger on pull request events targeting the beta branch
|
- beta # Trigger on pull request events targeting the beta branch
|
||||||
merge_group:
|
|
||||||
types: [checks_requested]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-tests: # Define a job named "run-tests"
|
run-tests: # Define a job named "run-tests"
|
||||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 179 B |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 179 B |
@ -130,7 +130,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
public gameSpeed: integer = 1;
|
public gameSpeed: integer = 1;
|
||||||
public damageNumbersMode: integer = 0;
|
public damageNumbersMode: integer = 0;
|
||||||
public reroll: boolean = false;
|
public reroll: boolean = false;
|
||||||
public shopCursorTarget: number = ShopCursorTarget.REWARDS;
|
public shopCursorTarget: number = ShopCursorTarget.CHECK_TEAM;
|
||||||
public showMovesetFlyout: boolean = true;
|
public showMovesetFlyout: boolean = true;
|
||||||
public showArenaFlyout: boolean = true;
|
public showArenaFlyout: boolean = true;
|
||||||
public showTimeOfDayWidget: boolean = true;
|
public showTimeOfDayWidget: boolean = true;
|
||||||
@ -2753,35 +2753,6 @@ export default class BattleScene extends SceneBase {
|
|||||||
(window as any).gameInfo = gameInfo;
|
(window as any).gameInfo = gameInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This function retrieves the sprite and audio keys for active Pokemon.
|
|
||||||
* Active Pokemon include both enemy and player Pokemon of the current wave.
|
|
||||||
* Note: Questions on garbage collection go to @frutescens
|
|
||||||
* @returns a string array of active sprite and audio keys that should not be deleted
|
|
||||||
*/
|
|
||||||
getActiveKeys(): string[] {
|
|
||||||
const keys: string[] = [];
|
|
||||||
const playerParty = this.getParty();
|
|
||||||
playerParty.forEach(p => {
|
|
||||||
keys.push("pkmn__" + p.species.getSpriteId(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant));
|
|
||||||
keys.push("pkmn__" + p.species.getSpriteId(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant, true));
|
|
||||||
keys.push("cry/" + p.species.getCryKey(p.species.formIndex));
|
|
||||||
if (p.fusionSpecies && p.getSpeciesForm() !== p.getFusionSpeciesForm()) {
|
|
||||||
keys.push("cry/"+p.getFusionSpeciesForm().getCryKey(p.fusionSpecies.formIndex));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon
|
|
||||||
const enemyParty = this.getEnemyParty();
|
|
||||||
enemyParty.forEach(p => {
|
|
||||||
keys.push(p.species.getSpriteKey(p.gender === Gender.FEMALE, p.species.formIndex, p.shiny, p.variant));
|
|
||||||
keys.push("cry/" + p.species.getCryKey(p.species.formIndex));
|
|
||||||
if (p.fusionSpecies && p.getSpeciesForm() !== p.getFusionSpeciesForm()) {
|
|
||||||
keys.push("cry/"+p.getFusionSpeciesForm().getCryKey(p.fusionSpecies.formIndex));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialized the 2nd phase of the final boss (e.g. form-change for Eternatus)
|
* Initialized the 2nd phase of the final boss (e.g. form-change for Eternatus)
|
||||||
* @param pokemon The (enemy) pokemon
|
* @param pokemon The (enemy) pokemon
|
||||||
|
@ -31,7 +31,7 @@ export enum BattlerIndex {
|
|||||||
|
|
||||||
export interface TurnCommand {
|
export interface TurnCommand {
|
||||||
command: Command;
|
command: Command;
|
||||||
cursor?: number;
|
cursor?: integer;
|
||||||
move?: QueuedMove;
|
move?: QueuedMove;
|
||||||
targets?: BattlerIndex[];
|
targets?: BattlerIndex[];
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
@ -39,40 +39,38 @@ export interface TurnCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TurnCommands {
|
interface TurnCommands {
|
||||||
[key: number]: TurnCommand | null
|
[key: integer]: TurnCommand | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Battle {
|
export default class Battle {
|
||||||
protected gameMode: GameMode;
|
protected gameMode: GameMode;
|
||||||
public waveIndex: number;
|
public waveIndex: integer;
|
||||||
public battleType: BattleType;
|
public battleType: BattleType;
|
||||||
public battleSpec: BattleSpec;
|
public battleSpec: BattleSpec;
|
||||||
public trainer: Trainer | null;
|
public trainer: Trainer | null;
|
||||||
public enemyLevels: number[] | undefined;
|
public enemyLevels: integer[] | undefined;
|
||||||
public enemyParty: EnemyPokemon[] = [];
|
public enemyParty: EnemyPokemon[];
|
||||||
public seenEnemyPartyMemberIds: Set<number> = new Set<number>();
|
public seenEnemyPartyMemberIds: Set<integer>;
|
||||||
public double: boolean;
|
public double: boolean;
|
||||||
public started: boolean = false;
|
public started: boolean;
|
||||||
public enemySwitchCounter: number = 0;
|
public enemySwitchCounter: integer;
|
||||||
public turn: number = 0;
|
public turn: integer;
|
||||||
public turnCommands: TurnCommands;
|
public turnCommands: TurnCommands;
|
||||||
public playerParticipantIds: Set<number> = new Set<number>();
|
public playerParticipantIds: Set<integer>;
|
||||||
public battleScore: number = 0;
|
public battleScore: integer;
|
||||||
public postBattleLoot: PokemonHeldItemModifier[] = [];
|
public postBattleLoot: PokemonHeldItemModifier[];
|
||||||
public escapeAttempts: number = 0;
|
public escapeAttempts: integer;
|
||||||
public lastMove: Moves;
|
public lastMove: Moves;
|
||||||
public battleSeed: string = Utils.randomString(16, true);
|
public battleSeed: string;
|
||||||
private battleSeedState: string | null = null;
|
private battleSeedState: string | null;
|
||||||
public moneyScattered: number = 0;
|
public moneyScattered: number;
|
||||||
public lastUsedPokeball: PokeballType | null = null;
|
public lastUsedPokeball: PokeballType | null;
|
||||||
/** The number of times a Pokemon on the player's side has fainted this battle */
|
public playerFaints: number; // The amount of times pokemon on the players side have fainted
|
||||||
public playerFaints: number = 0;
|
public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted
|
||||||
/** The number of times a Pokemon on the enemy's side has fainted this battle */
|
|
||||||
public enemyFaints: number = 0;
|
|
||||||
|
|
||||||
private rngCounter: number = 0;
|
private rngCounter: integer = 0;
|
||||||
|
|
||||||
constructor(gameMode: GameMode, waveIndex: number, battleType: BattleType, trainer?: Trainer, double?: boolean) {
|
constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer?: Trainer, double?: boolean) {
|
||||||
this.gameMode = gameMode;
|
this.gameMode = gameMode;
|
||||||
this.waveIndex = waveIndex;
|
this.waveIndex = waveIndex;
|
||||||
this.battleType = battleType;
|
this.battleType = battleType;
|
||||||
@ -81,7 +79,22 @@ export default class Battle {
|
|||||||
this.enemyLevels = battleType !== BattleType.TRAINER
|
this.enemyLevels = battleType !== BattleType.TRAINER
|
||||||
? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave())
|
? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave())
|
||||||
: trainer?.getPartyLevels(this.waveIndex);
|
: trainer?.getPartyLevels(this.waveIndex);
|
||||||
this.double = double ?? false;
|
this.enemyParty = [];
|
||||||
|
this.seenEnemyPartyMemberIds = new Set<integer>();
|
||||||
|
this.double = !!double;
|
||||||
|
this.enemySwitchCounter = 0;
|
||||||
|
this.turn = 0;
|
||||||
|
this.playerParticipantIds = new Set<integer>();
|
||||||
|
this.battleScore = 0;
|
||||||
|
this.postBattleLoot = [];
|
||||||
|
this.escapeAttempts = 0;
|
||||||
|
this.started = false;
|
||||||
|
this.battleSeed = Utils.randomString(16, true);
|
||||||
|
this.battleSeedState = null;
|
||||||
|
this.moneyScattered = 0;
|
||||||
|
this.lastUsedPokeball = null;
|
||||||
|
this.playerFaints = 0;
|
||||||
|
this.enemyFaints = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private initBattleSpec(): void {
|
private initBattleSpec(): void {
|
||||||
@ -92,7 +105,7 @@ export default class Battle {
|
|||||||
this.battleSpec = spec;
|
this.battleSpec = spec;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getLevelForWave(): number {
|
private getLevelForWave(): integer {
|
||||||
const levelWaveIndex = this.gameMode.getWaveForDifficulty(this.waveIndex);
|
const levelWaveIndex = this.gameMode.getWaveForDifficulty(this.waveIndex);
|
||||||
const baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2);
|
const baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2);
|
||||||
const bossMultiplier = 1.2;
|
const bossMultiplier = 1.2;
|
||||||
@ -125,7 +138,7 @@ export default class Battle {
|
|||||||
return rand / value;
|
return rand / value;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBattlerCount(): number {
|
getBattlerCount(): integer {
|
||||||
return this.double ? 2 : 1;
|
return this.double ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +373,7 @@ export default class Battle {
|
|||||||
* @param min The minimum integer to pick, default `0`
|
* @param min The minimum integer to pick, default `0`
|
||||||
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
|
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
|
||||||
*/
|
*/
|
||||||
randSeedInt(scene: BattleScene, range: number, min: number = 0): number {
|
randSeedInt(scene: BattleScene, range: integer, min: integer = 0): integer {
|
||||||
if (range <= 1) {
|
if (range <= 1) {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
@ -385,7 +398,7 @@ export default class Battle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class FixedBattle extends Battle {
|
export class FixedBattle extends Battle {
|
||||||
constructor(scene: BattleScene, waveIndex: number, config: FixedBattleConfig) {
|
constructor(scene: BattleScene, waveIndex: integer, config: FixedBattleConfig) {
|
||||||
super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double);
|
super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double);
|
||||||
if (config.getEnemyParty) {
|
if (config.getEnemyParty) {
|
||||||
this.enemyParty = config.getEnemyParty(scene);
|
this.enemyParty = config.getEnemyParty(scene);
|
||||||
@ -401,7 +414,7 @@ export class FixedBattleConfig {
|
|||||||
public double: boolean;
|
public double: boolean;
|
||||||
public getTrainer: GetTrainerFunc;
|
public getTrainer: GetTrainerFunc;
|
||||||
public getEnemyParty: GetEnemyPartyFunc;
|
public getEnemyParty: GetEnemyPartyFunc;
|
||||||
public seedOffsetWaveIndex: number;
|
public seedOffsetWaveIndex: integer;
|
||||||
|
|
||||||
setBattleType(battleType: BattleType): FixedBattleConfig {
|
setBattleType(battleType: BattleType): FixedBattleConfig {
|
||||||
this.battleType = battleType;
|
this.battleType = battleType;
|
||||||
@ -423,7 +436,7 @@ export class FixedBattleConfig {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSeedOffsetWave(seedOffsetWaveIndex: number): FixedBattleConfig {
|
setSeedOffsetWave(seedOffsetWaveIndex: integer): FixedBattleConfig {
|
||||||
this.seedOffsetWaveIndex = seedOffsetWaveIndex;
|
this.seedOffsetWaveIndex = seedOffsetWaveIndex;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -469,7 +482,7 @@ function getRandomTrainerFunc(trainerPool: (TrainerType | TrainerType[])[], rand
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FixedBattleConfigs {
|
export interface FixedBattleConfigs {
|
||||||
[key: number]: FixedBattleConfig
|
[key: integer]: FixedBattleConfig
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Youngster/Lass on 5
|
* Youngster/Lass on 5
|
||||||
|
60
src/data/ability.ts
Executable file → Normal file
@ -310,7 +310,7 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr {
|
|||||||
|
|
||||||
export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
|
export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
|
||||||
constructor(moveType: Type, damageMultiplier: number) {
|
constructor(moveType: Type, damageMultiplier: number) {
|
||||||
super((target, user, move) => user.getMoveType(move) === moveType, damageMultiplier);
|
super((user, target, move) => move.type === moveType, damageMultiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +455,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(attacker.getMoveType(move), attacker) < 2) {
|
if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(pokemon.getMoveType(move), attacker) < 2) {
|
||||||
cancelled.value = true; // Suppresses "No Effect" message
|
cancelled.value = true; // Suppresses "No Effect" message
|
||||||
(args[0] as Utils.NumberHolder).value = 0;
|
(args[0] as Utils.NumberHolder).value = 0;
|
||||||
return true;
|
return true;
|
||||||
@ -1085,7 +1085,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||||
if (attacker.getTag(BattlerTagType.DISABLED) === null) {
|
if (!attacker.summonData.disabledMove) {
|
||||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) {
|
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) {
|
||||||
if (simulated) {
|
if (simulated) {
|
||||||
return true;
|
return true;
|
||||||
@ -1093,12 +1093,21 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
|||||||
|
|
||||||
this.attacker = attacker;
|
this.attacker = attacker;
|
||||||
this.move = move;
|
this.move = move;
|
||||||
this.attacker.addTag(BattlerTagType.DISABLED, 4, 0, pokemon.id);
|
|
||||||
|
attacker.summonData.disabledMove = move.id;
|
||||||
|
attacker.summonData.disabledTurns = 4;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
|
||||||
|
return i18next.t("abilityTriggers:postDefendMoveDisable", {
|
||||||
|
pokemonNameWithAffix: getPokemonNameWithAffix(this.attacker),
|
||||||
|
moveName: this.move.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChangeAbAttr {
|
export class PostStatStageChangeStatStageChangeAbAttr extends PostStatStageChangeAbAttr {
|
||||||
@ -1453,7 +1462,7 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr {
|
|||||||
|
|
||||||
export class MoveTypePowerBoostAbAttr extends MovePowerBoostAbAttr {
|
export class MoveTypePowerBoostAbAttr extends MovePowerBoostAbAttr {
|
||||||
constructor(boostedType: Type, powerMultiplier?: number) {
|
constructor(boostedType: Type, powerMultiplier?: number) {
|
||||||
super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5);
|
super((pokemon, defender, move) => move.type === boostedType, powerMultiplier || 1.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1537,7 +1546,7 @@ export class PreAttackFieldMoveTypePowerBoostAbAttr extends FieldMovePowerBoostA
|
|||||||
* @param powerMultiplier - The multiplier to apply to the move's power, defaults to 1.5 if not provided.
|
* @param powerMultiplier - The multiplier to apply to the move's power, defaults to 1.5 if not provided.
|
||||||
*/
|
*/
|
||||||
constructor(boostedType: Type, powerMultiplier?: number) {
|
constructor(boostedType: Type, powerMultiplier?: number) {
|
||||||
super((pokemon, defender, move) => pokemon?.getMoveType(move) === boostedType, powerMultiplier || 1.5);
|
super((pokemon, defender, move) => move.type === boostedType, powerMultiplier || 1.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3466,12 +3475,12 @@ export class MoodyAbAttr extends PostTurnAbAttr {
|
|||||||
|
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
if (canRaise.length > 0) {
|
if (canRaise.length > 0) {
|
||||||
const raisedStat = canRaise[pokemon.randSeedInt(canRaise.length)];
|
const raisedStat = Utils.randSeedItem(canRaise);
|
||||||
canLower = canRaise.filter(s => s !== raisedStat);
|
canLower = canRaise.filter(s => s !== raisedStat);
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ raisedStat ], 2));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ raisedStat ], 2));
|
||||||
}
|
}
|
||||||
if (canLower.length > 0) {
|
if (canLower.length > 0) {
|
||||||
const loweredStat = canLower[pokemon.randSeedInt(canLower.length)];
|
const loweredStat = Utils.randSeedItem(canLower);
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ loweredStat ], -1));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ loweredStat ], -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5091,9 +5100,9 @@ export function initAbilities() {
|
|||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.TINTED_LENS, 4)
|
new Ability(Abilities.TINTED_LENS, 4)
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
.attr(DamageBoostAbAttr, 2, (user, target, move) => target?.getMoveEffectiveness(user, move) <= 0.5), // TODO: fix TS issues
|
.attr(DamageBoostAbAttr, 2, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5), // TODO: fix TS issues
|
||||||
new Ability(Abilities.FILTER, 4)
|
new Ability(Abilities.FILTER, 4)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75)
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.SLOW_START, 4)
|
new Ability(Abilities.SLOW_START, 4)
|
||||||
.attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5),
|
.attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5),
|
||||||
@ -5109,7 +5118,7 @@ export function initAbilities() {
|
|||||||
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL, WeatherType.SNOW)
|
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL, WeatherType.SNOW)
|
||||||
.partial(), // Healing not blocked by Heal Block
|
.partial(), // Healing not blocked by Heal Block
|
||||||
new Ability(Abilities.SOLID_ROCK, 4)
|
new Ability(Abilities.SOLID_ROCK, 4)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75)
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.SNOW_WARNING, 4)
|
new Ability(Abilities.SNOW_WARNING, 4)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SNOW)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SNOW)
|
||||||
@ -5227,13 +5236,10 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.MOXIE, 5)
|
new Ability(Abilities.MOXIE, 5)
|
||||||
.attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1),
|
.attr(PostVictoryStatStageChangeAbAttr, Stat.ATK, 1),
|
||||||
new Ability(Abilities.JUSTIFIED, 5)
|
new Ability(Abilities.JUSTIFIED, 5)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.DARK && move.category !== MoveCategory.STATUS, Stat.ATK, 1),
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.type === Type.DARK && move.category !== MoveCategory.STATUS, Stat.ATK, 1),
|
||||||
new Ability(Abilities.RATTLED, 5)
|
new Ability(Abilities.RATTLED, 5)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => {
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS && (move.type === Type.DARK || move.type === Type.BUG ||
|
||||||
const moveType = user.getMoveType(move);
|
move.type === Type.GHOST), Stat.SPD, 1)
|
||||||
return move.category !== MoveCategory.STATUS
|
|
||||||
&& (moveType === Type.DARK || moveType === Type.BUG || moveType === Type.GHOST);
|
|
||||||
}, Stat.SPD, 1)
|
|
||||||
.attr(PostIntimidateStatStageChangeAbAttr, [Stat.SPD], 1),
|
.attr(PostIntimidateStatStageChangeAbAttr, [Stat.SPD], 1),
|
||||||
new Ability(Abilities.MAGIC_BOUNCE, 5)
|
new Ability(Abilities.MAGIC_BOUNCE, 5)
|
||||||
.ignorable()
|
.ignorable()
|
||||||
@ -5307,7 +5313,7 @@ export function initAbilities() {
|
|||||||
.attr(UnsuppressableAbilityAbAttr)
|
.attr(UnsuppressableAbilityAbAttr)
|
||||||
.attr(NoFusionAbilityAbAttr),
|
.attr(NoFusionAbilityAbAttr),
|
||||||
new Ability(Abilities.GALE_WINGS, 6)
|
new Ability(Abilities.GALE_WINGS, 6)
|
||||||
.attr(ChangeMovePriorityAbAttr, (pokemon, move) => pokemon.isFullHp() && pokemon.getMoveType(move) === Type.FLYING, 1),
|
.attr(ChangeMovePriorityAbAttr, (pokemon, move) => pokemon.isFullHp() && move.type === Type.FLYING, 1),
|
||||||
new Ability(Abilities.MEGA_LAUNCHER, 6)
|
new Ability(Abilities.MEGA_LAUNCHER, 6)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PULSE_MOVE), 1.5),
|
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.PULSE_MOVE), 1.5),
|
||||||
new Ability(Abilities.GRASS_PELT, 6)
|
new Ability(Abilities.GRASS_PELT, 6)
|
||||||
@ -5362,7 +5368,7 @@ export function initAbilities() {
|
|||||||
.condition(getSheerForceHitDisableAbCondition())
|
.condition(getSheerForceHitDisableAbCondition())
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
new Ability(Abilities.WATER_COMPACTION, 7)
|
new Ability(Abilities.WATER_COMPACTION, 7)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
||||||
new Ability(Abilities.MERCILESS, 7)
|
new Ability(Abilities.MERCILESS, 7)
|
||||||
.attr(ConditionalCritAbAttr, (user, target, move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON),
|
.attr(ConditionalCritAbAttr, (user, target, move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON),
|
||||||
new Ability(Abilities.SHIELDS_DOWN, 7)
|
new Ability(Abilities.SHIELDS_DOWN, 7)
|
||||||
@ -5418,7 +5424,7 @@ export function initAbilities() {
|
|||||||
.attr(NoFusionAbilityAbAttr)
|
.attr(NoFusionAbilityAbAttr)
|
||||||
// Add BattlerTagType.DISGUISE if the pokemon is in its disguised form
|
// Add BattlerTagType.DISGUISE if the pokemon is in its disguised form
|
||||||
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
|
.conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false)
|
||||||
.attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE,
|
.attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getAttackTypeEffectiveness(move.type, user) > 0, 0, BattlerTagType.DISGUISE,
|
||||||
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
|
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
|
||||||
(pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8))
|
(pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8))
|
||||||
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
.attr(PostBattleInitFormChangeAbAttr, () => 0)
|
||||||
@ -5463,7 +5469,7 @@ export function initAbilities() {
|
|||||||
.attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL], 1.3),
|
.attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL], 1.3),
|
||||||
new Ability(Abilities.FLUFFY, 7)
|
new Ability(Abilities.FLUFFY, 7)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 0.5)
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 0.5)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => user.getMoveType(move) === Type.FIRE, 2)
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.type === Type.FIRE, 2)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.DAZZLING, 7)
|
new Ability(Abilities.DAZZLING, 7)
|
||||||
.attr(FieldPriorityMoveImmunityAbAttr)
|
.attr(FieldPriorityMoveImmunityAbAttr)
|
||||||
@ -5513,10 +5519,10 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.SHADOW_SHIELD, 7)
|
new Ability(Abilities.SHADOW_SHIELD, 7)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.isFullHp(), 0.5),
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.isFullHp(), 0.5),
|
||||||
new Ability(Abilities.PRISM_ARMOR, 7)
|
new Ability(Abilities.PRISM_ARMOR, 7)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getMoveEffectiveness(user, move) >= 2, 0.75),
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75),
|
||||||
new Ability(Abilities.NEUROFORCE, 7)
|
new Ability(Abilities.NEUROFORCE, 7)
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => target?.getMoveEffectiveness(user, move) >= 2, 1.25), // TODO: fix TS issues
|
.attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), // TODO: fix TS issues
|
||||||
new Ability(Abilities.INTREPID_SWORD, 8)
|
new Ability(Abilities.INTREPID_SWORD, 8)
|
||||||
.attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
|
.attr(PostSummonStatStageChangeAbAttr, [ Stat.ATK ], 1, true)
|
||||||
.condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)),
|
.condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)),
|
||||||
@ -5547,11 +5553,7 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.STALWART, 8)
|
new Ability(Abilities.STALWART, 8)
|
||||||
.attr(BlockRedirectAbAttr),
|
.attr(BlockRedirectAbAttr),
|
||||||
new Ability(Abilities.STEAM_ENGINE, 8)
|
new Ability(Abilities.STEAM_ENGINE, 8)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => {
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => (move.type === Type.FIRE || move.type === Type.WATER) && move.category !== MoveCategory.STATUS, Stat.SPD, 6),
|
||||||
const moveType = user.getMoveType(move);
|
|
||||||
return move.category !== MoveCategory.STATUS
|
|
||||||
&& (moveType === Type.FIRE || moveType === Type.WATER);
|
|
||||||
}, Stat.SPD, 6),
|
|
||||||
new Ability(Abilities.PUNK_ROCK, 8)
|
new Ability(Abilities.PUNK_ROCK, 8)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED), 1.3)
|
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED), 1.3)
|
||||||
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5)
|
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5)
|
||||||
@ -5651,7 +5653,7 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.SEED_SOWER, 9)
|
new Ability(Abilities.SEED_SOWER, 9)
|
||||||
.attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY),
|
.attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY),
|
||||||
new Ability(Abilities.THERMAL_EXCHANGE, 9)
|
new Ability(Abilities.THERMAL_EXCHANGE, 9)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1)
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.type === Type.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1)
|
||||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
|
.attr(StatusEffectImmunityAbAttr, StatusEffect.BURN)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.ANGER_SHELL, 9)
|
new Ability(Abilities.ANGER_SHELL, 9)
|
||||||
|
@ -39,15 +39,13 @@ export class BattlerTag {
|
|||||||
public turnCount: number;
|
public turnCount: number;
|
||||||
public sourceMove: Moves;
|
public sourceMove: Moves;
|
||||||
public sourceId?: number;
|
public sourceId?: number;
|
||||||
public isBatonPassable: boolean;
|
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number, isBatonPassable: boolean = false) {
|
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number) {
|
||||||
this.tagType = tagType;
|
this.tagType = tagType;
|
||||||
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ];
|
this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ];
|
||||||
this.turnCount = turnCount;
|
this.turnCount = turnCount;
|
||||||
this.sourceMove = sourceMove!; // TODO: is this bang correct?
|
this.sourceMove = sourceMove!; // TODO: is this bang correct?
|
||||||
this.sourceId = sourceId;
|
this.sourceId = sourceId;
|
||||||
this.isBatonPassable = isBatonPassable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
@ -98,127 +96,6 @@ export interface TerrainBattlerTag {
|
|||||||
terrainTypes: TerrainType[];
|
terrainTypes: TerrainType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for tags that restrict the usage of moves. This effect is generally referred to as "disabling" a move
|
|
||||||
* in-game. This is not to be confused with {@linkcode Moves.DISABLE}.
|
|
||||||
*
|
|
||||||
* Descendants can override {@linkcode isMoveRestricted} to restrict moves that
|
|
||||||
* match a condition. A restricted move gets cancelled before it is used. Players and enemies should not be allowed
|
|
||||||
* to select restricted moves.
|
|
||||||
*/
|
|
||||||
export abstract class MoveRestrictionBattlerTag extends BattlerTag {
|
|
||||||
constructor(tagType: BattlerTagType, turnCount: integer, sourceMove?: Moves, sourceId?: integer) {
|
|
||||||
super(tagType, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.TURN_END ], turnCount, sourceMove, sourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
|
||||||
if (lapseType === BattlerTagLapseType.PRE_MOVE) {
|
|
||||||
// Cancel the affected pokemon's selected move
|
|
||||||
const phase = pokemon.scene.getCurrentPhase() as MovePhase;
|
|
||||||
const move = phase.move;
|
|
||||||
|
|
||||||
if (this.isMoveRestricted(move.moveId)) {
|
|
||||||
pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId));
|
|
||||||
phase.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.lapse(pokemon, lapseType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets whether this tag is restricting a move.
|
|
||||||
*
|
|
||||||
* @param {Moves} move {@linkcode Moves} ID to check restriction for.
|
|
||||||
* @returns {boolean} `true` if the move is restricted by this tag, otherwise `false`.
|
|
||||||
*/
|
|
||||||
abstract isMoveRestricted(move: Moves): boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the text to display when the player attempts to select a move that is restricted by this tag.
|
|
||||||
*
|
|
||||||
* @param {Pokemon} pokemon {@linkcode Pokemon} for which the player is attempting to select the restricted move
|
|
||||||
* @param {Moves} move {@linkcode Moves} ID of the move that is having its selection denied
|
|
||||||
* @returns {string} text to display when the player attempts to select the restricted move
|
|
||||||
*/
|
|
||||||
abstract selectionDeniedText(pokemon: Pokemon, move: Moves): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the text to display when a move's execution is prevented as a result of the restriction.
|
|
||||||
* Because restriction effects also prevent selection of the move, this situation can only arise if a
|
|
||||||
* pokemon first selects a move, then gets outsped by a pokemon using a move that restricts the selected move.
|
|
||||||
*
|
|
||||||
* @param {Pokemon} pokemon {@linkcode Pokemon} attempting to use the restricted move
|
|
||||||
* @param {Moves} move {@linkcode Moves} ID of the move being interrupted
|
|
||||||
* @returns {string} text to display when the move is interrupted
|
|
||||||
*/
|
|
||||||
abstract interruptedText(pokemon: Pokemon, move: Moves): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tag representing the "disabling" effect performed by {@linkcode Moves.DISABLE} and {@linkcode Abilities.CURSED_BODY}.
|
|
||||||
* When the tag is added, the last-used move of the tag holder is set as the disabled move.
|
|
||||||
*/
|
|
||||||
export class DisabledTag extends MoveRestrictionBattlerTag {
|
|
||||||
/** The move being disabled. Gets set when {@linkcode onAdd} is called for this tag. */
|
|
||||||
private moveId: Moves = Moves.NONE;
|
|
||||||
|
|
||||||
constructor(sourceId: number) {
|
|
||||||
super(BattlerTagType.DISABLED, 4, Moves.DISABLE, sourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
override isMoveRestricted(move: Moves): boolean {
|
|
||||||
return move === this.moveId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @override
|
|
||||||
*
|
|
||||||
* Ensures that move history exists on `pokemon` and has a valid move. If so, sets the {@link moveId} and shows a message.
|
|
||||||
* Otherwise the move ID will not get assigned and this tag will get removed next turn.
|
|
||||||
*/
|
|
||||||
override onAdd(pokemon: Pokemon): void {
|
|
||||||
super.onAdd(pokemon);
|
|
||||||
|
|
||||||
const move = pokemon.getLastXMoves()
|
|
||||||
.find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual);
|
|
||||||
if (move === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.moveId = move.move;
|
|
||||||
|
|
||||||
pokemon.scene.queueMessage(i18next.t("battlerTags:disabledOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name }));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
override onRemove(pokemon: Pokemon): void {
|
|
||||||
super.onRemove(pokemon);
|
|
||||||
|
|
||||||
pokemon.scene.queueMessage(i18next.t("battlerTags:disabledLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[this.moveId].name }));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
override selectionDeniedText(pokemon: Pokemon, move: Moves): string {
|
|
||||||
return i18next.t("battle:moveDisabled", { moveName: allMoves[move].name });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
override interruptedText(pokemon: Pokemon, move: Moves): string {
|
|
||||||
return i18next.t("battle:disableInterruptedMove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: allMoves[move].name });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
override loadTag(source: BattlerTag | any): void {
|
|
||||||
super.loadTag(source);
|
|
||||||
this.moveId = source.moveId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BattlerTag that represents the "recharge" effects of moves like Hyper Beam.
|
* BattlerTag that represents the "recharge" effects of moves like Hyper Beam.
|
||||||
*/
|
*/
|
||||||
@ -329,7 +206,7 @@ export class ShellTrapTag extends BattlerTag {
|
|||||||
|
|
||||||
export class TrappedTag extends BattlerTag {
|
export class TrappedTag extends BattlerTag {
|
||||||
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) {
|
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: number, sourceMove: Moves, sourceId: number) {
|
||||||
super(tagType, lapseType, turnCount, sourceMove, sourceId, true);
|
super(tagType, lapseType, turnCount, sourceMove, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
@ -449,7 +326,7 @@ export class InterruptedTag extends BattlerTag {
|
|||||||
*/
|
*/
|
||||||
export class ConfusedTag extends BattlerTag {
|
export class ConfusedTag extends BattlerTag {
|
||||||
constructor(turnCount: number, sourceMove: Moves) {
|
constructor(turnCount: number, sourceMove: Moves) {
|
||||||
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove, undefined, true);
|
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
@ -509,7 +386,7 @@ export class ConfusedTag extends BattlerTag {
|
|||||||
*/
|
*/
|
||||||
export class DestinyBondTag extends BattlerTag {
|
export class DestinyBondTag extends BattlerTag {
|
||||||
constructor(sourceMove: Moves, sourceId: number) {
|
constructor(sourceMove: Moves, sourceId: number) {
|
||||||
super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId, true);
|
super(BattlerTagType.DESTINY_BOND, BattlerTagLapseType.PRE_MOVE, 1, sourceMove, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -628,7 +505,7 @@ export class SeedTag extends BattlerTag {
|
|||||||
private sourceIndex: number;
|
private sourceIndex: number;
|
||||||
|
|
||||||
constructor(sourceId: number) {
|
constructor(sourceId: number) {
|
||||||
super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId, true);
|
super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -899,7 +776,7 @@ export class OctolockTag extends TrappedTag {
|
|||||||
|
|
||||||
export class AquaRingTag extends BattlerTag {
|
export class AquaRingTag extends BattlerTag {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined, true);
|
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(pokemon: Pokemon): void {
|
onAdd(pokemon: Pokemon): void {
|
||||||
@ -931,7 +808,7 @@ export class AquaRingTag extends BattlerTag {
|
|||||||
/** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */
|
/** Tag used to allow moves that interact with {@link Moves.MINIMIZE} to function */
|
||||||
export class MinimizeTag extends BattlerTag {
|
export class MinimizeTag extends BattlerTag {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE);
|
super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
@ -1329,7 +1206,7 @@ export class SturdyTag extends BattlerTag {
|
|||||||
|
|
||||||
export class PerishSongTag extends BattlerTag {
|
export class PerishSongTag extends BattlerTag {
|
||||||
constructor(turnCount: number) {
|
constructor(turnCount: number) {
|
||||||
super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG, undefined, true);
|
super(BattlerTagType.PERISH_SONG, BattlerTagLapseType.TURN_END, turnCount, Moves.PERISH_SONG);
|
||||||
}
|
}
|
||||||
|
|
||||||
canAdd(pokemon: Pokemon): boolean {
|
canAdd(pokemon: Pokemon): boolean {
|
||||||
@ -1385,7 +1262,7 @@ export class AbilityBattlerTag extends BattlerTag {
|
|||||||
public ability: Abilities;
|
public ability: Abilities;
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) {
|
constructor(tagType: BattlerTagType, ability: Abilities, lapseType: BattlerTagLapseType, turnCount: number) {
|
||||||
super(tagType, lapseType, turnCount);
|
super(tagType, lapseType, turnCount, undefined);
|
||||||
|
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
}
|
}
|
||||||
@ -1561,7 +1438,7 @@ export class TypeImmuneTag extends BattlerTag {
|
|||||||
public immuneType: Type;
|
public immuneType: Type;
|
||||||
|
|
||||||
constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) {
|
constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number = 1) {
|
||||||
super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove, undefined, true);
|
super(tagType, BattlerTagLapseType.TURN_END, length, sourceMove);
|
||||||
|
|
||||||
this.immuneType = immuneType;
|
this.immuneType = immuneType;
|
||||||
}
|
}
|
||||||
@ -1625,7 +1502,7 @@ export class TypeBoostTag extends BattlerTag {
|
|||||||
|
|
||||||
export class CritBoostTag extends BattlerTag {
|
export class CritBoostTag extends BattlerTag {
|
||||||
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
||||||
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove, undefined, true);
|
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(pokemon: Pokemon): void {
|
onAdd(pokemon: Pokemon): void {
|
||||||
@ -1717,7 +1594,7 @@ export class CursedTag extends BattlerTag {
|
|||||||
private sourceIndex: number;
|
private sourceIndex: number;
|
||||||
|
|
||||||
constructor(sourceId: number) {
|
constructor(sourceId: number) {
|
||||||
super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId, true);
|
super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2116,8 +1993,6 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
|||||||
return new StockpilingTag(sourceMove);
|
return new StockpilingTag(sourceMove);
|
||||||
case BattlerTagType.OCTOLOCK:
|
case BattlerTagType.OCTOLOCK:
|
||||||
return new OctolockTag(sourceId);
|
return new OctolockTag(sourceId);
|
||||||
case BattlerTagType.DISABLED:
|
|
||||||
return new DisabledTag(sourceId);
|
|
||||||
case BattlerTagType.IGNORE_GHOST:
|
case BattlerTagType.IGNORE_GHOST:
|
||||||
return new ExposedTag(tagType, sourceMove, Type.GHOST, [Type.NORMAL, Type.FIGHTING]);
|
return new ExposedTag(tagType, sourceMove, Type.GHOST, [Type.NORMAL, Type.FIGHTING]);
|
||||||
case BattlerTagType.IGNORE_DARK:
|
case BattlerTagType.IGNORE_DARK:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { pokemonEvolutions, SpeciesFormEvolution } from "./pokemon-evolutions";
|
import {pokemonEvolutions, SpeciesFormEvolution} from "./pokemon-evolutions";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
@ -46,7 +46,7 @@ export const biomeLinks: BiomeLinks = {
|
|||||||
[Biome.SEABED]: [ Biome.CAVE, [ Biome.VOLCANO, 3 ] ],
|
[Biome.SEABED]: [ Biome.CAVE, [ Biome.VOLCANO, 3 ] ],
|
||||||
[Biome.MOUNTAIN]: [ Biome.VOLCANO, [ Biome.WASTELAND, 2 ], [ Biome.SPACE, 3 ] ],
|
[Biome.MOUNTAIN]: [ Biome.VOLCANO, [ Biome.WASTELAND, 2 ], [ Biome.SPACE, 3 ] ],
|
||||||
[Biome.BADLANDS]: [ Biome.DESERT, Biome.MOUNTAIN ],
|
[Biome.BADLANDS]: [ Biome.DESERT, Biome.MOUNTAIN ],
|
||||||
[Biome.CAVE]: [ Biome.BADLANDS, Biome.LAKE, [ Biome.LABORATORY, 2 ] ],
|
[Biome.CAVE]: [ Biome.BADLANDS, Biome.LAKE [ Biome.LABORATORY, 2 ] ],
|
||||||
[Biome.DESERT]: [ Biome.RUINS, [ Biome.CONSTRUCTION_SITE, 2 ] ],
|
[Biome.DESERT]: [ Biome.RUINS, [ Biome.CONSTRUCTION_SITE, 2 ] ],
|
||||||
[Biome.ICE_CAVE]: Biome.SNOWY_FOREST,
|
[Biome.ICE_CAVE]: Biome.SNOWY_FOREST,
|
||||||
[Biome.MEADOW]: [ Biome.PLAINS, Biome.FAIRY_CAVE ],
|
[Biome.MEADOW]: [ Biome.PLAINS, Biome.FAIRY_CAVE ],
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import { DexEntry, StarterDataEntry } from "#app/system/game-data";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores data associated with a specific egg and the hatched pokemon
|
|
||||||
* Allows hatch info to be stored at hatch then retrieved for display during egg summary
|
|
||||||
*/
|
|
||||||
export class EggHatchData {
|
|
||||||
/** the pokemon that hatched from the file (including shiny, IVs, ability) */
|
|
||||||
public pokemon: PlayerPokemon;
|
|
||||||
/** index of the egg move from the hatched pokemon (not stored in PlayerPokemon) */
|
|
||||||
public eggMoveIndex: number;
|
|
||||||
/** boolean indicating if the egg move for the hatch is new */
|
|
||||||
public eggMoveUnlocked: boolean;
|
|
||||||
/** stored copy of the hatched pokemon's dex entry before it was updated due to hatch */
|
|
||||||
public dexEntryBeforeUpdate: DexEntry;
|
|
||||||
/** stored copy of the hatched pokemon's starter entry before it was updated due to hatch */
|
|
||||||
public starterDataEntryBeforeUpdate: StarterDataEntry;
|
|
||||||
/** reference to the battle scene to get gamedata and update dex */
|
|
||||||
private scene: BattleScene;
|
|
||||||
|
|
||||||
constructor(scene: BattleScene, pokemon: PlayerPokemon, eggMoveIndex: number) {
|
|
||||||
this.scene = scene;
|
|
||||||
this.pokemon = pokemon;
|
|
||||||
this.eggMoveIndex = eggMoveIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the boolean for if the egg move for the hatch is a new unlock
|
|
||||||
* @param unlocked True if the EM is new
|
|
||||||
*/
|
|
||||||
setEggMoveUnlocked(unlocked: boolean) {
|
|
||||||
this.eggMoveUnlocked = unlocked;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a copy of the current DexEntry of the pokemon and StarterDataEntry of its starter
|
|
||||||
* Used before updating the dex, so comparing the pokemon to these entries will show the new attributes
|
|
||||||
*/
|
|
||||||
setDex() {
|
|
||||||
const currDexEntry = this.scene.gameData.dexData[this.pokemon.species.speciesId];
|
|
||||||
const currStarterDataEntry = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()];
|
|
||||||
this.dexEntryBeforeUpdate = {
|
|
||||||
seenAttr: currDexEntry.seenAttr,
|
|
||||||
caughtAttr: currDexEntry.caughtAttr,
|
|
||||||
natureAttr: currDexEntry.natureAttr,
|
|
||||||
seenCount: currDexEntry.seenCount,
|
|
||||||
caughtCount: currDexEntry.caughtCount,
|
|
||||||
hatchedCount: currDexEntry.hatchedCount,
|
|
||||||
ivs: [...currDexEntry.ivs]
|
|
||||||
};
|
|
||||||
this.starterDataEntryBeforeUpdate = {
|
|
||||||
moveset: currStarterDataEntry.moveset,
|
|
||||||
eggMoves: currStarterDataEntry.eggMoves,
|
|
||||||
candyCount: currStarterDataEntry.candyCount,
|
|
||||||
friendship: currStarterDataEntry.friendship,
|
|
||||||
abilityAttr: currStarterDataEntry.abilityAttr,
|
|
||||||
passiveAttr: currStarterDataEntry.passiveAttr,
|
|
||||||
valueReduction: currStarterDataEntry.valueReduction,
|
|
||||||
classicWinCount: currStarterDataEntry.classicWinCount
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the dex entry before update
|
|
||||||
* @returns Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex
|
|
||||||
*/
|
|
||||||
getDex(): DexEntry {
|
|
||||||
return this.dexEntryBeforeUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the starter dex entry before update
|
|
||||||
* @returns Starter Dex Entry corresponding to this pokemon before the pokemon was added / updated to dex
|
|
||||||
*/
|
|
||||||
getStarterEntry(): StarterDataEntry {
|
|
||||||
return this.starterDataEntryBeforeUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the pokedex data corresponding with the new hatch's pokemon data
|
|
||||||
* Also sets whether the egg move is a new unlock or not
|
|
||||||
* @param showMessage boolean to show messages for the new catches and egg moves (false by default)
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
updatePokemon(showMessage : boolean = false) {
|
|
||||||
return new Promise<void>(resolve => {
|
|
||||||
this.scene.gameData.setPokemonCaught(this.pokemon, true, true, showMessage).then(() => {
|
|
||||||
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
|
||||||
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex, showMessage).then((value) => {
|
|
||||||
this.setEggMoveUnlocked(value);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -761,7 +761,8 @@ export default class Move implements Localizable {
|
|||||||
.flat(),
|
.flat(),
|
||||||
);
|
);
|
||||||
for (const aura of fieldAuras) {
|
for (const aura of fieldAuras) {
|
||||||
aura.applyPreAttack(source, null, simulated, target, this, [power]);
|
// The only relevant values are `move` and the `power` holder
|
||||||
|
aura.applyPreAttack(null, null, simulated, null, this, [power]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField();
|
const alliedField: Pokemon[] = source instanceof PlayerPokemon ? source.scene.getPlayerField() : source.scene.getEnemyField();
|
||||||
@ -2674,7 +2675,7 @@ export class AcupressureStatStageChangeAttr extends MoveEffectAttr {
|
|||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
|
||||||
const randStats = BATTLE_STATS.filter(s => target.getStatStage(s) < 6);
|
const randStats = BATTLE_STATS.filter(s => target.getStatStage(s) < 6);
|
||||||
if (randStats.length > 0) {
|
if (randStats.length > 0) {
|
||||||
const boostStat = [randStats[user.randSeedInt(randStats.length)]];
|
const boostStat = [randStats[Utils.randInt(randStats.length)]];
|
||||||
user.scene.unshiftPhase(new StatStageChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2));
|
user.scene.unshiftPhase(new StatStageChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -4332,6 +4333,72 @@ export class TypelessAttr extends MoveAttr { }
|
|||||||
*/
|
*/
|
||||||
export class BypassRedirectAttr extends MoveAttr { }
|
export class BypassRedirectAttr extends MoveAttr { }
|
||||||
|
|
||||||
|
export class DisableMoveAttr extends MoveEffectAttr {
|
||||||
|
constructor() {
|
||||||
|
super(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
if (!super.apply(user, target, move, args)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveQueue = target.getLastXMoves();
|
||||||
|
let turnMove: TurnMove | undefined;
|
||||||
|
while (moveQueue.length) {
|
||||||
|
turnMove = moveQueue.shift();
|
||||||
|
if (turnMove?.virtual) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveIndex = target.getMoveset().findIndex(m => m?.moveId === turnMove?.move);
|
||||||
|
if (moveIndex === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const disabledMove = target.getMoveset()[moveIndex];
|
||||||
|
target.summonData.disabledMove = disabledMove?.moveId!; // TODO: is this bang correct?
|
||||||
|
target.summonData.disabledTurns = 4;
|
||||||
|
|
||||||
|
user.scene.queueMessage(i18next.t("abilityTriggers:postDefendMoveDisable", { pokemonNameWithAffix: getPokemonNameWithAffix(target), moveName: disabledMove?.getName()}));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCondition(): MoveConditionFunc {
|
||||||
|
return (user, target, move): boolean => { // TODO: Not sure what to do here
|
||||||
|
if (target.summonData.disabledMove || target.isMax()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveQueue = target.getLastXMoves();
|
||||||
|
let turnMove: TurnMove | undefined;
|
||||||
|
while (moveQueue.length) {
|
||||||
|
turnMove = moveQueue.shift();
|
||||||
|
if (turnMove?.virtual) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const move = target.getMoveset().find(m => m?.moveId === turnMove?.move);
|
||||||
|
if (!move) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
|
||||||
|
return -5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class FrenzyAttr extends MoveEffectAttr {
|
export class FrenzyAttr extends MoveEffectAttr {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(true, MoveEffectTrigger.HIT, false, true);
|
super(true, MoveEffectTrigger.HIT, false, true);
|
||||||
@ -4422,7 +4489,6 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
|||||||
case BattlerTagType.INFATUATED:
|
case BattlerTagType.INFATUATED:
|
||||||
case BattlerTagType.NIGHTMARE:
|
case BattlerTagType.NIGHTMARE:
|
||||||
case BattlerTagType.DROWSY:
|
case BattlerTagType.DROWSY:
|
||||||
case BattlerTagType.DISABLED:
|
|
||||||
return -5;
|
return -5;
|
||||||
case BattlerTagType.SEEDED:
|
case BattlerTagType.SEEDED:
|
||||||
case BattlerTagType.SALT_CURED:
|
case BattlerTagType.SALT_CURED:
|
||||||
@ -6608,8 +6674,7 @@ export function initMoves() {
|
|||||||
new AttackMove(Moves.SONIC_BOOM, Type.NORMAL, MoveCategory.SPECIAL, -1, 90, 20, -1, 0, 1)
|
new AttackMove(Moves.SONIC_BOOM, Type.NORMAL, MoveCategory.SPECIAL, -1, 90, 20, -1, 0, 1)
|
||||||
.attr(FixedDamageAttr, 20),
|
.attr(FixedDamageAttr, 20),
|
||||||
new StatusMove(Moves.DISABLE, Type.NORMAL, 100, 20, -1, 0, 1)
|
new StatusMove(Moves.DISABLE, Type.NORMAL, 100, 20, -1, 0, 1)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.DISABLED, false, true)
|
.attr(DisableMoveAttr)
|
||||||
.condition((user, target, move) => target.getMoveHistory().reverse().find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual) !== undefined)
|
|
||||||
.condition(failOnMaxCondition),
|
.condition(failOnMaxCondition),
|
||||||
new AttackMove(Moves.ACID, Type.POISON, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1)
|
new AttackMove(Moves.ACID, Type.POISON, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1)
|
||||||
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -1)
|
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -1)
|
||||||
|
@ -944,7 +944,7 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(Species.METAPOD, 1, false, false, false, "Cocoon Pokémon", Type.BUG, null, 0.7, 9.9, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 50, 20, 55, 25, 25, 30, 120, 50, 72, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.METAPOD, 1, false, false, false, "Cocoon Pokémon", Type.BUG, null, 0.7, 9.9, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 50, 20, 55, 25, 25, 30, 120, 50, 72, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(Species.BUTTERFREE, 1, false, false, false, "Butterfly Pokémon", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, GrowthRate.MEDIUM_FAST, 50, true, true,
|
new PokemonSpecies(Species.BUTTERFREE, 1, false, false, false, "Butterfly Pokémon", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, GrowthRate.MEDIUM_FAST, 50, true, true,
|
||||||
new PokemonForm("Normal", "", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, true, null, true),
|
new PokemonForm("Normal", "", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, true, null, true),
|
||||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 32, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true),
|
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 32, Abilities.TINTED_LENS, Abilities.TINTED_LENS, Abilities.TINTED_LENS, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.WEEDLE, 1, false, false, false, "Hairy Bug Pokémon", Type.BUG, Type.POISON, 0.3, 3.2, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 40, 35, 30, 20, 20, 50, 255, 70, 39, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.WEEDLE, 1, false, false, false, "Hairy Bug Pokémon", Type.BUG, Type.POISON, 0.3, 3.2, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 40, 35, 30, 20, 20, 50, 255, 70, 39, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(Species.KAKUNA, 1, false, false, false, "Cocoon Pokémon", Type.BUG, Type.POISON, 0.6, 10, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 45, 25, 50, 25, 25, 35, 120, 70, 72, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.KAKUNA, 1, false, false, false, "Cocoon Pokémon", Type.BUG, Type.POISON, 0.6, 10, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 45, 25, 50, 25, 25, 35, 120, 70, 72, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
|
@ -64,7 +64,6 @@ export enum BattlerTagType {
|
|||||||
STOCKPILING = "STOCKPILING",
|
STOCKPILING = "STOCKPILING",
|
||||||
RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE",
|
RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE",
|
||||||
ALWAYS_GET_HIT = "ALWAYS_GET_HIT",
|
ALWAYS_GET_HIT = "ALWAYS_GET_HIT",
|
||||||
DISABLED = "DISABLED",
|
|
||||||
IGNORE_GHOST = "IGNORE_GHOST",
|
IGNORE_GHOST = "IGNORE_GHOST",
|
||||||
IGNORE_DARK = "IGNORE_DARK",
|
IGNORE_DARK = "IGNORE_DARK",
|
||||||
GULP_MISSILE_ARROKUDA = "GULP_MISSILE_ARROKUDA",
|
GULP_MISSILE_ARROKUDA = "GULP_MISSILE_ARROKUDA",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Determines the row cursor target when entering the shop phase.
|
* Determines the cursor target when entering the shop phase.
|
||||||
*/
|
*/
|
||||||
export enum ShopCursorTarget {
|
export enum ShopCursorTarget {
|
||||||
/** Cursor points to Reroll row */
|
/** Cursor points to Reroll */
|
||||||
REROLL,
|
REROLL,
|
||||||
/** Cursor points to Rewards row */
|
/** Cursor points to Items */
|
||||||
REWARDS,
|
ITEMS,
|
||||||
/** Cursor points to Shop row */
|
/** Cursor points to Shop */
|
||||||
SHOP,
|
SHOP,
|
||||||
/** Cursor points to Check Team row */
|
/** Cursor points to Check Team */
|
||||||
CHECK_TEAM
|
CHECK_TEAM
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
|
|||||||
import { Status, StatusEffect, getRandomStatus } from "../data/status-effect";
|
import { Status, StatusEffect, getRandomStatus } from "../data/status-effect";
|
||||||
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions";
|
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions";
|
||||||
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms";
|
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms";
|
||||||
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag } from "../data/battler-tags";
|
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag } from "../data/battler-tags";
|
||||||
import { WeatherType } from "../data/weather";
|
import { WeatherType } from "../data/weather";
|
||||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag";
|
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag";
|
||||||
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "../data/ability";
|
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr } from "../data/ability";
|
||||||
@ -2660,7 +2660,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const tag of source.summonData.tags) {
|
for (const tag of source.summonData.tags) {
|
||||||
if (!tag.isBatonPassable) {
|
|
||||||
|
// bypass those can not be passed via Baton Pass
|
||||||
|
const excludeTagTypes = new Set([BattlerTagType.DROWSY, BattlerTagType.INFATUATED, BattlerTagType.FIRE_BOOST]);
|
||||||
|
|
||||||
|
if (excludeTagTypes.has(tag.tagType)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2670,33 +2674,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.updateInfo();
|
this.updateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets whether the given move is currently disabled for this Pokemon.
|
|
||||||
*
|
|
||||||
* @param {Moves} moveId {@linkcode Moves} ID of the move to check
|
|
||||||
* @returns {boolean} `true` if the move is disabled for this Pokemon, otherwise `false`
|
|
||||||
*
|
|
||||||
* @see {@linkcode MoveRestrictionBattlerTag}
|
|
||||||
*/
|
|
||||||
isMoveRestricted(moveId: Moves): boolean {
|
|
||||||
return this.getRestrictingTag(moveId) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the {@link MoveRestrictionBattlerTag} that is restricting a move, if it exists.
|
|
||||||
*
|
|
||||||
* @param {Moves} moveId {@linkcode Moves} ID of the move to check
|
|
||||||
* @returns {MoveRestrictionBattlerTag | null} the first tag on this Pokemon that restricts the move, or `null` if the move is not restricted.
|
|
||||||
*/
|
|
||||||
getRestrictingTag(moveId: Moves): MoveRestrictionBattlerTag | null {
|
|
||||||
for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) {
|
|
||||||
if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId)) {
|
|
||||||
return tag as MoveRestrictionBattlerTag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMoveHistory(): TurnMove[] {
|
getMoveHistory(): TurnMove[] {
|
||||||
return this.battleSummonData.moveHistory;
|
return this.battleSummonData.moveHistory;
|
||||||
}
|
}
|
||||||
@ -4503,6 +4480,8 @@ export interface AttackMoveResult {
|
|||||||
export class PokemonSummonData {
|
export class PokemonSummonData {
|
||||||
public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
public moveQueue: QueuedMove[] = [];
|
public moveQueue: QueuedMove[] = [];
|
||||||
|
public disabledMove: Moves = Moves.NONE;
|
||||||
|
public disabledTurns: number = 0;
|
||||||
public tags: BattlerTag[] = [];
|
public tags: BattlerTag[] = [];
|
||||||
public abilitySuppressed: boolean = false;
|
public abilitySuppressed: boolean = false;
|
||||||
public abilitiesApplied: Abilities[] = [];
|
public abilitiesApplied: Abilities[] = [];
|
||||||
@ -4583,7 +4562,7 @@ export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | Hit
|
|||||||
* It links to {@linkcode Move} class via the move ID.
|
* It links to {@linkcode Move} class via the move ID.
|
||||||
* Compared to {@linkcode Move}, this class also tracks if a move has received.
|
* Compared to {@linkcode Move}, this class also tracks if a move has received.
|
||||||
* PP Ups, amount of PP used, and things like that.
|
* PP Ups, amount of PP used, and things like that.
|
||||||
* @see {@linkcode isUsable} - checks if move is restricted, out of PP, or not implemented.
|
* @see {@linkcode isUsable} - checks if move is disabled, out of PP, or not implemented.
|
||||||
* @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID.
|
* @see {@linkcode getMove} - returns {@linkcode Move} object by looking it up via ID.
|
||||||
* @see {@linkcode usePp} - removes a point of PP from the move.
|
* @see {@linkcode usePp} - removes a point of PP from the move.
|
||||||
* @see {@linkcode getMovePp} - returns amount of PP a move currently has.
|
* @see {@linkcode getMovePp} - returns amount of PP a move currently has.
|
||||||
@ -4603,25 +4582,11 @@ export class PokemonMove {
|
|||||||
this.virtual = !!virtual;
|
this.virtual = !!virtual;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
isUsable(pokemon: Pokemon, ignorePp?: boolean): boolean {
|
||||||
* Checks whether the move can be selected or performed by a Pokemon, without consideration for the move's targets.
|
if (this.moveId && pokemon.summonData?.disabledMove === this.moveId) {
|
||||||
* The move is unusable if it is out of PP, restricted by an effect, or unimplemented.
|
|
||||||
*
|
|
||||||
* @param {Pokemon} pokemon {@linkcode Pokemon} that would be using this move
|
|
||||||
* @param {boolean} ignorePp If `true`, skips the PP check
|
|
||||||
* @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag})
|
|
||||||
* @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`.
|
|
||||||
*/
|
|
||||||
isUsable(pokemon: Pokemon, ignorePp?: boolean, ignoreRestrictionTags?: boolean): boolean {
|
|
||||||
if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1) && !this.getMove().name.endsWith(" (N)");
|
||||||
if (this.getMove().name.endsWith(" (N)")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMove(): Move {
|
getMove(): Move {
|
||||||
|
@ -78,7 +78,6 @@ export class LoadingScene extends SceneBase {
|
|||||||
this.loadAtlas("overlay_hp_boss", "ui");
|
this.loadAtlas("overlay_hp_boss", "ui");
|
||||||
this.loadImage("overlay_exp", "ui");
|
this.loadImage("overlay_exp", "ui");
|
||||||
this.loadImage("icon_owned", "ui");
|
this.loadImage("icon_owned", "ui");
|
||||||
this.loadImage("icon_egg_move", "ui");
|
|
||||||
this.loadImage("ability_bar_left", "ui");
|
this.loadImage("ability_bar_left", "ui");
|
||||||
this.loadImage("bgm_bar", "ui");
|
this.loadImage("bgm_bar", "ui");
|
||||||
this.loadImage("party_exp_bar", "ui");
|
this.loadImage("party_exp_bar", "ui");
|
||||||
@ -247,7 +246,7 @@ export class LoadingScene extends SceneBase {
|
|||||||
} else {
|
} else {
|
||||||
this.loadAtlas("types", "");
|
this.loadAtlas("types", "");
|
||||||
}
|
}
|
||||||
const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es", "pt-BR", "zh-CN"];
|
const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es", "pt_BR", "zh_CN"];
|
||||||
if (lang && availableLangs.includes(lang)) {
|
if (lang && availableLangs.includes(lang)) {
|
||||||
this.loadImage("september-update-"+lang, "events");
|
this.loadImage("september-update-"+lang, "events");
|
||||||
} else {
|
} else {
|
||||||
@ -273,7 +272,6 @@ export class LoadingScene extends SceneBase {
|
|||||||
this.loadImage("gacha_knob", "egg");
|
this.loadImage("gacha_knob", "egg");
|
||||||
|
|
||||||
this.loadImage("egg_list_bg", "ui");
|
this.loadImage("egg_list_bg", "ui");
|
||||||
this.loadImage("egg_summary_bg", "ui");
|
|
||||||
|
|
||||||
this.loadImage("end_m", "cg");
|
this.loadImage("end_m", "cg");
|
||||||
this.loadImage("end_f", "cg");
|
this.loadImage("end_f", "cg");
|
||||||
|
@ -94,6 +94,5 @@
|
|||||||
"retryBattle": "Möchtest du vom Beginn des Kampfes neustarten?",
|
"retryBattle": "Möchtest du vom Beginn des Kampfes neustarten?",
|
||||||
"unlockedSomething": "{{unlockedThing}} wurde freigeschaltet.",
|
"unlockedSomething": "{{unlockedThing}} wurde freigeschaltet.",
|
||||||
"congratulations": "Glückwunsch!",
|
"congratulations": "Glückwunsch!",
|
||||||
"beatModeFirstTime": "{{speciesName}} hat den {{gameMode}} Modus zum ersten Mal beendet! Du erhältst {{newModifier}}!",
|
"beatModeFirstTime": "{{speciesName}} hat den {{gameMode}} Modus zum ersten Mal beendet! Du erhältst {{newModifier}}!"
|
||||||
"eggSkipPrompt": "Zur Ei-Zusammenfassung springen?"
|
|
||||||
}
|
}
|
@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"noneSelected": "Keine ausgewählt",
|
|
||||||
"title": "Herausforderungsmodifikatoren",
|
"title": "Herausforderungsmodifikatoren",
|
||||||
"illegalEvolution": "{{pokemon}} hat sich in ein Pokémon verwandelt, dass für diese Herausforderung nicht zulässig ist!",
|
"illegalEvolution": "{{pokemon}} hat sich in ein Pokémon verwandelt, dass für diese Herausforderung nicht zulässig ist!",
|
||||||
"singleGeneration": {
|
"singleGeneration": {
|
||||||
"name": "Mono-Generation",
|
"name": "Mono-Generation",
|
||||||
"desc": "Du kannst nur Pokémon aus der {{gen}} Generation verwenden.",
|
"desc": "Du kannst nur Pokémon aus der {{gen}} Generation verwenden.",
|
||||||
"desc_default": "Du kannst nur Pokémon aus der gewählten Generation verwenden.",
|
"desc_default": "Du kannst nur Pokémon gewählten Generation verwenden.",
|
||||||
"gen_1": "ersten",
|
"gen_1": "ersten",
|
||||||
"gen_2": "zweiten",
|
"gen_2": "zweiten",
|
||||||
"gen_3": "dritten",
|
"gen_3": "dritten",
|
||||||
|
@ -25,6 +25,5 @@
|
|||||||
"unlinkGoogle": "Google trennen",
|
"unlinkGoogle": "Google trennen",
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"losingProgressionWarning": "Du wirst jeglichen Fortschritt seit Anfang dieses Kampfes verlieren. Fortfahren?",
|
"losingProgressionWarning": "Du wirst jeglichen Fortschritt seit Anfang dieses Kampfes verlieren. Fortfahren?",
|
||||||
"noEggs": "Du brütest aktuell keine Eier aus!",
|
"noEggs": "Du brütest aktuell keine Eier aus!"
|
||||||
"donate": "Spenden"
|
|
||||||
}
|
}
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "Bewegung Touch Steuerung",
|
"moveTouchControls": "Bewegung Touch Steuerung",
|
||||||
"shopOverlayOpacity": "Shop Overlay Deckkraft",
|
"shopOverlayOpacity": "Shop Overlay Deckkraft",
|
||||||
"shopCursorTarget": "Shop-Cursor Ziel",
|
"shopCursorTarget": "Shop-Cursor Ziel",
|
||||||
"rewards": "Items",
|
"items": "Items",
|
||||||
"reroll": "Neu rollen",
|
"reroll": "Neu rollen",
|
||||||
"shop": "Shop",
|
"shop": "Shop",
|
||||||
"checkTeam": "Team überprüfen"
|
"checkTeam": "Team überprüfen"
|
||||||
|
@ -39,6 +39,5 @@
|
|||||||
"matBlock": "Mat Block",
|
"matBlock": "Mat Block",
|
||||||
"craftyShield": "Crafty Shield",
|
"craftyShield": "Crafty Shield",
|
||||||
"tailwind": "Tailwind",
|
"tailwind": "Tailwind",
|
||||||
"happyHour": "Happy Hour",
|
"happyHour": "Happy Hour"
|
||||||
"safeguard": "Safeguard"
|
}
|
||||||
}
|
|
||||||
|
@ -47,11 +47,5 @@
|
|||||||
"tailwindOnRemovePlayer": "Your team's Tailwind petered out!",
|
"tailwindOnRemovePlayer": "Your team's Tailwind petered out!",
|
||||||
"tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!",
|
"tailwindOnRemoveEnemy": "The opposing team's Tailwind petered out!",
|
||||||
"happyHourOnAdd": "Everyone is caught up in the happy atmosphere!",
|
"happyHourOnAdd": "Everyone is caught up in the happy atmosphere!",
|
||||||
"happyHourOnRemove": "The atmosphere returned to normal.",
|
"happyHourOnRemove": "The atmosphere returned to normal."
|
||||||
"safeguardOnAdd": "The whole field is cloaked in a mystical veil!",
|
|
||||||
"safeguardOnAddPlayer": "Your team cloaked itself in a mystical veil!",
|
|
||||||
"safeguardOnAddEnemy": "The opposing team cloaked itself in a mystical veil!",
|
|
||||||
"safeguardOnRemove": "The field is no longer protected by Safeguard!",
|
|
||||||
"safeguardOnRemovePlayer": "Your team is no longer protected by Safeguard!",
|
|
||||||
"safeguardOnRemoveEnemy": "The opposing team is no longer protected by Safeguard!"
|
|
||||||
}
|
}
|
@ -44,7 +44,6 @@
|
|||||||
"moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.",
|
"moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.",
|
||||||
"moveNoPP": "There's no PP left for\nthis move!",
|
"moveNoPP": "There's no PP left for\nthis move!",
|
||||||
"moveDisabled": "{{moveName}} is disabled!",
|
"moveDisabled": "{{moveName}} is disabled!",
|
||||||
"disableInterruptedMove": "{{pokemonNameWithAffix}}'s {{moveName}}\nis disabled!",
|
|
||||||
"noPokeballForce": "An unseen force\nprevents using Poké Balls.",
|
"noPokeballForce": "An unseen force\nprevents using Poké Balls.",
|
||||||
"noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!",
|
"noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!",
|
||||||
"noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!",
|
"noPokeballMulti": "You can only throw a Poké Ball\nwhen there is one Pokémon remaining!",
|
||||||
@ -62,7 +61,6 @@
|
|||||||
"skipItemQuestion": "Are you sure you want to skip taking an item?",
|
"skipItemQuestion": "Are you sure you want to skip taking an item?",
|
||||||
"itemStackFull": "The stack for {{fullItemName}} is full.\nYou will receive {{itemName}} instead.",
|
"itemStackFull": "The stack for {{fullItemName}} is full.\nYou will receive {{itemName}} instead.",
|
||||||
"eggHatching": "Oh?",
|
"eggHatching": "Oh?",
|
||||||
"eggSkipPrompt": "Skip to egg summary?",
|
|
||||||
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?",
|
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?",
|
||||||
"wildPokemonWithAffix": "Wild {{pokemonName}}",
|
"wildPokemonWithAffix": "Wild {{pokemonName}}",
|
||||||
"foePokemonWithAffix": "Foe {{pokemonName}}",
|
"foePokemonWithAffix": "Foe {{pokemonName}}",
|
||||||
|
@ -67,7 +67,5 @@
|
|||||||
"saltCuredLapse": "{{pokemonNameWithAffix}} is hurt by {{moveName}}!",
|
"saltCuredLapse": "{{pokemonNameWithAffix}} is hurt by {{moveName}}!",
|
||||||
"cursedOnAdd": "{{pokemonNameWithAffix}} cut its own HP and put a curse on the {{pokemonName}}!",
|
"cursedOnAdd": "{{pokemonNameWithAffix}} cut its own HP and put a curse on the {{pokemonName}}!",
|
||||||
"cursedLapse": "{{pokemonNameWithAffix}} is afflicted by the Curse!",
|
"cursedLapse": "{{pokemonNameWithAffix}} is afflicted by the Curse!",
|
||||||
"stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!",
|
"stockpilingOnAdd": "{{pokemonNameWithAffix}} stockpiled {{stockpiledCount}}!"
|
||||||
"disabledOnAdd": "{{pokemonNameWithAffix}}'s {{moveName}}\nwas disabled!",
|
}
|
||||||
"disabledLapse": "{{pokemonNameWithAffix}}'s {{moveName}}\nis no longer disabled."
|
|
||||||
}
|
|
@ -413,7 +413,7 @@
|
|||||||
},
|
},
|
||||||
"ariana": {
|
"ariana": {
|
||||||
"encounter": {
|
"encounter": {
|
||||||
"1": "Hold it right there!\nWe can't have someone on the loose.\n$It's harmful to Team Rocket's pride, you see.",
|
"1": "Hold it right there! We can't someone on the loose.\n$It's harmful to Team Rocket's pride, you see.",
|
||||||
"2": "I don't know or care if what I'm doing is right or wrong...\n$I just put my faith in Giovanni and do as I am told",
|
"2": "I don't know or care if what I'm doing is right or wrong...\n$I just put my faith in Giovanni and do as I am told",
|
||||||
"3": "Your trip ends here. I'm going to take you down!"
|
"3": "Your trip ends here. I'm going to take you down!"
|
||||||
},
|
},
|
||||||
|
@ -65,6 +65,5 @@
|
|||||||
"suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!",
|
"suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!",
|
||||||
"revivalBlessing": "{{pokemonName}} was revived!",
|
"revivalBlessing": "{{pokemonName}} was revived!",
|
||||||
"swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!",
|
"swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!",
|
||||||
"exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!",
|
"exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!"
|
||||||
"safeguard": "{{targetName}} is protected by Safeguard!"
|
}
|
||||||
}
|
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "Move Touch Controls",
|
"moveTouchControls": "Move Touch Controls",
|
||||||
"shopOverlayOpacity": "Shop Overlay Opacity",
|
"shopOverlayOpacity": "Shop Overlay Opacity",
|
||||||
"shopCursorTarget": "Shop Cursor Target",
|
"shopCursorTarget": "Shop Cursor Target",
|
||||||
"rewards": "Rewards",
|
"items": "Items",
|
||||||
"reroll": "Reroll",
|
"reroll": "Reroll",
|
||||||
"shop": "Shop",
|
"shop": "Shop",
|
||||||
"checkTeam": "Check Team"
|
"checkTeam": "Check Team"
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
"activeBattleEffects": "Efectos de terreno activos",
|
"activeBattleEffects": "Efectos de Terreno Activos",
|
||||||
"player": "Jugador",
|
"player": "Jugador",
|
||||||
"neutral": "Neutral",
|
"neutral": "Neutral",
|
||||||
"enemy": "Enemigo",
|
"enemy": "Enemigo",
|
||||||
"sunny": "Sol",
|
"sunny": "Sol",
|
||||||
"rain": "Lluvia",
|
"rain": "Lluvia",
|
||||||
"sandstorm": "Tormenta de arena",
|
"sandstorm": "Tormenta de Arena",
|
||||||
"hail": "Granizo",
|
"hail": "Granizo",
|
||||||
"snow": "Nieve",
|
"snow": "Nieve",
|
||||||
"fog": "Niebla",
|
"fog": "Niebla",
|
||||||
"heavyRain": "Diluvio",
|
"heavyRain": "Diluvio",
|
||||||
"harshSun": "Sol abrasador",
|
"harshSun": "Sol Abrasador",
|
||||||
"strongWinds": "Turbulencias",
|
"strongWinds": "Turbulencias",
|
||||||
"misty": "Campo de niebla",
|
"misty": "Campo de Niebla",
|
||||||
"electric": "Campo eléctrico",
|
"electric": "Campo Eléctrico",
|
||||||
"grassy": "Campo de hierba",
|
"grassy": "Campo de Hierba",
|
||||||
"psychic": "Campo psíquico",
|
"psychic": "Campo Psíquico",
|
||||||
"mudSport": "Chapoteo Lodo",
|
"mudSport": "Chapoteo Lodo",
|
||||||
"waterSport": "Hidrochorro",
|
"waterSport": "Hidrochorro",
|
||||||
"spikes": "Púas",
|
"spikes": "Púas",
|
||||||
@ -37,4 +37,4 @@
|
|||||||
"craftyShield": "Truco Defensa",
|
"craftyShield": "Truco Defensa",
|
||||||
"tailwind": "Viento Afín",
|
"tailwind": "Viento Afín",
|
||||||
"happyHour": "Paga Extra"
|
"happyHour": "Paga Extra"
|
||||||
}
|
}
|
@ -1,53 +1,4 @@
|
|||||||
{
|
{
|
||||||
"yourTeam": "tu equipo",
|
|
||||||
"opposingTeam": "el equipo rival",
|
|
||||||
"arenaOnRemove": "Los efectos de {{moveName}} desaparecieron.",
|
|
||||||
"arenaOnRemovePlayer": "Los efectos de {{moveName}}\ndesaparecieron en tu bando.",
|
|
||||||
"arenaOnRemoveEnemy": "Los efectos de {{moveName}}\ndesaparecieron en el bando rival.",
|
|
||||||
"mistOnAdd": "¡Neblina de {{pokemonNameWithAffix}}\nha cubierto a su equipo!",
|
|
||||||
"mistApply": "¡La neblina evita los cambios de estadísticas!",
|
|
||||||
"reflectOnAdd": "¡Reflejo redujo el daño físico!",
|
|
||||||
"reflectOnAddPlayer": "¡Reflejo redujo el daño físico en tu bando!",
|
|
||||||
"reflectOnAddEnemy": "Reflejo redujo el daño físico en el bando rival.",
|
|
||||||
"lightScreenOnAdd": "¡Pantalla de Luz redujo el daño físico!",
|
|
||||||
"lightScreenOnAddPlayer": "¡Pantalla de Luz redujo el daño físico en tu bando!",
|
|
||||||
"lightScreenOnAddEnemy": "¡Pantalla de Luz redujo el daño físico en el bando enemigo!",
|
|
||||||
"auroraVeilOnAdd": "¡Velo Aurora redujo el daño físico!",
|
|
||||||
"auroraVeilOnAddPlayer": "¡Velo Aurora redujo el daño físico en tu bando!",
|
|
||||||
"auroraVeilOnAddEnemy": "¡Velo Aurora redujo el daño físico en el bando rival!",
|
|
||||||
"conditionalProtectOnAdd": "¡{{moveName}} protege a su bando!",
|
|
||||||
"conditionalProtectOnAddPlayer": "¡{{moveName}} protege a tu bando!",
|
|
||||||
"conditionalProtectOnAddEnemy": "¡{{moveName}} protege al bando rival!",
|
|
||||||
"conditionalProtectApply": "¡{{pokemonNameWithAffix}} ha sido protegido por {{moveName}}!",
|
|
||||||
"matBlockOnAdd": "¡{{pokemonNameWithAffix}} va a usar un tatami para bloquear ataques!",
|
|
||||||
"noCritOnAddPlayer": "¡{{moveName}} protege a tu bando de golpes críticos!",
|
|
||||||
"noCritOnAddEnemy": "¡{{moveName}} protege al bando rival de golpes críticos!",
|
|
||||||
"noCritOnRemove": "¡Los efectos de {{moveName}} de {{pokemonNameWithAffix}} se han disipado!",
|
|
||||||
"wishTagOnAdd": "¡El deseo de {{pokemonNameWithAffix}} se ha hecho realidad!",
|
|
||||||
"mudSportOnAdd": "¡Se han debilitado los ataques de tipo Eléctrico!",
|
|
||||||
"mudSportOnRemove": "Chapoteo Lodo ha dejado de surtir efecto.",
|
|
||||||
"waterSportOnAdd": "¡Se han debilitado los ataques\nde tipo Fuego!",
|
|
||||||
"waterSportOnRemove": "Hidrochorro ha dejado de surtir efecto.",
|
|
||||||
"spikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
|
|
||||||
"spikesActivateTrap": "¡Las púas han herido a {{pokemonNameWithAffix}}!",
|
|
||||||
"toxicSpikesOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por {{moveName}}!",
|
|
||||||
"toxicSpikesActivateTrapPoison": "¡{{pokemonNameWithAffix}} ha sido herido por {{moveName}}!",
|
|
||||||
"stealthRockOnAdd": "¡El equipo de {{opponentDesc}} ha sido rodeado por piedras puntiagudas!",
|
|
||||||
"stealthRockActivateTrap": "¡Unas piedras puntiagudas han dañado a {{pokemonNameWithAffix}}!",
|
|
||||||
"stickyWebOnAdd": "¡Una {{moveName}} se extiende a los pies del bando rival!",
|
|
||||||
"stickyWebActivateTrap": "¡{{pokemonName}} ha caído en una red viscosa!",
|
|
||||||
"trickRoomOnAdd": "¡{{pokemonNameWithAffix}} ha alterado las dimensiones!",
|
|
||||||
"trickRoomOnRemove": "Se han restaurado las dimensiones alteradas.",
|
|
||||||
"gravityOnAdd": "¡La gravedad se ha incrementado!",
|
|
||||||
"gravityOnRemove": "La gravedad ha vuelto a su estado normal.",
|
|
||||||
"tailwindOnAdd": "¡Sopla un viento afín!",
|
|
||||||
"tailwindOnAddPlayer": "¡El viento sopla a favor de tu bando!",
|
|
||||||
"tailwindOnAddEnemy": "¡El viento sopla a favor del bando rival!",
|
|
||||||
"tailwindOnRemove": "Ha dejado de soplar el viento afín.",
|
|
||||||
"tailwindOnRemovePlayer": "Ha dejado de soplar el viento que favorecía a tu equipo.",
|
|
||||||
"tailwindOnRemoveEnemy": "Ha dejado de soplar el viento que favorecía al bando rival.",
|
|
||||||
"happyHourOnAdd": "¡La felicidad se respira en el aire!",
|
|
||||||
"happyHourOnRemove": "La felicidad ya no se respira en el aire.",
|
|
||||||
"safeguardOnAdd": "¡Todos los Pokémon están protegidos por Velo Sagrado!",
|
"safeguardOnAdd": "¡Todos los Pokémon están protegidos por Velo Sagrado!",
|
||||||
"safeguardOnAddPlayer": "¡Tu equipo se ha protegido con Velo Sagrado!",
|
"safeguardOnAddPlayer": "¡Tu equipo se ha protegido con Velo Sagrado!",
|
||||||
"safeguardOnAddEnemy": "¡El equipo enemigo se ha protegido con Velo Sagrado!",
|
"safeguardOnAddEnemy": "¡El equipo enemigo se ha protegido con Velo Sagrado!",
|
||||||
|
@ -1,71 +1 @@
|
|||||||
{
|
{}
|
||||||
"trappedDesc": "trampa",
|
|
||||||
"flinchedDesc": "retroceso",
|
|
||||||
"confusedDesc": "confusión",
|
|
||||||
"infatuatedDesc": "enamoramiento",
|
|
||||||
"seedDesc": "drenado",
|
|
||||||
"nightmareDesc": "pesadillas",
|
|
||||||
"ingrainDesc": "raíces",
|
|
||||||
"drowsyDesc": "sueño",
|
|
||||||
"rechargingLapse": "¡{{pokemonNameWithAffix}} necesita\nrecuperarse de su ataque!",
|
|
||||||
"trappedOnAdd": "¡{{pokemonNameWithAffix}} no puede escapar!",
|
|
||||||
"trappedOnRemove": "¡{{pokemonNameWithAffix}} se ha\nliberado de {{moveName}}!",
|
|
||||||
"flinchedLapse": "¡{{pokemonNameWithAffix}} se amedrentó!",
|
|
||||||
"confusedOnAdd": "¡{{pokemonNameWithAffix}} se encuentra confuso!",
|
|
||||||
"confusedOnRemove": "¡{{pokemonNameWithAffix}} ya no está confuso!",
|
|
||||||
"confusedOnOverlap": "¡{{pokemonNameWithAffix}} ya está confuso!",
|
|
||||||
"confusedLapse": "¡{{pokemonNameWithAffix}} está confuso!",
|
|
||||||
"confusedLapseHurtItself": "¡Está tan confuso que se ha herido a sí mismo!",
|
|
||||||
"destinyBondLapseIsBoss": "Mismo Destino no afecta a {{pokemonNameWithAffix}}.",
|
|
||||||
"destinyBondLapse": "¡{{pokemonNameWithAffix2}} ha sufrido\nel mismo destino que {{pokemonNameWithAffix}}!",
|
|
||||||
"infatuatedOnAdd": "¡{{pokemonNameWithAffix}} se ha enamorado\nde {{sourcePokemonName}}!",
|
|
||||||
"infatuatedOnOverlap": "¡{{pokemonNameWithAffix}} ya está enamorado!",
|
|
||||||
"infatuatedLapse": "¡{{pokemonNameWithAffix}} se ha enamorado\ndebido a {{sourcePokemonName}}!",
|
|
||||||
"infatuatedLapseImmobilize": "¡El enamoramiento impide que\n{{pokemonNameWithAffix}} reaccione!",
|
|
||||||
"infatuatedOnRemove": "{{pokemonNameWithAffix}} ya no está enamorado.",
|
|
||||||
"seededOnAdd": "¡{{pokemonNameWithAffix}} ha sido infectado!",
|
|
||||||
"seededLapse": "¡Las drenadoras han restado salud a {{pokemonNameWithAffix}}!",
|
|
||||||
"seededLapseShed": "¡{{pokemonNameWithAffix}} ha absorbido el lodo líquido!",
|
|
||||||
"nightmareOnAdd": "¡{{pokemonNameWithAffix}} se ha sumido en una pesadilla!",
|
|
||||||
"nightmareOnOverlap": "¡{{pokemonNameWithAffix}} ya está teniendo pesadillas!",
|
|
||||||
"nightmareLapse": "¡{{pokemonNameWithAffix}} sufre pesadillas!",
|
|
||||||
"encoreOnAdd": "¡{{pokemonNameWithAffix}} sufre los efectos de Otra Vez!",
|
|
||||||
"encoreOnRemove": "¡{{pokemonNameWithAffix}} ya no sufre los efectos de Otra Vez!",
|
|
||||||
"helpingHandOnAdd": "¡{{pokemonNameWithAffix}} se prepara\npara ayudar a {{pokemonName}}!",
|
|
||||||
"ingrainLapse": "¡{{pokemonNameWithAffix}} ha absorbido\nnutrientes a través de sus raíces!",
|
|
||||||
"ingrainOnTrap": "¡{{pokemonNameWithAffix}} ha echado raíces!",
|
|
||||||
"aquaRingOnAdd": "¡{{pokemonNameWithAffix}} se ha rodeado de un manto de agua!",
|
|
||||||
"aquaRingLapse": "¡{{pokemonName}} restauró sus PS con {{moveName}}!",
|
|
||||||
"drowsyOnAdd": "¡{{pokemonNameWithAffix}} empieza a tener sueño!",
|
|
||||||
"damagingTrapLapse": "¡{{moveName}} hiere a {{pokemonNameWithAffix}}!",
|
|
||||||
"bindOnTrap": "¡{{moveName}} de {{sourcePokemonName}} oprime a {{pokemonNameWithAffix}}!",
|
|
||||||
"wrapOnTrap": "¡{{sourcePokemonName}} ha atrapado a {{pokemonNameWithAffix}} con una constricción!",
|
|
||||||
"vortexOnTrap": "¡{{pokemonNameWithAffix}} no puede salir del torbellino!",
|
|
||||||
"clampOnTrap": "¡{{sourcePokemonNameWithAffix}} ha atenazado a \n{{pokemonName}}!",
|
|
||||||
"sandTombOnTrap": "¡{{pokemonNameWithAffix}} ha sido atrapado por {{moveName}}!",
|
|
||||||
"magmaStormOnTrap": "¡La lluvia ígnea cae sobre {{pokemonNameWithAffix}}!",
|
|
||||||
"snapTrapOnTrap": "¡{{pokemonNameWithAffix}} cayó en un cepo!",
|
|
||||||
"thunderCageOnTrap": "¡{{sourcePokemonNameWithAffix}} ha enjaulado a {{pokemonNameWithAffix}}!",
|
|
||||||
"infestationOnTrap": "¡{{pokemonNameWithAffix}} es presa del acoso de {{sourcePokemonNameWithAffix}}!",
|
|
||||||
"protectedOnAdd": "{{pokemonNameWithAffix}}\nse está protegiendo.",
|
|
||||||
"protectedLapse": "¡{{pokemonNameWithAffix}}\nse ha protegido!",
|
|
||||||
"enduringOnAdd": "{{pokemonNameWithAffix}} se prepara para resistir los ataques...",
|
|
||||||
"enduringLapse": "¡{{pokemonNameWithAffix}} ha encajado el golpe!",
|
|
||||||
"sturdyLapse": "¡{{pokemonNameWithAffix}} ha encajado el golpe!",
|
|
||||||
"perishSongLapse": "La cuenta atrás de Canto Mortal de\n{{pokemonNameWithAffix}} ha bajado a {{turnCount}}.",
|
|
||||||
"centerOfAttentionOnAdd": "¡{{pokemonNameWithAffix}} es el centro de atención!",
|
|
||||||
"truantLapse": "{{pokemonNameWithAffix}} está holgazaneando...",
|
|
||||||
"slowStartOnAdd": "¡{{pokemonNameWithAffix}} no está dando todo de sí!",
|
|
||||||
"slowStartOnRemove": "¡{{pokemonNameWithAffix}} ya puede darlo todo!",
|
|
||||||
"highestStatBoostOnAdd": "¡{{pokemonNameWithAffix}} ha reforzado su {{statName}}!",
|
|
||||||
"highestStatBoostOnRemove": "¡Los efectos de {{abilityName}}\nde {{pokemonNameWithAffix}} han desaparecido!",
|
|
||||||
"magnetRisenOnAdd": "¡{{pokemonNameWithAffix}} levita gracias a un campo electromagnético!",
|
|
||||||
"magnetRisenOnRemove": "¡El campo electromagnético de {{pokemonNameWithAffix}} se ha disipado!",
|
|
||||||
"critBoostOnAdd": "¡{{pokemonNameWithAffix}} se está preparando para luchar!",
|
|
||||||
"critBoostOnRemove": "{{pokemonNameWithAffix}} se ha relajado.",
|
|
||||||
"saltCuredOnAdd": "¡{{pokemonNameWithAffix}} está en salazón!",
|
|
||||||
"saltCuredLapse": "¡{{moveName}} ha herido a {{pokemonNameWithAffix}}!",
|
|
||||||
"cursedOnAdd": "¡{{pokemonNameWithAffix}} sacrifica algunos PS y maldice a {{pokemonName}}!",
|
|
||||||
"cursedLapse": "¡{{pokemonNameWithAffix}} es víctima de una maldición!",
|
|
||||||
"stockpilingOnAdd": "¡{{pokemonNameWithAffix}} ha reservado energía por {{stockpiledCount}}ª vez!"
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"classic": "Clásico",
|
"classic": "Clásica",
|
||||||
"endless": "Infinito",
|
"endless": "Infinita",
|
||||||
"endlessSpliced": "Infinito (Fusión)",
|
"endlessSpliced": "Infinita (Fusión)",
|
||||||
"dailyRun": "Diario",
|
"dailyRun": "Diaria",
|
||||||
"unknown": "Desconocido",
|
"unknown": "Desconicido",
|
||||||
"challenge": "Desafío"
|
"challenge": "Desafío"
|
||||||
}
|
}
|
@ -1,9 +1,4 @@
|
|||||||
{
|
{
|
||||||
"SEND_OUT": "Enviar",
|
|
||||||
"SUMMARY": "Resumen",
|
|
||||||
"CANCEL": "Cancelar",
|
|
||||||
"RELEASE": "Liberar",
|
|
||||||
"APPLY": "Aplicar",
|
|
||||||
"TEACH": "Enseñar",
|
"TEACH": "Enseñar",
|
||||||
"SPLICE": "Fusionar",
|
"SPLICE": "Fusionar",
|
||||||
"UNSPLICE": "Separar",
|
"UNSPLICE": "Separar",
|
||||||
@ -12,23 +7,23 @@
|
|||||||
"TRANSFER": "Transferir",
|
"TRANSFER": "Transferir",
|
||||||
"ALL": "Todo",
|
"ALL": "Todo",
|
||||||
"PASS_BATON": "Relevo",
|
"PASS_BATON": "Relevo",
|
||||||
"UNPAUSE_EVOLUTION": "Reanudar evolución",
|
"UNPAUSE_EVOLUTION": "Reanudar Evolución",
|
||||||
"REVIVE": "Revivir",
|
"REVIVE": "Revivir",
|
||||||
"RENAME": "Rename",
|
"RENAME": "Rename",
|
||||||
"choosePokemon": "Elige a un Pokémon.",
|
"choosePokemon": "Elige a un Pokémon.",
|
||||||
"doWhatWithThisPokemon": "¿Que quieres hacer con este Pokémon?",
|
"doWhatWithThisPokemon": "¿Que quieres hacer con este Pokémon?",
|
||||||
"noEnergy": "¡A {{pokemonName}} no le\nquedan fuerzas para luchar!",
|
"noEnergy": "¡A {{pokemonName}} no le quedan\nfuerzas para luchar!",
|
||||||
"hasEnergy": "¡A {{pokemonName}} le\nquedan fuerzas para luchar!",
|
"hasEnergy": "¡A {{pokemonName}} le quedan\nfuerzas para luchar!",
|
||||||
"cantBeUsed": "¡{{pokemonName}} no puede usarse en este desafío!",
|
"cantBeUsed": "¡{{pokemonName}} no puede usarse\nen este desafío!",
|
||||||
"tooManyItems": "¡{{pokemonName}} tiene\ndemasiado de este objeto!",
|
"tooManyItems": "¡{{pokemonName}} tiene demasiados\nde este objeto!",
|
||||||
"anyEffect": "No tendría ningún efecto.",
|
"anyEffect": "No tendría ningún efecto.",
|
||||||
"unpausedEvolutions": "Se reanudaron las evoluciones de {{pokemonName}}.",
|
"unpausedEvolutions": "Se reanudó las evoluciones de {{pokemonName}}.",
|
||||||
"unspliceConfirmation": "¿Seguro que quiere separar a {{fusionName}}\nde {{pokemonName}}? {{fusionName}} se perderá.",
|
"unspliceConfirmation": "¿Seguro que quiere separar a {{fusionName}}\nde {{pokemonName}}? {{fusionName}} se perderá.",
|
||||||
"wasReverted": "{{fusionName}} se revirtió a {{pokemonName}}.",
|
"wasReverted": "{{fusionName}} se revirtió a {{pokemonName}}.",
|
||||||
"releaseConfirmation": "¿Quieres liberar a {{pokemonName}}?",
|
"releaseConfirmation": "¿Quieres liberar a {{pokemonName}}?",
|
||||||
"releaseInBattle": "¡No puedes liberar un Pokémon que está en batalla!",
|
"releaseInBattle": "¡No puedes liberar un Pokémon que está en batalla!",
|
||||||
"selectAMove": "Selecciona un movimiento.",
|
"selectAMove": "Selecciona un movimiento.",
|
||||||
"changeQuantity": "Selecciona un ítem para transferir.\nUsa < y > para calibrar.",
|
"changeQuantity": "Selecciona un objeto equipado para transferir.\nUsa < y > para cambiar la cantidad.",
|
||||||
"selectAnotherPokemonToSplice": "Selecciona otro Pokémon para fusionar.",
|
"selectAnotherPokemonToSplice": "Selecciona otro Pokémon para fusionar.",
|
||||||
"cancel": "Salir",
|
"cancel": "Salir",
|
||||||
"able": "Apto",
|
"able": "Apto",
|
||||||
@ -41,7 +36,7 @@
|
|||||||
"thisIsWhereWePart": "¡Aquí es donde nos despedimos, {{pokemonName}}!",
|
"thisIsWhereWePart": "¡Aquí es donde nos despedimos, {{pokemonName}}!",
|
||||||
"illMissYou": "¡Te echaré de menos, {{pokemonName}}!",
|
"illMissYou": "¡Te echaré de menos, {{pokemonName}}!",
|
||||||
"illNeverForgetYou": "¡Nunca te olvidaré, {{pokemonName}}!",
|
"illNeverForgetYou": "¡Nunca te olvidaré, {{pokemonName}}!",
|
||||||
"untilWeMeetAgain": "¡Hasta que nos volvamos a\nencontrar, {{pokemonName}}!",
|
"untilWeMeetAgain": "¡Hasta que nos volvamos a encontrar, {{pokemonName}}!",
|
||||||
"sayonara": "¡Sayonara, {{pokemonName}}!",
|
"sayonara": "¡Sayonara, {{pokemonName}}!",
|
||||||
"smellYaLater": "¡Nos vemos luego, {{pokemonName}}!"
|
"smellYaLater": "¡Nos vemos luego, {{pokemonName}}!"
|
||||||
}
|
}
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "Controles táctiles",
|
"moveTouchControls": "Controles táctiles",
|
||||||
"shopOverlayOpacity": "Opacidad de la fase de compra",
|
"shopOverlayOpacity": "Opacidad de la fase de compra",
|
||||||
"shopCursorTarget": "Cursor de la tienda",
|
"shopCursorTarget": "Cursor de la tienda",
|
||||||
"rewards": "Objetos",
|
"items": "Objetos",
|
||||||
"reroll": "Actualizar",
|
"reroll": "Actualizar",
|
||||||
"shop": "Tienda",
|
"shop": "Tienda",
|
||||||
"checkTeam": "Ver equipo"
|
"checkTeam": "Ver equipo"
|
||||||
|
@ -94,6 +94,5 @@
|
|||||||
"retryBattle": "Voulez-vous réessayer depuis le début du combat ?",
|
"retryBattle": "Voulez-vous réessayer depuis le début du combat ?",
|
||||||
"unlockedSomething": "{{unlockedThing}}\na été débloqué.",
|
"unlockedSomething": "{{unlockedThing}}\na été débloqué.",
|
||||||
"congratulations": "Félicitations !",
|
"congratulations": "Félicitations !",
|
||||||
"beatModeFirstTime": "{{speciesName}} a battu le mode {{gameMode}} pour la première fois !\nVous avez reçu {{newModifier}} !",
|
"beatModeFirstTime": "{{speciesName}} a battu le mode {{gameMode}} pour la première fois !\nVous avez reçu {{newModifier}} !"
|
||||||
"eggSkipPrompt": "Aller directement au résumé des Œufs éclos ?"
|
}
|
||||||
}
|
|
@ -25,6 +25,5 @@
|
|||||||
"unlinkGoogle": "Délier Google",
|
"unlinkGoogle": "Délier Google",
|
||||||
"cancel": "Retour",
|
"cancel": "Retour",
|
||||||
"losingProgressionWarning": "Vous allez perdre votre progression depuis le début du combat. Continuer ?",
|
"losingProgressionWarning": "Vous allez perdre votre progression depuis le début du combat. Continuer ?",
|
||||||
"noEggs": "Vous ne faites actuellement\néclore aucun Œuf !",
|
"noEggs": "Vous ne faites actuellement\néclore aucun Œuf !"
|
||||||
"donate": "Faire un don"
|
}
|
||||||
}
|
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "Déplacer les contrôles tactiles",
|
"moveTouchControls": "Déplacer les contrôles tactiles",
|
||||||
"shopOverlayOpacity": "Opacité boutique",
|
"shopOverlayOpacity": "Opacité boutique",
|
||||||
"shopCursorTarget": "Choix après relance",
|
"shopCursorTarget": "Choix après relance",
|
||||||
"rewards": "Obj. gratuits",
|
"items": "Obj. gratuits",
|
||||||
"reroll": "Relance",
|
"reroll": "Relance",
|
||||||
"shop": "Boutique",
|
"shop": "Boutique",
|
||||||
"checkTeam": "Équipe"
|
"checkTeam": "Équipe"
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"moveTouchControls": "Move Touch Controls",
|
"moveTouchControls": "Move Touch Controls",
|
||||||
"shopOverlayOpacity": "Opacità Finestra Negozio",
|
"shopOverlayOpacity": "Opacità Finestra Negozio",
|
||||||
"shopCursorTarget": "Target Cursore Negozio",
|
"shopCursorTarget": "Target Cursore Negozio",
|
||||||
"rewards": "Oggetti",
|
"items": "Oggetti",
|
||||||
"reroll": "Rerolla",
|
"reroll": "Rerolla",
|
||||||
"shop": "Negozio",
|
"shop": "Negozio",
|
||||||
"checkTeam": "Squadra"
|
"checkTeam": "Squadra"
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "タッチ移動操作",
|
"moveTouchControls": "タッチ移動操作",
|
||||||
"shopOverlayOpacity": "ショップオーバレイ不透明度",
|
"shopOverlayOpacity": "ショップオーバレイ不透明度",
|
||||||
"shopCursorTarget": "ショップカーソル初位置",
|
"shopCursorTarget": "ショップカーソル初位置",
|
||||||
"rewards": "ご褒美",
|
"items": "アイテム",
|
||||||
"reroll": "選択肢変更",
|
"reroll": "選択肢変更",
|
||||||
"shop": "ショップ",
|
"shop": "ショップ",
|
||||||
"checkTeam": "手持ちを確認"
|
"checkTeam": "手持ちを確認"
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "터치 컨트롤 이동",
|
"moveTouchControls": "터치 컨트롤 이동",
|
||||||
"shopOverlayOpacity": "상점 오버레이 투명도",
|
"shopOverlayOpacity": "상점 오버레이 투명도",
|
||||||
"shopCursorTarget": "상점 커서 위치",
|
"shopCursorTarget": "상점 커서 위치",
|
||||||
"rewards": "아이템",
|
"items": "아이템",
|
||||||
"reroll": "갱신",
|
"reroll": "갱신",
|
||||||
"shop": "상점",
|
"shop": "상점",
|
||||||
"checkTeam": "파티 확인"
|
"checkTeam": "파티 확인"
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
"moveTouchControls": "Mover Controles de Toque",
|
"moveTouchControls": "Mover Controles de Toque",
|
||||||
"shopOverlayOpacity": "Opacidade da Loja",
|
"shopOverlayOpacity": "Opacidade da Loja",
|
||||||
"shopCursorTarget": "Alvo do Cursor da Loja",
|
"shopCursorTarget": "Alvo do Cursor da Loja",
|
||||||
"rewards": "Itens",
|
"items": "Itens",
|
||||||
"reroll": "Atualizar",
|
"reroll": "Atualizar",
|
||||||
"shop": "Loja",
|
"shop": "Loja",
|
||||||
"checkTeam": "Checar Time"
|
"checkTeam": "Checar Time"
|
||||||
|
@ -99,7 +99,7 @@
|
|||||||
"moveTouchControls": "移动触摸控制",
|
"moveTouchControls": "移动触摸控制",
|
||||||
"shopOverlayOpacity": "商店显示不透明度",
|
"shopOverlayOpacity": "商店显示不透明度",
|
||||||
"shopCursorTarget": "商店指针位置",
|
"shopCursorTarget": "商店指针位置",
|
||||||
"rewards": "道具",
|
"items": "道具",
|
||||||
"reroll": "刷新",
|
"reroll": "刷新",
|
||||||
"shop": "购买",
|
"shop": "购买",
|
||||||
"checkTeam": "检查队伍"
|
"checkTeam": "检查队伍"
|
||||||
|
@ -2488,7 +2488,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string {
|
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string {
|
||||||
return i18next.t("modifier:turnHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.getNameToRender(), typeName: this.type.name });
|
return i18next.t("modifier:turnHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.name, typeName: this.type.name });
|
||||||
}
|
}
|
||||||
|
|
||||||
getMaxHeldItemCount(pokemon: Pokemon): integer {
|
getMaxHeldItemCount(pokemon: Pokemon): integer {
|
||||||
|
@ -1,34 +1,31 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene.js";
|
||||||
import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability";
|
import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability.js";
|
||||||
import { Stat } from "#app/enums/stat";
|
import { Stat } from "#app/enums/stat.js";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect.js";
|
||||||
import Pokemon, { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils.js";
|
||||||
import { BattleEndPhase } from "./battle-end-phase";
|
import { BattleEndPhase } from "./battle-end-phase";
|
||||||
import { NewBattlePhase } from "./new-battle-phase";
|
import { NewBattlePhase } from "./new-battle-phase";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
|
|
||||||
export class AttemptRunPhase extends PokemonPhase {
|
export class AttemptRunPhase extends PokemonPhase {
|
||||||
constructor(scene: BattleScene, fieldIndex: number) {
|
constructor(scene: BattleScene, fieldIndex: integer) {
|
||||||
super(scene, fieldIndex);
|
super(scene, fieldIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
const playerField = this.scene.getPlayerField();
|
const playerPokemon = this.getPokemon();
|
||||||
const enemyField = this.scene.getEnemyField();
|
const enemyField = this.scene.getEnemyField();
|
||||||
|
|
||||||
const playerPokemon = this.getPokemon();
|
const enemySpeed = enemyField.reduce((total: integer, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0) / enemyField.length;
|
||||||
|
|
||||||
const escapeChance = new Utils.NumberHolder(0);
|
|
||||||
|
|
||||||
this.attemptRunAway(playerField, enemyField, escapeChance);
|
|
||||||
|
|
||||||
|
const escapeChance = new Utils.IntegerHolder((((playerPokemon.getStat(Stat.SPD) * 128) / enemySpeed) + (30 * this.scene.currentBattle.escapeAttempts++)) % 256);
|
||||||
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
|
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
|
||||||
|
|
||||||
if (Utils.randSeedInt(100) < escapeChance.value) {
|
if (playerPokemon.randSeedInt(256) < escapeChance.value) {
|
||||||
this.scene.playSound("se/flee");
|
this.scene.playSound("se/flee");
|
||||||
this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
|
this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
|
||||||
|
|
||||||
@ -56,48 +53,4 @@ export class AttemptRunPhase extends PokemonPhase {
|
|||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
attemptRunAway(playerField: PlayerPokemon[], enemyField: EnemyPokemon[], escapeChance: Utils.NumberHolder) {
|
|
||||||
/** Sum of the speed of all enemy pokemon on the field */
|
|
||||||
const enemySpeed = enemyField.reduce((total: number, enemyPokemon: Pokemon) => total + enemyPokemon.getStat(Stat.SPD), 0);
|
|
||||||
/** Sum of the speed of all player pokemon on the field */
|
|
||||||
const playerSpeed = playerField.reduce((total: number, playerPokemon: Pokemon) => total + playerPokemon.getStat(Stat.SPD), 0);
|
|
||||||
|
|
||||||
/* The way the escape chance works is by looking at the difference between your speed and the enemy field's average speed as a ratio. The higher this ratio, the higher your chance of success.
|
|
||||||
* However, there is a cap for the ratio of your speed vs enemy speed which beyond that point, you won't gain any advantage. It also looks at how many times you've tried to escape.
|
|
||||||
* Again, the more times you've tried to escape, the higher your odds of escaping. Bosses and non-bosses are calculated differently - bosses are harder to escape from vs non-bosses
|
|
||||||
* Finally, there's a minimum and maximum escape chance as well so that escapes aren't guaranteed, yet they are never 0 either.
|
|
||||||
* The percentage chance to escape from a pokemon for both bosses and non bosses is linear and based on the minimum and maximum chances, and the speed ratio cap.
|
|
||||||
*
|
|
||||||
* At the time of writing, these conditions should be met:
|
|
||||||
* - The minimum escape chance should be 5% for bosses and non bosses
|
|
||||||
* - Bosses should have a maximum escape chance of 25%, whereas non-bosses should be 95%
|
|
||||||
* - The bonus per previous escape attempt should be 2% for bosses and 10% for non-bosses
|
|
||||||
* - The speed ratio cap should be 6x for bosses and 4x for non-bosses
|
|
||||||
* - The "default" escape chance when your speed equals the enemy speed should be 8.33% for bosses and 27.5% for non-bosses
|
|
||||||
*
|
|
||||||
* From the above, we can calculate the below values
|
|
||||||
*/
|
|
||||||
|
|
||||||
let isBoss = false;
|
|
||||||
for (let e = 0; e < enemyField.length; e++) {
|
|
||||||
isBoss = isBoss || enemyField[e].isBoss(); // this line checks if any of the enemy pokemon on the field are bosses; if so, the calculation for escaping is different
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The ratio between the speed of your active pokemon and the speed of the enemy field */
|
|
||||||
const speedRatio = playerSpeed / enemySpeed;
|
|
||||||
/** The max ratio before escape chance stops increasing. Increased if there is a boss on the field */
|
|
||||||
const speedCap = isBoss ? 6 : 4;
|
|
||||||
/** Minimum percent chance to escape */
|
|
||||||
const minChance = 5;
|
|
||||||
/** Maximum percent chance to escape. Decreased if a boss is on the field */
|
|
||||||
const maxChance = isBoss ? 25 : 95;
|
|
||||||
/** How much each escape attempt increases the chance of the next attempt. Decreased if a boss is on the field */
|
|
||||||
const escapeBonus = isBoss ? 2 : 10;
|
|
||||||
/** Slope of the escape chance curve */
|
|
||||||
const escapeSlope = (maxChance - minChance) / speedCap;
|
|
||||||
|
|
||||||
// This will calculate the escape chance given all of the above and clamp it to the range of [`minChance`, `maxChance`]
|
|
||||||
escapeChance.value = Phaser.Math.Clamp(Math.round((escapeSlope * speedRatio) + minChance + (escapeBonus * this.scene.currentBattle.escapeAttempts++)), minChance, maxChance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -107,9 +107,8 @@ export class CommandPhase extends FieldPhase {
|
|||||||
|
|
||||||
// Decides between a Disabled, Not Implemented, or No PP translation message
|
// Decides between a Disabled, Not Implemented, or No PP translation message
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
playerPokemon.isMoveRestricted(move.moveId)
|
playerPokemon.summonData.disabledMove === move.moveId ? "battle:moveDisabled" :
|
||||||
? playerPokemon.getRestrictingTag(move.moveId)!.selectionDeniedText(playerPokemon, move.moveId)
|
move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP";
|
||||||
: move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP";
|
|
||||||
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
|
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
|
||||||
|
|
||||||
this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => {
|
this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => {
|
||||||
|
@ -1,29 +1,23 @@
|
|||||||
import BattleScene, { AnySound } from "#app/battle-scene";
|
import BattleScene, { AnySound } from "#app/battle-scene.js";
|
||||||
import { Egg } from "#app/data/egg";
|
import { Egg, EGG_SEED } from "#app/data/egg.js";
|
||||||
import { EggCountChangedEvent } from "#app/events/egg";
|
import { EggCountChangedEvent } from "#app/events/egg.js";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import { PlayerPokemon } from "#app/field/pokemon.js";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase.js";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv.js";
|
||||||
import EggCounterContainer from "#app/ui/egg-counter-container";
|
import EggCounterContainer from "#app/ui/egg-counter-container.js";
|
||||||
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
|
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler.js";
|
||||||
import PokemonInfoContainer from "#app/ui/pokemon-info-container";
|
import PokemonInfoContainer from "#app/ui/pokemon-info-container.js";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils.js";
|
||||||
import { EggLapsePhase } from "./egg-lapse-phase";
|
|
||||||
import { EggHatchData } from "#app/data/egg-hatch-data";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that represents egg hatching
|
* Class that represents egg hatching
|
||||||
*/
|
*/
|
||||||
export class EggHatchPhase extends Phase {
|
export class EggHatchPhase extends Phase {
|
||||||
/** The egg that is hatching */
|
/** The egg that is hatching */
|
||||||
private egg: Egg;
|
private egg: Egg;
|
||||||
/** The new EggHatchData for the egg/pokemon that hatches */
|
|
||||||
private eggHatchData: EggHatchData;
|
|
||||||
|
|
||||||
/** The number of eggs that are hatching */
|
/** The number of eggs that are hatching */
|
||||||
private eggsToHatchCount: integer;
|
private eggsToHatchCount: integer;
|
||||||
@ -64,11 +58,10 @@ export class EggHatchPhase extends Phase {
|
|||||||
private skipped: boolean;
|
private skipped: boolean;
|
||||||
/** The sound effect being played when the egg is hatched */
|
/** The sound effect being played when the egg is hatched */
|
||||||
private evolutionBgm: AnySound;
|
private evolutionBgm: AnySound;
|
||||||
private eggLapsePhase: EggLapsePhase;
|
|
||||||
|
|
||||||
constructor(scene: BattleScene, hatchScene: EggLapsePhase, egg: Egg, eggsToHatchCount: integer) {
|
constructor(scene: BattleScene, egg: Egg, eggsToHatchCount: integer) {
|
||||||
super(scene);
|
super(scene);
|
||||||
this.eggLapsePhase = hatchScene;
|
|
||||||
this.egg = egg;
|
this.egg = egg;
|
||||||
this.eggsToHatchCount = eggsToHatchCount;
|
this.eggsToHatchCount = eggsToHatchCount;
|
||||||
}
|
}
|
||||||
@ -314,7 +307,6 @@ export class EggHatchPhase extends Phase {
|
|||||||
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
|
* Function to do the logic and animation of completing a hatch and revealing the Pokemon
|
||||||
*/
|
*/
|
||||||
doReveal(): void {
|
doReveal(): void {
|
||||||
// set the previous dex data so info container can show new unlocks in egg summary
|
|
||||||
const isShiny = this.pokemon.isShiny();
|
const isShiny = this.pokemon.isShiny();
|
||||||
if (this.pokemon.species.subLegendary) {
|
if (this.pokemon.species.subLegendary) {
|
||||||
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
||||||
@ -353,13 +345,13 @@ export class EggHatchPhase extends Phase {
|
|||||||
this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => {
|
this.scene.ui.showText(i18next.t("egg:hatchFromTheEgg", { pokemonName: getPokemonNameWithAffix(this.pokemon) }), null, () => {
|
||||||
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs);
|
||||||
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
|
this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => {
|
||||||
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then((value) => {
|
this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => {
|
||||||
this.eggHatchData.setEggMoveUnlocked(value);
|
|
||||||
this.scene.ui.showText("", 0);
|
this.scene.ui.showText("", 0);
|
||||||
this.end();
|
this.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, null, true, 3000);
|
}, null, true, 3000);
|
||||||
|
//this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
@ -443,11 +435,17 @@ export class EggHatchPhase extends Phase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a Pokemon to be hatched by the egg
|
* Generates a Pokemon to be hatched by the egg
|
||||||
* Also stores the generated pokemon in this.eggHatchData
|
|
||||||
* @returns the hatched PlayerPokemon
|
* @returns the hatched PlayerPokemon
|
||||||
*/
|
*/
|
||||||
generatePokemon(): PlayerPokemon {
|
generatePokemon(): PlayerPokemon {
|
||||||
this.eggHatchData = this.eggLapsePhase.generatePokemon(this.egg);
|
let ret: PlayerPokemon;
|
||||||
return this.eggHatchData.pokemon;
|
|
||||||
|
this.scene.executeWithSeedOffset(() => {
|
||||||
|
ret = this.egg.generatePlayerPokemon(this.scene);
|
||||||
|
this.eggMoveIndex = this.egg.eggMoveIndex;
|
||||||
|
|
||||||
|
}, this.egg.id, EGG_SEED.toString());
|
||||||
|
|
||||||
|
return ret!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,11 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene.js";
|
||||||
import { Egg, EGG_SEED } from "#app/data/egg";
|
import { Egg } from "#app/data/egg.js";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Overrides from "#app/overrides";
|
import Overrides from "#app/overrides";
|
||||||
import { EggHatchPhase } from "./egg-hatch-phase";
|
import { EggHatchPhase } from "./egg-hatch-phase";
|
||||||
import { Mode } from "#app/ui/ui";
|
|
||||||
import { achvs } from "#app/system/achv";
|
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import { EggSummaryPhase } from "./egg-summary-phase";
|
|
||||||
import { EggHatchData } from "#app/data/egg-hatch-data";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Phase that handles updating eggs, and hatching any ready eggs
|
|
||||||
* Also handles prompts for skipping animation, and calling the egg summary phase
|
|
||||||
*/
|
|
||||||
export class EggLapsePhase extends Phase {
|
export class EggLapsePhase extends Phase {
|
||||||
|
|
||||||
private eggHatchData: EggHatchData[] = [];
|
|
||||||
private readonly minEggsToPromptSkip: number = 5;
|
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
super(scene);
|
super(scene);
|
||||||
}
|
}
|
||||||
@ -28,111 +16,20 @@ export class EggLapsePhase extends Phase {
|
|||||||
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => {
|
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => {
|
||||||
return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1;
|
return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1;
|
||||||
});
|
});
|
||||||
const eggsToHatchCount: number = eggsToHatch.length;
|
|
||||||
this.eggHatchData= [];
|
|
||||||
|
|
||||||
if (eggsToHatchCount > 0) {
|
let eggCount: integer = eggsToHatch.length;
|
||||||
|
|
||||||
if (eggsToHatchCount >= this.minEggsToPromptSkip) {
|
if (eggCount) {
|
||||||
this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => {
|
this.scene.queueMessage(i18next.t("battle:eggHatching"));
|
||||||
// show prompt for skip
|
|
||||||
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0);
|
for (const egg of eggsToHatch) {
|
||||||
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
|
this.scene.unshiftPhase(new EggHatchPhase(this.scene, egg, eggCount));
|
||||||
this.hatchEggsSkipped(eggsToHatch);
|
if (eggCount > 0) {
|
||||||
this.showSummary();
|
eggCount--;
|
||||||
}, () => {
|
}
|
||||||
this.hatchEggsRegular(eggsToHatch);
|
|
||||||
this.showSummary();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}, 100, true);
|
|
||||||
} else {
|
|
||||||
// regular hatches, no summary
|
|
||||||
this.scene.queueMessage(i18next.t("battle:eggHatching"));
|
|
||||||
this.hatchEggsRegular(eggsToHatch);
|
|
||||||
this.end();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hatches eggs normally one by one, showing animations
|
|
||||||
* @param eggsToHatch list of eggs to hatch
|
|
||||||
*/
|
|
||||||
hatchEggsRegular(eggsToHatch: Egg[]) {
|
|
||||||
let eggsToHatchCount: number = eggsToHatch.length;
|
|
||||||
for (const egg of eggsToHatch) {
|
|
||||||
this.scene.unshiftPhase(new EggHatchPhase(this.scene, this, egg, eggsToHatchCount));
|
|
||||||
eggsToHatchCount--;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hatches eggs with no animations
|
|
||||||
* @param eggsToHatch list of eggs to hatch
|
|
||||||
*/
|
|
||||||
hatchEggsSkipped(eggsToHatch: Egg[]) {
|
|
||||||
for (const egg of eggsToHatch) {
|
|
||||||
this.hatchEggSilently(egg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showSummary() {
|
|
||||||
this.scene.unshiftPhase(new EggSummaryPhase(this.scene, this.eggHatchData));
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Hatches an egg and stores it in the local EggHatchData array without animations
|
|
||||||
* Also validates the achievements for the hatched pokemon and removes the egg
|
|
||||||
* @param egg egg to hatch
|
|
||||||
*/
|
|
||||||
hatchEggSilently(egg: Egg) {
|
|
||||||
const eggIndex = this.scene.gameData.eggs.findIndex(e => e.id === egg.id);
|
|
||||||
if (eggIndex === -1) {
|
|
||||||
return this.end();
|
|
||||||
}
|
|
||||||
this.scene.gameData.eggs.splice(eggIndex, 1);
|
|
||||||
|
|
||||||
const data = this.generatePokemon(egg);
|
|
||||||
const pokemon = data.pokemon;
|
|
||||||
if (pokemon.fusionSpecies) {
|
|
||||||
pokemon.clearFusionSpecies();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pokemon.species.subLegendary) {
|
|
||||||
this.scene.validateAchv(achvs.HATCH_SUB_LEGENDARY);
|
|
||||||
}
|
|
||||||
if (pokemon.species.legendary) {
|
|
||||||
this.scene.validateAchv(achvs.HATCH_LEGENDARY);
|
|
||||||
}
|
|
||||||
if (pokemon.species.mythical) {
|
|
||||||
this.scene.validateAchv(achvs.HATCH_MYTHICAL);
|
|
||||||
}
|
|
||||||
if (pokemon.isShiny()) {
|
|
||||||
this.scene.validateAchv(achvs.HATCH_SHINY);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a Pokemon and creates a new EggHatchData instance for the given egg
|
|
||||||
* @param egg the egg to hatch
|
|
||||||
* @returns the hatched PlayerPokemon
|
|
||||||
*/
|
|
||||||
generatePokemon(egg: Egg): EggHatchData {
|
|
||||||
let ret: PlayerPokemon;
|
|
||||||
let newHatchData: EggHatchData;
|
|
||||||
this.scene.executeWithSeedOffset(() => {
|
|
||||||
ret = egg.generatePlayerPokemon(this.scene);
|
|
||||||
newHatchData = new EggHatchData(this.scene, ret, egg.eggMoveIndex);
|
|
||||||
newHatchData.setDex();
|
|
||||||
this.eggHatchData.push(newHatchData);
|
|
||||||
|
|
||||||
}, egg.id, EGG_SEED.toString());
|
|
||||||
return newHatchData!;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { Phase } from "#app/phase";
|
|
||||||
import { Mode } from "#app/ui/ui";
|
|
||||||
import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler";
|
|
||||||
import { EggHatchData } from "#app/data/egg-hatch-data";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represents the egg summary phase
|
|
||||||
* It does some of the function for updating egg data
|
|
||||||
* Phase is handled mostly by the egg-hatch-scene-handler UI
|
|
||||||
*/
|
|
||||||
export class EggSummaryPhase extends Phase {
|
|
||||||
private eggHatchData: EggHatchData[];
|
|
||||||
private eggHatchHandler: EggHatchSceneHandler;
|
|
||||||
|
|
||||||
constructor(scene: BattleScene, eggHatchData: EggHatchData[]) {
|
|
||||||
super(scene);
|
|
||||||
this.eggHatchData = eggHatchData;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
super.start();
|
|
||||||
|
|
||||||
// updates next pokemon once the current update has been completed
|
|
||||||
const updateNextPokemon = (i: number) => {
|
|
||||||
if (i >= this.eggHatchData.length) {
|
|
||||||
this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => {
|
|
||||||
this.scene.fadeOutBgm(undefined, false);
|
|
||||||
this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler;
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.eggHatchData[i].setDex();
|
|
||||||
this.eggHatchData[i].updatePokemon().then(() => {
|
|
||||||
if (i < this.eggHatchData.length) {
|
|
||||||
updateNextPokemon(i + 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
updateNextPokemon(0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
end() {
|
|
||||||
this.eggHatchHandler.clear();
|
|
||||||
this.scene.ui.setModeForceTransition(Mode.MESSAGE).then(() => {});
|
|
||||||
super.end();
|
|
||||||
}
|
|
||||||
}
|
|
@ -161,11 +161,9 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
return this.scene.reset(true);
|
return this.scene.reset(true);
|
||||||
}
|
}
|
||||||
this.doEncounter();
|
this.doEncounter();
|
||||||
this.scene.resetSeed();
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.doEncounter();
|
this.doEncounter();
|
||||||
this.scene.resetSeed();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -77,8 +77,4 @@ export class EnemyCommandPhase extends FieldPhase {
|
|||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
getFieldIndex(): number {
|
|
||||||
return this.fieldIndex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -44,8 +44,8 @@ export class MovePhase extends BattlePhase {
|
|||||||
this.cancelled = false;
|
this.cancelled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
canMove(ignoreDisableTags?: boolean): boolean {
|
canMove(): boolean {
|
||||||
return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp, ignoreDisableTags) && !!this.targets.length;
|
return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp) && !!this.targets.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Signifies the current move should fail but still use PP */
|
/**Signifies the current move should fail but still use PP */
|
||||||
@ -63,7 +63,10 @@ export class MovePhase extends BattlePhase {
|
|||||||
|
|
||||||
console.log(Moves[this.move.moveId]);
|
console.log(Moves[this.move.moveId]);
|
||||||
|
|
||||||
if (!this.canMove(true)) {
|
if (!this.canMove()) {
|
||||||
|
if (this.move.moveId && this.pokemon.summonData?.disabledMove === this.move.moveId) {
|
||||||
|
this.scene.queueMessage(i18next.t("battle:moveDisabled", { moveName: this.move.getName() }));
|
||||||
|
}
|
||||||
if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails
|
if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails
|
||||||
this.fail();
|
this.fail();
|
||||||
this.showMoveText();
|
this.showMoveText();
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import BattleScene from "#app/battle-scene.js";
|
import BattleScene from "#app/battle-scene.js";
|
||||||
import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/ability.js";
|
import { applyPostTurnAbAttrs, PostTurnAbAttr } from "#app/data/ability.js";
|
||||||
import { BattlerTagLapseType } from "#app/data/battler-tags.js";
|
import { BattlerTagLapseType } from "#app/data/battler-tags.js";
|
||||||
|
import { allMoves } from "#app/data/move.js";
|
||||||
import { TerrainType } from "#app/data/terrain.js";
|
import { TerrainType } from "#app/data/terrain.js";
|
||||||
|
import { Moves } from "#app/enums/moves.js";
|
||||||
import { WeatherType } from "#app/enums/weather-type.js";
|
import { WeatherType } from "#app/enums/weather-type.js";
|
||||||
import { TurnEndEvent } from "#app/events/battle-scene.js";
|
import { TurnEndEvent } from "#app/events/battle-scene.js";
|
||||||
import Pokemon from "#app/field/pokemon.js";
|
import Pokemon from "#app/field/pokemon.js";
|
||||||
@ -9,6 +11,7 @@ import { getPokemonNameWithAffix } from "#app/messages.js";
|
|||||||
import { TurnHealModifier, EnemyTurnHealModifier, EnemyStatusEffectHealChanceModifier, TurnStatusEffectModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier.js";
|
import { TurnHealModifier, EnemyTurnHealModifier, EnemyStatusEffectHealChanceModifier, TurnStatusEffectModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier.js";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { FieldPhase } from "./field-phase";
|
import { FieldPhase } from "./field-phase";
|
||||||
|
import { MessagePhase } from "./message-phase";
|
||||||
import { PokemonHealPhase } from "./pokemon-heal-phase";
|
import { PokemonHealPhase } from "./pokemon-heal-phase";
|
||||||
|
|
||||||
export class TurnEndPhase extends FieldPhase {
|
export class TurnEndPhase extends FieldPhase {
|
||||||
@ -25,6 +28,11 @@ export class TurnEndPhase extends FieldPhase {
|
|||||||
const handlePokemon = (pokemon: Pokemon) => {
|
const handlePokemon = (pokemon: Pokemon) => {
|
||||||
pokemon.lapseTags(BattlerTagLapseType.TURN_END);
|
pokemon.lapseTags(BattlerTagLapseType.TURN_END);
|
||||||
|
|
||||||
|
if (pokemon.summonData.disabledMove && !--pokemon.summonData.disabledTurns) {
|
||||||
|
this.scene.pushPhase(new MessagePhase(this.scene, i18next.t("battle:notDisabled", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: allMoves[pokemon.summonData.disabledMove].name })));
|
||||||
|
pokemon.summonData.disabledMove = Moves.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon);
|
this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon);
|
||||||
|
|
||||||
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
|
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
|
||||||
|
@ -946,7 +946,7 @@ export class GameData {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSessionSaveData(scene: BattleScene): SessionSaveData {
|
private getSessionSaveData(scene: BattleScene): SessionSaveData {
|
||||||
return {
|
return {
|
||||||
seed: scene.seed,
|
seed: scene.seed,
|
||||||
playTime: scene.sessionPlayTime,
|
playTime: scene.sessionPlayTime,
|
||||||
@ -1553,11 +1553,11 @@ export class GameData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise<void> {
|
setPokemonCaught(pokemon: Pokemon, incrementCount: boolean = true, fromEgg: boolean = false): Promise<void> {
|
||||||
return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg, showMessage);
|
return this.setPokemonSpeciesCaught(pokemon, pokemon.species, incrementCount, fromEgg);
|
||||||
}
|
}
|
||||||
|
|
||||||
setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false, showMessage: boolean = true): Promise<void> {
|
setPokemonSpeciesCaught(pokemon: Pokemon, species: PokemonSpecies, incrementCount: boolean = true, fromEgg: boolean = false): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const dexEntry = this.dexData[species.speciesId];
|
const dexEntry = this.dexData[species.speciesId];
|
||||||
const caughtAttr = dexEntry.caughtAttr;
|
const caughtAttr = dexEntry.caughtAttr;
|
||||||
@ -1616,17 +1616,13 @@ export class GameData {
|
|||||||
const checkPrevolution = () => {
|
const checkPrevolution = () => {
|
||||||
if (hasPrevolution) {
|
if (hasPrevolution) {
|
||||||
const prevolutionSpecies = pokemonPrevolutions[species.speciesId];
|
const prevolutionSpecies = pokemonPrevolutions[species.speciesId];
|
||||||
this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg, showMessage).then(() => resolve());
|
return this.setPokemonSpeciesCaught(pokemon, getPokemonSpecies(prevolutionSpecies), incrementCount, fromEgg).then(() => resolve());
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (newCatch && speciesStarters.hasOwnProperty(species.speciesId)) {
|
if (newCatch && speciesStarters.hasOwnProperty(species.speciesId)) {
|
||||||
if (!showMessage) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.scene.playSound("level_up_fanfare");
|
this.scene.playSound("level_up_fanfare");
|
||||||
this.scene.ui.showText(i18next.t("battle:addedAsAStarter", { pokemonName: species.name }), null, () => checkPrevolution(), null, true);
|
this.scene.ui.showText(i18next.t("battle:addedAsAStarter", { pokemonName: species.name }), null, () => checkPrevolution(), null, true);
|
||||||
} else {
|
} else {
|
||||||
@ -1672,7 +1668,7 @@ export class GameData {
|
|||||||
this.starterData[species.speciesId].candyCount += count;
|
this.starterData[species.speciesId].candyCount += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer, showMessage: boolean = true): Promise<boolean> {
|
setEggMoveUnlocked(species: PokemonSpecies, eggMoveIndex: integer): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
const speciesId = species.speciesId;
|
const speciesId = species.speciesId;
|
||||||
if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) {
|
if (!speciesEggMoves.hasOwnProperty(speciesId) || !speciesEggMoves[speciesId][eggMoveIndex]) {
|
||||||
@ -1692,15 +1688,11 @@ export class GameData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.starterData[speciesId].eggMoves |= value;
|
this.starterData[speciesId].eggMoves |= value;
|
||||||
if (!showMessage) {
|
|
||||||
resolve(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.scene.playSound("level_up_fanfare");
|
this.scene.playSound("level_up_fanfare");
|
||||||
|
|
||||||
const moveName = allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name;
|
const moveName = allMoves[speciesEggMoves[speciesId][eggMoveIndex]].name;
|
||||||
this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, (() => {
|
this.scene.ui.showText(eggMoveIndex === 3 ? i18next.t("egg:rareEggMoveUnlock", { moveName: moveName }) : i18next.t("egg:eggMoveUnlock", { moveName: moveName }), null, () => resolve(true), null, true);
|
||||||
resolve(true);
|
|
||||||
}), null, true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +127,8 @@ export default class PokemonData {
|
|||||||
this.summonData.stats = source.summonData.stats;
|
this.summonData.stats = source.summonData.stats;
|
||||||
this.summonData.statStages = source.summonData.statStages;
|
this.summonData.statStages = source.summonData.statStages;
|
||||||
this.summonData.moveQueue = source.summonData.moveQueue;
|
this.summonData.moveQueue = source.summonData.moveQueue;
|
||||||
|
this.summonData.disabledMove = source.summonData.disabledMove;
|
||||||
|
this.summonData.disabledTurns = source.summonData.disabledTurns;
|
||||||
this.summonData.abilitySuppressed = source.summonData.abilitySuppressed;
|
this.summonData.abilitySuppressed = source.summonData.abilitySuppressed;
|
||||||
this.summonData.abilitiesApplied = source.summonData.abilitiesApplied;
|
this.summonData.abilitiesApplied = source.summonData.abilitiesApplied;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) => i
|
|||||||
value: "Mute",
|
value: "Mute",
|
||||||
label: getTranslation("settings:mute")
|
label: getTranslation("settings:mute")
|
||||||
});
|
});
|
||||||
|
|
||||||
const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).map((_, i) => {
|
const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).map((_, i) => {
|
||||||
const value = ((i + 1) * 10).toString();
|
const value = ((i + 1) * 10).toString();
|
||||||
return {
|
return {
|
||||||
@ -33,7 +32,6 @@ const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).ma
|
|||||||
label: value,
|
label: value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const OFF_ON: SettingOption[] = [
|
const OFF_ON: SettingOption[] = [
|
||||||
{
|
{
|
||||||
value: "Off",
|
value: "Off",
|
||||||
@ -55,40 +53,6 @@ const AUTO_DISABLED: SettingOption[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const SHOP_CURSOR_TARGET_OPTIONS: SettingOption[] = [
|
|
||||||
{
|
|
||||||
value: "Rewards",
|
|
||||||
label: i18next.t("settings:rewards")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "Shop",
|
|
||||||
label: i18next.t("settings:shop")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "Reroll",
|
|
||||||
label: i18next.t("settings:reroll")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: "Check Team",
|
|
||||||
label: i18next.t("settings:checkTeam")
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const shopCursorTargetIndexMap = SHOP_CURSOR_TARGET_OPTIONS.map(option => {
|
|
||||||
switch (option.value) {
|
|
||||||
case "Rewards":
|
|
||||||
return ShopCursorTarget.REWARDS;
|
|
||||||
case "Shop":
|
|
||||||
return ShopCursorTarget.SHOP;
|
|
||||||
case "Reroll":
|
|
||||||
return ShopCursorTarget.REROLL;
|
|
||||||
case "Check Team":
|
|
||||||
return ShopCursorTarget.CHECK_TEAM;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown value: ${option.value}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types for helping separate settings to different menus
|
* Types for helping separate settings to different menus
|
||||||
*/
|
*/
|
||||||
@ -139,7 +103,7 @@ export const SettingKeys = {
|
|||||||
Damage_Numbers: "DAMAGE_NUMBERS",
|
Damage_Numbers: "DAMAGE_NUMBERS",
|
||||||
Move_Animations: "MOVE_ANIMATIONS",
|
Move_Animations: "MOVE_ANIMATIONS",
|
||||||
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
|
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
|
||||||
Shop_Cursor_Target: "SHOP_CURSOR_TARGET",
|
Reroll_Target: "REROLL_TARGET",
|
||||||
Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION",
|
Candy_Upgrade_Notification: "CANDY_UPGRADE_NOTIFICATION",
|
||||||
Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY",
|
Candy_Upgrade_Display: "CANDY_UPGRADE_DISPLAY",
|
||||||
Move_Info: "MOVE_INFO",
|
Move_Info: "MOVE_INFO",
|
||||||
@ -632,10 +596,27 @@ export const Setting: Array<Setting> = [
|
|||||||
isHidden: () => !hasTouchscreen()
|
isHidden: () => !hasTouchscreen()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: SettingKeys.Shop_Cursor_Target,
|
key: SettingKeys.Reroll_Target,
|
||||||
label: i18next.t("settings:shopCursorTarget"),
|
label: i18next.t("settings:shopCursorTarget"),
|
||||||
options: SHOP_CURSOR_TARGET_OPTIONS,
|
options: [
|
||||||
default: 0,
|
{
|
||||||
|
value:"Reroll",
|
||||||
|
label: i18next.t("settings:reroll")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"Items",
|
||||||
|
label: i18next.t("settings:items")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"Shop",
|
||||||
|
label: i18next.t("settings:shop")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value:"Check Team",
|
||||||
|
label: i18next.t("settings:checkTeam")
|
||||||
|
}
|
||||||
|
],
|
||||||
|
default: ShopCursorTarget.CHECK_TEAM,
|
||||||
type: SettingType.DISPLAY
|
type: SettingType.DISPLAY
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -777,10 +758,8 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
|
|||||||
case SettingKeys.Show_Stats_on_Level_Up:
|
case SettingKeys.Show_Stats_on_Level_Up:
|
||||||
scene.showLevelUpStats = Setting[index].options[value].value === "On";
|
scene.showLevelUpStats = Setting[index].options[value].value === "On";
|
||||||
break;
|
break;
|
||||||
case SettingKeys.Shop_Cursor_Target:
|
case SettingKeys.Reroll_Target:
|
||||||
const selectedValue = shopCursorTargetIndexMap[value];
|
scene.shopCursorTarget = value;
|
||||||
scene.shopCursorTarget = selectedValue;
|
|
||||||
break;
|
|
||||||
case SettingKeys.EXP_Gains_Speed:
|
case SettingKeys.EXP_Gains_Speed:
|
||||||
scene.expGainsSpeed = value;
|
scene.expGainsSpeed = value;
|
||||||
break;
|
break;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { toDmgValue } from "#app/utils";
|
import { toDmgValue } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { StatusEffect } from "#app/data/status-effect";
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
@ -206,22 +205,4 @@ describe("Abilities - Disguise", () => {
|
|||||||
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
|
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
|
||||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||||
}, TIMEOUT);
|
}, TIMEOUT);
|
||||||
|
|
||||||
it("activates when Aerilate circumvents immunity to the move's base type", async () => {
|
|
||||||
game.override.ability(Abilities.AERILATE);
|
|
||||||
game.override.moveset([Moves.TACKLE]);
|
|
||||||
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const mimikyu = game.scene.getEnemyPokemon()!;
|
|
||||||
const maxHp = mimikyu.getMaxHp();
|
|
||||||
const disguiseDamage = toDmgValue(maxHp / 8);
|
|
||||||
|
|
||||||
game.move.select(Moves.TACKLE);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
|
||||||
|
|
||||||
expect(mimikyu.formIndex).toBe(bustedForm);
|
|
||||||
expect(mimikyu.hp).toBe(maxHp - disguiseDamage);
|
|
||||||
}, TIMEOUT);
|
|
||||||
});
|
});
|
||||||
|
@ -2,6 +2,12 @@ import { Stat } from "#enums/stat";
|
|||||||
import { StatusEffect } from "#app/data/status-effect";
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
import { Type } from "#app/data/type";
|
import { Type } from "#app/data/type";
|
||||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
|
import { BerryPhase } from "#app/phases/berry-phase";
|
||||||
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import { DamagePhase } from "#app/phases/damage-phase";
|
||||||
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
|
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||||
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import { toDmgValue } from "#app/utils";
|
import { toDmgValue } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
@ -9,7 +15,7 @@ import { Species } from "#enums/species";
|
|||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
||||||
|
|
||||||
const TIMEOUT = 20 * 1000;
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
@ -33,31 +39,36 @@ describe("Abilities - Parental Bond", () => {
|
|||||||
game.override.disableCrits();
|
game.override.disableCrits();
|
||||||
game.override.ability(Abilities.PARENTAL_BOND);
|
game.override.ability(Abilities.PARENTAL_BOND);
|
||||||
game.override.enemySpecies(Species.SNORLAX);
|
game.override.enemySpecies(Species.SNORLAX);
|
||||||
game.override.enemyAbility(Abilities.FUR_COAT);
|
game.override.enemyAbility(Abilities.INSOMNIA);
|
||||||
game.override.enemyMoveset(SPLASH_ONLY);
|
game.override.enemyMoveset(SPLASH_ONLY);
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should add second strike to attack move",
|
"ability should add second strike to attack move",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.TACKLE]);
|
game.override.moveset([Moves.TACKLE]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
let enemyStartingHp = enemyPokemon.hp;
|
let enemyStartingHp = enemyPokemon.hp;
|
||||||
|
|
||||||
game.move.select(Moves.TACKLE);
|
game.move.select(Moves.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
const firstStrikeDamage = enemyStartingHp - enemyPokemon.hp;
|
const firstStrikeDamage = enemyStartingHp - enemyPokemon.hp;
|
||||||
enemyStartingHp = enemyPokemon.hp;
|
enemyStartingHp = enemyPokemon.hp;
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp;
|
const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp;
|
||||||
|
|
||||||
@ -66,460 +77,556 @@ describe("Abilities - Parental Bond", () => {
|
|||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should apply secondary effects to both strikes",
|
"ability should apply secondary effects to both strikes",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.POWER_UP_PUNCH]);
|
game.override.moveset([Moves.POWER_UP_PUNCH]);
|
||||||
game.override.enemySpecies(Species.AMOONGUSS);
|
game.override.enemySpecies(Species.AMOONGUSS);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.POWER_UP_PUNCH);
|
game.move.select(Moves.POWER_UP_PUNCH);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2);
|
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply to Status moves",
|
"ability should not apply to Status moves",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.BABY_DOLL_EYES]);
|
game.override.moveset([Moves.BABY_DOLL_EYES]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.BABY_DOLL_EYES);
|
game.move.select(Moves.BABY_DOLL_EYES);
|
||||||
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
|
||||||
|
|
||||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply to multi-hit moves",
|
"ability should not apply to multi-hit moves",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.DOUBLE_HIT]);
|
game.override.moveset([Moves.DOUBLE_HIT]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.DOUBLE_HIT);
|
game.move.select(Moves.DOUBLE_HIT);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply to self-sacrifice moves",
|
"ability should not apply to self-sacrifice moves",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.SELF_DESTRUCT]);
|
game.override.moveset([Moves.SELF_DESTRUCT]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.SELF_DESTRUCT);
|
game.move.select(Moves.SELF_DESTRUCT);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase", false);
|
await game.phaseInterceptor.to(DamagePhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(1);
|
expect(leadPokemon.turnData.hitCount).toBe(1);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply to Rollout",
|
"ability should not apply to Rollout",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.ROLLOUT]);
|
game.override.moveset([Moves.ROLLOUT]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.ROLLOUT);
|
game.move.select(Moves.ROLLOUT);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase", false);
|
await game.phaseInterceptor.to(DamagePhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(1);
|
expect(leadPokemon.turnData.hitCount).toBe(1);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply multiplier to fixed-damage moves",
|
"ability should not apply multiplier to fixed-damage moves",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.DRAGON_RAGE]);
|
game.override.moveset([Moves.DRAGON_RAGE]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.DRAGON_RAGE);
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
|
||||||
|
|
||||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 80);
|
|
||||||
}, TIMEOUT
|
|
||||||
);
|
|
||||||
|
|
||||||
it(
|
|
||||||
"should not apply multiplier to counter moves",
|
|
||||||
async () => {
|
|
||||||
game.override.moveset([Moves.COUNTER]);
|
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
|
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.SHUCKLE]);
|
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.COUNTER);
|
const enemyStartingHp = enemyPokemon.hp;
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
|
||||||
|
|
||||||
const playerDamage = leadPokemon.getMaxHp() - leadPokemon.hp;
|
game.move.select(Moves.DRAGON_RAGE);
|
||||||
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
expect(enemyPokemon.hp).toBe(enemyStartingHp - 80);
|
||||||
|
|
||||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 4 * playerDamage);
|
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply to multi-target moves",
|
"ability should not apply multiplier to counter moves",
|
||||||
|
async () => {
|
||||||
|
game.override.moveset([Moves.COUNTER]);
|
||||||
|
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||||
|
|
||||||
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const playerStartingHp = leadPokemon.hp;
|
||||||
|
const enemyStartingHp = enemyPokemon.hp;
|
||||||
|
|
||||||
|
game.move.select(Moves.COUNTER);
|
||||||
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
|
const playerDamage = playerStartingHp - leadPokemon.hp;
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
|
expect(enemyPokemon.hp).toBe(enemyStartingHp - 4 * playerDamage);
|
||||||
|
}, TIMEOUT
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"ability should not apply to multi-target moves",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.battleType("double");
|
game.override.battleType("double");
|
||||||
game.override.moveset([Moves.EARTHQUAKE]);
|
game.override.moveset([Moves.EARTHQUAKE]);
|
||||||
game.override.passiveAbility(Abilities.LEVITATE);
|
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]);
|
await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]);
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
expect(playerPokemon.length).toBe(2);
|
||||||
|
playerPokemon.forEach(p => expect(p).not.toBe(undefined));
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
expect(enemyPokemon.length).toBe(2);
|
||||||
|
enemyPokemon.forEach(p => expect(p).not.toBe(undefined));
|
||||||
|
|
||||||
game.move.select(Moves.EARTHQUAKE);
|
game.move.select(Moves.EARTHQUAKE);
|
||||||
game.move.select(Moves.EARTHQUAKE, 1);
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
game.move.select(Moves.EARTHQUAKE, 1);
|
||||||
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1));
|
playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1));
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should apply to multi-target moves when hitting only one target",
|
"ability should apply to multi-target moves when hitting only one target",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.EARTHQUAKE]);
|
game.override.moveset([Moves.EARTHQUAKE]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.EARTHQUAKE);
|
game.move.select(Moves.EARTHQUAKE);
|
||||||
await game.phaseInterceptor.to("DamagePhase", false);
|
await game.phaseInterceptor.to(DamagePhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should only trigger post-target move effects once",
|
"ability should only trigger post-target move effects once",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.MIND_BLOWN]);
|
game.override.moveset([Moves.MIND_BLOWN]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.PIDGEOT]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.MIND_BLOWN);
|
game.move.select(Moves.MIND_BLOWN);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase", false);
|
await game.phaseInterceptor.to(DamagePhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
|
|
||||||
// This test will time out if the user faints
|
// This test will time out if the user faints
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.hp).toBe(Math.ceil(leadPokemon.getMaxHp() / 2));
|
expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp() / 2));
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Burn Up only removes type after the second strike",
|
"Burn Up only removes type after second strike with this ability",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.BURN_UP]);
|
game.override.moveset([Moves.BURN_UP]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.CHARIZARD]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.BURN_UP);
|
game.move.select(Moves.BURN_UP);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
expect(enemyPokemon.hp).toBeGreaterThan(0);
|
expect(enemyPokemon.hp).toBeGreaterThan(0);
|
||||||
expect(leadPokemon.isOfType(Type.FIRE)).toBe(true);
|
expect(leadPokemon.isOfType(Type.FIRE)).toBe(true);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.isOfType(Type.FIRE)).toBe(false);
|
expect(leadPokemon.isOfType(Type.FIRE)).toBe(false);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Moves boosted by this ability and Multi-Lens should strike 4 times",
|
"Moves boosted by this ability and Multi-Lens should strike 4 times",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.TACKLE]);
|
game.override.moveset([Moves.TACKLE]);
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.TACKLE);
|
game.move.select(Moves.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(4);
|
expect(leadPokemon.turnData.hitCount).toBe(4);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Super Fang boosted by this ability and Multi-Lens should strike twice",
|
"Super Fang boosted by this ability and Multi-Lens should strike twice",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.SUPER_FANG]);
|
game.override.moveset([Moves.SUPER_FANG]);
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyStartingHp = enemyPokemon.hp;
|
||||||
|
|
||||||
game.move.select(Moves.SUPER_FANG);
|
game.move.select(Moves.SUPER_FANG);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase", false);
|
await game.phaseInterceptor.to(MoveEndPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon.hp).toBe(Math.ceil(enemyPokemon.getMaxHp() * 0.25));
|
expect(enemyPokemon.hp).toBe(Math.ceil(enemyStartingHp * 0.25));
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Seismic Toss boosted by this ability and Multi-Lens should strike twice",
|
"Seismic Toss boosted by this ability and Multi-Lens should strike twice",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.SEISMIC_TOSS]);
|
game.override.moveset([Moves.SEISMIC_TOSS]);
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.hp;
|
const enemyStartingHp = enemyPokemon.hp;
|
||||||
|
|
||||||
game.move.select(Moves.SEISMIC_TOSS);
|
game.move.select(Moves.SEISMIC_TOSS);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase", false);
|
await game.phaseInterceptor.to(MoveEndPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon.hp).toBe(enemyStartingHp - 200);
|
expect(enemyPokemon.hp).toBe(enemyStartingHp - 200);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Hyper Beam boosted by this ability should strike twice, then recharge",
|
"Hyper Beam boosted by this ability should strike twice, then recharge",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.HYPER_BEAM]);
|
game.override.moveset([Moves.HYPER_BEAM]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.HYPER_BEAM);
|
game.move.select(Moves.HYPER_BEAM);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined();
|
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeDefined();
|
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeDefined();
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
/** TODO: Fix TRAPPED tag lapsing incorrectly, then run this test */
|
||||||
|
test(
|
||||||
"Anchor Shot boosted by this ability should only trap the target after the second hit",
|
"Anchor Shot boosted by this ability should only trap the target after the second hit",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.ANCHOR_SHOT]);
|
game.override.moveset([Moves.ANCHOR_SHOT]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.ANCHOR_SHOT);
|
game.move.select(Moves.ANCHOR_SHOT);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined();
|
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); // Passes
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
await game.phaseInterceptor.to(MoveEndPhase);
|
||||||
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined();
|
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); // Passes
|
||||||
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined();
|
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); // Fails :(
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Smack Down boosted by this ability should only ground the target after the second hit",
|
"Smack Down boosted by this ability should only ground the target after the second hit",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.SMACK_DOWN]);
|
game.override.moveset([Moves.SMACK_DOWN]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.SMACK_DOWN);
|
game.move.select(Moves.SMACK_DOWN);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"U-turn boosted by this ability should strike twice before forcing a switch",
|
"U-turn boosted by this ability should strike twice before forcing a switch",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.U_TURN]);
|
game.override.moveset([Moves.U_TURN]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP, Species.BLASTOISE]);
|
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.U_TURN);
|
game.move.select(Moves.U_TURN);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
|
|
||||||
// This will cause this test to time out if the switch was forced on the first hit.
|
// This will cause this test to time out if the switch was forced on the first hit.
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"Wake-Up Slap boosted by this ability should only wake up the target after the second hit",
|
"Wake-Up Slap boosted by this ability should only wake up the target after the second hit",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.WAKE_UP_SLAP]).enemyStatusEffect(StatusEffect.SLEEP);
|
game.override.moveset([Moves.WAKE_UP_SLAP]).enemyStatusEffect(StatusEffect.SLEEP);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.WAKE_UP_SLAP);
|
game.move.select(Moves.WAKE_UP_SLAP);
|
||||||
await game.move.forceHit();
|
await game.move.forceHit();
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
|
|
||||||
expect(leadPokemon.turnData.hitCount).toBe(2);
|
expect(leadPokemon.turnData.hitCount).toBe(2);
|
||||||
expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP);
|
expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon.status?.effect).toBeUndefined();
|
expect(enemyPokemon.status?.effect).toBeUndefined();
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not cause user to hit into King's Shield more than once",
|
"ability should not cause user to hit into King's Shield more than once",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.TACKLE]);
|
game.override.moveset([Moves.TACKLE]);
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.KINGS_SHIELD));
|
game.override.enemyMoveset([Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.TACKLE);
|
game.move.select(Moves.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not cause user to hit into Storm Drain more than once",
|
"ability should not cause user to hit into Storm Drain more than once",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.WATER_GUN]);
|
game.override.moveset([Moves.WATER_GUN]);
|
||||||
game.override.enemyAbility(Abilities.STORM_DRAIN);
|
game.override.enemyAbility(Abilities.STORM_DRAIN);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
expect(leadPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon).not.toBe(undefined);
|
||||||
|
|
||||||
game.move.select(Moves.WATER_GUN);
|
game.move.select(Moves.WATER_GUN);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
it(
|
test(
|
||||||
"should not apply to multi-target moves with Multi-Lens",
|
"ability should not apply to multi-target moves with Multi-Lens",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.battleType("double");
|
game.override.battleType("double");
|
||||||
game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]);
|
game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]);
|
||||||
game.override.passiveAbility(Abilities.LEVITATE);
|
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]);
|
await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]);
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
expect(playerPokemon.length).toBe(2);
|
||||||
|
playerPokemon.forEach(p => expect(p).not.toBe(undefined));
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
expect(enemyPokemon.length).toBe(2);
|
||||||
|
enemyPokemon.forEach(p => expect(p).not.toBe(undefined));
|
||||||
|
|
||||||
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||||
|
|
||||||
game.move.select(Moves.EARTHQUAKE);
|
game.move.select(Moves.EARTHQUAKE);
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(DamagePhase);
|
||||||
const enemyFirstHitDamage = enemyStartingHp.map((hp, i) => hp - enemyPokemon[i].hp);
|
const enemyFirstHitDamage = enemyStartingHp.map((hp, i) => hp - enemyPokemon[i].hp);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
await game.phaseInterceptor.to(BerryPhase, false);
|
||||||
|
|
||||||
enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i]));
|
enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i]));
|
||||||
|
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { allAbilities } from "#app/data/ability";
|
import { allAbilities } from "#app/data/ability";
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
import { Abilities } from "#app/enums/abilities";
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
@ -36,7 +37,7 @@ describe("Abilities - Steely Spirit", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("increases Steel-type moves' power used by the user and its allies by 50%", async () => {
|
it("increases Steel-type moves' power used by the user and its allies by 50%", async () => {
|
||||||
await game.classicMode.startBattle([Species.PIKACHU, Species.SHUCKLE]);
|
await game.startBattle([Species.PIKACHU, Species.SHUCKLE]);
|
||||||
const boostSource = game.scene.getPlayerField()[1];
|
const boostSource = game.scene.getPlayerField()[1];
|
||||||
const enemyToCheck = game.scene.getEnemyPokemon()!;
|
const enemyToCheck = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
@ -46,13 +47,13 @@ describe("Abilities - Steely Spirit", () => {
|
|||||||
|
|
||||||
game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex());
|
game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex());
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||||
|
|
||||||
expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * steelySpiritMultiplier);
|
expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * steelySpiritMultiplier);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("stacks if multiple users with this ability are on the field.", async () => {
|
it("stacks if multiple users with this ability are on the field.", async () => {
|
||||||
await game.classicMode.startBattle([Species.PIKACHU, Species.PIKACHU]);
|
await game.startBattle([Species.PIKACHU, Species.PIKACHU]);
|
||||||
const enemyToCheck = game.scene.getEnemyPokemon()!;
|
const enemyToCheck = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
game.scene.getPlayerField().forEach(p => {
|
game.scene.getPlayerField().forEach(p => {
|
||||||
@ -63,13 +64,13 @@ describe("Abilities - Steely Spirit", () => {
|
|||||||
|
|
||||||
game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex());
|
game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex());
|
||||||
game.move.select(moveToCheck, 1, enemyToCheck.getBattlerIndex());
|
game.move.select(moveToCheck, 1, enemyToCheck.getBattlerIndex());
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||||
|
|
||||||
expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * Math.pow(steelySpiritMultiplier, 2));
|
expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower * Math.pow(steelySpiritMultiplier, 2));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not take effect when suppressed", async () => {
|
it("does not take effect when suppressed", async () => {
|
||||||
await game.classicMode.startBattle([Species.PIKACHU, Species.SHUCKLE]);
|
await game.startBattle([Species.PIKACHU, Species.SHUCKLE]);
|
||||||
const boostSource = game.scene.getPlayerField()[1];
|
const boostSource = game.scene.getPlayerField()[1];
|
||||||
const enemyToCheck = game.scene.getEnemyPokemon()!;
|
const enemyToCheck = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
@ -83,25 +84,8 @@ describe("Abilities - Steely Spirit", () => {
|
|||||||
|
|
||||||
game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex());
|
game.move.select(moveToCheck, 0, enemyToCheck.getBattlerIndex());
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
await game.phaseInterceptor.to(MoveEffectPhase);
|
||||||
|
|
||||||
expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower);
|
expect(allMoves[moveToCheck].calculateBattlePower).toHaveReturnedWith(ironHeadPower);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("affects variable-type moves if their resolved type is Steel", async () => {
|
|
||||||
game.override
|
|
||||||
.ability(Abilities.STEELY_SPIRIT)
|
|
||||||
.moveset([Moves.REVELATION_DANCE]);
|
|
||||||
|
|
||||||
const revelationDance = allMoves[Moves.REVELATION_DANCE];
|
|
||||||
vi.spyOn(revelationDance, "calculateBattlePower");
|
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.KLINKLANG]);
|
|
||||||
|
|
||||||
game.move.select(Moves.REVELATION_DANCE);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
|
||||||
|
|
||||||
expect(revelationDance.calculateBattlePower).toHaveReturnedWith(revelationDance.power * 1.5);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -1,303 +0,0 @@
|
|||||||
import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
|
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
|
||||||
import * as Utils from "#app/utils";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import GameManager from "#test/utils/gameManager";
|
|
||||||
import Phaser from "phaser";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
describe("Escape chance calculations", () => {
|
|
||||||
let phaserGame: Phaser.Game;
|
|
||||||
let game: GameManager;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
phaserGame = new Phaser.Game({
|
|
||||||
type: Phaser.HEADLESS,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
game.phaseInterceptor.restoreOg();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
game = new GameManager(phaserGame);
|
|
||||||
game.override
|
|
||||||
.battleType("single")
|
|
||||||
.enemySpecies(Species.BULBASAUR)
|
|
||||||
.enemyAbility(Abilities.INSOMNIA)
|
|
||||||
.ability(Abilities.INSOMNIA);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("single non-boss opponent", async () => {
|
|
||||||
await game.classicMode.startBattle([Species.BULBASAUR]);
|
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
|
||||||
const enemyField = game.scene.getEnemyField();
|
|
||||||
const enemySpeed = 100;
|
|
||||||
// set enemyPokemon's speed to 100
|
|
||||||
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]);
|
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
|
||||||
const escapePercentage = new Utils.NumberHolder(0);
|
|
||||||
|
|
||||||
// this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping
|
|
||||||
const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [
|
|
||||||
{ pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 },
|
|
||||||
{ pokemonSpeedRatio: 0.1, escapeAttempts: 0, expectedEscapeChance: 7 },
|
|
||||||
{ pokemonSpeedRatio: 0.25, escapeAttempts: 0, expectedEscapeChance: 11 },
|
|
||||||
{ pokemonSpeedRatio: 0.5, escapeAttempts: 0, expectedEscapeChance: 16 },
|
|
||||||
{ pokemonSpeedRatio: 0.8, escapeAttempts: 0, expectedEscapeChance: 23 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 28 },
|
|
||||||
{ pokemonSpeedRatio: 1.2, escapeAttempts: 0, expectedEscapeChance: 32 },
|
|
||||||
{ pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 39 },
|
|
||||||
{ pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 73 },
|
|
||||||
{ pokemonSpeedRatio: 3.8, escapeAttempts: 0, expectedEscapeChance: 91 },
|
|
||||||
{ pokemonSpeedRatio: 4, escapeAttempts: 0, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 4.2, escapeAttempts: 0, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 10, escapeAttempts: 0, expectedEscapeChance: 95 },
|
|
||||||
|
|
||||||
// retries section
|
|
||||||
{ pokemonSpeedRatio: 0.4, escapeAttempts: 1, expectedEscapeChance: 24 },
|
|
||||||
{ pokemonSpeedRatio: 1.6, escapeAttempts: 2, expectedEscapeChance: 61 },
|
|
||||||
{ pokemonSpeedRatio: 3.7, escapeAttempts: 5, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 0.2, escapeAttempts: 2, expectedEscapeChance: 30 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 3, expectedEscapeChance: 58 },
|
|
||||||
{ pokemonSpeedRatio: 2.9, escapeAttempts: 0, expectedEscapeChance: 70 },
|
|
||||||
{ pokemonSpeedRatio: 0.01, escapeAttempts: 7, expectedEscapeChance: 75 },
|
|
||||||
{ pokemonSpeedRatio: 16.2, escapeAttempts: 4, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 2, escapeAttempts: 3, expectedEscapeChance: 80 },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < escapeChances.length; i++) {
|
|
||||||
// sets the number of escape attempts to the required amount
|
|
||||||
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
|
|
||||||
// set playerPokemon's speed to a multiple of the enemySpeed
|
|
||||||
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * enemySpeed]);
|
|
||||||
phase.attemptRunAway(playerPokemon, enemyField, escapePercentage);
|
|
||||||
expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance);
|
|
||||||
}
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("double non-boss opponent", async () => {
|
|
||||||
game.override.battleType("double");
|
|
||||||
await game.classicMode.startBattle([Species.BULBASAUR, Species.ABOMASNOW]);
|
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
|
||||||
const enemyField = game.scene.getEnemyField();
|
|
||||||
const enemyASpeed = 70;
|
|
||||||
const enemyBSpeed = 30;
|
|
||||||
// gets the sum of the speed of the two pokemon
|
|
||||||
const totalEnemySpeed = enemyASpeed + enemyBSpeed;
|
|
||||||
// this is used to find the ratio of the player's first pokemon
|
|
||||||
const playerASpeedPercentage = 0.4;
|
|
||||||
// set enemyAPokemon's speed to 70
|
|
||||||
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyASpeed]);
|
|
||||||
// set enemyBPokemon's speed to 30
|
|
||||||
vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]);
|
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
|
||||||
const escapePercentage = new Utils.NumberHolder(0);
|
|
||||||
|
|
||||||
// this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping
|
|
||||||
const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [
|
|
||||||
{ pokemonSpeedRatio: 0.3, escapeAttempts: 0, expectedEscapeChance: 12 },
|
|
||||||
{ pokemonSpeedRatio: 0.7, escapeAttempts: 0, expectedEscapeChance: 21 },
|
|
||||||
{ pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 39 },
|
|
||||||
{ pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 73 },
|
|
||||||
{ pokemonSpeedRatio: 9, escapeAttempts: 0, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 28 },
|
|
||||||
{ pokemonSpeedRatio: 4.3, escapeAttempts: 0, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 2.7, escapeAttempts: 0, expectedEscapeChance: 66 },
|
|
||||||
{ pokemonSpeedRatio: 2.1, escapeAttempts: 0, expectedEscapeChance: 52 },
|
|
||||||
{ pokemonSpeedRatio: 1.8, escapeAttempts: 0, expectedEscapeChance: 46 },
|
|
||||||
{ pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 95 },
|
|
||||||
|
|
||||||
// retries section
|
|
||||||
{ pokemonSpeedRatio: 0.9, escapeAttempts: 1, expectedEscapeChance: 35 },
|
|
||||||
{ pokemonSpeedRatio: 3.6, escapeAttempts: 2, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 0.03, escapeAttempts: 7, expectedEscapeChance: 76 },
|
|
||||||
{ pokemonSpeedRatio: 0.02, escapeAttempts: 7, expectedEscapeChance: 75 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 5, expectedEscapeChance: 78 },
|
|
||||||
{ pokemonSpeedRatio: 0.7, escapeAttempts: 3, expectedEscapeChance: 51 },
|
|
||||||
{ pokemonSpeedRatio: 2.4, escapeAttempts: 9, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 1.8, escapeAttempts: 7, expectedEscapeChance: 95 },
|
|
||||||
{ pokemonSpeedRatio: 2, escapeAttempts: 10, expectedEscapeChance: 95 },
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < escapeChances.length; i++) {
|
|
||||||
// sets the number of escape attempts to the required amount
|
|
||||||
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
|
|
||||||
// set the first playerPokemon's speed to a multiple of the enemySpeed
|
|
||||||
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage)]);
|
|
||||||
// set the second playerPokemon's speed to the remaining value of speed
|
|
||||||
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]]);
|
|
||||||
phase.attemptRunAway(playerPokemon, enemyField, escapePercentage);
|
|
||||||
// checks to make sure the escape values are the same
|
|
||||||
expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance);
|
|
||||||
// checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed
|
|
||||||
expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed);
|
|
||||||
}
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("single boss opponent", async () => {
|
|
||||||
game.override.startingWave(10);
|
|
||||||
await game.classicMode.startBattle([Species.BULBASAUR]);
|
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField()!;
|
|
||||||
const enemyField = game.scene.getEnemyField()!;
|
|
||||||
const enemySpeed = 100;
|
|
||||||
// set enemyPokemon's speed to 100
|
|
||||||
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemySpeed]);
|
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
|
||||||
const escapePercentage = new Utils.NumberHolder(0);
|
|
||||||
|
|
||||||
// this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping
|
|
||||||
const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [
|
|
||||||
{ pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 },
|
|
||||||
{ pokemonSpeedRatio: 0.1, escapeAttempts: 0, expectedEscapeChance: 5 },
|
|
||||||
{ pokemonSpeedRatio: 0.25, escapeAttempts: 0, expectedEscapeChance: 6 },
|
|
||||||
{ pokemonSpeedRatio: 0.5, escapeAttempts: 0, expectedEscapeChance: 7 },
|
|
||||||
{ pokemonSpeedRatio: 0.8, escapeAttempts: 0, expectedEscapeChance: 8 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 8 },
|
|
||||||
{ pokemonSpeedRatio: 1.2, escapeAttempts: 0, expectedEscapeChance: 9 },
|
|
||||||
{ pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 10 },
|
|
||||||
{ pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 15 },
|
|
||||||
{ pokemonSpeedRatio: 3.8, escapeAttempts: 0, expectedEscapeChance: 18 },
|
|
||||||
{ pokemonSpeedRatio: 4, escapeAttempts: 0, expectedEscapeChance: 18 },
|
|
||||||
{ pokemonSpeedRatio: 4.2, escapeAttempts: 0, expectedEscapeChance: 19 },
|
|
||||||
{ pokemonSpeedRatio: 4.7, escapeAttempts: 0, expectedEscapeChance: 21 },
|
|
||||||
{ pokemonSpeedRatio: 5, escapeAttempts: 0, expectedEscapeChance: 22 },
|
|
||||||
{ pokemonSpeedRatio: 5.9, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 6.7, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 10, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
|
|
||||||
// retries section
|
|
||||||
{ pokemonSpeedRatio: 0.4, escapeAttempts: 1, expectedEscapeChance: 8 },
|
|
||||||
{ pokemonSpeedRatio: 1.6, escapeAttempts: 2, expectedEscapeChance: 14 },
|
|
||||||
{ pokemonSpeedRatio: 3.7, escapeAttempts: 5, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 0.2, escapeAttempts: 2, expectedEscapeChance: 10 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 3, expectedEscapeChance: 14 },
|
|
||||||
{ pokemonSpeedRatio: 2.9, escapeAttempts: 0, expectedEscapeChance: 15 },
|
|
||||||
{ pokemonSpeedRatio: 0.01, escapeAttempts: 7, expectedEscapeChance: 19 },
|
|
||||||
{ pokemonSpeedRatio: 16.2, escapeAttempts: 4, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 2, escapeAttempts: 3, expectedEscapeChance: 18 },
|
|
||||||
{ pokemonSpeedRatio: 4.5, escapeAttempts: 1, expectedEscapeChance: 22 },
|
|
||||||
{ pokemonSpeedRatio: 6.8, escapeAttempts: 6, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 5.2, escapeAttempts: 8, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 4.7, escapeAttempts: 10, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 5.1, escapeAttempts: 1, expectedEscapeChance: 24 },
|
|
||||||
{ pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 5.9, escapeAttempts: 2, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 6.1, escapeAttempts: 3, expectedEscapeChance: 25 },
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < escapeChances.length; i++) {
|
|
||||||
// sets the number of escape attempts to the required amount
|
|
||||||
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
|
|
||||||
// set playerPokemon's speed to a multiple of the enemySpeed
|
|
||||||
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * enemySpeed]);
|
|
||||||
phase.attemptRunAway(playerPokemon, enemyField, escapePercentage);
|
|
||||||
expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance);
|
|
||||||
}
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("double boss opponent", async () => {
|
|
||||||
game.override.battleType("double");
|
|
||||||
game.override.startingWave(10);
|
|
||||||
await game.classicMode.startBattle([Species.BULBASAUR, Species.ABOMASNOW]);
|
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
|
||||||
const enemyField = game.scene.getEnemyField();
|
|
||||||
const enemyASpeed = 70;
|
|
||||||
const enemyBSpeed = 30;
|
|
||||||
// gets the sum of the speed of the two pokemon
|
|
||||||
const totalEnemySpeed = enemyASpeed + enemyBSpeed;
|
|
||||||
// this is used to find the ratio of the player's first pokemon
|
|
||||||
const playerASpeedPercentage = 0.8;
|
|
||||||
// set enemyAPokemon's speed to 70
|
|
||||||
vi.spyOn(enemyField[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyASpeed]);
|
|
||||||
// set enemyBPokemon's speed to 30
|
|
||||||
vi.spyOn(enemyField[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, enemyBSpeed]);
|
|
||||||
|
|
||||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
|
||||||
commandPhase.handleCommand(Command.RUN, 0);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(AttemptRunPhase, false);
|
|
||||||
const phase = game.scene.getCurrentPhase() as AttemptRunPhase;
|
|
||||||
const escapePercentage = new Utils.NumberHolder(0);
|
|
||||||
|
|
||||||
// this sets up an object for multiple attempts. The pokemonSpeedRatio is your speed divided by the enemy speed, the escapeAttempts are the number of escape attempts and the expectedEscapeChance is the chance it should be escaping
|
|
||||||
const escapeChances: { pokemonSpeedRatio: number, escapeAttempts: number, expectedEscapeChance: number }[] = [
|
|
||||||
{ pokemonSpeedRatio: 0.3, escapeAttempts: 0, expectedEscapeChance: 6 },
|
|
||||||
{ pokemonSpeedRatio: 0.7, escapeAttempts: 0, expectedEscapeChance: 7 },
|
|
||||||
{ pokemonSpeedRatio: 1.5, escapeAttempts: 0, expectedEscapeChance: 10 },
|
|
||||||
{ pokemonSpeedRatio: 3, escapeAttempts: 0, expectedEscapeChance: 15 },
|
|
||||||
{ pokemonSpeedRatio: 9, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 0.01, escapeAttempts: 0, expectedEscapeChance: 5 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 0, expectedEscapeChance: 8 },
|
|
||||||
{ pokemonSpeedRatio: 4.3, escapeAttempts: 0, expectedEscapeChance: 19 },
|
|
||||||
{ pokemonSpeedRatio: 2.7, escapeAttempts: 0, expectedEscapeChance: 14 },
|
|
||||||
{ pokemonSpeedRatio: 2.1, escapeAttempts: 0, expectedEscapeChance: 12 },
|
|
||||||
{ pokemonSpeedRatio: 1.8, escapeAttempts: 0, expectedEscapeChance: 11 },
|
|
||||||
{ pokemonSpeedRatio: 6, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 4, escapeAttempts: 0, expectedEscapeChance: 18 },
|
|
||||||
{ pokemonSpeedRatio: 5.7, escapeAttempts: 0, expectedEscapeChance: 24 },
|
|
||||||
{ pokemonSpeedRatio: 5, escapeAttempts: 0, expectedEscapeChance: 22 },
|
|
||||||
{ pokemonSpeedRatio: 6.1, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 6.8, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 10, escapeAttempts: 0, expectedEscapeChance: 25 },
|
|
||||||
|
|
||||||
// retries section
|
|
||||||
{ pokemonSpeedRatio: 0.9, escapeAttempts: 1, expectedEscapeChance: 10 },
|
|
||||||
{ pokemonSpeedRatio: 3.6, escapeAttempts: 2, expectedEscapeChance: 21 },
|
|
||||||
{ pokemonSpeedRatio: 0.03, escapeAttempts: 7, expectedEscapeChance: 19 },
|
|
||||||
{ pokemonSpeedRatio: 0.02, escapeAttempts: 7, expectedEscapeChance: 19 },
|
|
||||||
{ pokemonSpeedRatio: 1, escapeAttempts: 5, expectedEscapeChance: 18 },
|
|
||||||
{ pokemonSpeedRatio: 0.7, escapeAttempts: 3, expectedEscapeChance: 13 },
|
|
||||||
{ pokemonSpeedRatio: 2.4, escapeAttempts: 9, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 1.8, escapeAttempts: 7, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 2, escapeAttempts: 10, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 3, escapeAttempts: 1, expectedEscapeChance: 17 },
|
|
||||||
{ pokemonSpeedRatio: 4.5, escapeAttempts: 3, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 3.7, escapeAttempts: 1, expectedEscapeChance: 19 },
|
|
||||||
{ pokemonSpeedRatio: 6.5, escapeAttempts: 1, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 12, escapeAttempts: 4, expectedEscapeChance: 25 },
|
|
||||||
{ pokemonSpeedRatio: 5.2, escapeAttempts: 2, expectedEscapeChance: 25 },
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
for (let i = 0; i < escapeChances.length; i++) {
|
|
||||||
// sets the number of escape attempts to the required amount
|
|
||||||
game.scene.currentBattle.escapeAttempts = escapeChances[i].escapeAttempts;
|
|
||||||
// set the first playerPokemon's speed to a multiple of the enemySpeed
|
|
||||||
vi.spyOn(playerPokemon[0], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, Math.floor(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed * playerASpeedPercentage)]);
|
|
||||||
// set the second playerPokemon's speed to the remaining value of speed
|
|
||||||
vi.spyOn(playerPokemon[1], "stats", "get").mockReturnValue([20, 20, 20, 20, 20, escapeChances[i].pokemonSpeedRatio * totalEnemySpeed - playerPokemon[0].stats[5]]);
|
|
||||||
phase.attemptRunAway(playerPokemon, enemyField, escapePercentage);
|
|
||||||
// checks to make sure the escape values are the same
|
|
||||||
expect(escapePercentage.value).toBe(escapeChances[i].expectedEscapeChance);
|
|
||||||
// checks to make sure the sum of the player's speed for all pokemon is equal to the appropriate ratio of the total enemy speed
|
|
||||||
expect(playerPokemon[0].stats[5] + playerPokemon[1].stats[5]).toBe(escapeChances[i].pokemonSpeedRatio * totalEnemySpeed);
|
|
||||||
}
|
|
||||||
}, 20000);
|
|
||||||
});
|
|
@ -13,7 +13,6 @@ import { Button } from "#app/enums/buttons";
|
|||||||
import { CommandPhase } from "#app/phases/command-phase";
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
|
||||||
|
|
||||||
describe("Items - Dire Hit", () => {
|
describe("Items - Dire Hit", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -78,8 +77,8 @@ describe("Items - Dire Hit", () => {
|
|||||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||||
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
// Traverse to first modifier slot
|
// Traverse to first modifier slot
|
||||||
handler.setCursor(0);
|
handler.processInput(Button.LEFT);
|
||||||
handler.setRowCursor(ShopCursorTarget.REWARDS);
|
handler.processInput(Button.UP);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);
|
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
|||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
|
||||||
|
|
||||||
|
|
||||||
describe("Items - Temporary Stat Stage Boosters", () => {
|
describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
@ -155,8 +154,8 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
|||||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||||
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
// Traverse to first modifier slot
|
// Traverse to first modifier slot
|
||||||
handler.setCursor(0);
|
handler.processInput(Button.LEFT);
|
||||||
handler.setRowCursor(ShopCursorTarget.REWARDS);
|
handler.processInput(Button.UP);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);
|
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { Stat } from "#enums/stat";
|
||||||
|
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||||
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
|
||||||
|
|
||||||
describe("Moves - Baton Pass", () => {
|
describe("Moves - Baton Pass", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -27,17 +27,20 @@ describe("Moves - Baton Pass", () => {
|
|||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override
|
game.override
|
||||||
.battleType("single")
|
.battleType("single")
|
||||||
.enemySpecies(Species.MAGIKARP)
|
.enemySpecies(Species.DUGTRIO)
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
.startingLevel(1)
|
||||||
|
.startingWave(97)
|
||||||
.moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH])
|
.moveset([Moves.BATON_PASS, Moves.NASTY_PLOT, Moves.SPLASH])
|
||||||
.ability(Abilities.BALL_FETCH)
|
|
||||||
.enemyMoveset(SPLASH_ONLY)
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
.disableCrits();
|
.disableCrits();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("transfers all stat stages when player uses it", async() => {
|
it("transfers all stat stages when player uses it", async() => {
|
||||||
// arrange
|
// arrange
|
||||||
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
await game.startBattle([
|
||||||
|
Species.RAICHU,
|
||||||
|
Species.SHUCKLE
|
||||||
|
]);
|
||||||
|
|
||||||
// round 1 - buff
|
// round 1 - buff
|
||||||
game.move.select(Moves.NASTY_PLOT);
|
game.move.select(Moves.NASTY_PLOT);
|
||||||
@ -50,7 +53,7 @@ describe("Moves - Baton Pass", () => {
|
|||||||
// round 2 - baton pass
|
// round 2 - baton pass
|
||||||
game.move.select(Moves.BATON_PASS);
|
game.move.select(Moves.BATON_PASS);
|
||||||
game.doSelectPartyPokemon(1);
|
game.doSelectPartyPokemon(1);
|
||||||
await game.phaseInterceptor.to("TurnEndPhase");
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
playerPokemon = game.scene.getPlayerPokemon()!;
|
playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
@ -63,7 +66,10 @@ describe("Moves - Baton Pass", () => {
|
|||||||
game.override
|
game.override
|
||||||
.startingWave(5)
|
.startingWave(5)
|
||||||
.enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT]));
|
.enemyMoveset(new Array(4).fill([Moves.NASTY_PLOT]));
|
||||||
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
|
await game.startBattle([
|
||||||
|
Species.RAICHU,
|
||||||
|
Species.SHUCKLE
|
||||||
|
]);
|
||||||
|
|
||||||
// round 1 - ai buffs
|
// round 1 - ai buffs
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
@ -73,7 +79,7 @@ describe("Moves - Baton Pass", () => {
|
|||||||
game.scene.getEnemyPokemon()!.hp = 100;
|
game.scene.getEnemyPokemon()!.hp = 100;
|
||||||
game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS));
|
game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS));
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to("PostSummonPhase", false);
|
await game.phaseInterceptor.to(PostSummonPhase, false);
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
// check buffs are still there
|
// check buffs are still there
|
||||||
@ -88,20 +94,4 @@ describe("Moves - Baton Pass", () => {
|
|||||||
"PostSummonPhase"
|
"PostSummonPhase"
|
||||||
]);
|
]);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("doesn't transfer effects that aren't transferrable", async() => {
|
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.SALT_CURE));
|
|
||||||
await game.classicMode.startBattle([Species.PIKACHU, Species.FEEBAS]);
|
|
||||||
|
|
||||||
const [player1, player2] = game.scene.getParty();
|
|
||||||
|
|
||||||
game.move.select(Moves.BATON_PASS);
|
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
|
||||||
await game.phaseInterceptor.to("MoveEndPhase");
|
|
||||||
expect(player1.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeTruthy();
|
|
||||||
game.doSelectPartyPokemon(1);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
expect(player2.findTag((t) => t.tagType === BattlerTagType.SALT_CURED)).toBeUndefined();
|
|
||||||
}, 20000);
|
|
||||||
});
|
});
|
||||||
|
@ -110,7 +110,7 @@ describe("Moves - Ceaseless Edge", () => {
|
|||||||
|
|
||||||
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;
|
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;
|
||||||
// Check HP of pokemon that WILL BE switched in (index 1)
|
// Check HP of pokemon that WILL BE switched in (index 1)
|
||||||
game.forceEnemyToSwitch();
|
game.forceOpponentToSwitch();
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes);
|
expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes);
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
|
||||||
import { MoveResult } from "#app/field/pokemon";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import GameManager from "#test/utils/gameManager";
|
|
||||||
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
||||||
|
|
||||||
describe("Moves - Disable", () => {
|
|
||||||
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")
|
|
||||||
.ability(Abilities.BALL_FETCH)
|
|
||||||
.enemyAbility(Abilities.BALL_FETCH)
|
|
||||||
.moveset([Moves.DISABLE, Moves.SPLASH])
|
|
||||||
.enemyMoveset(SPLASH_ONLY)
|
|
||||||
.starterSpecies(Species.PIKACHU)
|
|
||||||
.enemySpecies(Species.SHUCKLE);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("restricts moves", async () => {
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const enemyMon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.DISABLE);
|
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
expect(enemyMon.getMoveHistory()).toHaveLength(1);
|
|
||||||
expect(enemyMon.isMoveRestricted(Moves.SPLASH)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("fails if enemy has no move history", async() => {
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const playerMon = game.scene.getPlayerPokemon()!;
|
|
||||||
const enemyMon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.DISABLE);
|
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
expect(playerMon.getMoveHistory()[0]).toMatchObject({ move: Moves.DISABLE, result: MoveResult.FAIL });
|
|
||||||
expect(enemyMon.isMoveRestricted(Moves.SPLASH)).toBe(false);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("causes STRUGGLE if all usable moves are disabled", async() => {
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const enemyMon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.DISABLE);
|
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
const enemyHistory = enemyMon.getMoveHistory();
|
|
||||||
expect(enemyHistory).toHaveLength(2);
|
|
||||||
expect(enemyHistory[0].move).toBe(Moves.SPLASH);
|
|
||||||
expect(enemyHistory[1].move).toBe(Moves.STRUGGLE);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("cannot disable STRUGGLE", async() => {
|
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.STRUGGLE));
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const playerMon = game.scene.getPlayerPokemon()!;
|
|
||||||
const enemyMon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.DISABLE);
|
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
expect(playerMon.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
|
||||||
expect(enemyMon.getLastXMoves()[0].move).toBe(Moves.STRUGGLE);
|
|
||||||
expect(enemyMon.isMoveRestricted(Moves.STRUGGLE)).toBe(false);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("interrupts target's move when target moves after", async() => {
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const enemyMon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
// Both mons just used Splash last turn; now have player use Disable.
|
|
||||||
game.move.select(Moves.DISABLE);
|
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
const enemyHistory = enemyMon.getMoveHistory();
|
|
||||||
expect(enemyHistory).toHaveLength(2);
|
|
||||||
expect(enemyHistory[0]).toMatchObject({ move: Moves.SPLASH, result: MoveResult.SUCCESS });
|
|
||||||
expect(enemyHistory[1].result).toBe(MoveResult.FAIL);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("disables NATURE POWER, not the move invoked by it", async() => {
|
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.NATURE_POWER));
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const enemyMon = game.scene.getEnemyPokemon()!;
|
|
||||||
|
|
||||||
game.move.select(Moves.DISABLE);
|
|
||||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
|
||||||
await game.toNextTurn();
|
|
||||||
|
|
||||||
expect(enemyMon.isMoveRestricted(Moves.NATURE_POWER)).toBe(true);
|
|
||||||
expect(enemyMon.isMoveRestricted(enemyMon.getLastXMoves(2)[1].move)).toBe(false);
|
|
||||||
}, 20000);
|
|
||||||
});
|
|
@ -123,7 +123,7 @@ describe("Moves - Focus Punch", () => {
|
|||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD]);
|
await game.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
game.forceEnemyToSwitch();
|
game.forceOpponentToSwitch();
|
||||||
game.move.select(Moves.FOCUS_PUNCH);
|
game.move.select(Moves.FOCUS_PUNCH);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnStartPhase);
|
await game.phaseInterceptor.to(TurnStartPhase);
|
||||||
|
@ -28,55 +28,48 @@ describe("Moves - Follow Me", () => {
|
|||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.battleType("double");
|
game.override.battleType("double");
|
||||||
game.override.starterSpecies(Species.AMOONGUSS);
|
game.override.starterSpecies(Species.AMOONGUSS);
|
||||||
game.override.ability(Abilities.BALL_FETCH);
|
|
||||||
game.override.enemySpecies(Species.SNORLAX);
|
game.override.enemySpecies(Species.SNORLAX);
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.TACKLE, Moves.FOLLOW_ME, Moves.SPLASH]);
|
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should redirect enemy attacks to the user",
|
"move should redirect enemy attacks to the user",
|
||||||
async () => {
|
async () => {
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
|
||||||
|
const playerStartingHp = playerPokemon.map(p => p.hp);
|
||||||
|
|
||||||
game.move.select(Moves.FOLLOW_ME);
|
game.move.select(Moves.FOLLOW_ME);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY);
|
||||||
|
|
||||||
// Force both enemies to target the player Pokemon that did not use Follow Me
|
|
||||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
|
||||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
expect(playerPokemon[0].hp).toBeLessThan(playerPokemon[0].getMaxHp());
|
expect(playerPokemon[0].hp).toBeLessThan(playerStartingHp[0]);
|
||||||
expect(playerPokemon[1].hp).toBe(playerPokemon[1].getMaxHp());
|
expect(playerPokemon[1].hp).toBe(playerStartingHp[1]);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should redirect enemy attacks to the first ally that uses it",
|
"move should redirect enemy attacks to the first ally that uses it",
|
||||||
async () => {
|
async () => {
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const playerPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
|
||||||
|
const playerStartingHp = playerPokemon.map(p => p.hp);
|
||||||
|
|
||||||
game.move.select(Moves.FOLLOW_ME);
|
game.move.select(Moves.FOLLOW_ME);
|
||||||
game.move.select(Moves.FOLLOW_ME, 1);
|
game.move.select(Moves.FOLLOW_ME, 1);
|
||||||
|
|
||||||
// Each player is targeted by an enemy
|
|
||||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER);
|
|
||||||
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD));
|
playerPokemon.sort((a, b) => a.getEffectiveStat(Stat.SPD) - b.getEffectiveStat(Stat.SPD));
|
||||||
|
|
||||||
expect(playerPokemon[1].hp).toBeLessThan(playerPokemon[1].getMaxHp());
|
expect(playerPokemon[1].hp).toBeLessThan(playerStartingHp[1]);
|
||||||
expect(playerPokemon[0].hp).toBe(playerPokemon[0].getMaxHp());
|
expect(playerPokemon[0].hp).toBe(playerStartingHp[0]);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -85,23 +78,21 @@ describe("Moves - Follow Me", () => {
|
|||||||
async () => {
|
async () => {
|
||||||
game.override.ability(Abilities.STALWART);
|
game.override.ability(Abilities.STALWART);
|
||||||
game.override.moveset([Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.QUICK_ATTACK]);
|
||||||
|
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
// Target doesn't need to be specified if the move is self-targeted
|
|
||||||
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp());
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -109,22 +100,21 @@ describe("Moves - Follow Me", () => {
|
|||||||
"move effect should be bypassed by Snipe Shot",
|
"move effect should be bypassed by Snipe Shot",
|
||||||
async () => {
|
async () => {
|
||||||
game.override.moveset([Moves.SNIPE_SHOT]);
|
game.override.moveset([Moves.SNIPE_SHOT]);
|
||||||
|
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
||||||
|
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||||
|
|
||||||
game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.SNIPE_SHOT, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.SNIPE_SHOT, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp());
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
@ -25,41 +26,20 @@ describe("Moves - Rage Powder", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.battleType("double");
|
game.override.battleType("double");
|
||||||
|
game.override.starterSpecies(Species.AMOONGUSS);
|
||||||
game.override.enemySpecies(Species.SNORLAX);
|
game.override.enemySpecies(Species.SNORLAX);
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.TACKLE, Moves.SPLASH]);
|
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move effect should be bypassed by Grass type",
|
"move effect should be bypassed by Grass type",
|
||||||
async () => {
|
async () => {
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.VENUSAUR]);
|
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
await game.startBattle([Species.AMOONGUSS, Species.VENUSAUR]);
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
|
||||||
|
|
||||||
await game.forceEnemyMove(Moves.RAGE_POWDER);
|
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
|
||||||
expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
|
||||||
}, TIMEOUT
|
|
||||||
);
|
|
||||||
|
|
||||||
test(
|
|
||||||
"move effect should be bypassed by Overcoat",
|
|
||||||
async () => {
|
|
||||||
game.override.ability(Abilities.OVERCOAT);
|
|
||||||
|
|
||||||
// Test with two non-Grass type player Pokemon
|
|
||||||
await game.classicMode.startBattle([Species.BLASTOISE, Species.CHARIZARD]);
|
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
@ -67,11 +47,30 @@ describe("Moves - Rage Powder", () => {
|
|||||||
|
|
||||||
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
await game.forceEnemyMove(Moves.RAGE_POWDER);
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||||
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||||
|
}, TIMEOUT
|
||||||
|
);
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
test(
|
||||||
|
"move effect should be bypassed by Overcoat",
|
||||||
|
async () => {
|
||||||
|
game.override.ability(Abilities.OVERCOAT);
|
||||||
|
game.override.enemyMoveset([Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER, Moves.RAGE_POWDER]);
|
||||||
|
|
||||||
|
// Test with two non-Grass type player Pokemon
|
||||||
|
await game.startBattle([Species.BLASTOISE, Species.CHARIZARD]);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||||
|
|
||||||
|
game.move.select(Moves.QUICK_ATTACK, 0, BattlerIndex.ENEMY);
|
||||||
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
// If redirection was bypassed, both enemies should be damaged
|
// If redirection was bypassed, both enemies should be damaged
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||||
|
@ -73,7 +73,7 @@ describe("Moves - Spikes", () => {
|
|||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
game.forceEnemyToSwitch();
|
game.forceOpponentToSwitch();
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyParty()[0];
|
const enemy = game.scene.getEnemyParty()[0];
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
@ -30,46 +31,52 @@ describe("Moves - Spotlight", () => {
|
|||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.enemyLevel(100);
|
game.override.enemyLevel(100);
|
||||||
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
game.override.moveset([Moves.FOLLOW_ME, Moves.RAGE_POWDER, Moves.SPOTLIGHT, Moves.QUICK_ATTACK]);
|
||||||
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.SPLASH]);
|
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should redirect attacks to the target",
|
"move should redirect attacks to the target",
|
||||||
async () => {
|
async () => {
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||||
|
|
||||||
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
||||||
|
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase, false);
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
expect(enemyPokemon[0].hp).toBeLessThan(enemyStartingHp[0]);
|
||||||
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp());
|
expect(enemyPokemon[1].hp).toBe(enemyStartingHp[1]);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
test(
|
test(
|
||||||
"move should cause other redirection moves to fail",
|
"move should cause other redirection moves to fail",
|
||||||
async () => {
|
async () => {
|
||||||
await game.classicMode.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
game.override.enemyMoveset([Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME, Moves.FOLLOW_ME]);
|
||||||
|
|
||||||
|
await game.startBattle([Species.AMOONGUSS, Species.CHARIZARD]);
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
game.move.select(Moves.SPOTLIGHT, 0, BattlerIndex.ENEMY);
|
/**
|
||||||
game.move.select(Moves.QUICK_ATTACK, 1, BattlerIndex.ENEMY_2);
|
* Spotlight will target the slower enemy. In this situation without Spotlight being used,
|
||||||
|
* the faster enemy would normally end up with the Center of Attention tag.
|
||||||
|
*/
|
||||||
|
enemyPokemon.sort((a, b) => b.getEffectiveStat(Stat.SPD) - a.getEffectiveStat(Stat.SPD));
|
||||||
|
const spotTarget = enemyPokemon[1].getBattlerIndex();
|
||||||
|
const attackTarget = enemyPokemon[0].getBattlerIndex();
|
||||||
|
|
||||||
await game.forceEnemyMove(Moves.SPLASH);
|
const enemyStartingHp = enemyPokemon.map(p => p.hp);
|
||||||
await game.forceEnemyMove(Moves.FOLLOW_ME);
|
|
||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase", false);
|
game.move.select(Moves.SPOTLIGHT, 0, spotTarget);
|
||||||
|
game.move.select(Moves.QUICK_ATTACK, 1, attackTarget);
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase, false);
|
||||||
|
|
||||||
expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp());
|
expect(enemyPokemon[1].hp).toBeLessThan(enemyStartingHp[1]);
|
||||||
expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp());
|
expect(enemyPokemon[0].hp).toBe(enemyStartingHp[0]);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
import { Species } from "#app/enums/species";
|
|
||||||
import { GameModes } from "#app/game-mode";
|
|
||||||
import GameManager from "#test/utils/gameManager";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
||||||
import { SPLASH_ONLY } from "./utils/testUtils";
|
|
||||||
import { Moves } from "#app/enums/moves";
|
|
||||||
import { Biome } from "#app/enums/biome";
|
|
||||||
|
|
||||||
describe("Reload", () => {
|
|
||||||
let phaserGame: Phaser.Game;
|
|
||||||
let game: GameManager;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
phaserGame = new Phaser.Game({
|
|
||||||
type: Phaser.HEADLESS,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
game.phaseInterceptor.restoreOg();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
game = new GameManager(phaserGame);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies in a Classic run", async () => {
|
|
||||||
await game.classicMode.startBattle();
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies after a biome switch", async () => {
|
|
||||||
game.override
|
|
||||||
.startingWave(10)
|
|
||||||
.startingBiome(Biome.CAVE) // Will lead to biomes with randomly generated weather
|
|
||||||
.battleType("single")
|
|
||||||
.startingLevel(100)
|
|
||||||
.enemyLevel(1000)
|
|
||||||
.disableTrainerWaves()
|
|
||||||
.moveset([Moves.KOWTOW_CLEAVE])
|
|
||||||
.enemyMoveset(SPLASH_ONLY);
|
|
||||||
await game.dailyMode.startBattle();
|
|
||||||
|
|
||||||
// Transition from Daily Run Wave 10 to Wave 11 in order to trigger biome switch
|
|
||||||
game.move.select(Moves.KOWTOW_CLEAVE);
|
|
||||||
await game.phaseInterceptor.to("DamagePhase");
|
|
||||||
await game.doKillOpponents();
|
|
||||||
await game.toNextWave();
|
|
||||||
expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase");
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies at a Daily run wild Pokemon fight", async () => {
|
|
||||||
await game.dailyMode.startBattle();
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies at a Daily run double battle", async () => {
|
|
||||||
game.override
|
|
||||||
.battleType("double");
|
|
||||||
await game.dailyMode.startBattle();
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies at a Daily run Gym Leader fight", async () => {
|
|
||||||
game.override
|
|
||||||
.battleType("single")
|
|
||||||
.startingWave(40);
|
|
||||||
await game.dailyMode.startBattle();
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies at a Daily run regular trainer fight", async () => {
|
|
||||||
game.override
|
|
||||||
.battleType("single")
|
|
||||||
.startingWave(45);
|
|
||||||
await game.dailyMode.startBattle();
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
|
|
||||||
it("should not have RNG inconsistencies at a Daily run wave 50 Boss fight", async () => {
|
|
||||||
game.override
|
|
||||||
.battleType("single")
|
|
||||||
.startingWave(50);
|
|
||||||
await game.runToFinalBossEncounter([Species.BULBASAUR], GameModes.DAILY);
|
|
||||||
|
|
||||||
const preReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
await game.reload.reloadSession();
|
|
||||||
|
|
||||||
const postReloadRngState = Phaser.Math.RND.state();
|
|
||||||
|
|
||||||
expect(preReloadRngState).toBe(postReloadRngState);
|
|
||||||
}, 20000);
|
|
||||||
});
|
|
@ -53,6 +53,9 @@ describe("UI - Starter select", () => {
|
|||||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
|
handler.processInput(Button.V);
|
||||||
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
@ -114,6 +117,9 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
|
handler.processInput(Button.V);
|
||||||
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
@ -178,6 +184,9 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
handler.processInput(Button.CYCLE_NATURE);
|
handler.processInput(Button.CYCLE_NATURE);
|
||||||
handler.processInput(Button.CYCLE_ABILITY);
|
handler.processInput(Button.CYCLE_ABILITY);
|
||||||
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
|
handler.processInput(Button.V);
|
||||||
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
@ -218,12 +227,11 @@ describe("UI - Starter select", () => {
|
|||||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||||
expect(game.scene.getParty()[0].gender).toBe(Gender.FEMALE);
|
|
||||||
expect(game.scene.getParty()[0].nature).toBe(Nature.LONELY);
|
expect(game.scene.getParty()[0].nature).toBe(Nature.LONELY);
|
||||||
expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.CHLOROPHYLL);
|
expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.CHLOROPHYLL);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("Bulbasaur - shiny - variant 2 female", async() => {
|
it("Bulbasaur - shiny - variant 2 female lonely chlorophyl", async() => {
|
||||||
await game.importData("src/test/utils/saves/everything.prsv");
|
await game.importData("src/test/utils/saves/everything.prsv");
|
||||||
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
||||||
const species = game.scene.gameData.dexData[key];
|
const species = game.scene.gameData.dexData[key];
|
||||||
@ -241,6 +249,9 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
|
handler.processInput(Button.V);
|
||||||
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
@ -302,7 +313,6 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||||
@ -361,7 +371,7 @@ describe("UI - Starter select", () => {
|
|||||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.V);
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
handler.processInput(Button.V);
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
@ -405,7 +415,7 @@ describe("UI - Starter select", () => {
|
|||||||
expect(game.scene.getParty()[0].variant).toBe(1);
|
expect(game.scene.getParty()[0].variant).toBe(1);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("Bulbasaur - shiny - variant 0", async() => {
|
it("Bulbasaur - shiny - variant 2", async() => {
|
||||||
await game.importData("src/test/utils/saves/everything.prsv");
|
await game.importData("src/test/utils/saves/everything.prsv");
|
||||||
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
||||||
const species = game.scene.gameData.dexData[key];
|
const species = game.scene.gameData.dexData[key];
|
||||||
@ -422,6 +432,8 @@ describe("UI - Starter select", () => {
|
|||||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||||
handler.processInput(Button.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.V);
|
handler.processInput(Button.V);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
game.phaseInterceptor.unlock();
|
||||||
@ -462,7 +474,7 @@ describe("UI - Starter select", () => {
|
|||||||
|
|
||||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||||
expect(game.scene.getParty()[0].variant).toBe(0);
|
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async() => {
|
it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column", async() => {
|
||||||
|
@ -2,8 +2,6 @@ import { updateUserInfo } from "#app/account";
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { BattleStyle } from "#app/enums/battle-style";
|
import { BattleStyle } from "#app/enums/battle-style";
|
||||||
import { Moves } from "#app/enums/moves";
|
|
||||||
import { getMoveTargets } from "#app/data/move";
|
|
||||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import Trainer from "#app/field/trainer";
|
import Trainer from "#app/field/trainer";
|
||||||
import { GameModes, getGameMode } from "#app/game-mode";
|
import { GameModes, getGameMode } from "#app/game-mode";
|
||||||
@ -11,7 +9,6 @@ import { ModifierTypeOption, modifierTypes } from "#app/modifier/modifier-type";
|
|||||||
import overrides from "#app/overrides";
|
import overrides from "#app/overrides";
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
import { EncounterPhase } from "#app/phases/encounter-phase";
|
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
|
||||||
import { FaintPhase } from "#app/phases/faint-phase";
|
import { FaintPhase } from "#app/phases/faint-phase";
|
||||||
import { LoginPhase } from "#app/phases/login-phase";
|
import { LoginPhase } from "#app/phases/login-phase";
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
import { MovePhase } from "#app/phases/move-phase";
|
||||||
@ -48,8 +45,6 @@ import { ChallengeModeHelper } from "./helpers/challengeModeHelper";
|
|||||||
import { MoveHelper } from "./helpers/moveHelper";
|
import { MoveHelper } from "./helpers/moveHelper";
|
||||||
import { OverridesHelper } from "./helpers/overridesHelper";
|
import { OverridesHelper } from "./helpers/overridesHelper";
|
||||||
import { SettingsHelper } from "./helpers/settingsHelper";
|
import { SettingsHelper } from "./helpers/settingsHelper";
|
||||||
import { ReloadHelper } from "./helpers/reloadHelper";
|
|
||||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to manage the game state and transitions between phases.
|
* Class to manage the game state and transitions between phases.
|
||||||
@ -66,7 +61,6 @@ export default class GameManager {
|
|||||||
public readonly dailyMode: DailyModeHelper;
|
public readonly dailyMode: DailyModeHelper;
|
||||||
public readonly challengeMode: ChallengeModeHelper;
|
public readonly challengeMode: ChallengeModeHelper;
|
||||||
public readonly settings: SettingsHelper;
|
public readonly settings: SettingsHelper;
|
||||||
public readonly reload: ReloadHelper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of GameManager.
|
* Creates an instance of GameManager.
|
||||||
@ -88,7 +82,6 @@ export default class GameManager {
|
|||||||
this.dailyMode = new DailyModeHelper(this);
|
this.dailyMode = new DailyModeHelper(this);
|
||||||
this.challengeMode = new ChallengeModeHelper(this);
|
this.challengeMode = new ChallengeModeHelper(this);
|
||||||
this.settings = new SettingsHelper(this);
|
this.settings = new SettingsHelper(this);
|
||||||
this.reload = new ReloadHelper(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,42 +231,15 @@ export default class GameManager {
|
|||||||
this.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
this.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||||
const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
|
const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
handler.processInput(Button.CANCEL);
|
handler.processInput(Button.CANCEL);
|
||||||
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase), true);
|
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase), true);
|
||||||
|
|
||||||
this.onNextPrompt("SelectModifierPhase", Mode.CONFIRM, () => {
|
this.onNextPrompt("SelectModifierPhase", Mode.CONFIRM, () => {
|
||||||
const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
|
const handler = this.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase) || this.isCurrentPhase(CheckSwitchPhase));
|
}, () => this.isCurrentPhase(CommandPhase) || this.isCurrentPhase(NewBattlePhase));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
forceOpponentToSwitch() {
|
||||||
* Forces the next enemy selecting a move to use the given move in its moveset against the
|
|
||||||
* given target (if applicable).
|
|
||||||
* @param moveId {@linkcode Moves} the move the enemy will use
|
|
||||||
* @param target {@linkcode BattlerIndex} the target on which the enemy will use the given move
|
|
||||||
*/
|
|
||||||
async forceEnemyMove(moveId: Moves, target?: BattlerIndex) {
|
|
||||||
// Wait for the next EnemyCommandPhase to start
|
|
||||||
await this.phaseInterceptor.to(EnemyCommandPhase, false);
|
|
||||||
const enemy = this.scene.getEnemyField()[(this.scene.getCurrentPhase() as EnemyCommandPhase).getFieldIndex()];
|
|
||||||
const legalTargets = getMoveTargets(enemy, moveId);
|
|
||||||
|
|
||||||
vi.spyOn(enemy, "getNextMove").mockReturnValueOnce({
|
|
||||||
move: moveId,
|
|
||||||
targets: (target && !legalTargets.multiple && legalTargets.targets.includes(target))
|
|
||||||
? [target]
|
|
||||||
: enemy.getNextTargets(moveId)
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the EnemyCommandPhase to completion.
|
|
||||||
* This allows this function to be called consecutively to
|
|
||||||
* force a move for each enemy in a double battle.
|
|
||||||
*/
|
|
||||||
await this.phaseInterceptor.to(EnemyCommandPhase);
|
|
||||||
}
|
|
||||||
|
|
||||||
forceEnemyToSwitch() {
|
|
||||||
const originalMatchupScore = Trainer.prototype.getPartyMemberMatchupScores;
|
const originalMatchupScore = Trainer.prototype.getPartyMemberMatchupScores;
|
||||||
Trainer.prototype.getPartyMemberMatchupScores = () => {
|
Trainer.prototype.getPartyMemberMatchupScores = () => {
|
||||||
Trainer.prototype.getPartyMemberMatchupScores = originalMatchupScore;
|
Trainer.prototype.getPartyMemberMatchupScores = originalMatchupScore;
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import { GameManagerHelper } from "./gameManagerHelper";
|
|
||||||
import { TitlePhase } from "#app/phases/title-phase";
|
|
||||||
import { Mode } from "#app/ui/ui";
|
|
||||||
import { vi } from "vitest";
|
|
||||||
import { BattleStyle } from "#app/enums/battle-style";
|
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
|
||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to allow reloading sessions in unit tests.
|
|
||||||
*/
|
|
||||||
export class ReloadHelper extends GameManagerHelper {
|
|
||||||
/**
|
|
||||||
* Simulate reloading the session from the title screen, until reaching the
|
|
||||||
* beginning of the first turn (equivalent to running `startBattle()`) for
|
|
||||||
* the reloaded session.
|
|
||||||
*/
|
|
||||||
async reloadSession() : Promise<void> {
|
|
||||||
const scene = this.game.scene;
|
|
||||||
const sessionData = scene.gameData.getSessionSaveData(scene);
|
|
||||||
const titlePhase = new TitlePhase(scene);
|
|
||||||
|
|
||||||
scene.clearPhaseQueue();
|
|
||||||
|
|
||||||
// Set the last saved session to the desired session data
|
|
||||||
vi.spyOn(scene.gameData, "getSession").mockReturnValue(
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
resolve(sessionData);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
scene.unshiftPhase(titlePhase);
|
|
||||||
this.game.endPhase(); // End the currently ongoing battle
|
|
||||||
|
|
||||||
titlePhase.loadSaveSlot(-1); // Load the desired session data
|
|
||||||
this.game.phaseInterceptor.shift(); // Loading the save slot also ended TitlePhase, clean it up
|
|
||||||
|
|
||||||
// Run through prompts for switching Pokemon, copied from classicModeHelper.ts
|
|
||||||
if (this.game.scene.battleStyle === BattleStyle.SWITCH) {
|
|
||||||
this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
|
||||||
this.game.setMode(Mode.MESSAGE);
|
|
||||||
this.game.endPhase();
|
|
||||||
}, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase));
|
|
||||||
|
|
||||||
this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
|
||||||
this.game.setMode(Mode.MESSAGE);
|
|
||||||
this.game.endPhase();
|
|
||||||
}, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase));
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.game.phaseInterceptor.to(CommandPhase);
|
|
||||||
console.log("==================[New Turn]==================");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import ErrorInterceptor from "#app/test/utils/errorInterceptor";
|
import ErrorInterceptor from "#app/test/utils/errorInterceptor";
|
||||||
import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
|
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
import { BerryPhase } from "#app/phases/berry-phase";
|
import { BerryPhase } from "#app/phases/berry-phase";
|
||||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||||
@ -12,14 +11,12 @@ import { EndEvolutionPhase } from "#app/phases/end-evolution-phase";
|
|||||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
||||||
import { EvolutionPhase } from "#app/phases/evolution-phase";
|
import { EvolutionPhase } from "#app/phases/evolution-phase";
|
||||||
import { FaintPhase } from "#app/phases/faint-phase";
|
import { FaintPhase } from "#app/phases/faint-phase";
|
||||||
import { LevelCapPhase } from "#app/phases/level-cap-phase";
|
|
||||||
import { LoginPhase } from "#app/phases/login-phase";
|
import { LoginPhase } from "#app/phases/login-phase";
|
||||||
import { MessagePhase } from "#app/phases/message-phase";
|
import { MessagePhase } from "#app/phases/message-phase";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
import { MovePhase } from "#app/phases/move-phase";
|
||||||
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
||||||
import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase";
|
|
||||||
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
import { NextEncounterPhase } from "#app/phases/next-encounter-phase";
|
||||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
||||||
@ -65,7 +62,6 @@ export default class PhaseInterceptor {
|
|||||||
[TitlePhase, this.startPhase],
|
[TitlePhase, this.startPhase],
|
||||||
[SelectGenderPhase, this.startPhase],
|
[SelectGenderPhase, this.startPhase],
|
||||||
[EncounterPhase, this.startPhase],
|
[EncounterPhase, this.startPhase],
|
||||||
[NewBiomeEncounterPhase, this.startPhase],
|
|
||||||
[SelectStarterPhase, this.startPhase],
|
[SelectStarterPhase, this.startPhase],
|
||||||
[PostSummonPhase, this.startPhase],
|
[PostSummonPhase, this.startPhase],
|
||||||
[SummonPhase, this.startPhase],
|
[SummonPhase, this.startPhase],
|
||||||
@ -100,8 +96,6 @@ export default class PhaseInterceptor {
|
|||||||
[PartyHealPhase, this.startPhase],
|
[PartyHealPhase, this.startPhase],
|
||||||
[EvolutionPhase, this.startPhase],
|
[EvolutionPhase, this.startPhase],
|
||||||
[EndEvolutionPhase, this.startPhase],
|
[EndEvolutionPhase, this.startPhase],
|
||||||
[LevelCapPhase, this.startPhase],
|
|
||||||
[AttemptRunPhase, this.startPhase],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
private endBySetMode = [
|
private endBySetMode = [
|
||||||
@ -243,22 +237,6 @@ export default class PhaseInterceptor {
|
|||||||
this.scene.shiftPhase();
|
this.scene.shiftPhase();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the current phase from the phase interceptor.
|
|
||||||
*
|
|
||||||
* Do not call this unless absolutely necessary. This function is intended
|
|
||||||
* for cleaning up the phase interceptor when, for whatever reason, a phase
|
|
||||||
* is manually ended without using the phase interceptor.
|
|
||||||
*
|
|
||||||
* @param shouldRun Whether or not the current scene should also be run.
|
|
||||||
*/
|
|
||||||
shift(shouldRun: boolean = false) : void {
|
|
||||||
this.onHold.shift();
|
|
||||||
if (shouldRun) {
|
|
||||||
this.scene.shiftPhase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to initialize phases and their corresponding methods.
|
* Method to initialize phases and their corresponding methods.
|
||||||
*/
|
*/
|
||||||
|
@ -33,7 +33,7 @@ const timedEvents: TimedEvent[] = [
|
|||||||
xPosition: 19,
|
xPosition: 19,
|
||||||
yPosition: 115,
|
yPosition: 115,
|
||||||
scale: 0.30,
|
scale: 0.30,
|
||||||
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es", "pt-BR", "zh-CN"]
|
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es", "pt_BR", "zh_CN"]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -215,11 +215,12 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||||||
getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] {
|
getTopIvs(ivs: integer[], shownIvsCount: integer): Stat[] {
|
||||||
let shownStats: Stat[] = [];
|
let shownStats: Stat[] = [];
|
||||||
if (shownIvsCount < 6) {
|
if (shownIvsCount < 6) {
|
||||||
const statsPool = PERMANENT_STATS.slice();
|
let highestIv = -1;
|
||||||
// Sort the stats from highest to lowest iv
|
|
||||||
statsPool.sort((s1, s2) => ivs[s2] - ivs[s1]);
|
|
||||||
for (let i = 0; i < shownIvsCount; i++) {
|
for (let i = 0; i < shownIvsCount; i++) {
|
||||||
shownStats.push(statsPool[i]);
|
if (ivs[i] > highestIv) {
|
||||||
|
shownStats.push(PERMANENT_STATS[i]);
|
||||||
|
highestIv = ivs[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shownStats = PERMANENT_STATS.slice();
|
shownStats = PERMANENT_STATS.slice();
|
||||||
|
@ -68,11 +68,7 @@ export default class CommandUiHandler extends UiHandler {
|
|||||||
messageHandler.movesWindowContainer.setVisible(false);
|
messageHandler.movesWindowContainer.setVisible(false);
|
||||||
messageHandler.message.setWordWrapWidth(1110);
|
messageHandler.message.setWordWrapWidth(1110);
|
||||||
messageHandler.showText(i18next.t("commandUiHandler:actionMessage", {pokemonName: getPokemonNameWithAffix(commandPhase.getPokemon())}), 0);
|
messageHandler.showText(i18next.t("commandUiHandler:actionMessage", {pokemonName: getPokemonNameWithAffix(commandPhase.getPokemon())}), 0);
|
||||||
if (this.getCursor() === Command.POKEMON) {
|
this.setCursor(this.getCursor());
|
||||||
this.setCursor(Command.FIGHT);
|
|
||||||
} else {
|
|
||||||
this.setCursor(this.getCursor());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -89,7 +85,7 @@ export default class CommandUiHandler extends UiHandler {
|
|||||||
if (button === Button.ACTION) {
|
if (button === Button.ACTION) {
|
||||||
switch (cursor) {
|
switch (cursor) {
|
||||||
// Fight
|
// Fight
|
||||||
case Command.FIGHT:
|
case 0:
|
||||||
if ((this.scene.getCurrentPhase() as CommandPhase).checkFightOverride()) {
|
if ((this.scene.getCurrentPhase() as CommandPhase).checkFightOverride()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -97,17 +93,17 @@ export default class CommandUiHandler extends UiHandler {
|
|||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
// Ball
|
// Ball
|
||||||
case Command.BALL:
|
case 1:
|
||||||
ui.setModeWithoutClear(Mode.BALL);
|
ui.setModeWithoutClear(Mode.BALL);
|
||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
// Pokemon
|
// Pokemon
|
||||||
case Command.POKEMON:
|
case 2:
|
||||||
ui.setMode(Mode.PARTY, PartyUiMode.SWITCH, (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), null, PartyUiHandler.FilterNonFainted);
|
ui.setMode(Mode.PARTY, PartyUiMode.SWITCH, (this.scene.getCurrentPhase() as CommandPhase).getPokemon().getFieldIndex(), null, PartyUiHandler.FilterNonFainted);
|
||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
// Run
|
// Run
|
||||||
case Command.RUN:
|
case 3:
|
||||||
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0);
|
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.RUN, 0);
|
||||||
success = true;
|
success = true;
|
||||||
break;
|
break;
|
||||||
|
@ -1,320 +0,0 @@
|
|||||||
import BattleScene from "../battle-scene";
|
|
||||||
import { Mode } from "./ui";
|
|
||||||
import PokemonIconAnimHandler, { PokemonIconAnimMode } from "./pokemon-icon-anim-handler";
|
|
||||||
import MessageUiHandler from "./message-ui-handler";
|
|
||||||
import { getEggTierForSpecies } from "../data/egg";
|
|
||||||
import {Button} from "#enums/buttons";
|
|
||||||
import { Gender } from "#app/data/gender";
|
|
||||||
import { getVariantTint } from "#app/data/variant";
|
|
||||||
import { EggTier } from "#app/enums/egg-type";
|
|
||||||
import PokemonHatchInfoContainer from "./pokemon-hatch-info-container";
|
|
||||||
import { EggSummaryPhase } from "#app/phases/egg-summary-phase";
|
|
||||||
import { DexAttr } from "#app/system/game-data";
|
|
||||||
import { EggHatchData } from "#app/data/egg-hatch-data";
|
|
||||||
|
|
||||||
const iconContainerX = 115;
|
|
||||||
const iconContainerY = 9;
|
|
||||||
const numCols = 11;
|
|
||||||
const iconSize = 18;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UI Handler for the egg summary.
|
|
||||||
* Handles navigation and display of each pokemon as a list
|
|
||||||
* Also handles display of the pokemon-hatch-info-container
|
|
||||||
*/
|
|
||||||
export default class EggSummaryUiHandler extends MessageUiHandler {
|
|
||||||
/** holds all elements in the scene */
|
|
||||||
private eggHatchContainer: Phaser.GameObjects.Container;
|
|
||||||
/** holds the icon containers and info container */
|
|
||||||
private summaryContainer: Phaser.GameObjects.Container;
|
|
||||||
/** container for the mini pokemon sprites */
|
|
||||||
private pokemonIconSpritesContainer: Phaser.GameObjects.Container;
|
|
||||||
/** container for the icons displayed alongside the mini icons (e.g. shiny, HA capsule) */
|
|
||||||
private pokemonIconsContainer: Phaser.GameObjects.Container;
|
|
||||||
/** hatch info container that displays the current pokemon / hatch (main element on left hand side) */
|
|
||||||
private infoContainer: PokemonHatchInfoContainer;
|
|
||||||
/** handles jumping animations for the pokemon sprite icons */
|
|
||||||
private iconAnimHandler: PokemonIconAnimHandler;
|
|
||||||
private eggHatchBg: Phaser.GameObjects.Image;
|
|
||||||
private cursorObj: Phaser.GameObjects.Image;
|
|
||||||
private eggHatchData: EggHatchData[];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows subscribers to listen for events
|
|
||||||
*
|
|
||||||
* Current Events:
|
|
||||||
* - {@linkcode EggEventType.EGG_COUNT_CHANGED} {@linkcode EggCountChangedEvent}
|
|
||||||
*/
|
|
||||||
public readonly eventTarget: EventTarget = new EventTarget();
|
|
||||||
|
|
||||||
constructor(scene: BattleScene) {
|
|
||||||
super(scene, Mode.EGG_HATCH_SUMMARY);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
const ui = this.getUi();
|
|
||||||
|
|
||||||
this.summaryContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
|
|
||||||
this.summaryContainer.setVisible(false);
|
|
||||||
ui.add(this.summaryContainer);
|
|
||||||
|
|
||||||
this.eggHatchContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
|
|
||||||
this.eggHatchContainer.setVisible(false);
|
|
||||||
ui.add(this.eggHatchContainer);
|
|
||||||
|
|
||||||
this.iconAnimHandler = new PokemonIconAnimHandler();
|
|
||||||
this.iconAnimHandler.setup(this.scene);
|
|
||||||
|
|
||||||
this.eggHatchBg = this.scene.add.image(0, 0, "egg_summary_bg");
|
|
||||||
this.eggHatchBg.setOrigin(0, 0);
|
|
||||||
this.eggHatchContainer.add(this.eggHatchBg);
|
|
||||||
|
|
||||||
this.pokemonIconsContainer = this.scene.add.container(iconContainerX, iconContainerY);
|
|
||||||
this.pokemonIconSpritesContainer = this.scene.add.container(iconContainerX, iconContainerY);
|
|
||||||
this.summaryContainer.add(this.pokemonIconsContainer);
|
|
||||||
this.summaryContainer.add(this.pokemonIconSpritesContainer);
|
|
||||||
|
|
||||||
this.cursorObj = this.scene.add.image(0, 0, "select_cursor");
|
|
||||||
this.cursorObj.setOrigin(0, 0);
|
|
||||||
this.summaryContainer.add(this.cursorObj);
|
|
||||||
|
|
||||||
this.infoContainer = new PokemonHatchInfoContainer(this.scene, this.summaryContainer);
|
|
||||||
this.infoContainer.setup();
|
|
||||||
this.infoContainer.changeToEggSummaryLayout();
|
|
||||||
this.infoContainer.setVisible(true);
|
|
||||||
this.summaryContainer.add(this.infoContainer);
|
|
||||||
|
|
||||||
this.cursor = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
super.clear();
|
|
||||||
this.cursor = -1;
|
|
||||||
this.summaryContainer.setVisible(false);
|
|
||||||
this.pokemonIconSpritesContainer.removeAll(true);
|
|
||||||
this.pokemonIconsContainer.removeAll(true);
|
|
||||||
this.eggHatchBg.setVisible(false);
|
|
||||||
this.getUi().hideTooltip();
|
|
||||||
// Note: Questions on garbage collection go to @frutescens
|
|
||||||
const activeKeys = this.scene.getActiveKeys();
|
|
||||||
// Removing unnecessary sprites from animation manager
|
|
||||||
const animKeys = Object.keys(this.scene.anims["anims"]["entries"]);
|
|
||||||
animKeys.forEach(key => {
|
|
||||||
if (key.startsWith("pkmn__") && !activeKeys.includes(key)) {
|
|
||||||
this.scene.anims.remove(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Removing unnecessary cries from audio cache
|
|
||||||
const audioKeys = Object.keys(this.scene.cache.audio.entries.entries);
|
|
||||||
audioKeys.forEach(key => {
|
|
||||||
if (key.startsWith("cry/") && !activeKeys.includes(key)) {
|
|
||||||
delete this.scene.cache.audio.entries.entries[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Clears eggHatchData in EggSummaryUiHandler
|
|
||||||
this.eggHatchData.length = 0;
|
|
||||||
// Removes Pokemon icons in EggSummaryUiHandler
|
|
||||||
this.iconAnimHandler.removeAll();
|
|
||||||
console.log("Egg Summary Handler cleared");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param args EggHatchData[][]
|
|
||||||
* args[0]: list of EggHatchData for each egg/pokemon hatched
|
|
||||||
*/
|
|
||||||
show(args: EggHatchData[][]): boolean {
|
|
||||||
super.show(args);
|
|
||||||
if (args.length >= 1) {
|
|
||||||
// sort the egg hatch data by egg tier then by species number (then by order hatched)
|
|
||||||
this.eggHatchData = args[0].sort(function sortHatchData(a: EggHatchData, b: EggHatchData) {
|
|
||||||
const speciesA = a.pokemon.species;
|
|
||||||
const speciesB = b.pokemon.species;
|
|
||||||
if (getEggTierForSpecies(speciesA) < getEggTierForSpecies(speciesB)) {
|
|
||||||
return -1;
|
|
||||||
} else if (getEggTierForSpecies(speciesA) > getEggTierForSpecies(speciesB)) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
if (speciesA.speciesId < speciesB.speciesId) {
|
|
||||||
return -1;
|
|
||||||
} else if (speciesA.speciesId > speciesB.speciesId) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getUi().bringToTop(this.summaryContainer);
|
|
||||||
this.summaryContainer.setVisible(true);
|
|
||||||
this.eggHatchContainer.setVisible(true);
|
|
||||||
this.pokemonIconsContainer.setVisible(true);
|
|
||||||
this.eggHatchBg.setVisible(true);
|
|
||||||
this.infoContainer.hideDisplayPokemon();
|
|
||||||
|
|
||||||
this.eggHatchData.forEach( (value: EggHatchData, i: number) => {
|
|
||||||
const x = (i % numCols) * iconSize;
|
|
||||||
const y = Math.floor(i / numCols) * iconSize;
|
|
||||||
|
|
||||||
const displayPokemon = value.pokemon;
|
|
||||||
const offset = 2;
|
|
||||||
const rightSideX = 12;
|
|
||||||
|
|
||||||
const bg = this.scene.add.image(x+2, y+5, "passive_bg");
|
|
||||||
bg.setOrigin(0, 0);
|
|
||||||
bg.setScale(0.75);
|
|
||||||
bg.setVisible(true);
|
|
||||||
this.pokemonIconsContainer.add(bg);
|
|
||||||
|
|
||||||
// set tint for passive bg
|
|
||||||
switch (getEggTierForSpecies(displayPokemon.species)) {
|
|
||||||
case EggTier.COMMON:
|
|
||||||
bg.setVisible(false);
|
|
||||||
break;
|
|
||||||
case EggTier.GREAT:
|
|
||||||
bg.setTint(0xabafff);
|
|
||||||
break;
|
|
||||||
case EggTier.ULTRA:
|
|
||||||
bg.setTint(0xffffaa);
|
|
||||||
break;
|
|
||||||
case EggTier.MASTER:
|
|
||||||
bg.setTint(0xdfffaf);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const species = displayPokemon.species;
|
|
||||||
const female = displayPokemon.gender === Gender.FEMALE;
|
|
||||||
const formIndex = displayPokemon.formIndex;
|
|
||||||
const variant = displayPokemon.variant;
|
|
||||||
const isShiny = displayPokemon.shiny;
|
|
||||||
|
|
||||||
// set pokemon icon (and replace with base sprite if there is a mismatch)
|
|
||||||
const icon = this.scene.add.sprite(x - offset, y + offset, species.getIconAtlasKey(formIndex, isShiny, variant));
|
|
||||||
icon.setScale(0.5);
|
|
||||||
icon.setOrigin(0, 0);
|
|
||||||
icon.setFrame(species.getIconId(female, formIndex, isShiny, variant));
|
|
||||||
|
|
||||||
if (icon.frame.name !== species.getIconId(female, formIndex, isShiny, variant)) {
|
|
||||||
console.log(`${species.name}'s variant icon does not exist. Replacing with default.`);
|
|
||||||
icon.setTexture(species.getIconAtlasKey(formIndex, false, variant));
|
|
||||||
icon.setFrame(species.getIconId(female, formIndex, false, variant));
|
|
||||||
}
|
|
||||||
this.pokemonIconSpritesContainer.add(icon);
|
|
||||||
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.NONE);
|
|
||||||
|
|
||||||
const shiny = this.scene.add.image(x + rightSideX, y + offset * 2, "shiny_star_small");
|
|
||||||
shiny.setScale(0.5);
|
|
||||||
shiny.setVisible(displayPokemon.shiny);
|
|
||||||
shiny.setTint(getVariantTint(displayPokemon.variant));
|
|
||||||
this.pokemonIconsContainer.add(shiny);
|
|
||||||
|
|
||||||
const ha = this.scene.add.image(x + rightSideX, y + 7, "ha_capsule");
|
|
||||||
ha.setScale(0.5);
|
|
||||||
ha.setVisible((displayPokemon.hasAbility(displayPokemon.species.abilityHidden)));
|
|
||||||
this.pokemonIconsContainer.add(ha);
|
|
||||||
|
|
||||||
const pb = this.scene.add.image(x + rightSideX, y + offset * 7, "icon_owned");
|
|
||||||
pb.setOrigin(0, 0);
|
|
||||||
pb.setScale(0.5);
|
|
||||||
|
|
||||||
// add animation for new unlocks (new catch or new shiny or new form)
|
|
||||||
const dexEntry = value.dexEntryBeforeUpdate;
|
|
||||||
const caughtAttr = dexEntry.caughtAttr;
|
|
||||||
const newShiny = BigInt(1 << (displayPokemon.shiny ? 1 : 0));
|
|
||||||
const newVariant = BigInt(1 << (displayPokemon.variant + 4));
|
|
||||||
const newShinyOrVariant = ((newShiny & caughtAttr) === BigInt(0)) || ((newVariant & caughtAttr) === BigInt(0));
|
|
||||||
const newForm = (BigInt(1 << displayPokemon.formIndex) * DexAttr.DEFAULT_FORM & caughtAttr) === BigInt(0);
|
|
||||||
|
|
||||||
pb.setVisible(!caughtAttr || newForm);
|
|
||||||
if (!caughtAttr || newShinyOrVariant || newForm) {
|
|
||||||
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE);
|
|
||||||
}
|
|
||||||
this.pokemonIconsContainer.add(pb);
|
|
||||||
|
|
||||||
const em = this.scene.add.image(x, y + offset, "icon_egg_move");
|
|
||||||
em.setOrigin(0, 0);
|
|
||||||
em.setScale(0.5);
|
|
||||||
em.setVisible(value.eggMoveUnlocked);
|
|
||||||
this.pokemonIconsContainer.add(em);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.setCursor(0);
|
|
||||||
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
processInput(button: Button): boolean {
|
|
||||||
const ui = this.getUi();
|
|
||||||
|
|
||||||
let success = false;
|
|
||||||
const error = false;
|
|
||||||
if (button === Button.CANCEL) {
|
|
||||||
const phase = this.scene.getCurrentPhase();
|
|
||||||
if (phase instanceof EggSummaryPhase) {
|
|
||||||
phase.end();
|
|
||||||
}
|
|
||||||
ui.revertMode();
|
|
||||||
success = true;
|
|
||||||
} else {
|
|
||||||
const count = this.eggHatchData.length;
|
|
||||||
const rows = Math.ceil(count / numCols);
|
|
||||||
const row = Math.floor(this.cursor / numCols);
|
|
||||||
switch (button) {
|
|
||||||
case Button.UP:
|
|
||||||
if (row) {
|
|
||||||
success = this.setCursor(this.cursor - numCols);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.DOWN:
|
|
||||||
if (row < rows - 2 || (row < rows - 1 && this.cursor % numCols <= (count - 1) % numCols)) {
|
|
||||||
success = this.setCursor(this.cursor + numCols);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.LEFT:
|
|
||||||
if (this.cursor % numCols) {
|
|
||||||
success = this.setCursor(this.cursor - 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Button.RIGHT:
|
|
||||||
if (this.cursor % numCols < (row < rows - 1 ? 10 : (count - 1) % numCols)) {
|
|
||||||
success = this.setCursor(this.cursor + 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
ui.playSelect();
|
|
||||||
} else if (error) {
|
|
||||||
ui.playError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return success || error;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCursor(cursor: number): boolean {
|
|
||||||
let changed = false;
|
|
||||||
|
|
||||||
const lastCursor = this.cursor;
|
|
||||||
|
|
||||||
changed = super.setCursor(cursor);
|
|
||||||
|
|
||||||
if (changed) {
|
|
||||||
this.cursorObj.setPosition(iconContainerX - 1 + iconSize * (cursor % numCols), iconContainerY + 1 + iconSize * Math.floor(cursor / numCols));
|
|
||||||
|
|
||||||
if (lastCursor > -1) {
|
|
||||||
this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(lastCursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.NONE);
|
|
||||||
}
|
|
||||||
this.iconAnimHandler.addOrUpdate(this.pokemonIconSpritesContainer.getAt(cursor) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.ACTIVE);
|
|
||||||
|
|
||||||
this.infoContainer.showHatchInfo(this.eggHatchData[cursor]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -95,13 +95,9 @@ export default class FightUiHandler extends UiHandler {
|
|||||||
messageHandler.bg.setVisible(false);
|
messageHandler.bg.setVisible(false);
|
||||||
messageHandler.commandWindow.setVisible(false);
|
messageHandler.commandWindow.setVisible(false);
|
||||||
messageHandler.movesWindowContainer.setVisible(true);
|
messageHandler.movesWindowContainer.setVisible(true);
|
||||||
const pokemon = (this.scene.getCurrentPhase() as CommandPhase).getPokemon();
|
this.setCursor(this.getCursor());
|
||||||
if (pokemon.battleSummonData.turnCount <= 1) {
|
|
||||||
this.setCursor(0);
|
|
||||||
} else {
|
|
||||||
this.setCursor(this.getCursor());
|
|
||||||
}
|
|
||||||
this.displayMoves();
|
this.displayMoves();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,9 +257,6 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
|||||||
if (this.scene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
|
if (this.scene.shopCursorTarget === ShopCursorTarget.CHECK_TEAM) {
|
||||||
this.setRowCursor(0);
|
this.setRowCursor(0);
|
||||||
this.setCursor(2);
|
this.setCursor(2);
|
||||||
} else if ((this.scene.shopCursorTarget === ShopCursorTarget.SHOP) && this.scene.gameMode.hasNoShop) {
|
|
||||||
this.setRowCursor(ShopCursorTarget.REWARDS);
|
|
||||||
this.setCursor(0);
|
|
||||||
} else {
|
} else {
|
||||||
this.setRowCursor(this.scene.shopCursorTarget);
|
this.setRowCursor(this.scene.shopCursorTarget);
|
||||||
this.setCursor(0);
|
this.setCursor(0);
|
||||||
|
@ -311,7 +311,7 @@ export default class PartyUiHandler extends MessageUiHandler {
|
|||||||
this.partyContainer.setVisible(true);
|
this.partyContainer.setVisible(true);
|
||||||
this.partyBg.setTexture(`party_bg${this.scene.currentBattle.double ? "_double" : ""}`);
|
this.partyBg.setTexture(`party_bg${this.scene.currentBattle.double ? "_double" : ""}`);
|
||||||
this.populatePartySlots();
|
this.populatePartySlots();
|
||||||
this.setCursor(0);
|
this.setCursor(this.cursor < 6 ? this.cursor : 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,189 +0,0 @@
|
|||||||
import PokemonInfoContainer from "./pokemon-info-container";
|
|
||||||
import BattleScene from "../battle-scene";
|
|
||||||
import { Gender } from "../data/gender";
|
|
||||||
import { Type } from "../data/type";
|
|
||||||
import * as Utils from "../utils";
|
|
||||||
import { TextStyle, addTextObject } from "./text";
|
|
||||||
import { speciesEggMoves } from "#app/data/egg-moves";
|
|
||||||
import { allMoves } from "#app/data/move";
|
|
||||||
import { Species } from "#app/enums/species";
|
|
||||||
import { getEggTierForSpecies } from "#app/data/egg";
|
|
||||||
import { starterColors } from "../battle-scene";
|
|
||||||
import { argbFromRgba } from "@material/material-color-utilities";
|
|
||||||
import { EggHatchData } from "#app/data/egg-hatch-data";
|
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import { getPokemonSpeciesForm } from "#app/data/pokemon-species";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for the hatch info summary of each pokemon
|
|
||||||
* Holds an info container as well as an additional egg sprite, name, egg moves and main sprite
|
|
||||||
*/
|
|
||||||
export default class PokemonHatchInfoContainer extends PokemonInfoContainer {
|
|
||||||
private currentPokemonSprite: Phaser.GameObjects.Sprite;
|
|
||||||
private pokemonNumberText: Phaser.GameObjects.Text;
|
|
||||||
private pokemonNameText: Phaser.GameObjects.Text;
|
|
||||||
private pokemonEggMovesContainer: Phaser.GameObjects.Container;
|
|
||||||
private pokemonEggMoveContainers: Phaser.GameObjects.Container[];
|
|
||||||
private pokemonEggMoveBgs: Phaser.GameObjects.NineSlice[];
|
|
||||||
private pokemonEggMoveLabels: Phaser.GameObjects.Text[];
|
|
||||||
private pokemonHatchedIcon : Phaser.GameObjects.Sprite;
|
|
||||||
private pokemonListContainer: Phaser.GameObjects.Container;
|
|
||||||
private pokemonCandyIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private pokemonCandyOverlayIcon: Phaser.GameObjects.Sprite;
|
|
||||||
private pokemonCandyCountText: Phaser.GameObjects.Text;
|
|
||||||
|
|
||||||
constructor(scene: BattleScene, listContainer : Phaser.GameObjects.Container, x: number = 115, y: number = 9,) {
|
|
||||||
super(scene, x, y);
|
|
||||||
this.pokemonListContainer = listContainer;
|
|
||||||
|
|
||||||
}
|
|
||||||
setup(): void {
|
|
||||||
super.setup();
|
|
||||||
super.changeToEggSummaryLayout();
|
|
||||||
|
|
||||||
this.currentPokemonSprite = this.scene.add.sprite(54, 80, "pkmn__sub");
|
|
||||||
this.currentPokemonSprite.setScale(0.8);
|
|
||||||
this.currentPokemonSprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
|
|
||||||
this.pokemonListContainer.add(this.currentPokemonSprite);
|
|
||||||
|
|
||||||
// setup name and number
|
|
||||||
this.pokemonNumberText = addTextObject(this.scene, 80, 107.5, "0000", TextStyle.SUMMARY, {fontSize: 74});
|
|
||||||
this.pokemonNumberText.setOrigin(0, 0);
|
|
||||||
this.pokemonListContainer.add(this.pokemonNumberText);
|
|
||||||
|
|
||||||
this.pokemonNameText = addTextObject(this.scene, 7, 107.5, "", TextStyle.SUMMARY, {fontSize: 74});
|
|
||||||
this.pokemonNameText.setOrigin(0, 0);
|
|
||||||
this.pokemonListContainer.add(this.pokemonNameText);
|
|
||||||
|
|
||||||
// setup egg icon and candy count
|
|
||||||
this.pokemonHatchedIcon = this.scene.add.sprite(-5, 90, "egg_icons");
|
|
||||||
this.pokemonHatchedIcon.setOrigin(0, 0.2);
|
|
||||||
this.pokemonHatchedIcon.setScale(0.8);
|
|
||||||
this.pokemonListContainer.add(this.pokemonHatchedIcon);
|
|
||||||
|
|
||||||
this.pokemonCandyIcon = this.scene.add.sprite(4.5, 40, "candy");
|
|
||||||
this.pokemonCandyIcon.setScale(0.5);
|
|
||||||
this.pokemonCandyIcon.setOrigin(0, 0);
|
|
||||||
this.pokemonListContainer.add(this.pokemonCandyIcon);
|
|
||||||
|
|
||||||
this.pokemonCandyOverlayIcon = this.scene.add.sprite(4.5, 40, "candy_overlay");
|
|
||||||
this.pokemonCandyOverlayIcon.setScale(0.5);
|
|
||||||
this.pokemonCandyOverlayIcon.setOrigin(0, 0);
|
|
||||||
this.pokemonListContainer.add(this.pokemonCandyOverlayIcon);
|
|
||||||
|
|
||||||
this.pokemonCandyCountText = addTextObject(this.scene, 14, 40, "x0", TextStyle.SUMMARY, { fontSize: "56px" });
|
|
||||||
this.pokemonCandyCountText.setOrigin(0, 0);
|
|
||||||
this.pokemonListContainer.add(this.pokemonCandyCountText);
|
|
||||||
|
|
||||||
// setup egg moves
|
|
||||||
this.pokemonEggMoveContainers = [];
|
|
||||||
this.pokemonEggMoveBgs = [];
|
|
||||||
this.pokemonEggMoveLabels = [];
|
|
||||||
this.pokemonEggMovesContainer = this.scene.add.container(0, 200);
|
|
||||||
this.pokemonEggMovesContainer.setVisible(false);
|
|
||||||
this.pokemonEggMovesContainer.setScale(0.5);
|
|
||||||
|
|
||||||
for (let m = 0; m < 4; m++) {
|
|
||||||
const eggMoveContainer = this.scene.add.container(0, 0 + 6 * m);
|
|
||||||
|
|
||||||
const eggMoveBg = this.scene.add.nineslice(70, 0, "type_bgs", "unknown", 92, 14, 2, 2, 2, 2);
|
|
||||||
eggMoveBg.setOrigin(1, 0);
|
|
||||||
|
|
||||||
const eggMoveLabel = addTextObject(this.scene, 70 -eggMoveBg.width / 2, 0, "???", TextStyle.PARTY);
|
|
||||||
eggMoveLabel.setOrigin(0.5, 0);
|
|
||||||
|
|
||||||
this.pokemonEggMoveBgs.push(eggMoveBg);
|
|
||||||
this.pokemonEggMoveLabels.push(eggMoveLabel);
|
|
||||||
|
|
||||||
eggMoveContainer.add(eggMoveBg);
|
|
||||||
eggMoveContainer.add(eggMoveLabel);
|
|
||||||
eggMoveContainer.setScale(0.44);
|
|
||||||
|
|
||||||
this.pokemonEggMoveContainers.push(eggMoveContainer);
|
|
||||||
|
|
||||||
this.pokemonEggMovesContainer.add(eggMoveContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.add(this.pokemonEggMoveContainers);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable the sprite (and replace with substitute)
|
|
||||||
*/
|
|
||||||
hideDisplayPokemon() {
|
|
||||||
this.currentPokemonSprite.setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display a given pokemon sprite with animations
|
|
||||||
* assumes the specific pokemon sprite has already been loaded
|
|
||||||
*/
|
|
||||||
displayPokemon(pokemon: PlayerPokemon) {
|
|
||||||
const species = pokemon.species;
|
|
||||||
const female = pokemon.gender === Gender.FEMALE;
|
|
||||||
const formIndex = pokemon.formIndex;
|
|
||||||
const shiny = pokemon.shiny;
|
|
||||||
const variant = pokemon.variant;
|
|
||||||
this.currentPokemonSprite.setVisible(false);
|
|
||||||
species.loadAssets(this.scene, female, formIndex, shiny, variant, true).then(() => {
|
|
||||||
|
|
||||||
getPokemonSpeciesForm(species.speciesId, pokemon.formIndex).cry(this.scene);
|
|
||||||
this.currentPokemonSprite.play(species.getSpriteKey(female, formIndex, shiny, variant));
|
|
||||||
this.currentPokemonSprite.setPipelineData("shiny", shiny);
|
|
||||||
this.currentPokemonSprite.setPipelineData("variant", variant);
|
|
||||||
this.currentPokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female, formIndex, shiny, variant));
|
|
||||||
this.currentPokemonSprite.setVisible(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the info container with the appropriate dex data and starter entry from the hatchInfo
|
|
||||||
* Also updates the displayed name, number, egg moves and main animated sprite for the pokemon
|
|
||||||
* @param hatchInfo The EggHatchData of the pokemon / new hatch to show
|
|
||||||
*/
|
|
||||||
showHatchInfo(hatchInfo: EggHatchData) {
|
|
||||||
this.pokemonEggMovesContainer.setVisible(true);
|
|
||||||
|
|
||||||
const pokemon = hatchInfo.pokemon;
|
|
||||||
const species = pokemon.species;
|
|
||||||
this.displayPokemon(pokemon);
|
|
||||||
|
|
||||||
super.show(pokemon, false, 1, hatchInfo.getDex(), hatchInfo.getStarterEntry(), true);
|
|
||||||
const colorScheme = starterColors[species.speciesId];
|
|
||||||
|
|
||||||
this.pokemonCandyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
|
|
||||||
this.pokemonCandyIcon.setVisible(true);
|
|
||||||
this.pokemonCandyOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
|
|
||||||
this.pokemonCandyOverlayIcon.setVisible(true);
|
|
||||||
this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`);
|
|
||||||
this.pokemonCandyCountText.setVisible(true);
|
|
||||||
|
|
||||||
this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4));
|
|
||||||
this.pokemonNameText.setText(species.name);
|
|
||||||
|
|
||||||
const hasEggMoves = species && speciesEggMoves.hasOwnProperty(species.speciesId);
|
|
||||||
|
|
||||||
for (let em = 0; em < 4; em++) {
|
|
||||||
const eggMove = hasEggMoves ? allMoves[speciesEggMoves[species.speciesId][em]] : null;
|
|
||||||
const eggMoveUnlocked = eggMove && this.scene.gameData.starterData[species.speciesId].eggMoves & Math.pow(2, em);
|
|
||||||
this.pokemonEggMoveBgs[em].setFrame(Type[eggMove ? eggMove.type : Type.UNKNOWN].toString().toLowerCase());
|
|
||||||
|
|
||||||
this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : "???");
|
|
||||||
if (!(eggMove && hatchInfo.starterDataEntryBeforeUpdate.eggMoves & Math.pow(2, em)) && eggMoveUnlocked) {
|
|
||||||
this.pokemonEggMoveLabels[em].setText("(+) " + eggMove.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// will always have at least one egg move
|
|
||||||
this.pokemonEggMovesContainer.setVisible(true);
|
|
||||||
|
|
||||||
if (species.speciesId === Species.MANAPHY || species.speciesId === Species.PHIONE) {
|
|
||||||
this.pokemonHatchedIcon.setFrame("manaphy");
|
|
||||||
} else {
|
|
||||||
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -6,7 +6,7 @@ import { getNatureName } from "../data/nature";
|
|||||||
import { Type } from "../data/type";
|
import { Type } from "../data/type";
|
||||||
import Pokemon from "../field/pokemon";
|
import Pokemon from "../field/pokemon";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { DexAttr, DexEntry, StarterDataEntry } from "../system/game-data";
|
import { DexAttr } from "../system/game-data";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import ConfirmUiHandler from "./confirm-ui-handler";
|
import ConfirmUiHandler from "./confirm-ui-handler";
|
||||||
import { StatsContainer } from "./stats-container";
|
import { StatsContainer } from "./stats-container";
|
||||||
@ -24,7 +24,7 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
|||||||
infoContainerTextSize: "64px"
|
infoContainerTextSize: "64px"
|
||||||
},
|
},
|
||||||
"de": {
|
"de": {
|
||||||
infoContainerTextSize: "64px",
|
infoContainerTextSize: "64px"
|
||||||
},
|
},
|
||||||
"es": {
|
"es": {
|
||||||
infoContainerTextSize: "64px"
|
infoContainerTextSize: "64px"
|
||||||
@ -63,7 +63,6 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
private pokemonMovesContainers: Phaser.GameObjects.Container[];
|
private pokemonMovesContainers: Phaser.GameObjects.Container[];
|
||||||
private pokemonMoveBgs: Phaser.GameObjects.NineSlice[];
|
private pokemonMoveBgs: Phaser.GameObjects.NineSlice[];
|
||||||
private pokemonMoveLabels: Phaser.GameObjects.Text[];
|
private pokemonMoveLabels: Phaser.GameObjects.Text[];
|
||||||
private infoBg;
|
|
||||||
|
|
||||||
private numCharsBeforeCutoff = 16;
|
private numCharsBeforeCutoff = 16;
|
||||||
|
|
||||||
@ -84,9 +83,9 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
|
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
|
||||||
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
|
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
|
||||||
const textSettings = languageSettings[langSettingKey];
|
const textSettings = languageSettings[langSettingKey];
|
||||||
this.infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132);
|
const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132);
|
||||||
this.infoBg.setOrigin(0.5, 0.5);
|
infoBg.setOrigin(0.5, 0.5);
|
||||||
this.infoBg.setName("window-info-bg");
|
infoBg.setName("window-info-bg");
|
||||||
|
|
||||||
this.pokemonMovesContainer = this.scene.add.container(6, 14);
|
this.pokemonMovesContainer = this.scene.add.container(6, 14);
|
||||||
this.pokemonMovesContainer.setName("pkmn-moves");
|
this.pokemonMovesContainer.setName("pkmn-moves");
|
||||||
@ -134,7 +133,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
this.statsContainer = new StatsContainer(this.scene, -48, -64, true);
|
this.statsContainer = new StatsContainer(this.scene, -48, -64, true);
|
||||||
|
|
||||||
this.add(this.infoBg);
|
this.add(infoBg);
|
||||||
this.add(this.statsContainer);
|
this.add(this.statsContainer);
|
||||||
|
|
||||||
// The position should be set per language
|
// The position should be set per language
|
||||||
@ -208,16 +207,9 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
this.setVisible(false);
|
this.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1, dexEntry?: DexEntry, starterEntry?: StarterDataEntry, eggInfo = false): Promise<void> {
|
show(pokemon: Pokemon, showMoves: boolean = false, speedMultiplier: number = 1): Promise<void> {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
if (!dexEntry) {
|
const caughtAttr = BigInt(pokemon.scene.gameData.dexData[pokemon.species.speciesId].caughtAttr);
|
||||||
dexEntry = pokemon.scene.gameData.dexData[pokemon.species.speciesId];
|
|
||||||
}
|
|
||||||
if (!starterEntry) {
|
|
||||||
starterEntry = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()];
|
|
||||||
}
|
|
||||||
|
|
||||||
const caughtAttr = BigInt(dexEntry.caughtAttr);
|
|
||||||
if (pokemon.gender > Gender.GENDERLESS) {
|
if (pokemon.gender > Gender.GENDERLESS) {
|
||||||
this.pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
|
this.pokemonGenderText.setText(getGenderSymbol(pokemon.gender));
|
||||||
this.pokemonGenderText.setColor(getGenderColor(pokemon.gender));
|
this.pokemonGenderText.setColor(getGenderColor(pokemon.gender));
|
||||||
@ -276,7 +268,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
|
const opponentPokemonAbilityIndex = (opponentPokemonOneNormalAbility && pokemon.abilityIndex === 1) ? 2 : pokemon.abilityIndex;
|
||||||
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
|
const opponentPokemonAbilityAttr = 1 << opponentPokemonAbilityIndex;
|
||||||
|
|
||||||
const rootFormHasHiddenAbility = starterEntry.abilityAttr & opponentPokemonAbilityAttr;
|
const rootFormHasHiddenAbility = pokemon.scene.gameData.starterData[pokemon.species.getRootSpeciesId()].abilityAttr & opponentPokemonAbilityAttr;
|
||||||
|
|
||||||
if (!rootFormHasHiddenAbility) {
|
if (!rootFormHasHiddenAbility) {
|
||||||
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
|
this.pokemonAbilityLabelText.setColor(getTextColor(TextStyle.SUMMARY_BLUE, false, this.scene.uiTheme));
|
||||||
@ -288,7 +280,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme));
|
this.pokemonNatureText.setText(getNatureName(pokemon.getNature(), true, false, false, this.scene.uiTheme));
|
||||||
|
|
||||||
const dexNatures = dexEntry.natureAttr;
|
const dexNatures = pokemon.scene.gameData.dexData[pokemon.species.speciesId].natureAttr;
|
||||||
const newNature = 1 << (pokemon.nature + 1);
|
const newNature = 1 << (pokemon.nature + 1);
|
||||||
|
|
||||||
if (!(dexNatures & newNature)) {
|
if (!(dexNatures & newNature)) {
|
||||||
@ -332,31 +324,31 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const starterSpeciesId = pokemon.species.getRootSpeciesId();
|
const starterSpeciesId = pokemon.species.getRootSpeciesId();
|
||||||
const originalIvs: integer[] | null = eggInfo ? (dexEntry.caughtAttr ? dexEntry.ivs : null) : (this.scene.gameData.dexData[starterSpeciesId].caughtAttr
|
const originalIvs: integer[] | null = this.scene.gameData.dexData[starterSpeciesId].caughtAttr
|
||||||
? this.scene.gameData.dexData[starterSpeciesId].ivs : null);
|
? this.scene.gameData.dexData[starterSpeciesId].ivs
|
||||||
|
: null;
|
||||||
|
|
||||||
this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct?
|
this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct?
|
||||||
if (!eggInfo) {
|
|
||||||
this.scene.tweens.add({
|
|
||||||
targets: this,
|
|
||||||
duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)),
|
|
||||||
ease: "Cubic.easeInOut",
|
|
||||||
x: this.initialX - this.infoWindowWidth,
|
|
||||||
onComplete: () => {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (showMoves) {
|
this.scene.tweens.add({
|
||||||
this.scene.tweens.add({
|
targets: this,
|
||||||
delay: Utils.fixedInt(Math.floor(325 / speedMultiplier)),
|
duration: Utils.fixedInt(Math.floor(750 / speedMultiplier)),
|
||||||
targets: this.pokemonMovesContainer,
|
ease: "Cubic.easeInOut",
|
||||||
duration: Utils.fixedInt(Math.floor(325 / speedMultiplier)),
|
x: this.initialX - this.infoWindowWidth,
|
||||||
ease: "Cubic.easeInOut",
|
onComplete: () => {
|
||||||
x: this.movesContainerInitialX - 57,
|
resolve();
|
||||||
onComplete: () => resolve()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (showMoves) {
|
||||||
|
this.scene.tweens.add({
|
||||||
|
delay: Utils.fixedInt(Math.floor(325 / speedMultiplier)),
|
||||||
|
targets: this.pokemonMovesContainer,
|
||||||
|
duration: Utils.fixedInt(Math.floor(325 / speedMultiplier)),
|
||||||
|
ease: "Cubic.easeInOut",
|
||||||
|
x: this.movesContainerInitialX - 57,
|
||||||
|
onComplete: () => resolve()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let m = 0; m < 4; m++) {
|
for (let m = 0; m < 4; m++) {
|
||||||
@ -372,36 +364,6 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
changeToEggSummaryLayout() {
|
|
||||||
// The position should be set per language (and shifted for new layout)
|
|
||||||
const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct?
|
|
||||||
const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct?
|
|
||||||
const textSettings = languageSettings[langSettingKey];
|
|
||||||
|
|
||||||
const eggLabelTextOffset = 43;
|
|
||||||
const infoContainerLabelXPos = (textSettings?.infoContainerLabelXPos || -18) + eggLabelTextOffset;
|
|
||||||
const infoContainerTextXPos = (textSettings?.infoContainerTextXPos || -14) + eggLabelTextOffset;
|
|
||||||
|
|
||||||
this.x = this.initialX - this.infoWindowWidth;
|
|
||||||
|
|
||||||
this.pokemonGenderText.setPosition(89, -2);
|
|
||||||
this.pokemonGenderNewText.setPosition(79, -2);
|
|
||||||
this.pokemonShinyIcon.setPosition(82, 87);
|
|
||||||
this.pokemonShinyNewIcon.setPosition(72, 87);
|
|
||||||
|
|
||||||
this.pokemonFormLabelText.setPosition(infoContainerLabelXPos, 152);
|
|
||||||
this.pokemonFormText.setPosition(infoContainerTextXPos, 152);
|
|
||||||
this.pokemonAbilityLabelText.setPosition(infoContainerLabelXPos, 110);
|
|
||||||
this.pokemonAbilityText.setPosition(infoContainerTextXPos, 110);
|
|
||||||
this.pokemonNatureLabelText.setPosition(infoContainerLabelXPos, 125);
|
|
||||||
this.pokemonNatureText.setPosition(infoContainerTextXPos, 125);
|
|
||||||
|
|
||||||
this.statsContainer.setScale(0.7);
|
|
||||||
this.statsContainer.setPosition(30, -3);
|
|
||||||
this.infoBg.setVisible(false);
|
|
||||||
this.pokemonMovesContainer.setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeRoomForConfirmUi(speedMultiplier: number = 1, fromCatch: boolean = false): Promise<void> {
|
makeRoomForConfirmUi(speedMultiplier: number = 1, fromCatch: boolean = false): Promise<void> {
|
||||||
const xPosition = fromCatch ? this.initialX - this.infoWindowWidth - 65 : this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth;
|
const xPosition = fromCatch ? this.initialX - this.infoWindowWidth - 65 : this.initialX - this.infoWindowWidth - ConfirmUiHandler.windowWidth;
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
|
@ -1853,14 +1853,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.CYCLE_SHINY:
|
case Button.CYCLE_SHINY:
|
||||||
if (this.canCycleShiny) {
|
if (this.canCycleShiny) {
|
||||||
starterAttributes.shiny = starterAttributes.shiny !== undefined ? !starterAttributes.shiny : false;
|
const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : props.variant;
|
||||||
|
starterAttributes.shiny = starterAttributes.shiny ? !starterAttributes.shiny : true;
|
||||||
|
this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, props.shiny ? 0 : newVariant, undefined, undefined);
|
||||||
if (starterAttributes.shiny) {
|
if (starterAttributes.shiny) {
|
||||||
// Change to shiny, we need to get the proper default variant
|
|
||||||
const newProps = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.getCurrentDexProps(this.lastSpecies.speciesId));
|
|
||||||
const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : newProps.variant;
|
|
||||||
this.setSpeciesDetails(this.lastSpecies, true, undefined, undefined, newVariant, undefined, undefined);
|
|
||||||
|
|
||||||
this.scene.playSound("se/sparkle");
|
this.scene.playSound("se/sparkle");
|
||||||
// Set the variant label to the shiny tint
|
// Set the variant label to the shiny tint
|
||||||
const tint = getVariantTint(newVariant);
|
const tint = getVariantTint(newVariant);
|
||||||
@ -1868,7 +1864,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonShinyIcon.setTint(tint);
|
this.pokemonShinyIcon.setTint(tint);
|
||||||
this.pokemonShinyIcon.setVisible(true);
|
this.pokemonShinyIcon.setVisible(true);
|
||||||
} else {
|
} else {
|
||||||
this.setSpeciesDetails(this.lastSpecies, false, undefined, undefined, 0, undefined, undefined);
|
|
||||||
this.pokemonShinyIcon.setVisible(false);
|
this.pokemonShinyIcon.setVisible(false);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -3492,23 +3487,24 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
props += DexAttr.MALE;
|
props += DexAttr.MALE;
|
||||||
}
|
}
|
||||||
/* This part is very similar to above, but instead of for gender, it checks for shiny within starter preferences.
|
/* This part is very similar to above, but instead of for gender, it checks for shiny within starter preferences.
|
||||||
* If they're not there, it enables shiny state by default if any shiny was caught
|
* If they're not there, it checks the caughtAttr for shiny only (i.e. SHINY === true && NON_SHINY === false)
|
||||||
*/
|
*/
|
||||||
if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && this.starterPreferences[speciesId]?.shiny !== false)) {
|
if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && (caughtAttr & DexAttr.NON_SHINY) === 0n)) {
|
||||||
props += DexAttr.SHINY;
|
props += DexAttr.SHINY;
|
||||||
if (this.starterPreferences[speciesId]?.variant !== undefined) {
|
if (this.starterPreferences[speciesId]?.variant) {
|
||||||
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
|
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
|
||||||
} else {
|
} else {
|
||||||
/* This calculates the correct variant if there's no starter preferences for it.
|
/* This calculates the correct variant if there's no starter preferences for it.
|
||||||
* This gets the highest tier variant that you've caught and adds it to the temp props
|
* This gets the lowest tier variant that you've caught (in line with other mechanics) and adds it to the temp props
|
||||||
*/
|
*/
|
||||||
if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
|
if ((caughtAttr & DexAttr.DEFAULT_VARIANT) > 0) {
|
||||||
props += DexAttr.VARIANT_3;
|
|
||||||
} else if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
|
|
||||||
props += DexAttr.VARIANT_2;
|
|
||||||
} else {
|
|
||||||
props += DexAttr.DEFAULT_VARIANT;
|
props += DexAttr.DEFAULT_VARIANT;
|
||||||
}
|
}
|
||||||
|
if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
|
||||||
|
props += DexAttr.VARIANT_2;
|
||||||
|
} else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
|
||||||
|
props += DexAttr.VARIANT_3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
props += DexAttr.NON_SHINY;
|
props += DexAttr.NON_SHINY;
|
||||||
|
@ -49,7 +49,6 @@ import RenameFormUiHandler from "./rename-form-ui-handler";
|
|||||||
import AdminUiHandler from "./admin-ui-handler";
|
import AdminUiHandler from "./admin-ui-handler";
|
||||||
import RunHistoryUiHandler from "./run-history-ui-handler";
|
import RunHistoryUiHandler from "./run-history-ui-handler";
|
||||||
import RunInfoUiHandler from "./run-info-ui-handler";
|
import RunInfoUiHandler from "./run-info-ui-handler";
|
||||||
import EggSummaryUiHandler from "./egg-summary-ui-handler";
|
|
||||||
import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
|
import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
|
||||||
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
||||||
|
|
||||||
@ -67,7 +66,6 @@ export enum Mode {
|
|||||||
STARTER_SELECT,
|
STARTER_SELECT,
|
||||||
EVOLUTION_SCENE,
|
EVOLUTION_SCENE,
|
||||||
EGG_HATCH_SCENE,
|
EGG_HATCH_SCENE,
|
||||||
EGG_HATCH_SUMMARY,
|
|
||||||
CONFIRM,
|
CONFIRM,
|
||||||
OPTION_SELECT,
|
OPTION_SELECT,
|
||||||
MENU,
|
MENU,
|
||||||
@ -173,7 +171,6 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||||||
new StarterSelectUiHandler(scene),
|
new StarterSelectUiHandler(scene),
|
||||||
new EvolutionSceneHandler(scene),
|
new EvolutionSceneHandler(scene),
|
||||||
new EggHatchSceneHandler(scene),
|
new EggHatchSceneHandler(scene),
|
||||||
new EggSummaryUiHandler(scene),
|
|
||||||
new ConfirmUiHandler(scene),
|
new ConfirmUiHandler(scene),
|
||||||
new OptionSelectUiHandler(scene),
|
new OptionSelectUiHandler(scene),
|
||||||
new MenuUiHandler(scene),
|
new MenuUiHandler(scene),
|
||||||
|