Merge branch 'beta' into pr-illusion

This commit is contained in:
Lylian BALL 2024-10-14 19:16:31 +02:00 committed by GitHub
commit d46c3a9f49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 119 additions and 27 deletions

View File

@ -5484,37 +5484,38 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
*/
const switchOutTarget = this.selfSwitch ? user : target;
if (switchOutTarget instanceof PlayerPokemon) {
// Switch out logic for the player's Pokemon
if (switchOutTarget.scene.getParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false;
}
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
if (switchOutTarget.hp > 0) {
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
user.scene.prependToPhase(new SwitchPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase);
return true;
}
return false;
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) {
// Switch out logic for trainer battles
if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
return false;
}
// Switch out logic for trainer battles
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
if (switchOutTarget.hp > 0) {
// for opponent switching out
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(),
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
false, false), MoveEndPhase);
}
} else {
// Switch out logic for everything else (eg: WILD battles)
if (user.scene.currentBattle.waveIndex % 10 === 0) {
return false;
}
// Switch out logic for everything else (eg: WILD battles)
switchOutTarget.leaveField(false);
if (switchOutTarget.hp) {
if (switchOutTarget.hp > 0) {
switchOutTarget.leaveField(false);
user.scene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500);
// in double battles redirect potential moves off fled pokemon

View File

@ -383,7 +383,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
const battle = this.scene.currentBattle;
const template = this.getPartyTemplate();
let species: PokemonSpecies;
let baseSpecies: PokemonSpecies;
if (this.config.speciesPools) {
const tierValue = Utils.randSeedInt(512);
let tier = tierValue >= 156 ? TrainerPoolTier.COMMON : tierValue >= 32 ? TrainerPoolTier.UNCOMMON : tierValue >= 6 ? TrainerPoolTier.RARE : tierValue >= 1 ? TrainerPoolTier.SUPER_RARE : TrainerPoolTier.ULTRA_RARE;
@ -393,17 +393,17 @@ export default class Trainer extends Phaser.GameObjects.Container {
tier--;
}
const tierPool = this.config.speciesPools[tier];
species = getPokemonSpecies(Utils.randSeedItem(tierPool));
baseSpecies = getPokemonSpecies(Utils.randSeedItem(tierPool));
} else {
species = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
baseSpecies = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter);
}
let ret = getPokemonSpecies(species.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex));
let ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex));
let retry = false;
console.log(ret.getName());
if (pokemonPrevolutions.hasOwnProperty(species.speciesId) && ret.speciesId !== species.speciesId) {
if (pokemonPrevolutions.hasOwnProperty(baseSpecies.speciesId) && ret.speciesId !== baseSpecies.speciesId) {
retry = true;
} else if (template.isBalanced(battle.enemyParty.length)) {
const partyMemberTypes = battle.enemyParty.map(p => p.getTypes(true)).flat();
@ -417,7 +417,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
console.log("Attempting reroll of species evolution to fit specialty type...");
let evoAttempt = 0;
while (retry && evoAttempt++ < 10) {
ret = getPokemonSpecies(species.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex));
ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex));
console.log(ret.name);
if (this.config.specialtyTypes.find(t => ret.isOfType(t))) {
retry = false;
@ -426,7 +426,7 @@ export default class Trainer extends Phaser.GameObjects.Container {
}
// Prompts reroll of party member species if species already present in the enemy party
if (this.checkDuplicateSpecies(ret)) {
if (this.checkDuplicateSpecies(ret, baseSpecies)) {
console.log("Duplicate species detected, prompting reroll...");
retry = true;
}
@ -442,13 +442,16 @@ export default class Trainer extends Phaser.GameObjects.Container {
/**
* Checks if the enemy trainer already has the Pokemon species in their party
* @param {PokemonSpecies} species {@linkcode PokemonSpecies}
* @param {PokemonSpecies} baseSpecies {@linkcode PokemonSpecies} - baseSpecies of the Pokemon if species is forced to evolve
* @returns `true` if the species is already present in the party
*/
checkDuplicateSpecies(species: PokemonSpecies): boolean {
checkDuplicateSpecies(species: PokemonSpecies, baseSpecies: PokemonSpecies): boolean {
const staticPartyPokemon = (signatureSpecies[TrainerType[this.config.trainerType]] ?? []).flat(1);
const currentPartySpecies = this.scene.getEnemyParty().map(p => {
return p.species.speciesId;
});
return currentPartySpecies.includes(species.speciesId);
return currentPartySpecies.includes(species.speciesId) || staticPartyPokemon.includes(baseSpecies.speciesId);
}
getPartyMemberMatchupScores(trainerSlot: TrainerSlot = TrainerSlot.NONE, forSwitch: boolean = false): [integer, integer][] {

View File

@ -33,7 +33,7 @@ export class EggLapsePhase extends Phase {
if (eggsToHatchCount > 0) {
if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 1) {
this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => {
// show prompt for skip
// show prompt for skip, blocking inputs for 1 second
this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0);
this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => {
this.hatchEggsSkipped(eggsToHatch);
@ -41,7 +41,8 @@ export class EggLapsePhase extends Phase {
}, () => {
this.hatchEggsRegular(eggsToHatch);
this.end();
}
},
null, null, null, 1000, true
);
}, 100, true);
} else if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 2) {

View File

@ -1,7 +1,6 @@
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";
/**
@ -11,7 +10,6 @@ import { EggHatchData } from "#app/data/egg-hatch-data";
*/
export class EggSummaryPhase extends Phase {
private eggHatchData: EggHatchData[];
private eggHatchHandler: EggHatchSceneHandler;
constructor(scene: BattleScene, eggHatchData: EggHatchData[]) {
super(scene);
@ -26,7 +24,6 @@ export class EggSummaryPhase extends Phase {
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 {

View File

@ -139,4 +139,58 @@ describe("Moves - Dragon Tail", () => {
expect(enemy.isFullHp()).toBe(false);
});
it("should force a switch upon fainting an opponent normally", async () => {
game.override.startingWave(5)
.startingLevel(1000); // To make sure Dragon Tail KO's the opponent
await game.classicMode.startBattle([ Species.DRATINI ]);
game.move.select(Moves.DRAGON_TAIL);
await game.toNextTurn();
// Make sure the enemy switched to a healthy Pokemon
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).toBeDefined();
expect(enemy.isFullHp()).toBe(true);
// Make sure the enemy has a fainted Pokemon in their party and not on the field
const faintedEnemy = game.scene.getEnemyParty().find(p => !p.isAllowedInBattle());
expect(faintedEnemy).toBeDefined();
expect(game.scene.getEnemyField().length).toBe(1);
});
it("should not cause a softlock when activating an opponent trainer's reviver seed", async () => {
game.override.startingWave(5)
.enemyHeldItems([{ name: "REVIVER_SEED" }])
.startingLevel(1000); // To make sure Dragon Tail KO's the opponent
await game.classicMode.startBattle([ Species.DRATINI ]);
game.move.select(Moves.DRAGON_TAIL);
await game.toNextTurn();
// Make sure the enemy field is not empty and has a revived Pokemon
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy).toBeDefined();
expect(enemy.hp).toBe(Math.floor(enemy.getMaxHp() / 2));
expect(game.scene.getEnemyField().length).toBe(1);
});
it("should not cause a softlock when activating a player's reviver seed", async () => {
game.override.startingHeldItems([{ name: "REVIVER_SEED" }])
.enemyMoveset(Moves.DRAGON_TAIL)
.enemyLevel(1000); // To make sure Dragon Tail KO's the player
await game.classicMode.startBattle([ Species.DRATINI, Species.BULBASAUR ]);
game.move.select(Moves.SPLASH);
await game.toNextTurn();
// Make sure the player's field is not empty and has a revived Pokemon
const dratini = game.scene.getPlayerPokemon()!;
expect(dratini).toBeDefined();
expect(dratini.hp).toBe(Math.floor(dratini.getMaxHp() / 2));
expect(game.scene.getPlayerField().length).toBe(1);
});
});

View File

@ -96,4 +96,23 @@ describe("Moves - U-turn", () => {
expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
}, 20000);
it("still forces a switch if u-turn KO's the opponent", async () => {
game.override.startingLevel(1000); // Ensure that U-Turn KO's the opponent
await game.classicMode.startBattle([
Species.RAICHU,
Species.SHUCKLE
]);
const enemy = game.scene.getEnemyPokemon()!;
// KO the opponent with U-Turn
game.move.select(Moves.U_TURN);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to(TurnEndPhase);
expect(enemy.isFainted()).toBe(true);
// Check that U-Turn forced a switch
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE);
});
});

