Merge branch 'beta' into spanish-es-to-eses

This commit is contained in:
Lugiad 2024-10-29 22:53:25 +01:00 committed by GitHub
commit b1b1faf662
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1287 additions and 42 deletions

View File

@ -3614,22 +3614,19 @@ export class MoodyAbAttr extends PostTurnAbAttr {
}
}
export class PostTurnStatStageChangeAbAttr extends PostTurnAbAttr {
private stats: BattleStat[];
private stages: number;
export class SpeedBoostAbAttr extends PostTurnAbAttr {
constructor(stats: BattleStat[], stages: number) {
constructor() {
super(true);
this.stats = Array.isArray(stats)
? stats
: [ stats ];
this.stages = stages;
}
applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
if (!simulated) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.stages));
if (!pokemon.turnData.switchedInThisTurn && !pokemon.turnData.failedRunAway) {
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPD ], 1));
} else {
return false;
}
}
return true;
}
@ -5011,7 +5008,7 @@ export function initAbilities() {
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
new Ability(Abilities.SPEED_BOOST, 3)
.attr(PostTurnStatStageChangeAbAttr, [ Stat.SPD ], 1),
.attr(SpeedBoostAbAttr),
new Ability(Abilities.BATTLE_ARMOR, 3)
.attr(BlockCritAbAttr)
.ignorable(),

File diff suppressed because it is too large Load Diff

View File

@ -1420,6 +1420,11 @@ export class RecoilAttr extends MoveEffectAttr {
return false;
}
// Chloroblast and Struggle should not deal recoil damage if the move was not successful
if (this.useHp && [ MoveResult.FAIL, MoveResult.MISS ].includes(user.getLastXMoves(1)[0]?.result)) {
return false;
}
const damageValue = (!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio;
const minValue = user.turnData.damageDealt ? 1 : 0;
const recoilDamage = Utils.toDmgValue(damageValue, minValue);
@ -2177,7 +2182,10 @@ export class StatusEffectAttr extends MoveEffectAttr {
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, false);
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(this.effect, true, false, user) ? Math.floor(moveChance * -0.1) : 0;
const score = (moveChance < 0) ? -10 : Math.floor(moveChance * -0.1);
const pokemon = this.selfTarget ? user : target;
return !pokemon.status && pokemon.canSetStatus(this.effect, true, false, user) ? score : 0;
}
}
@ -2197,7 +2205,10 @@ export class MultiStatusEffectAttr extends StatusEffectAttr {
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, false);
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(this.effect, true, false, user) ? Math.floor(moveChance * -0.1) : 0;
const score = (moveChance < 0) ? -10 : Math.floor(moveChance * -0.1);
const pokemon = this.selfTarget ? user : target;
return !pokemon.status && pokemon.canSetStatus(this.effect, true, false, user) ? score : 0;
}
}
@ -2228,7 +2239,7 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr {
}
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
return !(this.selfTarget ? user : target).status && (this.selfTarget ? user : target).canSetStatus(user.status?.effect, true, false, user) ? Math.floor(move.chance * -0.1) : 0;
return !target.status && target.canSetStatus(user.status?.effect, true, false, user) ? -10 : 0;
}
}
/**

View File

@ -3,6 +3,8 @@
* or {@linkcode SwitchSummonPhase} will carry out.
*/
export enum SwitchType {
/** Switchout specifically for when combat starts and the player is prompted if they will switch Pokemon */
INITIAL_SWITCH,
/** Basic switchout where the Pokemon to switch in is selected */
SWITCH,
/** Transfers stat stages and other effects from the returning Pokemon to the switched in Pokemon */

View File

@ -5143,6 +5143,8 @@ export class PokemonTurnData {
public statStagesDecreased: boolean = false;
public moveEffectiveness: TypeDamageMultiplier | null = null;
public combiningPledge?: Moves;
public switchedInThisTurn: boolean = false;
public failedRunAway: boolean = false;
}
export enum AiType {

View File

@ -10,6 +10,10 @@ import { NewBattlePhase } from "./new-battle-phase";
import { PokemonPhase } from "./pokemon-phase";
export class AttemptRunPhase extends PokemonPhase {
/** For testing purposes: this is to force the pokemon to fail and escape */
public forceFailEscape = false;
constructor(scene: BattleScene, fieldIndex: number) {
super(scene, fieldIndex);
}
@ -28,7 +32,7 @@ export class AttemptRunPhase extends PokemonPhase {
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
if (playerPokemon.randSeedInt(100) < escapeChance.value) {
if (playerPokemon.randSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
this.scene.playSound("se/flee");
this.scene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
@ -51,6 +55,7 @@ export class AttemptRunPhase extends PokemonPhase {
this.scene.pushPhase(new BattleEndPhase(this.scene));
this.scene.pushPhase(new NewBattlePhase(this.scene));
} else {
playerPokemon.turnData.failedRunAway = true;
this.scene.queueMessage(i18next.t("battle:runAwayCannotEscape"), null, true, 500);
}

View File

@ -51,7 +51,7 @@ export class CheckSwitchPhase extends BattlePhase {
this.scene.ui.setMode(Mode.CONFIRM, () => {
this.scene.ui.setMode(Mode.MESSAGE);
this.scene.tryRemovePhase(p => p instanceof PostSummonPhase && p.player && p.fieldIndex === this.fieldIndex);
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.SWITCH, this.fieldIndex, false, true));
this.scene.unshiftPhase(new SwitchPhase(this.scene, SwitchType.INITIAL_SWITCH, this.fieldIndex, false, true));
this.end();
}, () => {
this.scene.ui.setMode(Mode.MESSAGE);

View File

@ -65,6 +65,15 @@ export class FaintPhase extends PokemonPhase {
}
}
/** In case the current pokemon was just switched in, make sure it is counted as participating in the combat */
this.scene.getPlayerField().forEach((pokemon, i) => {
if (pokemon?.isActive(true)) {
if (pokemon.isPlayer()) {
this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon);
}
}
});
if (!this.tryOverrideForBattleSpec()) {
this.doFaint();
}

View File

@ -64,10 +64,8 @@ export class SwitchSummonPhase extends SummonPhase {
}
const pokemon = this.getPokemon();
(this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id));
if (this.switchType === SwitchType.SWITCH) {
if (this.switchType === SwitchType.SWITCH || this.switchType === SwitchType.INITIAL_SWITCH) {
const substitute = pokemon.getTag(SubstituteTag);
if (substitute) {
this.scene.tweens.add({
@ -186,6 +184,11 @@ export class SwitchSummonPhase extends SummonPhase {
}
}
if (this.switchType !== SwitchType.INITIAL_SWITCH) {
pokemon.resetTurnData();
pokemon.turnData.switchedInThisTurn = true;
}
this.lastPokemon?.resetSummonData();
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);

View File

@ -0,0 +1,125 @@
import { Stat } from "#enums/stat";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { CommandPhase } from "#app/phases/command-phase";
import { Command } from "#app/ui/command-ui-handler";
import { AttemptRunPhase } from "#app/phases/attempt-run-phase";
describe("Abilities - Speed Boost", () => {
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.DRAGAPULT)
.ability(Abilities.SPEED_BOOST)
.enemyMoveset(Moves.SPLASH)
.moveset([ Moves.SPLASH, Moves.U_TURN ]);
});
it("should increase speed by 1 stage at end of turn",
async () => {
await game.classicMode.startBattle();
const playerPokemon = game.scene.getPlayerPokemon()!;
game.move.select(Moves.SPLASH);
await game.toNextTurn();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
});
it("should not trigger this turn if pokemon was switched into combat via attack, but the turn after",
async () => {
await game.classicMode.startBattle([
Species.SHUCKLE,
Species.NINJASK
]);
game.move.select(Moves.U_TURN);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
const playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(Moves.SPLASH);
await game.toNextTurn();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
});
it("checking back to back swtiches",
async () => {
await game.classicMode.startBattle([
Species.SHUCKLE,
Species.NINJASK
]);
game.move.select(Moves.U_TURN);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
let playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(Moves.U_TURN);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(Moves.SPLASH);
await game.toNextTurn();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
});
it("should not trigger this turn if pokemon was switched into combat via normal switch, but the turn after",
async () => {
await game.classicMode.startBattle([
Species.SHUCKLE,
Species.NINJASK
]);
game.doSwitchPokemon(1);
await game.toNextTurn();
const playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(Moves.SPLASH);
await game.toNextTurn();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
});
it("should not trigger if pokemon fails to escape",
async () => {
await game.classicMode.startBattle([ Species.SHUCKLE ]);
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
commandPhase.handleCommand(Command.RUN, 0);
const runPhase = game.scene.getCurrentPhase() as AttemptRunPhase;
runPhase.forceFailEscape = true;
await game.phaseInterceptor.to(AttemptRunPhase);
await game.toNextTurn();
const playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(0);
game.move.select(Moves.SPLASH);
await game.toNextTurn();
expect(playerPokemon.getStatStage(Stat.SPD)).toBe(1);
});
});

View File

@ -0,0 +1,42 @@
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Chloroblast", () => {
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
.moveset([ Moves.CHLOROBLAST ])
.ability(Abilities.BALL_FETCH)
.battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.PROTECT);
});
it("should not deal recoil damage if the opponent uses protect", async () => {
await game.classicMode.startBattle([ Species.FEEBAS ]);
game.move.select(Moves.CHLOROBLAST);
await game.phaseInterceptor.to("BerryPhase");
expect(game.scene.getPlayerPokemon()!.isFullHp()).toBe(true);
});
});

View File

@ -219,14 +219,20 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container {
const formKey = (pokemon.species?.forms?.[pokemon.formIndex!]?.formKey);
const formText = Utils.capitalizeString(formKey, "-", false, false) || "";
const speciesName = Utils.capitalizeString(Species[pokemon.species.getRootSpeciesId()], "_", true, false);
const speciesName = Utils.capitalizeString(Species[pokemon.species.speciesId], "_", true, false);
let formName = "";
if (pokemon.species.speciesId === Species.ARCEUS) {
formName = i18next.t(`pokemonInfo:Type.${formText?.toUpperCase()}`);
} else {
const i18key = `pokemonForm:${speciesName}${formText}`;
formName = i18next.exists(i18key) ? i18next.t(i18key) : formText;
if (i18next.exists(i18key)) {
formName = i18next.t(i18key);
} else {
const rootSpeciesName = Utils.capitalizeString(Species[pokemon.species.getRootSpeciesId()], "_", true, false);
const i18RootKey = `pokemonForm:${rootSpeciesName}${formText}`;
formName = i18next.exists(i18RootKey) ? i18next.t(i18RootKey) : formText;
}
}
if (formName) {