View File

@ -165,6 +165,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
if (this.config.delay) {
this.blockInput = true;
this.optionSelectText.setAlpha(0.5);
this.cursorObj?.setAlpha(0.8);
this.scene.time.delayedCall(Utils.fixedInt(this.config.delay), () => this.unblockInput());
}
@ -256,6 +257,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
this.blockInput = false;
this.optionSelectText.setAlpha(1);
this.cursorObj?.setAlpha(1);
}
getOptionsWithScroll(): OptionSelectItem[] {

View File

@ -76,7 +76,8 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
}
}
],
delay: args.length >= 6 && args[5] !== null ? args[5] as integer : 0
delay: args.length >= 6 && args[5] !== null ? args[5] as number : 0,
noCancel: args.length >= 7 && args[6] !== null ? args[6] as boolean : false,
};
super.show([ config ]);
@ -96,7 +97,7 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler {
}
processInput(button: Button): boolean {
if (button === Button.CANCEL && this.blockInput) {
if (button === Button.CANCEL && this.blockInput && !this.config?.noCancel) {
this.unblockInput();
}

View File

@ -42,6 +42,9 @@ export default class EggSummaryUiHandler extends MessageUiHandler {
private scrollGridHandler : ScrollableGridUiHandler;
private cursorObj: Phaser.GameObjects.Image;
/** used to add a delay before which it is not possible to exit the summary */
private blockExit: boolean;
/**
* Allows subscribers to listen for events
*
@ -168,6 +171,13 @@ export default class EggSummaryUiHandler extends MessageUiHandler {
this.setCursor(0);
this.scene.playSoundWithoutBgm("evolution_fanfare");
// Prevent exiting the egg summary for 2 seconds if the egg hatching
// was skipped automatically and for 1 second otherwise
const exitBlockingDuration = (this.scene.eggSkipPreference === 2) ? 2000 : 1000;
this.blockExit = true;
this.scene.time.delayedCall(exitBlockingDuration, () => this.blockExit = false);
return true;
}
@ -203,13 +213,17 @@ export default class EggSummaryUiHandler extends MessageUiHandler {
const ui = this.getUi();
let success = false;
const error = false;
let error = false;
if (button === Button.CANCEL) {
const phase = this.scene.getCurrentPhase();
if (phase instanceof EggSummaryPhase) {
phase.end();
if (!this.blockExit) {
const phase = this.scene.getCurrentPhase();
if (phase instanceof EggSummaryPhase) {
phase.end();
}
success = true;
} else {
error = true;
}
success = true;
} else {
this.scrollGridHandler.processInput(button);
}