mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-06 00:12:16 +02:00
Compare commits
17 Commits
7c3ace7204
...
9c97e37c27
Author | SHA1 | Date | |
---|---|---|---|
|
9c97e37c27 | ||
|
bf5b6e3f6b | ||
|
aa7c354be8 | ||
|
ff7429f240 | ||
|
3022aabc32 | ||
|
7422ebdb28 | ||
|
1a149bfa04 | ||
|
3d87e86e58 | ||
|
ece7c9f2d5 | ||
|
f3898dbabd | ||
|
eb058d7eb7 | ||
|
83c11a0865 | ||
|
63d98fe70e | ||
|
0a17c2495a | ||
|
636cb9c8f2 | ||
|
f5f98ec537 | ||
|
97dde2d1f3 |
3
.env
3
.env
@ -1,2 +1,3 @@
|
||||
VITE_BYPASS_LOGIN=0
|
||||
VITE_BYPASS_TUTORIAL=0
|
||||
VITE_BYPASS_TUTORIAL=0
|
||||
VITE_SERVER_URL=http://localhost:8001
|
@ -1,2 +1,3 @@
|
||||
VITE_BYPASS_LOGIN=1
|
||||
VITE_BYPASS_TUTORIAL=0
|
||||
VITE_BYPASS_TUTORIAL=0
|
||||
VITE_SERVER_URL=http://localhost:8001
|
@ -46,7 +46,14 @@ Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to
|
||||
- Keisuke Ito
|
||||
- Arata Iiyoshi
|
||||
- Atsuhiro Ishizuna
|
||||
- Pokémon HeartGold/SoulSilver
|
||||
- Pokémon Black/White 2
|
||||
- Pokémon X/Y
|
||||
- Pokémon Omega Ruby/Alpha Sapphire
|
||||
- Pokémon Sun/Moon
|
||||
- Pokémon Ultra Sun/Ultra Moon
|
||||
- Pokémon Sword/Shield
|
||||
- Pokémon Scarlet/Violet
|
||||
- Firel (Custom Metropolis and Laboratory biome music)
|
||||
- Lmz (Custom Jungle biome music)
|
||||
|
||||
|
BIN
public/audio/bgm/battle_legendary_arceus.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_arceus.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_birds_galar.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_birds_galar.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_calyrex.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_calyrex.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_deoxys.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_deoxys.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_dia_pal.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_dia_pal.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_dusk_dawn.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_dusk_dawn.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_entei.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_entei.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_giratina.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_giratina.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_glas_spec.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_glas_spec.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_gro_kyo.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_gro_kyo.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_ho_oh.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_ho_oh.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_kanto.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_kanto.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_lake_trio.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_lake_trio.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_loyal_three.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_loyal_three.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_lugia.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_lugia.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_ogerpon.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_ogerpon.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_pecharunt.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_pecharunt.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_raikou.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_raikou.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_rayquaza.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_rayquaza.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_regis_g6.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_regis_g6.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_ruinous.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_ruinous.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_sinnoh.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_sinnoh.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_sol_lun.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_sol_lun.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_suicune.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_suicune.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_tapu.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_tapu.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_terapagos.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_terapagos.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_ub.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_ub.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_ultra_nec.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_ultra_nec.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_xern_yvel.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_xern_yvel.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_zac_zam.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_zac_zam.mp3
Normal file
Binary file not shown.
@ -126,6 +126,7 @@ export default class BattleScene extends SceneBase {
|
||||
public uiTheme: UiTheme = UiTheme.DEFAULT;
|
||||
public windowType: integer = 0;
|
||||
public experimentalSprites: boolean = false;
|
||||
public musicPreference: integer = 0;
|
||||
public moveAnimations: boolean = true;
|
||||
public expGainsSpeed: integer = 0;
|
||||
public skipSeenDialogues: boolean = false;
|
||||
@ -159,7 +160,7 @@ export default class BattleScene extends SceneBase {
|
||||
public gameData: GameData;
|
||||
public sessionSlotId: integer;
|
||||
|
||||
private phaseQueue: Phase[];
|
||||
public phaseQueue: Phase[];
|
||||
private phaseQueuePrepend: Phase[];
|
||||
private phaseQueuePrependSpliceIndex: integer;
|
||||
private nextCommandPhaseQueue: Phase[];
|
||||
@ -201,7 +202,7 @@ export default class BattleScene extends SceneBase {
|
||||
public arenaFlyout: ArenaFlyout;
|
||||
|
||||
private fieldOverlay: Phaser.GameObjects.Rectangle;
|
||||
private modifiers: PersistentModifier[];
|
||||
public modifiers: PersistentModifier[];
|
||||
private enemyModifiers: PersistentModifier[];
|
||||
public uiContainer: Phaser.GameObjects.Container;
|
||||
public ui: UI;
|
||||
@ -300,7 +301,8 @@ export default class BattleScene extends SceneBase {
|
||||
this.fieldSpritePipeline = new FieldSpritePipeline(this.game);
|
||||
(this.renderer as Phaser.Renderer.WebGL.WebGLRenderer).pipelines.add("FieldSprite", this.fieldSpritePipeline);
|
||||
|
||||
this.time.delayedCall(20, () => this.launchBattle());
|
||||
|
||||
this.launchBattle();
|
||||
}
|
||||
|
||||
update() {
|
||||
@ -947,7 +949,8 @@ export default class BattleScene extends SceneBase {
|
||||
}
|
||||
|
||||
newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle {
|
||||
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (startingWave - 1)) + 1);
|
||||
const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave;
|
||||
const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1);
|
||||
let newDouble: boolean;
|
||||
let newBattleType: BattleType;
|
||||
let newTrainer: Trainer;
|
||||
@ -1007,6 +1010,9 @@ export default class BattleScene extends SceneBase {
|
||||
if (Overrides.DOUBLE_BATTLE_OVERRIDE) {
|
||||
newDouble = true;
|
||||
}
|
||||
if (Overrides.SINGLE_BATTLE_OVERRIDE) {
|
||||
newDouble = false;
|
||||
}
|
||||
|
||||
const lastBattle = this.currentBattle;
|
||||
|
||||
@ -1376,8 +1382,7 @@ export default class BattleScene extends SceneBase {
|
||||
if (this.money === undefined) {
|
||||
return;
|
||||
}
|
||||
const formattedMoney =
|
||||
this.moneyFormat === MoneyFormat.ABBREVIATED ? Utils.formatFancyLargeNumber(this.money, 3) : this.money.toLocaleString();
|
||||
const formattedMoney = Utils.formatMoney(this.moneyFormat, this.money);
|
||||
this.moneyText.setText(`₽${formattedMoney}`);
|
||||
this.fieldUI.moveAbove(this.moneyText, this.luckText);
|
||||
if (forceVisible) {
|
||||
@ -1694,14 +1699,74 @@ export default class BattleScene extends SceneBase {
|
||||
return 13.122;
|
||||
case "battle_unova_gym":
|
||||
return 19.145;
|
||||
case "battle_legendary_regis": //B2W2 Legendary Titan Battle
|
||||
case "battle_legendary_kanto": //XY Kanto Legendary Battle
|
||||
return 32.966;
|
||||
case "battle_legendary_raikou": //HGSS Raikou Battle
|
||||
return 12.632;
|
||||
case "battle_legendary_entei": //HGSS Entei Battle
|
||||
return 2.905;
|
||||
case "battle_legendary_suicune": //HGSS Suicune Battle
|
||||
return 12.636;
|
||||
case "battle_legendary_lugia": //HGSS Lugia Battle
|
||||
return 19.770;
|
||||
case "battle_legendary_ho_oh": //HGSS Ho-oh Battle
|
||||
return 17.668;
|
||||
case "battle_legendary_regis_g5": //B2W2 Legendary Titan Battle
|
||||
return 49.500;
|
||||
case "battle_legendary_regis_g6": //ORAS Legendary Titan Battle
|
||||
return 21.130;
|
||||
case "battle_legendary_gro_kyo": //ORAS Groudon & Kyogre Battle
|
||||
return 10.547;
|
||||
case "battle_legendary_rayquaza": //ORAS Rayquaza Battle
|
||||
return 10.495;
|
||||
case "battle_legendary_deoxys": //ORAS Deoxys Battle
|
||||
return 13.333;
|
||||
case "battle_legendary_lake_trio": //ORAS Lake Guardians Battle
|
||||
return 16.887;
|
||||
case "battle_legendary_sinnoh": //ORAS Sinnoh Legendary Battle
|
||||
return 22.770;
|
||||
case "battle_legendary_dia_pal": //ORAS Dialga & Palkia Battle
|
||||
return 16.009;
|
||||
case "battle_legendary_giratina": //ORAS Giratina Battle
|
||||
return 10.451;
|
||||
case "battle_legendary_arceus": //HGSS Arceus Battle
|
||||
return 9.595;
|
||||
case "battle_legendary_unova": //BW Unova Legendary Battle
|
||||
return 13.855;
|
||||
case "battle_legendary_kyurem": //BW Kyurem Battle
|
||||
return 18.314;
|
||||
case "battle_legendary_res_zek": //BW Reshiram & Zekrom Battle
|
||||
return 18.329;
|
||||
case "battle_legendary_xern_yvel": //XY Xerneas & Yveltal Battle
|
||||
return 26.468;
|
||||
case "battle_legendary_tapu": //SM Tapu Battle
|
||||
return 0.000;
|
||||
case "battle_legendary_sol_lun": //SM Solgaleo & Lunala Battle
|
||||
return 6.525;
|
||||
case "battle_legendary_ub": //SM Ultra Beast Battle
|
||||
return 9.818;
|
||||
case "battle_legendary_dusk_dawn": //USUM Dusk Mane & Dawn Wings Necrozma Battle
|
||||
return 5.211;
|
||||
case "battle_legendary_ultra_nec": //USUM Ultra Necrozma Battle
|
||||
return 10.344;
|
||||
case "battle_legendary_zac_zam": //SWSH Zacian & Zamazenta Battle
|
||||
return 11.424;
|
||||
case "battle_legendary_glas_spec": //SWSH Glastrier & Spectrier Battle
|
||||
return 12.503;
|
||||
case "battle_legendary_calyrex": //SWSH Calyrex Battle
|
||||
return 50.641;
|
||||
case "battle_legendary_birds_galar": //SWSH Galarian Legendary Birds Battle
|
||||
return 0.175;
|
||||
case "battle_legendary_ruinous": //SV Treasures of Ruin Battle
|
||||
return 6.333;
|
||||
case "battle_legendary_loyal_three": //SV Loyal Three Battle
|
||||
return 6.500;
|
||||
case "battle_legendary_ogerpon": //SV Ogerpon Battle
|
||||
return 14.335;
|
||||
case "battle_legendary_terapagos": //SV Terapagos Battle
|
||||
return 24.377;
|
||||
case "battle_legendary_pecharunt": //SV Pecharunt Battle
|
||||
return 6.508;
|
||||
case "battle_rival":
|
||||
return 13.689;
|
||||
case "battle_rival_2":
|
||||
|
122
src/battle.ts
122
src/battle.ts
@ -209,22 +209,116 @@ export default class Battle {
|
||||
return "battle_final_encounter";
|
||||
}
|
||||
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
||||
return "battle_legendary_regis";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
||||
if (scene.musicPreference === 0) {
|
||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
||||
return "battle_legendary_regis_g5";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
||||
return "battle_legendary_kyurem";
|
||||
}
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
} else {
|
||||
if (pokemon.species.speciesId === Species.ARTICUNO || pokemon.species.speciesId === Species.ZAPDOS || pokemon.species.speciesId === Species.MOLTRES || pokemon.species.speciesId === Species.MEWTWO || pokemon.species.speciesId === Species.MEW) {
|
||||
return "battle_legendary_kanto";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.RAIKOU) {
|
||||
return "battle_legendary_raikou";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.ENTEI) {
|
||||
return "battle_legendary_entei";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.SUICUNE) {
|
||||
return "battle_legendary_suicune";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.LUGIA) {
|
||||
return "battle_legendary_lugia";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.HO_OH) {
|
||||
return "battle_legendary_ho_oh";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
||||
return "battle_legendary_regis_g6";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GROUDON || pokemon.species.speciesId === Species.KYOGRE) {
|
||||
return "battle_legendary_gro_kyo";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.RAYQUAZA) {
|
||||
return "battle_legendary_rayquaza";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.DEOXYS) {
|
||||
return "battle_legendary_deoxys";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.UXIE || pokemon.species.speciesId === Species.MESPRIT || pokemon.species.speciesId === Species.AZELF) {
|
||||
return "battle_legendary_lake_trio";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.HEATRAN || pokemon.species.speciesId === Species.CRESSELIA || pokemon.species.speciesId === Species.DARKRAI || pokemon.species.speciesId === Species.SHAYMIN) {
|
||||
return "battle_legendary_sinnoh";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.DIALGA || pokemon.species.speciesId === Species.PALKIA) {
|
||||
return "battle_legendary_dia_pal";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GIRATINA) {
|
||||
return "battle_legendary_giratina";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.ARCEUS) {
|
||||
return "battle_legendary_arceus";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
||||
return "battle_legendary_kyurem";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.XERNEAS || pokemon.species.speciesId === Species.YVELTAL || pokemon.species.speciesId === Species.ZYGARDE) {
|
||||
return "battle_legendary_xern_yvel";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.TAPU_KOKO || pokemon.species.speciesId === Species.TAPU_LELE || pokemon.species.speciesId === Species.TAPU_BULU || pokemon.species.speciesId === Species.TAPU_FINI) {
|
||||
return "battle_legendary_tapu";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.COSMOG || pokemon.species.speciesId === Species.COSMOEM || pokemon.species.speciesId === Species.SOLGALEO || pokemon.species.speciesId === Species.LUNALA || pokemon.species.speciesId === Species.NECROZMA) {
|
||||
return "battle_legendary_sol_lun";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.NIHILEGO || pokemon.species.speciesId === Species.BUZZWOLE || pokemon.species.speciesId === Species.PHEROMOSA || pokemon.species.speciesId === Species.XURKITREE || pokemon.species.speciesId === Species.CELESTEELA || pokemon.species.speciesId === Species.KARTANA || pokemon.species.speciesId === Species.GUZZLORD || pokemon.species.speciesId === Species.POIPOLE || pokemon.species.speciesId === Species.NAGANADEL || pokemon.species.speciesId === Species.STAKATAKA || pokemon.species.speciesId === Species.BLACEPHALON) {
|
||||
return "battle_legendary_ub";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.ZACIAN || pokemon.species.speciesId === Species.ZAMAZENTA) {
|
||||
return "battle_legendary_zac_zam";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GLASTRIER || pokemon.species.speciesId === Species.SPECTRIER) {
|
||||
return "battle_legendary_glas_spec";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.CALYREX) {
|
||||
return "battle_legendary_calyrex";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GALAR_ARTICUNO || pokemon.species.speciesId === Species.GALAR_ZAPDOS || pokemon.species.speciesId === Species.GALAR_MOLTRES) {
|
||||
return "battle_legendary_birds_galar";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.WO_CHIEN || pokemon.species.speciesId === Species.CHIEN_PAO || pokemon.species.speciesId === Species.TING_LU || pokemon.species.speciesId === Species.CHI_YU) {
|
||||
return "battle_legendary_ruinous";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.OKIDOGI || pokemon.species.speciesId === Species.MUNKIDORI || pokemon.species.speciesId === Species.FEZANDIPITI) {
|
||||
return "battle_legendary_loyal_three";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.OGERPON) {
|
||||
return "battle_legendary_ogerpon";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.TERAPAGOS) {
|
||||
return "battle_legendary_terapagos";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.PECHARUNT) {
|
||||
return "battle_legendary_pecharunt";
|
||||
}
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.RESHIRAM || pokemon.species.speciesId === Species.ZEKROM) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
||||
return "battle_legendary_kyurem";
|
||||
}
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,10 +235,10 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: PokemonMove) => boolean;
|
||||
type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean;
|
||||
|
||||
export class PreDefendAbAttr extends AbAttr {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -252,7 +252,7 @@ export class PreDefendFormChangeAbAttr extends PreDefendAbAttr {
|
||||
this.formFunc = formFunc;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const formIndex = this.formFunc(pokemon);
|
||||
if (formIndex !== pokemon.formIndex) {
|
||||
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
|
||||
@ -263,7 +263,7 @@ export class PreDefendFormChangeAbAttr extends PreDefendAbAttr {
|
||||
}
|
||||
}
|
||||
export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (pokemon.hp === pokemon.getMaxHp() &&
|
||||
pokemon.getMaxHp() > 1 && //Checks if pokemon has wonder_guard (which forces 1hp)
|
||||
(args[0] as Utils.NumberHolder).value >= pokemon.hp) { //Damage >= hp
|
||||
@ -308,8 +308,8 @@ export class ReceivedMoveDamageMultiplierAbAttr extends PreDefendAbAttr {
|
||||
this.powerMultiplier = powerMultiplier;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move.getMove())) {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move)) {
|
||||
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
|
||||
return true;
|
||||
}
|
||||
@ -329,8 +329,8 @@ export class PreDefendMovePowerToOneAbAttr extends ReceivedMoveDamageMultiplierA
|
||||
super(condition, 1);
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move.getMove())) {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move)) {
|
||||
(args[0] as Utils.NumberHolder).value = 1;
|
||||
return true;
|
||||
}
|
||||
@ -339,6 +339,12 @@ export class PreDefendMovePowerToOneAbAttr extends ReceivedMoveDamageMultiplierA
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a Pokemon is immune to a move because of an ability.
|
||||
* @extends PreDefendAbAttr
|
||||
* @see {@linkcode applyPreDefend}
|
||||
* @see {@linkcode getCondition}
|
||||
*/
|
||||
export class TypeImmunityAbAttr extends PreDefendAbAttr {
|
||||
private immuneType: Type;
|
||||
private condition: AbAttrCondition;
|
||||
@ -350,8 +356,17 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if ((move.getMove() instanceof AttackMove || move.getMove().getAttrs(StatusMoveTypeImmunityAttr).find(attr => attr.immuneType === this.immuneType)) && move.getMove().type === this.immuneType) {
|
||||
/**
|
||||
* @param pokemon {@linkcode Pokemon} the defending Pokemon
|
||||
* @param passive N/A
|
||||
* @param attacker {@linkcode Pokemon} the attacking Pokemon
|
||||
* @param move {@linkcode Move} the attacking move
|
||||
* @param cancelled N/A
|
||||
* @param args [0] {@linkcode Utils.NumberHolder} gets set to 0 if move is immuned by an ability.
|
||||
* @param args [1] {@linkcode Utils.NumberHolder} type of move being defended against in case it has changed from default type
|
||||
*/
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if ((move instanceof AttackMove || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => attr.immuneType === this.immuneType)) && move.type === this.immuneType) {
|
||||
(args[0] as Utils.NumberHolder).value = 0;
|
||||
return true;
|
||||
}
|
||||
@ -369,7 +384,7 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr {
|
||||
super(immuneType);
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
|
||||
|
||||
if (ret) {
|
||||
@ -399,7 +414,7 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr {
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
|
||||
|
||||
if (ret) {
|
||||
@ -425,7 +440,7 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr {
|
||||
this.turnCount = turnCount;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
|
||||
|
||||
if (ret) {
|
||||
@ -445,8 +460,8 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
||||
super(null, condition);
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, attacker) < 2) {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (move instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.type, attacker) < 2) {
|
||||
cancelled.value = true;
|
||||
(args[0] as Utils.NumberHolder).value = 0;
|
||||
return true;
|
||||
@ -461,15 +476,15 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
||||
}
|
||||
|
||||
export class PostDefendAbAttr extends AbAttr {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class PostDefendDisguiseAbAttr extends PostDefendAbAttr {
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (pokemon.formIndex === 0 && pokemon.battleData.hitCount !== 0 && (move.getMove().category === MoveCategory.SPECIAL || move.getMove().category === MoveCategory.PHYSICAL)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (pokemon.formIndex === 0 && pokemon.battleData.hitCount !== 0 && (move.category === MoveCategory.SPECIAL || move.category === MoveCategory.PHYSICAL)) {
|
||||
|
||||
const recoilDamage = Math.ceil((pokemon.getMaxHp() / 8) - attacker.turnData.damageDealt);
|
||||
if (!recoilDamage) {
|
||||
@ -494,7 +509,7 @@ export class PostDefendFormChangeAbAttr extends PostDefendAbAttr {
|
||||
this.formFunc = formFunc;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
const formIndex = this.formFunc(pokemon);
|
||||
if (formIndex !== pokemon.formIndex) {
|
||||
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
|
||||
@ -506,16 +521,16 @@ export class PostDefendFormChangeAbAttr extends PostDefendAbAttr {
|
||||
}
|
||||
|
||||
export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const attackPriority = new Utils.IntegerHolder(move.getMove().priority);
|
||||
applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move.getMove(),attackPriority);
|
||||
applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move.getMove(), attackPriority);
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const attackPriority = new Utils.IntegerHolder(move.priority);
|
||||
applyMoveAttrs(IncrementMovePriorityAttr,attacker,null,move,attackPriority);
|
||||
applyAbAttrs(IncrementMovePriorityAbAttr, attacker, null, move, attackPriority);
|
||||
|
||||
if (move.getMove().moveTarget===MoveTarget.USER || move.getMove().moveTarget===MoveTarget.NEAR_ALLY) {
|
||||
if (move.moveTarget===MoveTarget.USER || move.moveTarget===MoveTarget.NEAR_ALLY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attackPriority.value > 0 && !move.getMove().isMultiTarget()) {
|
||||
if (attackPriority.value > 0 && !move.isMultiTarget()) {
|
||||
cancelled.value = true;
|
||||
return true;
|
||||
}
|
||||
@ -539,7 +554,7 @@ export class MoveImmunityAbAttr extends PreDefendAbAttr {
|
||||
this.immuneCondition = immuneCondition;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
if (this.immuneCondition(pokemon, attacker, move)) {
|
||||
cancelled.value = true;
|
||||
return true;
|
||||
@ -563,7 +578,7 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr {
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const ret = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
|
||||
if (ret) {
|
||||
const simulated = args.length > 1 && args[1];
|
||||
@ -593,8 +608,8 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr {
|
||||
* @args N/A
|
||||
* @returns true if healing should be reversed on a healing move, false otherwise.
|
||||
*/
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().hasAttr(HitHealAttr)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.hasAttr(HitHealAttr)) {
|
||||
pokemon.scene.queueMessage(getPokemonMessage(attacker, " sucked up the liquid ooze!"));
|
||||
return true;
|
||||
}
|
||||
@ -619,8 +634,8 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr {
|
||||
this.allOthers = allOthers;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move.getMove())) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move)) {
|
||||
if (this.allOthers) {
|
||||
const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents();
|
||||
for (const other of otherPokemon) {
|
||||
@ -653,10 +668,10 @@ export class PostDefendHpGatedStatChangeAbAttr extends PostDefendAbAttr {
|
||||
this.selfTarget = selfTarget;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
const hpGateFlat: integer = Math.ceil(pokemon.getMaxHp() * this.hpGate);
|
||||
const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1];
|
||||
if (this.condition(pokemon, attacker, move.getMove()) && (pokemon.hp <= hpGateFlat && (pokemon.hp + lastAttackReceived.damage) > hpGateFlat)) {
|
||||
if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + lastAttackReceived.damage) > hpGateFlat)) {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.levels));
|
||||
return true;
|
||||
}
|
||||
@ -676,8 +691,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
|
||||
this.tagType = tagType;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move.getMove())) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move)) {
|
||||
const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag;
|
||||
if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) {
|
||||
pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER);
|
||||
@ -698,11 +713,11 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
|
||||
this.tagType = tagType;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move.getMove())) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.condition(pokemon, attacker, move)) {
|
||||
if (!pokemon.getTag(this.tagType)) {
|
||||
pokemon.addTag(this.tagType, undefined, undefined, pokemon.id);
|
||||
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: pokemon.name, moveName: move.getName() }));
|
||||
pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: pokemon.name, moveName: move.name }));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -711,9 +726,9 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
|
||||
}
|
||||
|
||||
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (hitResult < HitResult.NO_EFFECT) {
|
||||
const type = move.getMove().type;
|
||||
const type = move.type;
|
||||
const pokemonTypes = pokemon.getTypes(true);
|
||||
if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) {
|
||||
pokemon.summonData.types = [ type ];
|
||||
@ -738,7 +753,7 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr {
|
||||
this.terrainType = terrainType;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (hitResult < HitResult.NO_EFFECT) {
|
||||
return pokemon.scene.arena.trySetTerrain(this.terrainType, true);
|
||||
}
|
||||
@ -758,8 +773,8 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) {
|
||||
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
|
||||
return attacker.trySetStatus(effect, true, pokemon);
|
||||
}
|
||||
@ -773,7 +788,7 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr
|
||||
super(10, StatusEffect.POISON, StatusEffect.PARALYSIS, StatusEffect.SLEEP);
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (attacker.hasAbility(Abilities.OVERCOAT) || attacker.isOfType(Type.GRASS)) {
|
||||
return false;
|
||||
}
|
||||
@ -794,9 +809,9 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
|
||||
this.turnCount = turnCount;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) {
|
||||
return attacker.addTag(this.tagType, this.turnCount, move.moveId, attacker.id);
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) {
|
||||
return attacker.addTag(this.tagType, this.turnCount, move.id, attacker.id);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -814,7 +829,7 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr {
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels));
|
||||
|
||||
return true;
|
||||
@ -834,8 +849,8 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
|
||||
this.damageRatio = damageRatio;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
|
||||
attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.turnData.damageTaken += Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio));
|
||||
return true;
|
||||
@ -864,8 +879,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr {
|
||||
this.turns = turns;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
|
||||
if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) {
|
||||
return false;
|
||||
} else {
|
||||
@ -891,7 +906,7 @@ export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr {
|
||||
this.weatherType = weatherType;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (!pokemon.scene.arena.weather?.isImmutable()) {
|
||||
return pokemon.scene.arena.trySetWeather(this.weatherType, true);
|
||||
}
|
||||
@ -905,8 +920,8 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
|
||||
super();
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) {
|
||||
const tempAbilityId = attacker.getAbility().id;
|
||||
attacker.summonData.ability = pokemon.getAbility().id;
|
||||
pokemon.summonData.ability = tempAbilityId;
|
||||
@ -929,8 +944,8 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
|
||||
this.ability = ability;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) {
|
||||
attacker.summonData.ability = this.ability;
|
||||
|
||||
return true;
|
||||
@ -947,7 +962,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
|
||||
export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
||||
private chance: integer;
|
||||
private attacker: Pokemon;
|
||||
private move: PokemonMove;
|
||||
private move: Move;
|
||||
|
||||
constructor(chance: integer) {
|
||||
super();
|
||||
@ -955,13 +970,13 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
||||
this.chance = chance;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (!attacker.summonData.disabledMove) {
|
||||
if (move.getMove().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()) {
|
||||
this.attacker = attacker;
|
||||
this.move = move;
|
||||
|
||||
attacker.summonData.disabledMove = move.moveId;
|
||||
attacker.summonData.disabledMove = move.id;
|
||||
attacker.summonData.disabledTurns = 4;
|
||||
return true;
|
||||
}
|
||||
@ -970,7 +985,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
|
||||
}
|
||||
|
||||
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
|
||||
return getPokemonMessage(this.attacker, `'s ${this.move.getName()}\nwas disabled!`);
|
||||
return getPokemonMessage(this.attacker, `'s ${this.move.name}\nwas disabled!`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -998,49 +1013,18 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr {
|
||||
}
|
||||
|
||||
export class PreAttackAbAttr extends AbAttr {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean | Promise<boolean> {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableMovePowerAbAttr extends PreAttackAbAttr {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
|
||||
//const power = args[0] as Utils.NumberHolder;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableMoveTypeAbAttr extends AbAttr {
|
||||
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
//const power = args[0] as Utils.IntegerHolder;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveTypeChangePowerMultiplierAbAttr extends VariableMoveTypeAbAttr {
|
||||
private matchType: Type;
|
||||
private newType: Type;
|
||||
private powerMultiplier: number;
|
||||
|
||||
constructor(matchType: Type, newType: Type, powerMultiplier: number) {
|
||||
super(true);
|
||||
this.matchType = matchType;
|
||||
this.newType = newType;
|
||||
this.powerMultiplier = powerMultiplier;
|
||||
}
|
||||
|
||||
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
if (type.value === this.matchType) {
|
||||
type.value = this.newType;
|
||||
(args[1] as Utils.NumberHolder).value *= this.powerMultiplier;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class FieldPreventExplosiveMovesAbAttr extends AbAttr {
|
||||
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
||||
cancelled.value = true;
|
||||
@ -1060,11 +1044,10 @@ export class MoveTypeChangeAttr extends PreAttackAbAttr {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move.getMove())) {
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
type.value = this.newType;
|
||||
(args[1] as Utils.NumberHolder).value *= this.powerMultiplier;
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move)) {
|
||||
move.type = this.newType;
|
||||
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1097,8 +1080,8 @@ export class DamageBoostAbAttr extends PreAttackAbAttr {
|
||||
* @param args Utils.NumberHolder as damage
|
||||
* @returns true if the function succeeds
|
||||
*/
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move.getMove())) {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move)) {
|
||||
const power = args[0] as Utils.NumberHolder;
|
||||
power.value = Math.floor(power.value * this.damageMultiplier);
|
||||
return true;
|
||||
@ -1118,8 +1101,8 @@ export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr {
|
||||
this.powerMultiplier = powerMultiplier;
|
||||
}
|
||||
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move.getMove())) {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move)) {
|
||||
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
|
||||
|
||||
return true;
|
||||
@ -1165,8 +1148,8 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
const multiplier = this.mult(pokemon, defender, move.getMove());
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move, args: any[]): boolean {
|
||||
const multiplier = this.mult(pokemon, defender, move);
|
||||
if (multiplier !== 1) {
|
||||
(args[0] as Utils.NumberHolder).value *= multiplier;
|
||||
return true;
|
||||
@ -1177,7 +1160,7 @@ export class VariableMovePowerBoostAbAttr extends VariableMovePowerAbAttr {
|
||||
}
|
||||
|
||||
export class FieldVariableMovePowerAbAttr extends AbAttr {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
|
||||
//const power = args[0] as Utils.NumberHolder;
|
||||
return false;
|
||||
}
|
||||
@ -1193,8 +1176,8 @@ export class FieldMovePowerBoostAbAttr extends FieldVariableMovePowerAbAttr {
|
||||
this.powerMultiplier = powerMultiplier;
|
||||
}
|
||||
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move.getMove())) {
|
||||
applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (this.condition(pokemon, defender, move)) {
|
||||
(args[0] as Utils.NumberHolder).value *= this.powerMultiplier;
|
||||
|
||||
return true;
|
||||
@ -1235,7 +1218,7 @@ export class BattleStatMultiplierAbAttr extends AbAttr {
|
||||
}
|
||||
|
||||
export class PostAttackAbAttr extends AbAttr {
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1249,9 +1232,9 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): Promise<boolean> {
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move.getMove()))) {
|
||||
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, defender, move))) {
|
||||
const heldItems = this.getTargetHeldItems(defender).filter(i => i.getTransferrable(false));
|
||||
if (heldItems.length) {
|
||||
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
|
||||
@ -1287,8 +1270,8 @@ export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (pokemon !== attacker && (!this.contactRequired || move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance && !pokemon.status) {
|
||||
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
|
||||
return attacker.trySetStatus(effect, true, pokemon);
|
||||
}
|
||||
@ -1305,11 +1288,11 @@ export class PostAttackContactApplyStatusEffectAbAttr extends PostAttackApplySta
|
||||
|
||||
export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
|
||||
private contactRequired: boolean;
|
||||
private chance: (user: Pokemon, target: Pokemon, move: PokemonMove) => integer;
|
||||
private chance: (user: Pokemon, target: Pokemon, move: Move) => integer;
|
||||
private effects: BattlerTagType[];
|
||||
|
||||
|
||||
constructor(contactRequired: boolean, chance: (user: Pokemon, target: Pokemon, move: PokemonMove) => integer, ...effects: BattlerTagType[]) {
|
||||
constructor(contactRequired: boolean, chance: (user: Pokemon, target: Pokemon, move: Move) => integer, ...effects: BattlerTagType[]) {
|
||||
super();
|
||||
|
||||
this.contactRequired = contactRequired;
|
||||
@ -1317,8 +1300,8 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (pokemon !== attacker && (!this.contactRequired || move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) {
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (pokemon !== attacker && (!this.contactRequired || move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) && pokemon.randSeedInt(100) < this.chance(attacker, pokemon, move) && !pokemon.status) {
|
||||
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)];
|
||||
|
||||
|
||||
@ -1338,9 +1321,9 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): Promise<boolean> {
|
||||
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise<boolean> {
|
||||
return new Promise<boolean>(resolve => {
|
||||
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move.getMove()))) {
|
||||
if (hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) {
|
||||
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.getTransferrable(false));
|
||||
if (heldItems.length) {
|
||||
const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)];
|
||||
@ -2002,9 +1985,9 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr {
|
||||
* @param args [0] {@linkcode StatusEffect} applied by move
|
||||
* @returns true if defender is confused
|
||||
*/
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (this.effects.indexOf(args[0]) > -1 && !defender.isFainted()) {
|
||||
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.moveId, defender.id);
|
||||
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3,2), move.id, defender.id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -2607,7 +2590,7 @@ export class PostTurnLootAbAttr extends PostTurnAbAttr {
|
||||
|
||||
if (!berryModifier) {
|
||||
pokemon.scene.addModifier(new BerryModifier(chosenBerry, pokemon.id, chosenBerryType, 1));
|
||||
} else {
|
||||
} else if (berryModifier.stackCount < berryModifier.getMaxHeldItemCount(pokemon)) {
|
||||
berryModifier.stackCount++;
|
||||
}
|
||||
|
||||
@ -3028,7 +3011,7 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr {
|
||||
}
|
||||
|
||||
export class PostFaintAbAttr extends AbAttr {
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -3047,7 +3030,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr {
|
||||
* @param args N/A
|
||||
* @returns {boolean} Returns true if the weather clears, otherwise false.
|
||||
*/
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
const weatherType = pokemon.scene.arena.weather.weatherType;
|
||||
let turnOffWeather = false;
|
||||
|
||||
@ -3091,8 +3074,8 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
||||
this.damageRatio = damageRatio;
|
||||
}
|
||||
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
pokemon.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled));
|
||||
if (cancelled.value) {
|
||||
@ -3119,7 +3102,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
|
||||
super ();
|
||||
}
|
||||
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
|
||||
const damage = pokemon.turnData.attacksReceived[0].damage;
|
||||
attacker.damageAndUpdate((damage), HitResult.OTHER);
|
||||
attacker.turnData.damageTaken += damage;
|
||||
@ -3456,7 +3439,7 @@ export class IceFaceMoveImmunityAbAttr extends MoveImmunityAbAttr {
|
||||
* @param {any[]} args - Additional arguments.
|
||||
* @returns {boolean} - Whether the immunity was applied.
|
||||
*/
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||
const isImmune = super.applyPreDefend(pokemon, passive, attacker, move, cancelled, args);
|
||||
|
||||
if (isImmune) {
|
||||
@ -3566,13 +3549,13 @@ export function applyPostBattleInitAbAttrs(attrType: { new(...args: any[]): Post
|
||||
}
|
||||
|
||||
export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr },
|
||||
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
|
||||
pokemon: Pokemon, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
|
||||
const simulated = args.length > 1 && args[1];
|
||||
return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, false, simulated);
|
||||
}
|
||||
|
||||
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
|
||||
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise<void> {
|
||||
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args);
|
||||
}
|
||||
|
||||
@ -3587,12 +3570,12 @@ export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[])
|
||||
}
|
||||
|
||||
export function applyPreAttackAbAttrs(attrType: { new(...args: any[]): PreAttackAbAttr },
|
||||
pokemon: Pokemon, defender: Pokemon, move: PokemonMove, ...args: any[]): Promise<void> {
|
||||
pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PreAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args);
|
||||
}
|
||||
|
||||
export function applyPostAttackAbAttrs(attrType: { new(...args: any[]): PostAttackAbAttr },
|
||||
pokemon: Pokemon, defender: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise<void> {
|
||||
pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostAttackAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args);
|
||||
}
|
||||
|
||||
@ -3673,7 +3656,7 @@ export function applyPostBattleAbAttrs(attrType: { new(...args: any[]): PostBatt
|
||||
}
|
||||
|
||||
export function applyPostFaintAbAttrs(attrType: { new(...args: any[]): PostFaintAbAttr },
|
||||
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): Promise<void> {
|
||||
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostFaintAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, attacker, move, hitResult, args), args);
|
||||
}
|
||||
|
||||
@ -3692,7 +3675,7 @@ export const allAbilities = [ new Ability(Abilities.NONE, 3) ];
|
||||
export function initAbilities() {
|
||||
allAbilities.push(
|
||||
new Ability(Abilities.STENCH, 3)
|
||||
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.getMove().category !== MoveCategory.STATUS && !move.getMove().hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED),
|
||||
.attr(PostAttackApplyBattlerTagAbAttr, false, (user, target, move) => (move.category !== MoveCategory.STATUS && !move.hasAttr(FlinchAttr)) ? 10 : 0, BattlerTagType.FLINCHED),
|
||||
new Ability(Abilities.DRIZZLE, 3)
|
||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.RAIN)
|
||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.RAIN),
|
||||
@ -3829,7 +3812,7 @@ export function initAbilities() {
|
||||
return false;
|
||||
}),
|
||||
new Ability(Abilities.SOUNDPROOF, 3)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.SOUND_BASED))
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.SOUND_BASED))
|
||||
.ignorable(),
|
||||
new Ability(Abilities.RAIN_DISH, 3)
|
||||
.attr(PostWeatherLapseHealAbAttr, 1, WeatherType.RAIN, WeatherType.HEAVY_RAIN)
|
||||
@ -4115,13 +4098,13 @@ export function initAbilities() {
|
||||
)
|
||||
.partial(),
|
||||
new Ability(Abilities.TELEPATHY, 5)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move.getMove() instanceof AttackMove)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.MOODY, 5)
|
||||
.attr(MoodyAbAttr),
|
||||
new Ability(Abilities.OVERCOAT, 5)
|
||||
.attr(BlockWeatherDamageAttr)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.POWDER_MOVE))
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.POWDER_MOVE))
|
||||
.ignorable(),
|
||||
new Ability(Abilities.POISON_TOUCH, 5)
|
||||
.attr(PostAttackContactApplyStatusEffectAbAttr, 30, StatusEffect.POISON),
|
||||
@ -4210,14 +4193,14 @@ export function initAbilities() {
|
||||
new Ability(Abilities.MAGICIAN, 6)
|
||||
.attr(PostAttackStealHeldItemAbAttr),
|
||||
new Ability(Abilities.BULLETPROOF, 6)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.BALLBOMB_MOVE))
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.BALLBOMB_MOVE))
|
||||
.ignorable(),
|
||||
new Ability(Abilities.COMPETITIVE, 6)
|
||||
.attr(PostStatChangeStatChangeAbAttr, (target, statsChanged, levels) => levels < 0, [BattleStat.SPATK], 2),
|
||||
new Ability(Abilities.STRONG_JAW, 6)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5),
|
||||
new Ability(Abilities.REFRIGERATE, 6)
|
||||
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ICE, 1.2),
|
||||
.attr(MoveTypeChangeAttr, Type.ICE, 1.2, (user, target, move) => move.type === Type.NORMAL),
|
||||
new Ability(Abilities.SWEET_VEIL, 6)
|
||||
.attr(StatusEffectImmunityAbAttr, StatusEffect.SLEEP)
|
||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
|
||||
@ -4240,11 +4223,11 @@ export function initAbilities() {
|
||||
new Ability(Abilities.TOUGH_CLAWS, 6)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3),
|
||||
new Ability(Abilities.PIXILATE, 6)
|
||||
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FAIRY, 1.2),
|
||||
.attr(MoveTypeChangeAttr, Type.FAIRY, 1.2, (user, target, move) => move.type === Type.NORMAL),
|
||||
new Ability(Abilities.GOOEY, 6)
|
||||
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false),
|
||||
new Ability(Abilities.AERILATE, 6)
|
||||
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FLYING, 1.2),
|
||||
.attr(MoveTypeChangeAttr, Type.FLYING, 1.2, (user, target, move) => move.type === Type.NORMAL),
|
||||
new Ability(Abilities.PARENTAL_BOND, 6)
|
||||
.unimplemented(),
|
||||
new Ability(Abilities.DARK_AURA, 6)
|
||||
@ -4314,7 +4297,7 @@ export function initAbilities() {
|
||||
new Ability(Abilities.TRIAGE, 7)
|
||||
.attr(IncrementMovePriorityAbAttr, (pokemon, move) => move.hasFlag(MoveFlags.TRIAGE_MOVE), 3),
|
||||
new Ability(Abilities.GALVANIZE, 7)
|
||||
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ELECTRIC, 1.2),
|
||||
.attr(MoveTypeChangeAttr, Type.ELECTRIC, 1.2, (user, target, move) => move.type === Type.NORMAL),
|
||||
new Ability(Abilities.SURGE_SURFER, 7)
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2),
|
||||
new Ability(Abilities.SCHOOLING, 7)
|
||||
@ -4482,7 +4465,7 @@ export function initAbilities() {
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW), PostSummonAddBattlerTagAbAttr, BattlerTagType.ICE_FACE, 0)
|
||||
// When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE
|
||||
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW)
|
||||
.attr(IceFaceMoveImmunityAbAttr, (target, user, move) => move.getMove().category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE))
|
||||
.attr(IceFaceMoveImmunityAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE))
|
||||
.ignorable(),
|
||||
new Ability(Abilities.POWER_SPOT, 8)
|
||||
.unimplemented(),
|
||||
@ -4567,7 +4550,7 @@ export function initAbilities() {
|
||||
.attr(TypeImmunityStatChangeAbAttr, Type.FIRE, BattleStat.DEF, 2)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.WIND_RIDER, 9)
|
||||
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().hasFlag(MoveFlags.WIND_MOVE) && move.getMove().category !== MoveCategory.STATUS, BattleStat.ATK, 1)
|
||||
.attr(MoveImmunityStatChangeAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.hasFlag(MoveFlags.WIND_MOVE) && move.category !== MoveCategory.STATUS, BattleStat.ATK, 1)
|
||||
.attr(PostSummonStatChangeOnArenaAbAttr, ArenaTagType.TAILWIND)
|
||||
.ignorable(),
|
||||
new Ability(Abilities.GUARD_DOG, 9)
|
||||
@ -4607,7 +4590,7 @@ export function initAbilities() {
|
||||
.attr(NoTransformAbilityAbAttr)
|
||||
.partial(), // While setting the tag, the getbattlestat should ignore all modifiers to stats except stat stages
|
||||
new Ability(Abilities.GOOD_AS_GOLD, 9)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.getMove().category === MoveCategory.STATUS)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS)
|
||||
.ignorable()
|
||||
.partial(),
|
||||
new Ability(Abilities.VESSEL_OF_RUIN, 9)
|
||||
|
@ -99,6 +99,7 @@ export default class Move implements Localizable {
|
||||
public id: Moves;
|
||||
public name: string;
|
||||
public type: Type;
|
||||
public defaultType: Type;
|
||||
public category: MoveCategory;
|
||||
public moveTarget: MoveTarget;
|
||||
public power: integer;
|
||||
@ -118,6 +119,7 @@ export default class Move implements Localizable {
|
||||
|
||||
this.nameAppend = "";
|
||||
this.type = type;
|
||||
this.defaultType = type;
|
||||
this.category = category;
|
||||
this.moveTarget = defaultMoveTarget;
|
||||
this.power = power;
|
||||
@ -1629,7 +1631,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||
}
|
||||
if ((!pokemon.status || (pokemon.status.effect === this.effect && move.chance < 0))
|
||||
&& pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) {
|
||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, new PokemonMove(move.id), null,this.effect);
|
||||
applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null,this.effect);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -3323,23 +3325,22 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.GENESECT)) {
|
||||
const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies.formIndex;
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (form) {
|
||||
case 1: // Shock Drive
|
||||
type.value = Type.ELECTRIC;
|
||||
move.type = Type.ELECTRIC;
|
||||
break;
|
||||
case 2: // Burn Drive
|
||||
type.value = Type.FIRE;
|
||||
move.type = Type.FIRE;
|
||||
break;
|
||||
case 3: // Chill Drive
|
||||
type.value = Type.ICE;
|
||||
move.type = Type.ICE;
|
||||
break;
|
||||
case 4: // Douse Drive
|
||||
type.value = Type.WATER;
|
||||
move.type = Type.WATER;
|
||||
break;
|
||||
default:
|
||||
type.value = Type.NORMAL;
|
||||
move.type = Type.NORMAL;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@ -3353,14 +3354,13 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)) {
|
||||
const form = user.species.speciesId === Species.MORPEKO ? user.formIndex : user.fusionSpecies.formIndex;
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (form) {
|
||||
case 1: // Hangry Mode
|
||||
type.value = Type.DARK;
|
||||
move.type = Type.DARK;
|
||||
break;
|
||||
default: // Full Belly Mode
|
||||
type.value = Type.ELECTRIC;
|
||||
move.type = Type.ELECTRIC;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@ -3374,17 +3374,16 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.PALDEA_TAUROS)) {
|
||||
const form = user.species.speciesId === Species.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies.formIndex;
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (form) {
|
||||
case 1: // Blaze breed
|
||||
type.value = Type.FIRE;
|
||||
move.type = Type.FIRE;
|
||||
break;
|
||||
case 2: // Aqua breed
|
||||
type.value = Type.WATER;
|
||||
move.type = Type.WATER;
|
||||
break;
|
||||
default:
|
||||
type.value = Type.FIGHTING;
|
||||
move.type = Type.FIGHTING;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@ -3398,32 +3397,31 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) {
|
||||
const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex;
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (form) {
|
||||
case 1: // Wellspring Mask
|
||||
type.value = Type.WATER;
|
||||
move.type = Type.WATER;
|
||||
break;
|
||||
case 2: // Hearthflame Mask
|
||||
type.value = Type.FIRE;
|
||||
move.type = Type.FIRE;
|
||||
break;
|
||||
case 3: // Cornerstone Mask
|
||||
type.value = Type.ROCK;
|
||||
move.type = Type.ROCK;
|
||||
break;
|
||||
case 4: // Teal Mask Tera
|
||||
type.value = Type.GRASS;
|
||||
move.type = Type.GRASS;
|
||||
break;
|
||||
case 5: // Wellspring Mask Tera
|
||||
type.value = Type.WATER;
|
||||
move.type = Type.WATER;
|
||||
break;
|
||||
case 6: // Hearthflame Mask Tera
|
||||
type.value = Type.FIRE;
|
||||
move.type = Type.FIRE;
|
||||
break;
|
||||
case 7: // Cornerstone Mask Tera
|
||||
type.value = Type.ROCK;
|
||||
move.type = Type.ROCK;
|
||||
break;
|
||||
default:
|
||||
type.value = Type.GRASS;
|
||||
move.type = Type.GRASS;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
@ -3436,23 +3434,21 @@ export class IvyCudgelTypeAttr extends VariableMoveTypeAttr {
|
||||
export class WeatherBallTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!user.scene.arena.weather?.isEffectSuppressed(user.scene)) {
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (user.scene.arena.weather?.weatherType) {
|
||||
case WeatherType.SUNNY:
|
||||
case WeatherType.HARSH_SUN:
|
||||
type.value = Type.FIRE;
|
||||
move.type = Type.FIRE;
|
||||
break;
|
||||
case WeatherType.RAIN:
|
||||
case WeatherType.HEAVY_RAIN:
|
||||
type.value = Type.WATER;
|
||||
move.type = Type.WATER;
|
||||
break;
|
||||
case WeatherType.SANDSTORM:
|
||||
type.value = Type.ROCK;
|
||||
move.type = Type.ROCK;
|
||||
break;
|
||||
case WeatherType.HAIL:
|
||||
case WeatherType.SNOW:
|
||||
type.value = Type.ICE;
|
||||
move.type = Type.ICE;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@ -3484,20 +3480,18 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
|
||||
}
|
||||
|
||||
const currentTerrain = user.scene.arena.getTerrainType();
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
switch (currentTerrain) {
|
||||
case TerrainType.MISTY:
|
||||
type.value = Type.FAIRY;
|
||||
move.type = Type.FAIRY;
|
||||
break;
|
||||
case TerrainType.ELECTRIC:
|
||||
type.value = Type.ELECTRIC;
|
||||
move.type = Type.ELECTRIC;
|
||||
break;
|
||||
case TerrainType.GRASSY:
|
||||
type.value = Type.GRASS;
|
||||
move.type = Type.GRASS;
|
||||
break;
|
||||
case TerrainType.PSYCHIC:
|
||||
type.value = Type.PSYCHIC;
|
||||
move.type = Type.PSYCHIC;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@ -3508,8 +3502,6 @@ export class TerrainPulseTypeAttr extends VariableMoveTypeAttr {
|
||||
|
||||
export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
const iv_val = Math.floor(((user.ivs[Stat.HP] & 1)
|
||||
+(user.ivs[Stat.ATK] & 1) * 2
|
||||
+(user.ivs[Stat.DEF] & 1) * 4
|
||||
@ -3517,7 +3509,7 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
|
||||
+(user.ivs[Stat.SPATK] & 1) * 16
|
||||
+(user.ivs[Stat.SPDEF] & 1) * 32) * 15/63);
|
||||
|
||||
type.value = [
|
||||
move.type = [
|
||||
Type.FIGHTING, Type.FLYING, Type.POISON, Type.GROUND,
|
||||
Type.ROCK, Type.BUG, Type.GHOST, Type.STEEL,
|
||||
Type.FIRE, Type.WATER, Type.GRASS, Type.ELECTRIC,
|
||||
@ -3529,16 +3521,14 @@ export class HiddenPowerTypeAttr extends VariableMoveTypeAttr {
|
||||
|
||||
export class MatchUserTypeAttr extends VariableMoveTypeAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const type = (args[0] as Utils.IntegerHolder);
|
||||
|
||||
const userTypes = user.getTypes(true);
|
||||
|
||||
if (userTypes.includes(Type.STELLAR)) { // will not change to stellar type
|
||||
const nonTeraTypes = user.getTypes();
|
||||
type.value = nonTeraTypes[0];
|
||||
move.type = nonTeraTypes[0];
|
||||
return true;
|
||||
} else if (userTypes.length > 0) {
|
||||
type.value = userTypes[0];
|
||||
move.type = userTypes[0];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -4345,7 +4335,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
// Check if the move category is not STATUS or if the switch out condition is not met
|
||||
if (!this.getSwitchOutCondition()(user, target, move)) {
|
||||
//Apply effects before switch out i.e. poison point, flame body, etc
|
||||
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, new PokemonMove(move.id), null);
|
||||
applyPostDefendAbAttrs(PostDefendContactApplyStatusEffectAbAttr, target, user, move, null);
|
||||
return resolve(false);
|
||||
}
|
||||
|
||||
@ -6015,7 +6005,7 @@ export function initMoves() {
|
||||
], true)
|
||||
.attr(RemoveArenaTrapAttr),
|
||||
new StatusMove(Moves.SWEET_SCENT, Type.NORMAL, 100, 20, -1, 0, 2)
|
||||
.attr(StatChangeAttr, BattleStat.EVA, -1)
|
||||
.attr(StatChangeAttr, BattleStat.EVA, -2)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new AttackMove(Moves.IRON_TAIL, Type.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, 30, 0, 2)
|
||||
.attr(StatChangeAttr, BattleStat.DEF, -1),
|
||||
|
@ -363,7 +363,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
||||
new SpeciesFormChange(Species.PIDGEOT, "", SpeciesFormKey.MEGA, new SpeciesFormChangeItemTrigger(FormChangeItem.PIDGEOTITE))
|
||||
],
|
||||
[Species.PIKACHU]: [
|
||||
new SpeciesFormChange(Species.PIKACHU, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
new SpeciesFormChange(Species.PIKACHU, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
|
||||
new SpeciesFormChange(Species.PIKACHU, "partner", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
],
|
||||
[Species.MEOWTH]: [
|
||||
new SpeciesFormChange(Species.MEOWTH, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
@ -397,7 +398,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
|
||||
new SpeciesFormChange(Species.LAPRAS, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
],
|
||||
[Species.EEVEE]: [
|
||||
new SpeciesFormChange(Species.EEVEE, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
new SpeciesFormChange(Species.EEVEE, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS)),
|
||||
new SpeciesFormChange(Species.EEVEE, "partner", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
],
|
||||
[Species.SNORLAX]: [
|
||||
new SpeciesFormChange(Species.SNORLAX, "", SpeciesFormKey.GIGANTAMAX, new SpeciesFormChangeItemTrigger(FormChangeItem.MAX_MUSHROOMS))
|
||||
|
@ -78,7 +78,7 @@ export const trainerNamePools = {
|
||||
[TrainerType.ACE_TRAINER]: [["Aaron","Allen","Blake","Brian","Gaven","Jake","Kevin","Mike","Nick","Paul","Ryan","Sean","Darin","Albert","Berke","Clyde","Edgar","George","Leroy","Owen","Parker","Randall","Ruben","Samuel","Vincent","Warren","Wilton","Zane","Alfred","Braxton","Felix","Gerald","Jonathan","Leonel","Marcel","Mitchell","Quincy","Roderick","Colby","Rolando","Yuji","Abel","Anton","Arthur","Cesar","Dalton","Dennis","Ernest","Garrett","Graham","Henry","Isaiah","Jonah","Jose","Keenan","Micah","Omar","Quinn","Rodolfo","Saul","Sergio","Skylar","Stefan","Zachery","Alton","Arabella","Bonita","Cal","Cody","French","Kobe","Paulo","Shaye","Austin","Beckett","Charlie","Corky","David","Dwayne","Elmer","Jesse","Jared","Johan","Jordan","Kipp","Lou","Terry","Tom","Webster","Billy","Doyle","Enzio","Geoff","Grant","Kelsey","Miguel","Pierce","Ray","Santino","Shel","Adelbert","Bence","Emil","Evan","Mathis","Maxim","Neil","Rico","Robbie","Theo","Viktor","Benedict","Cornelius","Hisato","Leopold","Neville","Vito","Chase","Cole","Hiroshi","Jackson","Jim","Kekoa","Makana","Yuki","Elwood","Seth","Alvin","Arjun","Arnold","Cameron","Carl","Carlton","Christopher","Dave","Dax","Dominic","Edmund","Finn","Fred","Garret","Grayson","Jace","Jaxson","Jay","Jirard","Johnson","Kayden","Kite","Louis","Mac","Marty","Percy","Raymond","Ronnie","Satch","Tim","Zach","Conner","Vince","Bedro","Boda","Botan","Daras","Dury","Herton","Rewn","Stum","Tock","Trilo","Berki","Cruik","Dazon","Desid","Dillot","Farfin","Forgon","Hebel","Morfon","Moril","Shadd","Vanhub","Bardo","Carben","Degin","Gorps","Klept","Lask","Malex","Mopar","Niled","Noxon","Teslor","Tetil"],["Beth","Carol","Cybil","Emma","Fran","Gwen","Irene","Jenn","Joyce","Kate","Kelly","Lois","Lola","Megan","Quinn","Reena","Cara","Alexa","Brooke","Caroline","Elaine","Hope","Jennifer","Jody","Julie","Lori","Mary","Michelle","Shannon","Wendy","Alexia","Alicia","Athena","Carolina","Cristin","Darcy","Dianne","Halle","Jazmyn","Katelynn","Keira","Marley","Allyson","Kathleen","Naomi","Alyssa","Ariana","Brandi","Breanna","Brenda","Brenna","Catherine","Clarice","Dana","Deanna","Destiny","Jamie","Jasmin","Kassandra","Laura","Maria","Mariah","Maya","Meagan","Mikayla","Monique","Natasha","Olivia","Sandra","Savannah","Sydney","Moira","Piper","Salma","Allison","Beverly","Cathy","Cheyenne","Clara","Dara","Eileen","Glinda","Junko","Lena","Lucille","Mariana","Olwen","Shanta","Stella","Angi","Belle","Chandra","Cora","Eve","Jacqueline","Jeanne","Juliet","Kathrine","Layla","Lucca","Melina","Miki","Nina","Sable","Shelly","Summer","Trish","Vicki","Alanza","Cordelia","Hilde","Imelda","Michele","Mireille","Claudia","Constance","Harriet","Honor","Melba","Portia","Alexis","Angela","Karla","Lindsey","Tori","Sheri","Jada","Kailee","Amanda","Annie","Kindra","Kyla","Sofia","Yvette","Becky","Flora","Gloria","Buna","Ferda","Lehan","Liqui","Lomen","Neira","Atilo","Detta","Gilly","Gosney","Levens","Moden","Rask","Rateis","Rosno","Tynan","Veron","Zoel","Cida","Dibsin","Dodin","Ebson","Equin","Flostin","Gabsen","Halsion","Hileon","Quelor","Rapeel","Roze","Tensin"]],
|
||||
[TrainerType.ARTIST]: [["Ismael","William","Horton","Pierre","Zach","Gough","Salvador","Vincent","Duncan"],["Georgia"]],
|
||||
[TrainerType.BACKERS]: [["Alf & Fred","Hawk & Dar","Joe & Ross","Les & Web","Masa & Yas","Stu & Art"],["Ai & Ciel","Ami & Eira","Cam & Abby","Fey & Sue","Kat & Phae","Kay & Ali","Ava & Aya","Cleo & Rio","May & Mal"]],
|
||||
[TrainerType.BACKPACKER]: [["Alexander","Carlos","Herman","Jerome","Keane","Kelsey","Kiyo","Michael","Nate","Peter","Sam","Stephen","Talon","Terrance","Toru","Waylon","Boone","Clifford","Ivan","Kendall","Lowell","Randall","Reece","Roland","Shane","Walt","Farid","Heike","Joren","Lane","Roderick","Darnell","Deon","Emory","Graeme","Grayson","Ashley","Mikiko","Kiana","Perdy","Maria","Yuho","Peren","Barbara","Diane","Ruth","Aitor","Alex","Arturo","Asier","Jaime","Jonathan","Julio","Kevin","Kosuke","Lander","Markel","Mateo","Nil","Pau","Samuel"],["Anna","Corin","Elaine","Emi","Jill","Kumiko","Liz","Lois","Lora","Molly","Patty","Ruth","Vicki","Annie","Blossom","Clara","Eileen","Mae","Myra","Rachel","Tami"]],
|
||||
[TrainerType.BACKPACKER]: [["Alexander","Carlos","Herman","Jerome","Keane","Kelsey","Kiyo","Michael","Nate","Peter","Sam","Stephen","Talon","Terrance","Toru","Waylon","Boone","Clifford","Ivan","Kendall","Lowell","Randall","Reece","Roland","Shane","Walt","Farid","Heike","Joren","Lane","Roderick","Darnell","Deon","Emory","Graeme","Grayson","Aitor","Alex","Arturo","Asier","Jaime","Jonathan","Julio","Kevin","Kosuke","Lander","Markel","Mateo","Nil","Pau","Samuel"],["Anna","Corin","Elaine","Emi","Jill","Kumiko","Liz","Lois","Lora","Molly","Patty","Ruth","Vicki","Annie","Blossom","Clara","Eileen","Mae","Myra","Rachel","Tami","Ashley","Mikiko","Kiana","Perdy","Maria","Yuho","Peren","Barbara","Diane"]],
|
||||
[TrainerType.BAKER]: ["Chris","Jenn","Lilly"],
|
||||
[TrainerType.BEAUTY]: ["Cassie","Julia","Olivia","Samantha","Valerie","Victoria","Bridget","Connie","Jessica","Johanna","Melissa","Sheila","Shirley","Tiffany","Namiko","Thalia","Grace","Lola","Lori","Maura","Tamia","Cyndy","Devon","Gabriella","Harley","Lindsay","Nicola","Callie","Charlotte","Kassandra","December","Fleming","Nikola","Aimee","Anais","Brigitte","Cassandra","Andrea","Brittney","Carolyn","Krystal","Alexis","Alice","Aina","Anya","Arianna","Aubrey","Beverly","Camille","Beauty","Evette","Hansol","Haruka","Jill","Jo","Lana","Lois","Lucy","Mai","Nickie","Nicole","Prita","Rose","Shelly","Suzy","Tessa","Anita","Alissa","Rita","Cudsy","Eloff","Miru","Minot","Nevah","Niven","Ogoin"],
|
||||
[TrainerType.BIKER]: ["Charles","Dwayne","Glenn","Harris","Joel","Riley","Zeke","Alex","Billy","Ernest","Gerald","Hideo","Isaac","Jared","Jaren","Jaxon","Jordy","Lao","Lukas","Malik","Nikolas","Ricardo","Ruben","Virgil","William","Aiden","Dale","Dan","Jacob","Markey","Reese","Teddy","Theron","Jeremy","Morgann","Phillip","Philip","Stanley","Dillon"],
|
||||
@ -93,7 +93,7 @@ export const trainerNamePools = {
|
||||
[TrainerType.FISHERMAN]: ["Andre","Arnold","Barney","Chris","Edgar","Henry","Jonah","Justin","Kyle","Martin","Marvin","Ralph","Raymond","Scott","Stephen","Wilton","Tully","Andrew","Barny","Carter","Claude","Dale","Elliot","Eugene","Ivan","Ned","Nolan","Roger","Ronald","Wade","Wayne","Darian","Kai","Chip","Hank","Kaden","Tommy","Tylor","Alec","Brett","Cameron","Cody","Cole","Cory","Erick","George","Joseph","Juan","Kenneth","Luc","Miguel","Travis","Walter","Zachary","Josh","Gideon","Kyler","Liam","Murphy","Bruce","Damon","Devon","Hubert","Jones","Lydon","Mick","Pete","Sean","Sid","Vince","Bucky","Dean","Eustace","Kenzo","Leroy","Mack","Ryder","Ewan","Finn","Murray","Seward","Shad","Wharton","Finley","Fisher","Fisk","River","Sheaffer","Timin","Carl","Ernest","Hal","Herbert","Hisato","Mike","Vernon","Harriet","Marina","Chase"],
|
||||
[TrainerType.GUITARIST]: ["Anna","Beverly","January","Tina","Alicia","Claudia","Julia","Lidia","Mireia","Noelia","Sara","Sheila","Tatiana"],
|
||||
[TrainerType.HARLEQUIN]: ["Charley","Ian","Jack","Kerry","Louis","Pat","Paul","Rick","Anders","Clarence","Gary"],
|
||||
[TrainerType.HIKER]: ["Anthony","Bailey","Benjamin","Daniel","Erik","Jim","Kenny","Leonard","Michael","Parry","Phillip","Russell","Sidney","Tim","Timothy","Alan","Brice","Clark","Eric","Lenny","Lucas","Mike","Trent","Devan","Eli","Marc","Sawyer","Allen","Daryl","Dudley","Earl","Franklin","Jeremy","Marcos","Nob","Oliver","Wayne","Alexander","Damon","Jonathan","Justin","Kevin","Lorenzo","Louis","Maurice","Nicholas","Reginald","Robert","Theodore","Bruce","Clarke","Devin","Dwight","Edwin","Eoin","Noland","Russel","Andy","Bret","Darrell","Gene","Hardy","Hugh","Jebediah","Jeremiah","Kit","Neil","Terrell","Don","Doug","Hunter","Jared","Jerome","Keith","Manuel","Markus","Otto","Shelby","Stephen","Teppei","Tobias","Wade","Zaiem","Aaron","Alain","Bergin","Bernard","Brent","Corwin","Craig","Delmon","Dunstan","Orestes","Ross","Davian","Calhoun","David","Gabriel","Ryan","Thomas","Travis","Zachary","Anuhea","Barnaby","Claus","Collin","Colson","Dexter","Dillan","Eugine","Farkas","Hisato","Julius","Kenji","Irwin","Lionel","Paul","Richter","Valentino","Donald","Douglas","Kevyn","Angela","Carla","Celia","Daniela","Estela","Fatima","Helena","Leire","Lucia","Luna","Manuela","Mar","Marina","Miyu","Nancy","Nerea","Paula","Rocio","Yanira","Chester"],
|
||||
[TrainerType.HIKER]: ["Anthony","Bailey","Benjamin","Daniel","Erik","Jim","Kenny","Leonard","Michael","Parry","Phillip","Russell","Sidney","Tim","Timothy","Alan","Brice","Clark","Eric","Lenny","Lucas","Mike","Trent","Devan","Eli","Marc","Sawyer","Allen","Daryl","Dudley","Earl","Franklin","Jeremy","Marcos","Nob","Oliver","Wayne","Alexander","Damon","Jonathan","Justin","Kevin","Lorenzo","Louis","Maurice","Nicholas","Reginald","Robert","Theodore","Bruce","Clarke","Devin","Dwight","Edwin","Eoin","Noland","Russel","Andy","Bret","Darrell","Gene","Hardy","Hugh","Jebediah","Jeremiah","Kit","Neil","Terrell","Don","Doug","Hunter","Jared","Jerome","Keith","Manuel","Markus","Otto","Shelby","Stephen","Teppei","Tobias","Wade","Zaiem","Aaron","Alain","Bergin","Bernard","Brent","Corwin","Craig","Delmon","Dunstan","Orestes","Ross","Davian","Calhoun","David","Gabriel","Ryan","Thomas","Travis","Zachary","Anuhea","Barnaby","Claus","Collin","Colson","Dexter","Dillan","Eugine","Farkas","Hisato","Julius","Kenji","Irwin","Lionel","Paul","Richter","Valentino","Donald","Douglas","Kevyn","Chester"], //["Angela","Carla","Celia","Daniela","Estela","Fatima","Helena","Leire","Lucia","Luna","Manuela","Mar","Marina","Miyu","Nancy","Nerea","Paula","Rocio","Yanira"]
|
||||
[TrainerType.HOOLIGANS]: ["Jim & Cas","Rob & Sal"],
|
||||
[TrainerType.HOOPSTER]: ["Bobby","John","Lamarcus","Derrick","Nicolas"],
|
||||
[TrainerType.INFIELDER]: ["Alex","Connor","Todd"],
|
||||
|
@ -4,7 +4,7 @@ import { Variant, VariantSet, variantColorCache } from "#app/data/variant";
|
||||
import { variantData } from "#app/data/variant";
|
||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
||||
import { Moves } from "../data/enums/moves";
|
||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, MoveFlags } from "../data/move";
|
||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, VariableMoveTypeAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, MoveFlags } from "../data/move";
|
||||
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
||||
import * as Utils from "../utils";
|
||||
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
|
||||
@ -27,7 +27,7 @@ import { TempBattleStat } from "../data/temp-battle-stat";
|
||||
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from "../data/arena-tag";
|
||||
import { ArenaTagType } from "../data/enums/arena-tag-type";
|
||||
import { Biome } from "../data/enums/biome";
|
||||
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr } from "../data/ability";
|
||||
import { Ability, AbAttr, BattleStatMultiplierAbAttr, MoveTypeChangeAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr } from "../data/ability";
|
||||
import { Abilities } from "#app/data/enums/abilities";
|
||||
import PokemonData from "../system/pokemon-data";
|
||||
import { BattlerIndex } from "../battle";
|
||||
@ -1057,9 +1057,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return !this.isOfType(Type.FLYING, true) && !this.hasAbility(Abilities.LEVITATE);
|
||||
}
|
||||
|
||||
getAttackMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier {
|
||||
const typeless = move.getMove().hasAttr(TypelessAttr);
|
||||
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type, source));
|
||||
getAttackMoveEffectiveness(source: Pokemon, pokemonMove: PokemonMove): TypeDamageMultiplier {
|
||||
const move = pokemonMove.getMove();
|
||||
const typeless = move.hasAttr(TypelessAttr);
|
||||
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.type, source));
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
if (!typeless) {
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
|
||||
@ -1620,9 +1621,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
return (this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.getFieldIndex() ? 0 : 1];
|
||||
}
|
||||
|
||||
apply(source: Pokemon, battlerMove: PokemonMove): HitResult {
|
||||
apply(source: Pokemon, move: Move): HitResult {
|
||||
let result: HitResult;
|
||||
const move = battlerMove.getMove();
|
||||
const damage = new Utils.NumberHolder(0);
|
||||
const defendingSidePlayField = this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField();
|
||||
|
||||
@ -1630,19 +1630,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, variableCategory);
|
||||
const moveCategory = variableCategory.value as MoveCategory;
|
||||
|
||||
const variableType = new Utils.IntegerHolder(move.type);
|
||||
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
|
||||
applyMoveAttrs(VariableMoveTypeAttr, source, this, move, variableType);
|
||||
// 2nd argument is for MoveTypeChangePowerMultiplierAbAttr
|
||||
applyAbAttrs(VariableMoveTypeAbAttr, source, null, variableType, typeChangeMovePowerMultiplier);
|
||||
applyPreAttackAbAttrs(MoveTypeChangeAttr, source, this, battlerMove, variableType, typeChangeMovePowerMultiplier);
|
||||
const type = variableType.value as Type;
|
||||
applyMoveAttrs(VariableMoveTypeAttr, source, this, move);
|
||||
applyPreAttackAbAttrs(MoveTypeChangeAttr, source, this, move, typeChangeMovePowerMultiplier);
|
||||
const types = this.getTypes(true, true);
|
||||
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
const typeless = move.hasAttr(TypelessAttr);
|
||||
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
|
||||
? this.getAttackTypeEffectiveness(type, source)
|
||||
? this.getAttackTypeEffectiveness(move.type, source)
|
||||
: 1);
|
||||
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
|
||||
if (typeless) {
|
||||
@ -1667,44 +1663,44 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||
const power = new Utils.NumberHolder(move.power);
|
||||
const sourceTeraType = source.getTeraType();
|
||||
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MultiHitAttr) && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MultiHitAttr) && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
|
||||
power.value = 60;
|
||||
}
|
||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power);
|
||||
this.scene.getField(true).map(p => applyPreAttackAbAttrs(FieldVariableMovePowerAbAttr, this, source, battlerMove, power));
|
||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, move, power);
|
||||
this.scene.getField(true).map(p => applyPreAttackAbAttrs(FieldVariableMovePowerAbAttr, this, source, move, power));
|
||||
|
||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, battlerMove, cancelled, power);
|
||||
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, power);
|
||||
|
||||
power.value *= typeChangeMovePowerMultiplier.value;
|
||||
|
||||
if (!typeless) {
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
||||
}
|
||||
if (!cancelled.value) {
|
||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, battlerMove, cancelled, typeMultiplier));
|
||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier));
|
||||
}
|
||||
|
||||
if (cancelled.value) {
|
||||
result = HitResult.NO_EFFECT;
|
||||
} else {
|
||||
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === type) as TypeBoostTag;
|
||||
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === move.type) as TypeBoostTag;
|
||||
if (typeBoost) {
|
||||
power.value *= typeBoost.boostValue;
|
||||
if (typeBoost.oneUse) {
|
||||
source.removeTag(typeBoost.tagType);
|
||||
}
|
||||
}
|
||||
const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(type, source.isGrounded()));
|
||||
const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(move.type, source.isGrounded()));
|
||||
applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier);
|
||||
if (this.scene.arena.getTerrainType() === TerrainType.GRASSY && this.isGrounded() && type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS) {
|
||||
if (this.scene.arena.getTerrainType() === TerrainType.GRASSY && this.isGrounded() && move.type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS) {
|
||||
power.value /= 2;
|
||||
}
|
||||
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||
this.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, new Utils.IntegerHolder(0), power);
|
||||
if (!typeless) {
|
||||
this.scene.arena.applyTags(WeakenMoveTypeTag, type, power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, type, power);
|
||||
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, move.type, power);
|
||||
}
|
||||
if (source.getTag(HelpingHandTag)) {
|
||||
power.value *= 1.5;
|
||||
@ -1731,6 +1727,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(critLevel.value, 3))];
|
||||
isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !this.scene.randBattleSeedInt(critChance));
|
||||
if (Overrides.NEVER_CRIT_OVERRIDE) {
|
||||
isCritical = false;
|
||||
}
|
||||
}
|
||||
if (isCritical) {
|
||||
const blockCrit = new Utils.BooleanHolder(false);
|
||||
@ -1749,11 +1748,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0;
|
||||
const sourceTypes = source.getTypes();
|
||||
const matchesSourceType = sourceTypes[0] === type || (sourceTypes.length > 1 && sourceTypes[1] === type);
|
||||
const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type);
|
||||
const stabMultiplier = new Utils.NumberHolder(1);
|
||||
if (sourceTeraType === Type.UNKNOWN && matchesSourceType) {
|
||||
stabMultiplier.value += 0.5;
|
||||
} else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type) {
|
||||
} else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type) {
|
||||
stabMultiplier.value += 0.5;
|
||||
}
|
||||
|
||||
@ -1778,7 +1777,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
}
|
||||
|
||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, battlerMove, damage);
|
||||
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, damage);
|
||||
|
||||
/**
|
||||
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
|
||||
@ -1793,7 +1792,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
});
|
||||
}
|
||||
|
||||
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && type === Type.DRAGON) {
|
||||
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && move.type === Type.DRAGON) {
|
||||
damage.value = Math.floor(damage.value / 2);
|
||||
}
|
||||
|
||||
@ -1848,7 +1847,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
const oneHitKo = result === HitResult.ONE_HIT_KO;
|
||||
if (damage.value) {
|
||||
if (this.getHpRatio() === 1) {
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, battlerMove, cancelled, damage);
|
||||
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, damage);
|
||||
} else if (!this.isPlayer() && damage.value >= this.hp) {
|
||||
this.scene.applyModifiers(EnemyEndureChanceModifier, false, this);
|
||||
}
|
||||
@ -1913,11 +1912,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
break;
|
||||
case MoveCategory.STATUS:
|
||||
if (!typeless) {
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
||||
}
|
||||
if (!cancelled.value) {
|
||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, battlerMove, cancelled, typeMultiplier));
|
||||
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier);
|
||||
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, source, move, cancelled, typeMultiplier));
|
||||
}
|
||||
if (!typeMultiplier.value) {
|
||||
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: this.name }));
|
||||
@ -3381,10 +3380,6 @@ export class EnemyPokemon extends Pokemon {
|
||||
const pokemonMove = movePool[m];
|
||||
const move = pokemonMove.getMove();
|
||||
|
||||
const variableType = new Utils.IntegerHolder(move.type);
|
||||
applyAbAttrs(VariableMoveTypeAbAttr, this, null, variableType);
|
||||
const moveType = variableType.value as Type;
|
||||
|
||||
let moveScore = moveScores[m];
|
||||
const targetScores: integer[] = [];
|
||||
|
||||
@ -3402,12 +3397,12 @@ export class EnemyPokemon extends Pokemon {
|
||||
const effectiveness = target.getAttackMoveEffectiveness(this, pokemonMove);
|
||||
if (target.isPlayer() !== this.isPlayer()) {
|
||||
targetScore *= effectiveness;
|
||||
if (this.isOfType(moveType)) {
|
||||
if (this.isOfType(move.type)) {
|
||||
targetScore *= 1.5;
|
||||
}
|
||||
} else if (effectiveness) {
|
||||
targetScore /= effectiveness;
|
||||
if (this.isOfType(moveType)) {
|
||||
if (this.isOfType(move.type)) {
|
||||
targetScore /= 1.5;
|
||||
}
|
||||
}
|
||||
@ -3789,6 +3784,19 @@ export enum HitResult {
|
||||
|
||||
export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | HitResult.NOT_VERY_EFFECTIVE | HitResult.ONE_HIT_KO | HitResult.OTHER;
|
||||
|
||||
/**
|
||||
* Wrapper class for the {@linkcode Move} class for Pokemon to interact with.
|
||||
* These are the moves assigned to a {@linkcode Pokemon} object.
|
||||
* It links to {@linkcode Move} class via the move ID.
|
||||
* Compared to {@linkcode Move}, this class also tracks if a move has received.
|
||||
* PP Ups, amount of PP used, and things like that.
|
||||
* @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 usePp} - removes a point of PP from the move.
|
||||
* @see {@linkcode getMovePp} - returns amount of PP a move currently has.
|
||||
* @see {@linkcode getPpRatio} - returns the current PP amount / max PP amount.
|
||||
* @see {@linkcode getName} - returns name of {@linkcode Move}.
|
||||
**/
|
||||
export class PokemonMove {
|
||||
public moveId: Moves;
|
||||
public ppUsed: integer;
|
||||
|
@ -94,7 +94,6 @@ export class InputsController {
|
||||
|
||||
private buttonLock: Button;
|
||||
private interactions: Map<Button, Map<string, boolean>> = new Map();
|
||||
private time: Phaser.Time.Clock;
|
||||
private configs: Map<string, InterfaceConfig> = new Map();
|
||||
|
||||
public gamepadSupport: boolean = true;
|
||||
@ -121,7 +120,6 @@ export class InputsController {
|
||||
|
||||
constructor(scene: BattleScene) {
|
||||
this.scene = scene;
|
||||
this.time = this.scene.time;
|
||||
this.selectedDevice = {
|
||||
[Device.GAMEPAD]: null,
|
||||
[Device.KEYBOARD]: "default"
|
||||
@ -246,6 +244,9 @@ export class InputsController {
|
||||
* If an interaction is valid and should be processed, it emits an 'input_down' event with details of the interaction.
|
||||
*/
|
||||
update(): void {
|
||||
if (this.pauseUpdate) {
|
||||
return;
|
||||
}
|
||||
for (const b of Utils.getEnumValues(Button).reverse()) {
|
||||
if (
|
||||
this.interactions.hasOwnProperty(b) &&
|
||||
@ -256,8 +257,7 @@ export class InputsController {
|
||||
if (
|
||||
(!this.gamepadSupport && this.interactions[b].source === "gamepad") ||
|
||||
(this.interactions[b].source === "gamepad" && this.interactions[b].sourceName && this.interactions[b].sourceName !== this.selectedDevice[Device.GAMEPAD]) ||
|
||||
(this.interactions[b].source === "keyboard" && this.interactions[b].sourceName && this.interactions[b].sourceName !== this.selectedDevice[Device.KEYBOARD]) ||
|
||||
this.pauseUpdate
|
||||
(this.interactions[b].source === "keyboard" && this.interactions[b].sourceName && this.interactions[b].sourceName !== this.selectedDevice[Device.KEYBOARD])
|
||||
) {
|
||||
// Deletes the last interaction for a button if gamepad is disabled.
|
||||
this.delLastProcessedMovementTime(b as Button);
|
||||
@ -548,7 +548,8 @@ export class InputsController {
|
||||
if (!this.isButtonLocked(button)) {
|
||||
return false;
|
||||
}
|
||||
if (this.time.now - this.interactions[button].pressTime >= repeatInputDelayMillis) {
|
||||
const duration = Date.now() - this.interactions[button].pressTime;
|
||||
if (duration >= repeatInputDelayMillis) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -573,7 +574,7 @@ export class InputsController {
|
||||
return;
|
||||
}
|
||||
this.setButtonLock(button);
|
||||
this.interactions[button].pressTime = this.time.now;
|
||||
this.interactions[button].pressTime = Date.now();
|
||||
this.interactions[button].isPressed = true;
|
||||
this.interactions[button].source = source;
|
||||
this.interactions[button].sourceName = sourceName.toLowerCase();
|
||||
@ -633,7 +634,7 @@ export class InputsController {
|
||||
this.interactions[b].sourceName = null;
|
||||
}
|
||||
}
|
||||
setTimeout(() => this.pauseUpdate = false, 500);
|
||||
this.pauseUpdate = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,9 +16,11 @@ import {initPokemonForms} from "#app/data/pokemon-forms";
|
||||
import {initSpecies} from "#app/data/pokemon-species";
|
||||
import {initMoves} from "#app/data/move";
|
||||
import {initAbilities} from "#app/data/ability";
|
||||
import {initAchievements} from "#app/system/achv";
|
||||
import {initTrainerTypeDialogue} from "#app/data/dialogue";
|
||||
import i18next from "i18next";
|
||||
import { initStatsKeys } from "./ui/game-stats-ui-handler";
|
||||
import { initVouchers } from "./system/voucher";
|
||||
|
||||
export class LoadingScene extends SceneBase {
|
||||
constructor() {
|
||||
@ -135,7 +137,6 @@ export class LoadingScene extends SceneBase {
|
||||
this.loadImage("summary_stats_overlay_exp", "ui");
|
||||
this.loadImage("summary_moves", "ui");
|
||||
this.loadImage("summary_moves_effect", "ui");
|
||||
this.loadImage("summary_moves_effect_type", "ui");
|
||||
this.loadImage("summary_moves_overlay_row", "ui");
|
||||
this.loadImage("summary_moves_overlay_pp", "ui");
|
||||
this.loadAtlas("summary_moves_cursor", "ui");
|
||||
@ -329,6 +330,8 @@ export class LoadingScene extends SceneBase {
|
||||
|
||||
this.loadLoadingScreen();
|
||||
|
||||
initVouchers();
|
||||
initAchievements();
|
||||
initStatsKeys();
|
||||
initPokemonPrevolutions();
|
||||
initBiomes();
|
||||
|
@ -369,26 +369,26 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"firebreather": {
|
||||
"encounter": {
|
||||
1: "My flames shall devour you!",
|
||||
2: "My soul is on fire. I'll show you how hot it burns!",
|
||||
3: "Step right up and take a look!"
|
||||
1: "Meine Flammen werden dich verschlingen!",
|
||||
2: "Meine Seele hat Feuer gefangen. Ich werde dir zeigen, wie heiß sie brennt!",
|
||||
3: "Komm näher und sieh dir meine Flammen an!"
|
||||
},
|
||||
"victory": {
|
||||
1: "I burned down to ashes...",
|
||||
2: "Yow! That's hot!",
|
||||
3: "Ow! I scorched the tip of my nose!"
|
||||
1: "Verbrannt bis zur Asche...",
|
||||
2: "Yow! Das ist heiß!",
|
||||
3: "Auuu! Ich habe mir die Nasenspitze verbrannt!"
|
||||
},
|
||||
},
|
||||
"sailor": {
|
||||
"encounter": {
|
||||
1: "Matey, you're walking the plank if you lose!",
|
||||
2: "Come on then! My sailor's pride is at stake!",
|
||||
3: "Ahoy there! Are you seasick?"
|
||||
1: "Matrose, du gehst über Bord, wenn du verlierst!",
|
||||
2: "Komm schon! Mein Stolz als Seemann steht auf dem Spiel!",
|
||||
3: "Ahoj! Bist du seekrank?"
|
||||
},
|
||||
"victory": {
|
||||
1: "Argh! Beaten by a kid!",
|
||||
2: "Your spirit sank me!",
|
||||
3: "I think it's me that's seasick..."
|
||||
1: "Argh! Von einem Kind besiegt!",
|
||||
2: "Dein Geist hat mich versenkt!",
|
||||
3: "Ich glaube, ich bin der der seekrank ist..."
|
||||
},
|
||||
},
|
||||
"brock": {
|
||||
|
@ -283,7 +283,7 @@ export const pokemon: SimpleTranslationEntries = {
|
||||
"ralts": "Trasla",
|
||||
"kirlia": "Kirlia",
|
||||
"gardevoir": "Gardevoir",
|
||||
"surskit": "Geweiher",
|
||||
"surskit": "Gehweiher",
|
||||
"masquerain": "Maskeregen",
|
||||
"shroomish": "Knilz",
|
||||
"breloom": "Kapilz",
|
||||
|
@ -243,64 +243,64 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"scientist": {
|
||||
"encounter": {
|
||||
1: "My research will lead this world to peace and joy.",
|
||||
1: "제 연구는 이 세상을 평화와 기쁨으로 이끌 겁니다.",
|
||||
},
|
||||
"victory": {
|
||||
1: "I am a genius… I am not supposed to lose against someone like you…",
|
||||
1: "전 천재니까… 당신 같은 사람에게 질 수 없는데…",
|
||||
},
|
||||
},
|
||||
"school_kid": {
|
||||
"encounter": {
|
||||
1: "…Heehee. I'm confident in my calculations and analysis.",
|
||||
2: "I'm gaining as much experience as I can because I want to be a Gym Leader someday."
|
||||
1: "…헤헷. 계산과 분석에는 자신 있어.",
|
||||
2: "언젠가 체육관 관장이 되고 싶어서, 최대한 많은 경험을 쌓고 있어."
|
||||
},
|
||||
"victory": {
|
||||
1: "Ohhhh… Calculation and analysis are perhaps no match for chance…",
|
||||
2: "Even difficult, trying experiences have their purpose, I suppose."
|
||||
1: "으아아… 이번에는 아마 계산과 분석이 빗나간 것 같아…",
|
||||
2: "내가 보기엔, 어렵고 힘든 경험도 나름의 의미가 있는 것 같아."
|
||||
}
|
||||
},
|
||||
"artist": {
|
||||
"encounter": {
|
||||
1: "I used to be popular, but now I am all washed up.",
|
||||
1: "예전엔 인기가 많았지만, 지금은 모두 사라졌다네.",
|
||||
},
|
||||
"victory": {
|
||||
1: "As times change, values also change. I realized that too late.",
|
||||
1: "시대가 변하면, 가치관도 변하지. 난 그걸 너무 늦게 깨달았어.",
|
||||
},
|
||||
},
|
||||
"guitarist": {
|
||||
"encounter": {
|
||||
1: "Get ready to feel the rhythm of defeat as I strum my way to victory!",
|
||||
1: "패배의 리듬을 느낄 준비는 됐겠지? 내가 승리할 거니까!",
|
||||
},
|
||||
"victory": {
|
||||
1: "Silenced for now, but my melody of resilience will play on.",
|
||||
1: "지금은 조용하지만, 회복의 멜로디를 연주할 거야.",
|
||||
},
|
||||
},
|
||||
"worker": {
|
||||
"encounter": {
|
||||
1: "It bothers me that people always misunderstand me. I'm a lot more pure than everyone thinks.",
|
||||
1: "사람들이 저를 오해하는 게 신경 쓰여요. 전 생각보다 훨씬 깨끗하답니다.",
|
||||
},
|
||||
"victory": {
|
||||
1: "I really don't want my skin to burn, so I want to stay in the shade while I work.",
|
||||
1: "피부가 타는 게 싫어서, 일하는 동안엔 그늘에 머물고 싶어요.",
|
||||
},
|
||||
},
|
||||
"worker_female": {
|
||||
"encounter": {
|
||||
1: `It bothers me that people always misunderstand me.
|
||||
$I'm a lot more pure than everyone thinks.`
|
||||
1: `사람들이 나를 오해하는 게 신경 쓰여.
|
||||
$나는 생각보다 훨씬 깨끗한데.`
|
||||
},
|
||||
"victory": {
|
||||
1: "I really don't want my skin to burn, so I want to stay in the shade while I work."
|
||||
1: "피부가 타는 게 싫어서, 일하는 동안엔 그늘에 머물고 싶어."
|
||||
},
|
||||
"defeat": {
|
||||
1: "My body and mind aren't necessarily always in sync."
|
||||
1: "생각처럼 몸이 잘 안따라주네."
|
||||
}
|
||||
},
|
||||
"worker_double": {
|
||||
"encounter": {
|
||||
1: "I'll show you we can break you. We've been training in the field!",
|
||||
1: "너를 무너뜨릴 수 있다는 것을 보여줄게. 우리는 실전 경험이 있거든!",
|
||||
},
|
||||
"victory": {
|
||||
1: "How strange… How could this be… I shouldn't have been outmuscled.",
|
||||
1: "이상하네… 어떻게 이럴 수 있지… 힘으로 압도할 수 없다니.",
|
||||
},
|
||||
},
|
||||
"hex_maniac": {
|
||||
@ -697,19 +697,19 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"falkner": {
|
||||
"encounter": {
|
||||
1: "I'll show you the real power of the magnificent bird Pokémon!",
|
||||
2: "Winds, stay with me!",
|
||||
3: "Dad! I hope you're watching me battle from above!"
|
||||
1: "넓은 하늘을 화려하게 나는 새 포켓몬의 진정한 강함을 알게 해주겠다!",
|
||||
2: "바람이여, 나에게 오라!",
|
||||
3: "아버지, 내 시합을 하늘에서도 봐줘!"
|
||||
},
|
||||
"victory": {
|
||||
1: "I understand… I'll bow out gracefully.",
|
||||
2: "A defeat is a defeat. You are strong indeed.",
|
||||
3: "…Shoot! Yeah, I lost."
|
||||
1: "알았다… 미련없이 땅에 내려가지.",
|
||||
2: "패배는 패배니까. 넌 정말 강하군.",
|
||||
3: "…큭! 그래, 내가 졌다."
|
||||
},
|
||||
"defeat": {
|
||||
1: "Dad! I won with your cherished bird Pokémon…",
|
||||
2: "Bird Pokémon are the best after all!",
|
||||
3: "Feels like I'm catching up to my dad!"
|
||||
1: "아버지! 소중히 여기던 새 포켓몬으로 이겼어…",
|
||||
2: "언제나 새 포켓몬이 최강이다!",
|
||||
3: "아버지를 따라 잡은 기분이군!"
|
||||
}
|
||||
},
|
||||
"nessa": {
|
||||
@ -874,34 +874,34 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"morty": {
|
||||
"encounter": {
|
||||
1: `With a little more, I could see a future in which I meet the legendary Pokémon.
|
||||
$You're going to help me reach that level!`,
|
||||
2: `It's said that a rainbow-hued Pokémon will come down to appear before a truly powerful Trainer.
|
||||
$I believed that tale, so I have secretly trained here all my life. As a result, I can now see what others cannot.
|
||||
$I see a shadow of the person who will make the Pokémon appear.
|
||||
$I believe that person is me! You're going to help me reach that level!`,
|
||||
3: "Whether you choose to believe or not, mystic power does exist.",
|
||||
4: "You can bear witness to the fruits of my training.",
|
||||
5: "You must make your soul one with that of Pokémon. Can you do this?",
|
||||
6: "Say, do you want to be part of my training?"
|
||||
1: `조금만 더 노력하면, 내가 전설의 포켓몬을 만나는 미래가 보여.
|
||||
$내가 그 수준에 도달할 수 있게 도와줘!`,
|
||||
2: `커다란 무지개색 포켓몬은 진정한 강함을 가진 트레이너 앞에 나타난다는 이야기가 있어.
|
||||
$난 그 이야기를 믿고, 줄곧 이 곳에서 몰래 수행하고 있어. 그 결과로, 다른 사람들은 볼 수 없는 것을 볼 수 있게 됐지.
|
||||
$내겐 그 포켓몬을 나타나게 할 사람의 그림자가 보이거든.
|
||||
$난 그게 나라고 믿어! 넌 내가 그 수준에 올라갈 수 있도록 도와줘야겠어!`,
|
||||
3: "네가 믿든 믿지 않든, 불가사의한 힘은 존재해.",
|
||||
4: "넌 내 수련의 결실을 보게 될 거야.",
|
||||
5: "포켓몬과 너의 영혼을 하나로 만들어야 해. 가능하겠어?",
|
||||
6: "저기, 너 내 수행의 일부분이 되고 싶은거지?"
|
||||
},
|
||||
"victory": {
|
||||
1: "I'm not good enough yet…",
|
||||
2: `I see… Your journey has taken you to far-away places and you have witnessed much more than I.
|
||||
$I envy you for that…`,
|
||||
3: "How is this possible…",
|
||||
4: `I don't think our potentials are so different.
|
||||
$But you seem to have something more than that… So be it.`,
|
||||
5: "Guess I need more training.",
|
||||
6: "That's a shame."
|
||||
1: "나는 아직 멀었구나…",
|
||||
2: `그래… 여행으로 먼 곳을 돌아다니면서, 나보다 훨씬 많은 것을 봐왔구나.
|
||||
$네가 조금 부럽네…`,
|
||||
3: "이게 어떻게 가능한 거지…",
|
||||
4: `우리의 잠재력은 그렇게 다르진 않은 것 같아.
|
||||
$그치만 넌 그것과 다른 무언가를 많이 갖고 있는 것 같네… 흐음.`,
|
||||
5: "수련이 더 필요하겠군.",
|
||||
6: "안타깝게 됐네."
|
||||
},
|
||||
"defeat": {
|
||||
1: "I moved… one step ahead again.",
|
||||
2: "Fufufu…",
|
||||
3: "Wh-what?! It can't be! Even that wasn't enough?",
|
||||
4: "I feel like I just smashed through a really stubborn boulder!",
|
||||
5: "Ahahahah!",
|
||||
6: "I knew I would win!"
|
||||
1: "내가…다시 한 발짝 앞섰어.",
|
||||
2: "후후훗…",
|
||||
3: "뭐-뭐야?! 이럴 수가! 그것도 부족해?",
|
||||
4: "정말 단단한 바위를 뚫고 나온 기분인데!",
|
||||
5: "아하하하하!",
|
||||
6: "내가 이길 줄 알았어!"
|
||||
}
|
||||
},
|
||||
"crispin": {
|
||||
@ -1146,26 +1146,26 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"lorelei": {
|
||||
"encounter": {
|
||||
1: `No one can best me when it comes to icy Pokémon! Freezing moves are powerful!
|
||||
$Your Pokémon will be at my mercy when they are frozen solid! Hahaha! Are you ready?`,
|
||||
1: `얼음포켓몬을 내보내면 대적할 사람이 없지! 상대를 얼린다는 건 매우 강력한 공격이야.
|
||||
$네 포켓몬이 꽁꽁 얼면 그때부턴 내 손바닥 위거든! 아하하! 준비됐어?`,
|
||||
},
|
||||
"victory": {
|
||||
1: "How dare you!"
|
||||
1: "어떻게 감히!"
|
||||
},
|
||||
"defeat": {
|
||||
1: "There's nothing you can do once you're frozen."
|
||||
1: "얼어붙은 넌 아무것도 할 수 없어."
|
||||
}
|
||||
},
|
||||
"will": {
|
||||
"encounter": {
|
||||
1: `I have trained all around the world, making my psychic Pokémon powerful.
|
||||
$I can only keep getting better! Losing is not an option!`,
|
||||
1: `나는 전세계를 돌아다니며, 강한 에스퍼 포켓몬을 만들도록 수행해왔다.
|
||||
$계속 더 정진하겠다! 패배는 선택지에 없어!`,
|
||||
},
|
||||
"victory": {
|
||||
1: "I… I can't… believe it…"
|
||||
1: "이… 내가… 믿을수 없어…"
|
||||
},
|
||||
"defeat": {
|
||||
1: "That was close. I wonder what it is that you lack."
|
||||
1: "근소한 차이였다. 네게 부족한 것이 무엇인지 궁금하군."
|
||||
}
|
||||
},
|
||||
"malva": {
|
||||
@ -1216,35 +1216,35 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"bruno": {
|
||||
"encounter": {
|
||||
1: "We will grind you down with our superior power! Hoo hah!"
|
||||
1: "우월한 힘으로 너를 부숴주지! 우! 하~앗!"
|
||||
},
|
||||
"victory": {
|
||||
1: "Why? How could I lose?"
|
||||
1: "하? 어떻게 내가 진 거지?"
|
||||
},
|
||||
"defeat": {
|
||||
1: "You can challenge me all you like, but the results will never change!"
|
||||
1: "얼마든지 내게 도전 할 수 있지만, 결과는 절대 바뀌지 않을 거다!"
|
||||
}
|
||||
},
|
||||
"bugsy": {
|
||||
"encounter": {
|
||||
1: "I'm Bugsy! I never lose when it comes to bug Pokémon!"
|
||||
1: "내 이름은 호일! 벌레 포켓몬에 대해서라면 누구에게도 지지 않아!"
|
||||
},
|
||||
"victory": {
|
||||
1: "Whoa, amazing! You're an expert on Pokémon!\nMy research isn't complete yet. OK, you win."
|
||||
1: "우와, 대단해! 넌 포켓몬 전문가구나!\n내 연구는 아직 안 끝났네. 응, 네가 이겼어."
|
||||
},
|
||||
"defeat": {
|
||||
1: "Thanks! Thanks to our battle, I was also able to make progress in my research!"
|
||||
1: "고마워! 방금 승부 덕분에, 내 연구도 진전을 이룬 것 같아!"
|
||||
}
|
||||
},
|
||||
"koga": {
|
||||
"encounter": {
|
||||
1: "Fwahahahaha! Pokémon are not merely about brute force--you shall see soon enough!"
|
||||
1: "후하하하! 포켓몬은 딘순히 강한 것만이 아니다--곧 알려주지!"
|
||||
},
|
||||
"victory": {
|
||||
1: "Ah! You've proven your worth!"
|
||||
1: "하! 스스로 증명해냈군!"
|
||||
},
|
||||
"defeat": {
|
||||
1: "Have you learned to fear the techniques of the ninja?"
|
||||
1: "인술을 피하는 방법을 배워보겠나?"
|
||||
}
|
||||
},
|
||||
"bertha": {
|
||||
@ -1319,13 +1319,13 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"agatha": {
|
||||
"encounter": {
|
||||
1: "Pokémon are for battling! I'll show you how a real Trainer battles!"
|
||||
1: "포켓몬은 싸우게 하려고 있는 것이야! 진정한 싸움이라는 것을 보여주겠다!"
|
||||
},
|
||||
"victory": {
|
||||
1: "Oh my! You're something special, child!"
|
||||
1: "이런! 넌 무언가 특별하구나, 꼬마야!"
|
||||
},
|
||||
"defeat": {
|
||||
1: "Bahaha. That's how a proper battle's done!"
|
||||
1: "바하하하. 제대로 된 승부는 이렇게 하는거다!"
|
||||
}
|
||||
},
|
||||
"flint": {
|
||||
@ -1414,33 +1414,33 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"lance": {
|
||||
"encounter": {
|
||||
1: "I've been waiting for you. Allow me to test your skill.",
|
||||
2: "I thought that you would be able to get this far. Let's get this started."
|
||||
1: "널 기다리고 있었다. 그 실력을 시험해보겠어.",
|
||||
2: "여기까지 올 수 있을거라고 생각했다. 슬슬 시작해볼까."
|
||||
},
|
||||
"victory": {
|
||||
1: "You got me. You are magnificent!",
|
||||
2: "I never expected another trainer to beat me… I'm surprised."
|
||||
1: "날 따라잡았군. 훌륭해!",
|
||||
2: "다른 트레이너가 날 이길 거라곤 생각 못했는데… 놀랍군."
|
||||
},
|
||||
"defeat": {
|
||||
1: "That was close. Want to try again?",
|
||||
2: "It's not that you are weak. Don't let it bother you."
|
||||
1: "근소하군. 다시 해볼까?",
|
||||
2: "네가 약해서가 아니다. 신경쓰지 말도록."
|
||||
}
|
||||
},
|
||||
"karen": {
|
||||
"encounter": {
|
||||
1: "I am Karen. Would you care for a showdown with my Dark-type Pokémon?",
|
||||
2: "I am unlike those you've already met.",
|
||||
3: "You've assembled a charming team. Our battle should be a good one."
|
||||
1: "난 카렌! 내 악 타입 포켓몬과의 승부를 원하니?",
|
||||
2: "난 네가 이전에 만났던 트레이너들과는 달라.",
|
||||
3: "강한 포켓몬, 약한 포켓몬, 그런 건 사람이 멋대로 정하는 것."
|
||||
},
|
||||
"victory": {
|
||||
1: "No! I can't win. How did you become so strong?",
|
||||
2: "I will not stray from my chosen path.",
|
||||
3: "The Champion is looking forward to meeting you."
|
||||
1: "좋아하는 마음이 전해진다면 포켓몬도 답할거야. 그렇게 강해지는 거지",
|
||||
2: "난 내가 선택한 길을 걸어갈거야.",
|
||||
3: "챔피언이 너를 기다리고 있어."
|
||||
},
|
||||
"defeat": {
|
||||
1: "That's about what I expected.",
|
||||
2: "Well, that was relatively entertaining.",
|
||||
3: "Come visit me anytime."
|
||||
1: "정말 강한 트레이너라면 좋아하는 포켓몬으로 이길 수 있도록 열심히 해야 해.",
|
||||
2: "뭐, 비교적 재밌었어.",
|
||||
3: "언제라도 다시 찾아와, 상대해줄게."
|
||||
}
|
||||
},
|
||||
"milo": {
|
||||
@ -1507,13 +1507,13 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"blue": {
|
||||
"encounter": {
|
||||
1: "You must be pretty good to get this far."
|
||||
1: "여기까지 왔다니, 실력이 꽤 봐줄만 할 것 같은데."
|
||||
},
|
||||
"victory": {
|
||||
1: "I've only lost to him and now to you… Him? Hee, hee…"
|
||||
1: "그 녀석한테만 지는 줄 알았는데… 누구냐고? 하, 하…"
|
||||
},
|
||||
"defeat": {
|
||||
1: "See? My power is what got me here."
|
||||
1: "봤지? 여기까지 온 내 실력."
|
||||
}
|
||||
},
|
||||
"piers": {
|
||||
@ -1540,24 +1540,24 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"jasmine": {
|
||||
"encounter": {
|
||||
1: "Oh… Your Pokémon are impressive. I think I will enjoy this."
|
||||
1: "와… 당신의 포켓몬은 인상적이네요. 재미있을 것 같아요."
|
||||
},
|
||||
"victory": {
|
||||
1: "You are truly strong. I'll have to try much harder, too."
|
||||
1: "당신은 정말 강하네요. 저도 더 열심히 노력해야겠어요."
|
||||
},
|
||||
"defeat": {
|
||||
1: "I never expected to win."
|
||||
1: "이길 줄은 몰랐어요."
|
||||
}
|
||||
},
|
||||
"lance_champion": {
|
||||
"encounter": {
|
||||
1: "I am still the Champion. I won't hold anything back."
|
||||
1: "여전히 난 챔피언이다. 더이상 주저할 게 없군."
|
||||
},
|
||||
"victory": {
|
||||
1: "This is the emergence of a new Champion."
|
||||
1: "새로운 챔피언의 등장이군."
|
||||
},
|
||||
"defeat": {
|
||||
1: "I successfully defended my Championship."
|
||||
1: "성공적으로 챔피언 자리를 지켜냈다."
|
||||
}
|
||||
},
|
||||
"steven": {
|
||||
@ -1652,24 +1652,24 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"whitney": {
|
||||
"encounter": {
|
||||
1: "Hey! Don't you think Pokémon are, like, super cute?"
|
||||
1: "있지! 포켓몬들 말이야, 정말 너무 귀엽지?"
|
||||
},
|
||||
"victory": {
|
||||
1: "Waaah! Waaah! You're so mean!"
|
||||
1: "흑! 으아앙! 너무해!"
|
||||
},
|
||||
"defeat": {
|
||||
1: "And that's that!"
|
||||
1: "이걸로 끝!"
|
||||
}
|
||||
},
|
||||
"chuck": {
|
||||
"encounter": {
|
||||
1: "Hah! You want to challenge me? Are you brave or just ignorant?"
|
||||
1: "하! 나에게 도전하겠다고? 용감한 거냐, 아니면 그냥 무모한 거냐?"
|
||||
},
|
||||
"victory": {
|
||||
1: "You're strong! Would you please make me your apprentice?"
|
||||
1: "자네 강하군! 나를 제자로 삼아주겠나?"
|
||||
},
|
||||
"defeat": {
|
||||
1: "There. Do you realize how much more powerful I am than you?"
|
||||
1: "자. 내가 자네보다 얼마나 더 강력한지 깨달았겠지?"
|
||||
}
|
||||
},
|
||||
"katy": {
|
||||
@ -1685,24 +1685,24 @@ export const PGMdialogue: DialogueTranslationEntries = {
|
||||
},
|
||||
"pryce": {
|
||||
"encounter": {
|
||||
1: "Youth alone does not ensure victory! Experience is what counts."
|
||||
1: "젊음만으로는 승리를 보장할 수 없다! 중요한 것은 경험이다."
|
||||
},
|
||||
"victory": {
|
||||
1: "Outstanding! That was perfect. Try not to forget what you feel now."
|
||||
1: "특출하군! 완벽해. 지금 이 느낌을 잊지 말도록."
|
||||
},
|
||||
"defeat": {
|
||||
1: "Just as I envisioned."
|
||||
1: "내가 예상했던 그대로군."
|
||||
}
|
||||
},
|
||||
"clair": {
|
||||
"encounter": {
|
||||
1: "Do you know who I am? And you still dare to challenge me?"
|
||||
1: "내가 누군지 알지? 그런데도 감히 내게 도전해?"
|
||||
},
|
||||
"victory": {
|
||||
1: "I wonder how far you can get with your skill level. This should be fascinating."
|
||||
1: "네 실력이 어디까지 올라갈 수 있는지 궁금하네. 아주 흥미진진하겠어."
|
||||
},
|
||||
"defeat": {
|
||||
1: "That's that."
|
||||
1: "끝이다."
|
||||
}
|
||||
},
|
||||
"maylene": {
|
||||
|
@ -25,6 +25,7 @@ import i18next from "#app/plugins/i18n";
|
||||
import { getModifierTierTextTint } from "#app/ui/text";
|
||||
import { BattlerTagType } from "#app/data/enums/battler-tag-type.js";
|
||||
import * as Overrides from "../overrides";
|
||||
import { MoneyMultiplierModifier } from "./modifier";
|
||||
|
||||
const outputModifierData = false;
|
||||
const useMaxWeightForOutput = false;
|
||||
@ -631,9 +632,13 @@ export class MoneyRewardModifierType extends ModifierType {
|
||||
}
|
||||
|
||||
getDescription(scene: BattleScene): string {
|
||||
const moneyAmount = new Utils.IntegerHolder(scene.getWaveMoneyAmount(this.moneyMultiplier));
|
||||
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
|
||||
const formattedMoney = Utils.formatMoney(scene.moneyFormat, moneyAmount.value);
|
||||
|
||||
return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", {
|
||||
moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any),
|
||||
moneyAmount: scene.getWaveMoneyAmount(this.moneyMultiplier).toLocaleString("en-US"),
|
||||
moneyAmount: formattedMoney,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import { modifierTypes } from "./modifier/modifier-type";
|
||||
export const SEED_OVERRIDE: string = "";
|
||||
export const WEATHER_OVERRIDE: WeatherType = WeatherType.NONE;
|
||||
export const DOUBLE_BATTLE_OVERRIDE: boolean = false;
|
||||
export const SINGLE_BATTLE_OVERRIDE: boolean = false;
|
||||
export const STARTING_WAVE_OVERRIDE: integer = 0;
|
||||
export const STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN;
|
||||
export const ARENA_TINT_OVERRIDE: TimeOfDay = null;
|
||||
@ -110,6 +111,7 @@ export const OPP_MODIFIER_OVERRIDE: Array<ModifierOverride> = [];
|
||||
|
||||
export const STARTING_HELD_ITEMS_OVERRIDE: Array<ModifierOverride> = [];
|
||||
export const OPP_HELD_ITEMS_OVERRIDE: Array<ModifierOverride> = [];
|
||||
export const NEVER_CRIT_OVERRIDE: boolean = false;
|
||||
|
||||
/**
|
||||
* An array of items by keys as defined in the "modifierTypes" object in the "modifier/modifier-type.ts" file.
|
||||
|
162
src/phases.ts
162
src/phases.ts
@ -149,7 +149,7 @@ export class LoginPhase extends Phase {
|
||||
export class TitlePhase extends Phase {
|
||||
private loaded: boolean;
|
||||
private lastSessionData: SessionSaveData;
|
||||
private gameMode: GameModes;
|
||||
public gameMode: GameModes;
|
||||
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene);
|
||||
@ -527,60 +527,63 @@ export class SelectStarterPhase extends Phase {
|
||||
return this.end();
|
||||
}
|
||||
this.scene.sessionSlotId = slotId;
|
||||
|
||||
const party = this.scene.getParty();
|
||||
const loadPokemonAssets: Promise<void>[] = [];
|
||||
starters.forEach((starter: Starter, i: integer) => {
|
||||
if (!i && Overrides.STARTER_SPECIES_OVERRIDE) {
|
||||
starter.species = getPokemonSpecies(Overrides.STARTER_SPECIES_OVERRIDE as Species);
|
||||
}
|
||||
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
||||
let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
|
||||
if (!i && Overrides.STARTER_SPECIES_OVERRIDE) {
|
||||
starterFormIndex = Overrides.STARTER_FORM_OVERRIDE;
|
||||
}
|
||||
let starterGender = starter.species.malePercent !== null
|
||||
? !starterProps.female ? Gender.MALE : Gender.FEMALE
|
||||
: Gender.GENDERLESS;
|
||||
if (Overrides.GENDER_OVERRIDE !== null) {
|
||||
starterGender = Overrides.GENDER_OVERRIDE;
|
||||
}
|
||||
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
|
||||
const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, starterIvs, starter.nature);
|
||||
starterPokemon.tryPopulateMoveset(starter.moveset);
|
||||
if (starter.passive) {
|
||||
starterPokemon.passive = true;
|
||||
}
|
||||
starterPokemon.luck = this.scene.gameData.getDexAttrLuck(this.scene.gameData.dexData[starter.species.speciesId].caughtAttr);
|
||||
if (starter.pokerus) {
|
||||
starterPokemon.pokerus = true;
|
||||
}
|
||||
if (this.scene.gameMode.isSplicedOnly) {
|
||||
starterPokemon.generateFusionSpecies(true);
|
||||
}
|
||||
starterPokemon.setVisible(false);
|
||||
party.push(starterPokemon);
|
||||
loadPokemonAssets.push(starterPokemon.loadAssets());
|
||||
});
|
||||
overrideModifiers(this.scene);
|
||||
overrideHeldItems(this.scene, party[0]);
|
||||
Promise.all(loadPokemonAssets).then(() => {
|
||||
SoundFade.fadeOut(this.scene, this.scene.sound.get("menu"), 500, true);
|
||||
this.scene.time.delayedCall(500, () => this.scene.playBgm());
|
||||
if (this.scene.gameMode.isClassic) {
|
||||
this.scene.gameData.gameStats.classicSessionsPlayed++;
|
||||
} else {
|
||||
this.scene.gameData.gameStats.endlessSessionsPlayed++;
|
||||
}
|
||||
this.scene.newBattle();
|
||||
this.scene.arena.init();
|
||||
this.scene.sessionPlayTime = 0;
|
||||
this.scene.lastSavePlayTime = 0;
|
||||
this.end();
|
||||
});
|
||||
this.initBattle(starters);
|
||||
});
|
||||
}, this.gameMode);
|
||||
}
|
||||
|
||||
initBattle(starters: Starter[]) {
|
||||
const party = this.scene.getParty();
|
||||
const loadPokemonAssets: Promise<void>[] = [];
|
||||
starters.forEach((starter: Starter, i: integer) => {
|
||||
if (!i && Overrides.STARTER_SPECIES_OVERRIDE) {
|
||||
starter.species = getPokemonSpecies(Overrides.STARTER_SPECIES_OVERRIDE as Species);
|
||||
}
|
||||
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
||||
let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
|
||||
if (!i && Overrides.STARTER_SPECIES_OVERRIDE) {
|
||||
starterFormIndex = Overrides.STARTER_FORM_OVERRIDE;
|
||||
}
|
||||
let starterGender = starter.species.malePercent !== null
|
||||
? !starterProps.female ? Gender.MALE : Gender.FEMALE
|
||||
: Gender.GENDERLESS;
|
||||
if (Overrides.GENDER_OVERRIDE !== null) {
|
||||
starterGender = Overrides.GENDER_OVERRIDE;
|
||||
}
|
||||
const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0);
|
||||
const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, starterIvs, starter.nature);
|
||||
starterPokemon.tryPopulateMoveset(starter.moveset);
|
||||
if (starter.passive) {
|
||||
starterPokemon.passive = true;
|
||||
}
|
||||
starterPokemon.luck = this.scene.gameData.getDexAttrLuck(this.scene.gameData.dexData[starter.species.speciesId].caughtAttr);
|
||||
if (starter.pokerus) {
|
||||
starterPokemon.pokerus = true;
|
||||
}
|
||||
if (this.scene.gameMode.isSplicedOnly) {
|
||||
starterPokemon.generateFusionSpecies(true);
|
||||
}
|
||||
starterPokemon.setVisible(false);
|
||||
party.push(starterPokemon);
|
||||
loadPokemonAssets.push(starterPokemon.loadAssets());
|
||||
});
|
||||
overrideModifiers(this.scene);
|
||||
overrideHeldItems(this.scene, party[0]);
|
||||
Promise.all(loadPokemonAssets).then(() => {
|
||||
SoundFade.fadeOut(this.scene, this.scene.sound.get("menu"), 500, true);
|
||||
this.scene.time.delayedCall(500, () => this.scene.playBgm());
|
||||
if (this.scene.gameMode.isClassic) {
|
||||
this.scene.gameData.gameStats.classicSessionsPlayed++;
|
||||
} else {
|
||||
this.scene.gameData.gameStats.endlessSessionsPlayed++;
|
||||
}
|
||||
this.scene.newBattle();
|
||||
this.scene.arena.init();
|
||||
this.scene.sessionPlayTime = 0;
|
||||
this.scene.lastSavePlayTime = 0;
|
||||
this.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class BattlePhase extends Phase {
|
||||
@ -2710,9 +2713,10 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
const overridden = new Utils.BooleanHolder(false);
|
||||
const move = this.move.getMove();
|
||||
|
||||
// Assume single target for override
|
||||
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget(), this.move.getMove(), overridden, this.move.virtual).then(() => {
|
||||
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget(), move, overridden, this.move.virtual).then(() => {
|
||||
|
||||
if (overridden.value) {
|
||||
return this.end();
|
||||
@ -2723,8 +2727,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
if (user.turnData.hitsLeft === undefined) {
|
||||
const hitCount = new Utils.IntegerHolder(1);
|
||||
// Assume single target for multi hit
|
||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount);
|
||||
if (this.move.getMove() instanceof AttackMove && !this.move.getMove().hasAttr(FixedDamageAttr)) {
|
||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget(), move, hitCount);
|
||||
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
||||
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
||||
}
|
||||
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
||||
@ -2735,13 +2739,13 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
|
||||
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ]));
|
||||
const activeTargets = targets.map(t => t.isActive(true));
|
||||
if (!activeTargets.length || (!this.move.getMove().hasAttr(VariableTargetAttr) && !this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) {
|
||||
if (!activeTargets.length || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) {
|
||||
user.turnData.hitCount = 1;
|
||||
user.turnData.hitsLeft = 1;
|
||||
if (activeTargets.length) {
|
||||
this.scene.queueMessage(getPokemonMessage(user, "'s\nattack missed!"));
|
||||
moveHistoryEntry.result = MoveResult.MISS;
|
||||
applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove());
|
||||
applyMoveAttrs(MissEffectAttr, user, null, move);
|
||||
} else {
|
||||
this.scene.queueMessage(i18next.t("battle:attackFailed"));
|
||||
moveHistoryEntry.result = MoveResult.FAIL;
|
||||
@ -2752,7 +2756,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
const applyAttrs: Promise<void>[] = [];
|
||||
|
||||
// Move animation only needs one target
|
||||
new MoveAnim(this.move.getMove().id as Moves, user, this.getTarget()?.getBattlerIndex()).play(this.scene, () => {
|
||||
new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()).play(this.scene, () => {
|
||||
for (const target of targets) {
|
||||
if (!targetHitChecks[target.getBattlerIndex()]) {
|
||||
user.turnData.hitCount = 1;
|
||||
@ -2761,31 +2765,31 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
if (moveHistoryEntry.result === MoveResult.PENDING) {
|
||||
moveHistoryEntry.result = MoveResult.MISS;
|
||||
}
|
||||
applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove());
|
||||
applyMoveAttrs(MissEffectAttr, user, null, move);
|
||||
continue;
|
||||
}
|
||||
|
||||
const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType));
|
||||
const isProtected = !move.hasFlag(MoveFlags.IGNORE_PROTECT) && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType));
|
||||
|
||||
const firstHit = moveHistoryEntry.result !== MoveResult.SUCCESS;
|
||||
|
||||
moveHistoryEntry.result = MoveResult.SUCCESS;
|
||||
|
||||
const hitResult = !isProtected ? target.apply(user, this.move) : HitResult.NO_EFFECT;
|
||||
const hitResult = !isProtected ? target.apply(user, move) : HitResult.NO_EFFECT;
|
||||
|
||||
this.scene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
||||
|
||||
applyAttrs.push(new Promise(resolve => {
|
||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit),
|
||||
user, target, this.move.getMove()).then(() => {
|
||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit),
|
||||
user, target, move).then(() => {
|
||||
if (hitResult !== HitResult.FAIL) {
|
||||
const chargeEffect = !!this.move.getMove().getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget(), this.move.getMove()));
|
||||
const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget(), move));
|
||||
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
||||
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
||||
&& (attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove())).then(() => {
|
||||
Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||
&& attr.selfTarget && (!attr.firstHitOnly || firstHit), user, target, move)).then(() => {
|
||||
if (hitResult !== HitResult.NO_EFFECT) {
|
||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
||||
&& !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit), user, target, this.move.getMove()).then(() => {
|
||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||
&& !attr.selfTarget && (!attr.firstHitOnly || firstHit), user, target, move).then(() => {
|
||||
if (hitResult < HitResult.NO_EFFECT) {
|
||||
const flinched = new Utils.BooleanHolder(false);
|
||||
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
||||
@ -2793,15 +2797,15 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
||||
}
|
||||
}
|
||||
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit),
|
||||
user, target, this.move.getMove()).then(() => {
|
||||
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult).then(() => {
|
||||
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
||||
Utils.executeIf(!isProtected && !chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.HIT && (!attr.firstHitOnly || firstHit),
|
||||
user, target, move).then(() => {
|
||||
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, move, hitResult).then(() => {
|
||||
if (!user.isPlayer() && move instanceof AttackMove) {
|
||||
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
|
||||
}
|
||||
})).then(() => {
|
||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move, hitResult).then(() => {
|
||||
if (this.move.getMove() instanceof AttackMove) {
|
||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, move, hitResult).then(() => {
|
||||
if (move instanceof AttackMove) {
|
||||
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex());
|
||||
}
|
||||
resolve();
|
||||
@ -2811,7 +2815,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
).then(() => resolve());
|
||||
});
|
||||
} else {
|
||||
applyMoveAttrs(NoEffectAttr, user, null, this.move.getMove()).then(() => resolve());
|
||||
applyMoveAttrs(NoEffectAttr, user, null, move).then(() => resolve());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -2821,8 +2825,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
}));
|
||||
}
|
||||
// Trigger effect which should only apply one time after all targeted effects have already applied
|
||||
const postTarget = applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_TARGET,
|
||||
user, null, this.move.getMove());
|
||||
const postTarget = applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET,
|
||||
user, null, move);
|
||||
|
||||
if (applyAttrs.length) { // If there is a pending asynchronous move effect, do this after
|
||||
applyAttrs[applyAttrs.length - 1]?.then(() => postTarget);
|
||||
@ -2836,6 +2840,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||
}
|
||||
|
||||
end() {
|
||||
const move = this.move.getMove();
|
||||
move.type = move.defaultType;
|
||||
const user = this.getUserPokemon();
|
||||
if (user) {
|
||||
if (--user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
|
||||
@ -3542,7 +3548,7 @@ export class FaintPhase extends PokemonPhase {
|
||||
|
||||
if (pokemon.turnData?.attacksReceived?.length) {
|
||||
const lastAttack = pokemon.turnData.attacksReceived[0];
|
||||
applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId), new PokemonMove(lastAttack.move), lastAttack.result);
|
||||
applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId), new PokemonMove(lastAttack.move).getMove(), lastAttack.result);
|
||||
}
|
||||
|
||||
const alivePlayField = this.scene.getField(true);
|
||||
|
@ -40,6 +40,10 @@ export class Achv {
|
||||
return i18next.t(`achv:${this.localizationKey}.name`);
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
getIconImage(): string {
|
||||
return this.iconImage;
|
||||
}
|
||||
@ -259,14 +263,12 @@ export const achvs = {
|
||||
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY","", "CLASSIC_VICTORY.description", "relic_crown", 150),
|
||||
};
|
||||
|
||||
{
|
||||
(function() {
|
||||
const achvKeys = Object.keys(achvs);
|
||||
achvKeys.forEach((a: string, i: integer) => {
|
||||
achvs[a].id = a;
|
||||
if (achvs[a].hasParent) {
|
||||
achvs[a].parentId = achvKeys[i - 1];
|
||||
}
|
||||
});
|
||||
})();
|
||||
export function initAchievements() {
|
||||
const achvKeys = Object.keys(achvs);
|
||||
achvKeys.forEach((a: string, i: integer) => {
|
||||
achvs[a].id = a;
|
||||
if (achvs[a].hasParent) {
|
||||
achvs[a].parentId = achvKeys[i - 1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -60,13 +60,13 @@ export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): str
|
||||
}
|
||||
}
|
||||
|
||||
function encrypt(data: string, bypassLogin: boolean): string {
|
||||
export function encrypt(data: string, bypassLogin: boolean): string {
|
||||
return (bypassLogin
|
||||
? (data: string) => btoa(data)
|
||||
: (data: string) => AES.encrypt(data, saveKey))(data);
|
||||
}
|
||||
|
||||
function decrypt(data: string, bypassLogin: boolean): string {
|
||||
export function decrypt(data: string, bypassLogin: boolean): string {
|
||||
return (bypassLogin
|
||||
? (data: string) => atob(data)
|
||||
: (data: string) => AES.decrypt(data, saveKey).toString(enc.Utf8))(data);
|
||||
@ -493,7 +493,7 @@ export class GameData {
|
||||
});
|
||||
}
|
||||
|
||||
private parseSystemData(dataStr: string): SystemSaveData {
|
||||
parseSystemData(dataStr: string): SystemSaveData {
|
||||
return JSON.parse(dataStr, (k: string, v: any) => {
|
||||
if (k === "gameStats") {
|
||||
return new GameStats(v);
|
||||
@ -512,7 +512,7 @@ export class GameData {
|
||||
}) as SystemSaveData;
|
||||
}
|
||||
|
||||
private convertSystemDataStr(dataStr: string, shorten: boolean = false): string {
|
||||
convertSystemDataStr(dataStr: string, shorten: boolean = false): string {
|
||||
if (!shorten) {
|
||||
// Account for past key oversight
|
||||
dataStr = dataStr.replace(/\$pAttr/g, "$pa");
|
||||
|
@ -66,7 +66,8 @@ export const SettingKeys = {
|
||||
Player_Gender: "PLAYER_GENDER",
|
||||
Master_Volume: "MASTER_VOLUME",
|
||||
BGM_Volume: "BGM_VOLUME",
|
||||
SE_Volume: "SE_VOLUME"
|
||||
SE_Volume: "SE_VOLUME",
|
||||
Music_Preference: "MUSIC_PREFERENCE"
|
||||
};
|
||||
|
||||
/**
|
||||
@ -287,6 +288,14 @@ export const Setting: Array<Setting> = [
|
||||
options: VOLUME_OPTIONS,
|
||||
default: 10,
|
||||
type: SettingType.AUDIO
|
||||
},
|
||||
{
|
||||
key: SettingKeys.Music_Preference,
|
||||
label: "Music Preference",
|
||||
options: ["Consistent", "Mixed"],
|
||||
default: 0,
|
||||
type: SettingType.AUDIO,
|
||||
requireReload: true
|
||||
}
|
||||
];
|
||||
|
||||
@ -335,6 +344,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
|
||||
scene.seVolume = value ? parseInt(Setting[index].options[value]) * 0.01 : 0;
|
||||
scene.updateSoundVolume();
|
||||
break;
|
||||
case SettingKeys.Music_Preference:
|
||||
scene.musicPreference = value;
|
||||
break;
|
||||
case SettingKeys.Damage_Numbers:
|
||||
scene.damageNumbersMode = value;
|
||||
break;
|
||||
|
@ -83,42 +83,40 @@ export const vouchers: Vouchers = {};
|
||||
|
||||
const voucherAchvs: Achv[] = [ achvs.CLASSIC_VICTORY ];
|
||||
|
||||
{
|
||||
(function() {
|
||||
import("../data/trainer-config").then(tc => {
|
||||
const trainerConfigs = tc.trainerConfigs;
|
||||
export function initVouchers() {
|
||||
import("../data/trainer-config").then(tc => {
|
||||
const trainerConfigs = tc.trainerConfigs;
|
||||
|
||||
for (const achv of voucherAchvs) {
|
||||
const voucherType = achv.score >= 150
|
||||
? VoucherType.GOLDEN
|
||||
: achv.score >= 100
|
||||
? VoucherType.PREMIUM
|
||||
: achv.score >= 75
|
||||
? VoucherType.PLUS
|
||||
: VoucherType.REGULAR;
|
||||
vouchers[achv.id] = new Voucher(voucherType, getAchievementDescription(achv.localizationKey));
|
||||
}
|
||||
for (const achv of voucherAchvs) {
|
||||
const voucherType = achv.score >= 150
|
||||
? VoucherType.GOLDEN
|
||||
: achv.score >= 100
|
||||
? VoucherType.PREMIUM
|
||||
: achv.score >= 75
|
||||
? VoucherType.PLUS
|
||||
: VoucherType.REGULAR;
|
||||
vouchers[achv.id] = new Voucher(voucherType, getAchievementDescription(achv.localizationKey));
|
||||
}
|
||||
|
||||
const bossTrainerTypes = Object.keys(trainerConfigs)
|
||||
.filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL);
|
||||
const bossTrainerTypes = Object.keys(trainerConfigs)
|
||||
.filter(tt => trainerConfigs[tt].isBoss && trainerConfigs[tt].getDerivedType() !== TrainerType.RIVAL);
|
||||
|
||||
for (const trainerType of bossTrainerTypes) {
|
||||
const voucherType = trainerConfigs[trainerType].moneyMultiplier < 10
|
||||
? VoucherType.PLUS
|
||||
: VoucherType.PREMIUM;
|
||||
const key = TrainerType[trainerType];
|
||||
const trainerName = trainerConfigs[trainerType].name;
|
||||
const trainer = trainerConfigs[trainerType];
|
||||
const title = trainer.title ? ` (${trainer.title})` : "";
|
||||
vouchers[key] = new Voucher(
|
||||
voucherType,
|
||||
`${i18next.t("voucher:defeatTrainer", { trainerName })} ${title}`,
|
||||
);
|
||||
}
|
||||
const voucherKeys = Object.keys(vouchers);
|
||||
for (const k of voucherKeys) {
|
||||
vouchers[k].id = k;
|
||||
}
|
||||
});
|
||||
})();
|
||||
for (const trainerType of bossTrainerTypes) {
|
||||
const voucherType = trainerConfigs[trainerType].moneyMultiplier < 10
|
||||
? VoucherType.PLUS
|
||||
: VoucherType.PREMIUM;
|
||||
const key = TrainerType[trainerType];
|
||||
const trainerName = trainerConfigs[trainerType].name;
|
||||
const trainer = trainerConfigs[trainerType];
|
||||
const title = trainer.title ? ` (${trainer.title})` : "";
|
||||
vouchers[key] = new Voucher(
|
||||
voucherType,
|
||||
`${i18next.t("voucher:defeatTrainer", { trainerName })} ${title}`,
|
||||
);
|
||||
}
|
||||
const voucherKeys = Object.keys(vouchers);
|
||||
for (const k of voucherKeys) {
|
||||
vouchers[k].id = k;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
83
src/test/abilities/intimidate.test.ts
Normal file
83
src/test/abilities/intimidate.test.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CheckSwitchPhase, CommandPhase, MessagePhase,
|
||||
PostSummonPhase,
|
||||
ShinySparklePhase,
|
||||
ShowAbilityPhase,
|
||||
StatChangePhase,
|
||||
SummonPhase,
|
||||
ToggleDoublePositionPhase, TurnInitPhase
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {BattleStat} from "#app/data/battle-stat";
|
||||
|
||||
|
||||
describe("Abilities - Intimidate", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MIGHTYENA);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INTIMIDATE);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INTIMIDATE);
|
||||
});
|
||||
|
||||
it("INTIMIDATE", async() => {
|
||||
await game.runToSummon([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
await game.phaseInterceptor.run(PostSummonPhase);
|
||||
|
||||
|
||||
expect(game.scene.getParty()[0].summonData).not.toBeUndefined();
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(0);
|
||||
await game.phaseInterceptor.run(ShowAbilityPhase);
|
||||
await game.phaseInterceptor.run(StatChangePhase);
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
|
||||
await game.phaseInterceptor.run(SummonPhase);
|
||||
await game.phaseInterceptor.run(ShinySparklePhase, () => game.isCurrentPhase(ToggleDoublePositionPhase));
|
||||
await game.phaseInterceptor.run(ToggleDoublePositionPhase);
|
||||
game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
});
|
||||
await game.phaseInterceptor.run(CheckSwitchPhase);
|
||||
await game.phaseInterceptor.run(PostSummonPhase);
|
||||
|
||||
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(0);
|
||||
await game.phaseInterceptor.run(ShowAbilityPhase);
|
||||
game.scene.moveAnimations = null; // Mandatory to avoid crash
|
||||
await game.phaseInterceptor.run(StatChangePhase);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(-1);
|
||||
|
||||
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
await game.phaseInterceptor.run(TurnInitPhase);
|
||||
await game.phaseInterceptor.run(CommandPhase);
|
||||
}, 20000);
|
||||
});
|
65
src/test/abilities/intrepid_sword.test.ts
Normal file
65
src/test/abilities/intrepid_sword.test.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
MessagePhase,
|
||||
PostSummonPhase,
|
||||
ShowAbilityPhase,
|
||||
StatChangePhase,
|
||||
ToggleDoublePositionPhase
|
||||
} from "#app/phases";
|
||||
import {BattleStat} from "#app/data/battle-stat";
|
||||
|
||||
|
||||
describe("Abilities - Intrepid Sword", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.ZACIAN);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INTREPID_SWORD);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INTREPID_SWORD);
|
||||
});
|
||||
|
||||
it("INTREPID SWORD on player", async() => {
|
||||
await game.runToSummon([
|
||||
Species.ZACIAN,
|
||||
]);
|
||||
await game.phaseInterceptor.runFrom(PostSummonPhase).to(PostSummonPhase);
|
||||
expect(game.scene.getParty()[0].summonData).not.toBeUndefined();
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(0);
|
||||
await game.phaseInterceptor.mustRun(ShowAbilityPhase).catch((error) => expect(error).toBe(ShowAbilityPhase));
|
||||
await game.phaseInterceptor.mustRun(StatChangePhase).catch((error) => expect(error).toBe(StatChangePhase));
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(1);
|
||||
}, 20000);
|
||||
|
||||
it("INTREPID SWORD on opponent", async() => {
|
||||
await game.runToSummon([
|
||||
Species.ZACIAN,
|
||||
]);
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(0);
|
||||
await game.phaseInterceptor.runFrom(PostSummonPhase).to(ToggleDoublePositionPhase);
|
||||
await game.phaseInterceptor.mustRun(StatChangePhase).catch((error) => expect(error).toBe(StatChangePhase));
|
||||
await game.phaseInterceptor.whenAboutToRun(MessagePhase);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.ATK]).toBe(1);
|
||||
}, 20000);
|
||||
});
|
67
src/test/abilities/moxie.test.ts
Normal file
67
src/test/abilities/moxie.test.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CommandPhase,
|
||||
EnemyCommandPhase,
|
||||
VictoryPhase
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {Stat} from "#app/data/pokemon-stat";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {BattleStat} from "#app/data/battle-stat";
|
||||
|
||||
|
||||
describe("Abilities - Moxie", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
const moveToUse = Moves.AERIAL_ACE;
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MOXIE);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MOXIE);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(2000);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([moveToUse]);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
|
||||
});
|
||||
|
||||
it("MOXIE", async() => {
|
||||
const moveToUse = Moves.AERIAL_ACE;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[Stat.ATK]).toBe(0);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase);
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.ATK]).toBe(1);
|
||||
}, 20000);
|
||||
});
|
@ -1,9 +0,0 @@
|
||||
import { MoneyAchv } from "#app/system/achv";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("check some Achievement related stuff", () => {
|
||||
it ("should check Achievement creation", () => {
|
||||
const ach = new MoneyAchv("", "Achievement", 1000, null, 100);
|
||||
expect(ach.name).toBe("Achievement");
|
||||
});
|
||||
});
|
274
src/test/achievements/achievement.test.ts
Normal file
274
src/test/achievements/achievement.test.ts
Normal file
@ -0,0 +1,274 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import {MoneyAchv, Achv, AchvTier, RibbonAchv, DamageAchv, HealAchv, LevelAchv, ModifierAchv, achvs} from "#app/system/achv";
|
||||
import BattleScene from "../../battle-scene";
|
||||
import { IntegerHolder, NumberHolder } from "#app/utils.js";
|
||||
import { TurnHeldItemTransferModifier } from "#app/modifier/modifier.js";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
|
||||
describe("check some Achievement related stuff", () => {
|
||||
it ("should check Achievement creation", () => {
|
||||
const ach = new MoneyAchv("", "Achievement", 1000, null, 100);
|
||||
expect(ach.name).toBe("Achievement");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("Achv", () => {
|
||||
let achv: Achv;
|
||||
|
||||
beforeEach(() => {
|
||||
achv = new Achv("", "Test Achievement", "This is a test achievement", "test_icon", 10);
|
||||
});
|
||||
|
||||
it("should have the correct name", () => {
|
||||
expect(achv.getDescription()).toBe("This is a test achievement");
|
||||
});
|
||||
|
||||
it("should have the correct icon image", () => {
|
||||
expect(achv.getIconImage()).toBe("test_icon");
|
||||
});
|
||||
|
||||
it("should set the achievement as secret", () => {
|
||||
achv.setSecret();
|
||||
expect(achv.secret).toBe(true);
|
||||
expect(achv.hasParent).toBe(false);
|
||||
|
||||
achv.setSecret(true);
|
||||
expect(achv.secret).toBe(true);
|
||||
expect(achv.hasParent).toBe(true);
|
||||
|
||||
achv.setSecret(false);
|
||||
expect(achv.secret).toBe(true);
|
||||
expect(achv.hasParent).toBe(false);
|
||||
});
|
||||
|
||||
it("should return the correct tier based on the score", () => {
|
||||
const achv1 = new Achv("", "Test Achievement 1", "Test Description", "test_icon", 10);
|
||||
const achv2 = new Achv("", "Test Achievement 2", "Test Description", "test_icon", 25);
|
||||
const achv3 = new Achv("", "Test Achievement 3", "Test Description", "test_icon", 50);
|
||||
const achv4 = new Achv("", "Test Achievement 4", "Test Description", "test_icon", 75);
|
||||
const achv5 = new Achv("", "Test Achievement 5", "Test Description", "test_icon", 100);
|
||||
|
||||
expect(achv1.getTier()).toBe(AchvTier.COMMON);
|
||||
expect(achv2.getTier()).toBe(AchvTier.GREAT);
|
||||
expect(achv3.getTier()).toBe(AchvTier.ULTRA);
|
||||
expect(achv4.getTier()).toBe(AchvTier.ROGUE);
|
||||
expect(achv5.getTier()).toBe(AchvTier.MASTER);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the condition function", () => {
|
||||
const conditionFunc = jest.fn((scene: BattleScene, args: any[]) => args[0] === 10);
|
||||
const achv = new Achv("", "Test Achievement", "Test Description", "test_icon", 10, conditionFunc);
|
||||
|
||||
expect(achv.validate(new BattleScene(), [5])).toBe(false);
|
||||
expect(achv.validate(new BattleScene(), [10])).toBe(true);
|
||||
expect(conditionFunc).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("MoneyAchv", () => {
|
||||
it("should create an instance of MoneyAchv", () => {
|
||||
const moneyAchv = new MoneyAchv("", "Test Money Achievement", 10000, "money_icon", 10);
|
||||
expect(moneyAchv).toBeInstanceOf(MoneyAchv);
|
||||
expect(moneyAchv instanceof Achv).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the money amount", () => {
|
||||
const moneyAchv = new MoneyAchv("", "Test Money Achievement", 10000, "money_icon", 10);
|
||||
const scene = new BattleScene();
|
||||
scene.money = 5000;
|
||||
|
||||
expect(moneyAchv.validate(scene, [])).toBe(false);
|
||||
|
||||
scene.money = 15000;
|
||||
expect(moneyAchv.validate(scene, [])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RibbonAchv", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let scene: BattleScene;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(0);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(0);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(0);
|
||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(0);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([]);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]);
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
});
|
||||
|
||||
it("should create an instance of RibbonAchv", () => {
|
||||
const ribbonAchv = new RibbonAchv("", "Test Ribbon Achievement", 10, "ribbon_icon", 10);
|
||||
expect(ribbonAchv).toBeInstanceOf(RibbonAchv);
|
||||
expect(ribbonAchv instanceof Achv).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the ribbon amount", () => {
|
||||
const ribbonAchv = new RibbonAchv("", "Test Ribbon Achievement", 10, "ribbon_icon", 10);
|
||||
scene.gameData.gameStats.ribbonsOwned = 5;
|
||||
|
||||
expect(ribbonAchv.validate(scene, [])).toBe(false);
|
||||
|
||||
scene.gameData.gameStats.ribbonsOwned = 15;
|
||||
expect(ribbonAchv.validate(scene, [])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DamageAchv", () => {
|
||||
it("should create an instance of DamageAchv", () => {
|
||||
const damageAchv = new DamageAchv("", "Test Damage Achievement", 250, "damage_icon", 10);
|
||||
expect(damageAchv).toBeInstanceOf(DamageAchv);
|
||||
expect(damageAchv instanceof Achv).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the damage amount", () => {
|
||||
const damageAchv = new DamageAchv("", "Test Damage Achievement", 250, "damage_icon", 10);
|
||||
const scene = new BattleScene();
|
||||
const numberHolder = new NumberHolder(200);
|
||||
|
||||
expect(damageAchv.validate(scene, [numberHolder])).toBe(false);
|
||||
|
||||
numberHolder.value = 300;
|
||||
expect(damageAchv.validate(scene, [numberHolder])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("HealAchv", () => {
|
||||
it("should create an instance of HealAchv", () => {
|
||||
const healAchv = new HealAchv("", "Test Heal Achievement", 250, "heal_icon", 10);
|
||||
expect(healAchv).toBeInstanceOf(HealAchv);
|
||||
expect(healAchv instanceof Achv).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the heal amount", () => {
|
||||
const healAchv = new HealAchv("", "Test Heal Achievement", 250, "heal_icon", 10);
|
||||
const scene = new BattleScene();
|
||||
const numberHolder = new NumberHolder(200);
|
||||
|
||||
expect(healAchv.validate(scene, [numberHolder])).toBe(false);
|
||||
|
||||
numberHolder.value = 300;
|
||||
expect(healAchv.validate(scene, [numberHolder])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("LevelAchv", () => {
|
||||
it("should create an instance of LevelAchv", () => {
|
||||
const levelAchv = new LevelAchv("", "Test Level Achievement", 100, "level_icon", 10);
|
||||
expect(levelAchv).toBeInstanceOf(LevelAchv);
|
||||
expect(levelAchv instanceof Achv).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the level", () => {
|
||||
const levelAchv = new LevelAchv("", "Test Level Achievement", 100, "level_icon", 10);
|
||||
const scene = new BattleScene();
|
||||
const integerHolder = new IntegerHolder(50);
|
||||
|
||||
expect(levelAchv.validate(scene, [integerHolder])).toBe(false);
|
||||
|
||||
integerHolder.value = 150;
|
||||
expect(levelAchv.validate(scene, [integerHolder])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ModifierAchv", () => {
|
||||
it("should create an instance of ModifierAchv", () => {
|
||||
const modifierAchv = new ModifierAchv("", "Test Modifier Achievement", "Test Description", "modifier_icon", 10, () => true);
|
||||
expect(modifierAchv).toBeInstanceOf(ModifierAchv);
|
||||
expect(modifierAchv instanceof Achv).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate the achievement based on the modifier function", () => {
|
||||
const modifierAchv = new ModifierAchv("", "Test Modifier Achievement", "Test Description", "modifier_icon", 10, () => true);
|
||||
const scene = new BattleScene();
|
||||
const modifier = new TurnHeldItemTransferModifier(null, 3, 1);
|
||||
|
||||
expect(modifierAchv.validate(scene, [modifier])).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("achvs", () => {
|
||||
it("should contain the predefined achievements", () => {
|
||||
expect(achvs._10K_MONEY).toBeInstanceOf(MoneyAchv);
|
||||
expect(achvs._100K_MONEY).toBeInstanceOf(MoneyAchv);
|
||||
expect(achvs._1M_MONEY).toBeInstanceOf(MoneyAchv);
|
||||
expect(achvs._10M_MONEY).toBeInstanceOf(MoneyAchv);
|
||||
expect(achvs._250_DMG).toBeInstanceOf(DamageAchv);
|
||||
expect(achvs._1000_DMG).toBeInstanceOf(DamageAchv);
|
||||
expect(achvs._2500_DMG).toBeInstanceOf(DamageAchv);
|
||||
expect(achvs._10000_DMG).toBeInstanceOf(DamageAchv);
|
||||
expect(achvs._250_HEAL).toBeInstanceOf(HealAchv);
|
||||
expect(achvs._1000_HEAL).toBeInstanceOf(HealAchv);
|
||||
expect(achvs._2500_HEAL).toBeInstanceOf(HealAchv);
|
||||
expect(achvs._10000_HEAL).toBeInstanceOf(HealAchv);
|
||||
expect(achvs.LV_100).toBeInstanceOf(LevelAchv);
|
||||
expect(achvs.LV_250).toBeInstanceOf(LevelAchv);
|
||||
expect(achvs.LV_1000).toBeInstanceOf(LevelAchv);
|
||||
expect(achvs._10_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs._25_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs._50_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs._75_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs._100_RIBBONS).toBeInstanceOf(RibbonAchv);
|
||||
expect(achvs.TRANSFER_MAX_BATTLE_STAT).toBeInstanceOf(Achv);
|
||||
expect(achvs.MAX_FRIENDSHIP).toBeInstanceOf(Achv);
|
||||
expect(achvs.MEGA_EVOLVE).toBeInstanceOf(Achv);
|
||||
expect(achvs.GIGANTAMAX).toBeInstanceOf(Achv);
|
||||
expect(achvs.TERASTALLIZE).toBeInstanceOf(Achv);
|
||||
expect(achvs.STELLAR_TERASTALLIZE).toBeInstanceOf(Achv);
|
||||
expect(achvs.SPLICE).toBeInstanceOf(Achv);
|
||||
expect(achvs.MINI_BLACK_HOLE).toBeInstanceOf(ModifierAchv);
|
||||
expect(achvs.CATCH_MYTHICAL).toBeInstanceOf(Achv);
|
||||
expect(achvs.CATCH_SUB_LEGENDARY).toBeInstanceOf(Achv);
|
||||
expect(achvs.CATCH_LEGENDARY).toBeInstanceOf(Achv);
|
||||
expect(achvs.SEE_SHINY).toBeInstanceOf(Achv);
|
||||
expect(achvs.SHINY_PARTY).toBeInstanceOf(Achv);
|
||||
expect(achvs.HATCH_MYTHICAL).toBeInstanceOf(Achv);
|
||||
expect(achvs.HATCH_SUB_LEGENDARY).toBeInstanceOf(Achv);
|
||||
expect(achvs.HATCH_LEGENDARY).toBeInstanceOf(Achv);
|
||||
expect(achvs.HATCH_SHINY).toBeInstanceOf(Achv);
|
||||
expect(achvs.HIDDEN_ABILITY).toBeInstanceOf(Achv);
|
||||
expect(achvs.PERFECT_IVS).toBeInstanceOf(Achv);
|
||||
expect(achvs.CLASSIC_VICTORY).toBeInstanceOf(Achv);
|
||||
});
|
||||
|
||||
it("should initialize the achievements with IDs and parent IDs", () => {
|
||||
|
||||
expect(achvs._10K_MONEY.id).toBe("_10K_MONEY");
|
||||
expect(achvs._10K_MONEY.hasParent).toBe(undefined);
|
||||
expect(achvs._100K_MONEY.id).toBe("_100K_MONEY");
|
||||
expect(achvs._100K_MONEY.hasParent).toBe(true);
|
||||
expect(achvs._100K_MONEY.parentId).toBe("_10K_MONEY");
|
||||
expect(achvs._1M_MONEY.id).toBe("_1M_MONEY");
|
||||
expect(achvs._1M_MONEY.hasParent).toBe(true);
|
||||
expect(achvs._1M_MONEY.parentId).toBe("_100K_MONEY");
|
||||
expect(achvs._10M_MONEY.id).toBe("_10M_MONEY");
|
||||
expect(achvs._10M_MONEY.hasParent).toBe(true);
|
||||
expect(achvs._10M_MONEY.parentId).toBe("_1M_MONEY");
|
||||
expect(achvs.LV_100.id).toBe("LV_100");
|
||||
expect(achvs.LV_100.hasParent).toBe(false);
|
||||
expect(achvs.LV_250.id).toBe("LV_250");
|
||||
expect(achvs.LV_250.hasParent).toBe(true);
|
||||
expect(achvs.LV_250.parentId).toBe("LV_100");
|
||||
expect(achvs.LV_1000.id).toBe("LV_1000");
|
||||
expect(achvs.LV_1000.hasParent).toBe(true);
|
||||
expect(achvs.LV_1000.parentId).toBe("LV_250");
|
||||
});
|
||||
});
|
220
src/test/battle/battle-order.test.ts
Normal file
220
src/test/battle/battle-order.test.ts
Normal file
@ -0,0 +1,220 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CommandPhase, EnemyCommandPhase,
|
||||
TurnStartPhase
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {Stat} from "#app/data/pokemon-stat";
|
||||
import TargetSelectUiHandler from "#app/ui/target-select-ui-handler";
|
||||
import {Button} from "#app/enums/buttons";
|
||||
|
||||
|
||||
describe("Battle order", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MEWTWO);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE]);
|
||||
});
|
||||
|
||||
it("opponent faster than player 50 vs 150", async() => {
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
]);
|
||||
game.scene.getParty()[0].stats[Stat.SPD] = 50;
|
||||
game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.whenAboutToRun(TurnStartPhase);
|
||||
const phase = game.scene.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getOrder();
|
||||
expect(order[0]).toBe(2);
|
||||
expect(order[1]).toBe(0);
|
||||
}, 20000);
|
||||
|
||||
it("Player faster than opponent 150 vs 50", async() => {
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
]);
|
||||
game.scene.getParty()[0].stats[Stat.SPD] = 150;
|
||||
game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 50;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.whenAboutToRun(TurnStartPhase);
|
||||
const phase = game.scene.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getOrder();
|
||||
expect(order[0]).toBe(0);
|
||||
expect(order[1]).toBe(2);
|
||||
}, 20000);
|
||||
|
||||
it("double - both opponents faster than player 50/50 vs 150/150", async() => {
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
Species.BLASTOISE,
|
||||
]);
|
||||
game.scene.getParty()[0].stats[Stat.SPD] = 50;
|
||||
game.scene.getParty()[1].stats[Stat.SPD] = 50;
|
||||
game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 150;
|
||||
game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as TargetSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as TargetSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(CommandPhase).to(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.whenAboutToRun(TurnStartPhase);
|
||||
const phase = game.scene.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getOrder();
|
||||
expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(2));
|
||||
expect(order.indexOf(0)).toBeGreaterThan(order.indexOf(3));
|
||||
expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(2));
|
||||
expect(order.indexOf(1)).toBeGreaterThan(order.indexOf(3));
|
||||
}, 20000);
|
||||
|
||||
it("double - speed tie except 1 - 100/100 vs 100/150", async() => {
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
Species.BLASTOISE,
|
||||
]);
|
||||
game.scene.getParty()[0].stats[Stat.SPD] = 100;
|
||||
game.scene.getParty()[1].stats[Stat.SPD] = 100;
|
||||
game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100;
|
||||
game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as TargetSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as TargetSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(CommandPhase).to(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.whenAboutToRun(TurnStartPhase);
|
||||
const phase = game.scene.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getOrder();
|
||||
expect(order.indexOf(3)).toBeLessThan(order.indexOf(0));
|
||||
expect(order.indexOf(3)).toBeLessThan(order.indexOf(1));
|
||||
expect(order.indexOf(3)).toBeLessThan(order.indexOf(2));
|
||||
}, 20000);
|
||||
|
||||
it("double - speed tie 100/150 vs 100/150", async() => {
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(false);
|
||||
vi.spyOn(overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
Species.BLASTOISE,
|
||||
]);
|
||||
game.scene.getParty()[0].stats[Stat.SPD] = 100;
|
||||
game.scene.getParty()[1].stats[Stat.SPD] = 150;
|
||||
game.scene.currentBattle.enemyParty[0].stats[Stat.SPD] = 100;
|
||||
game.scene.currentBattle.enemyParty[1].stats[Stat.SPD] = 150;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as TargetSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
game.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as TargetSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(CommandPhase).to(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.whenAboutToRun(TurnStartPhase);
|
||||
const phase = game.scene.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getOrder();
|
||||
expect(order.indexOf(1)).toBeLessThan(order.indexOf(0));
|
||||
expect(order.indexOf(1)).toBeLessThan(order.indexOf(2));
|
||||
expect(order.indexOf(3)).toBeLessThan(order.indexOf(0));
|
||||
expect(order.indexOf(3)).toBeLessThan(order.indexOf(2));
|
||||
}, 20000);
|
||||
});
|
248
src/test/battle/battle.test.ts
Normal file
248
src/test/battle/battle.test.ts
Normal file
@ -0,0 +1,248 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import {generateStarter, getMovePosition, waitUntil,} from "#app/test/utils/gameManagerUtils";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {GameModes} from "#app/game-mode";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import * as overrides from "../../overrides";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {
|
||||
BattleEndPhase,
|
||||
BerryPhase,
|
||||
CommandPhase,
|
||||
DamagePhase,
|
||||
EggLapsePhase,
|
||||
EncounterPhase,
|
||||
EnemyCommandPhase,
|
||||
FaintPhase,
|
||||
LoginPhase,
|
||||
MessagePhase,
|
||||
MoveEffectPhase,
|
||||
MoveEndPhase,
|
||||
MovePhase,
|
||||
PostSummonPhase,
|
||||
SelectGenderPhase,
|
||||
SelectModifierPhase,
|
||||
SelectStarterPhase,
|
||||
StatChangePhase,
|
||||
TitlePhase,
|
||||
TurnEndPhase,
|
||||
TurnInitPhase,
|
||||
TurnStartPhase,
|
||||
VictoryPhase,
|
||||
} from "#app/phases";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import {allSpecies} from "#app/data/pokemon-species";
|
||||
import {PlayerGender} from "#app/data/enums/player-gender";
|
||||
|
||||
describe("Test Battle Phase", () => {
|
||||
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("test phase interceptor with remove", async() => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
|
||||
await game.phaseInterceptor.run(LoginPhase, () => {
|
||||
return game.phaseInterceptor.log.includes("LoginPhase");
|
||||
});
|
||||
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
await game.phaseInterceptor.remove(SelectGenderPhase, () => game.isCurrentPhase(TitlePhase));
|
||||
|
||||
await game.phaseInterceptor.run(TitlePhase);
|
||||
await waitUntil(() => game.scene.ui?.getMode() === Mode.TITLE);
|
||||
|
||||
expect(game.scene.ui?.getMode()).toBe(Mode.TITLE);
|
||||
}, 100000);
|
||||
|
||||
it("test phase interceptor with prompt", async() => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
|
||||
game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.run(SelectGenderPhase);
|
||||
|
||||
await game.phaseInterceptor.run(TitlePhase);
|
||||
await game.waitMode(Mode.TITLE);
|
||||
|
||||
|
||||
expect(game.scene.ui?.getMode()).toBe(Mode.TITLE);
|
||||
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
|
||||
}, 100000);
|
||||
|
||||
it("test phase interceptor with prompt with preparation for a future prompt", async() => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
|
||||
game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
});
|
||||
|
||||
game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||
game.setMode(Mode.MESSAGE);
|
||||
game.endPhase();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectGenderPhase);
|
||||
|
||||
await game.phaseInterceptor.run(TitlePhase);
|
||||
await game.waitMode(Mode.TITLE);
|
||||
|
||||
|
||||
expect(game.scene.ui?.getMode()).toBe(Mode.TITLE);
|
||||
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
|
||||
}, 100000);
|
||||
|
||||
it("newGame one-liner", async() => {
|
||||
await game.startBattle();
|
||||
expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND);
|
||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||
}, 100000);
|
||||
|
||||
it("do attack wave 3 - single battle - regular - OHKO", async() => {
|
||||
vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MEWTWO);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(2000);
|
||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(3);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE]);
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
await game.startBattle();
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.run(TurnStartPhase);
|
||||
|
||||
await game.phaseInterceptor.run(MovePhase);
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
await game.phaseInterceptor.run(MoveEffectPhase);
|
||||
await game.phaseInterceptor.run(DamagePhase);
|
||||
await game.phaseInterceptor.run(MessagePhase, () => game.isCurrentPhase(FaintPhase));
|
||||
await game.phaseInterceptor.run(FaintPhase);
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
|
||||
await game.phaseInterceptor.run(VictoryPhase);
|
||||
await game.phaseInterceptor.run(MoveEndPhase);
|
||||
await game.phaseInterceptor.run(MovePhase);
|
||||
await game.phaseInterceptor.run(BerryPhase);
|
||||
await game.phaseInterceptor.run(TurnEndPhase);
|
||||
await game.phaseInterceptor.run(BattleEndPhase);
|
||||
await game.phaseInterceptor.run(EggLapsePhase);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
expect(game.scene.ui?.getMode()).toBe(Mode.MODIFIER_SELECT);
|
||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(SelectModifierPhase.name);
|
||||
}, 100000);
|
||||
|
||||
it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async() => {
|
||||
vi.spyOn(overrides, "STARTER_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MEWTWO);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(5);
|
||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(3);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE]);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TAIL_WHIP]);
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
await game.startBattle();
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, Moves.TACKLE);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.run(TurnStartPhase);
|
||||
|
||||
await game.phaseInterceptor.run(MovePhase);
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
await game.phaseInterceptor.run(MoveEffectPhase);
|
||||
await game.phaseInterceptor.run(DamagePhase);
|
||||
await game.phaseInterceptor.run(MessagePhase, () => game.isCurrentPhase(MoveEndPhase));
|
||||
await game.phaseInterceptor.run(MoveEndPhase);
|
||||
|
||||
await game.phaseInterceptor.run(MovePhase);
|
||||
await game.phaseInterceptor.run(MessagePhase, () => game.isCurrentPhase(MoveEffectPhase));
|
||||
await game.phaseInterceptor.run(MoveEffectPhase);
|
||||
game.scene.moveAnimations = null; // Mandatory to avoid the crash
|
||||
await game.phaseInterceptor.run(StatChangePhase, () => game.isCurrentPhase(MessagePhase) || game.isCurrentPhase(MoveEndPhase) || game.isCurrentPhase(DamagePhase));
|
||||
await game.phaseInterceptor.run(DamagePhase, () => game.isCurrentPhase(MessagePhase) || game.isCurrentPhase(MoveEndPhase));
|
||||
await game.phaseInterceptor.run(MessagePhase, () => game.isCurrentPhase(MoveEndPhase));
|
||||
await game.phaseInterceptor.run(MoveEndPhase);
|
||||
|
||||
await game.phaseInterceptor.run(BerryPhase);
|
||||
await game.phaseInterceptor.run(MessagePhase, () => game.isCurrentPhase(TurnEndPhase));
|
||||
await game.phaseInterceptor.run(TurnEndPhase);
|
||||
|
||||
await game.phaseInterceptor.run(TurnInitPhase);
|
||||
await game.phaseInterceptor.run(CommandPhase);
|
||||
await waitUntil(() => game.scene.ui?.getMode() === Mode.COMMAND);
|
||||
expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND);
|
||||
expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||
}, 100000);
|
||||
|
||||
it("load 100% data file", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
||||
const species = game.scene.gameData.dexData[key];
|
||||
return species.caughtAttr !== 0n;
|
||||
}).length;
|
||||
expect(caughtCount).toBe(Object.keys(allSpecies).length);
|
||||
}, 50000);
|
||||
|
||||
it("start battle with selected team", async() => {
|
||||
await game.startBattle([
|
||||
Species.CHARIZARD,
|
||||
Species.CHANSEY,
|
||||
Species.MEW
|
||||
]);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.CHARIZARD);
|
||||
expect(game.scene.getParty()[1].species.speciesId).toBe(Species.CHANSEY);
|
||||
expect(game.scene.getParty()[2].species.speciesId).toBe(Species.MEW);
|
||||
}, 50000);
|
||||
|
||||
it("assert next phase", async() => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
game.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
}, () => game.isCurrentPhase(TitlePhase));
|
||||
await game.phaseInterceptor.mustRun(SelectGenderPhase).catch((error) => expect(error).toBe(SelectGenderPhase));
|
||||
await game.phaseInterceptor.mustRun(TitlePhase).catch((error) => expect(error).toBe(TitlePhase));
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const starters = generateStarter(game.scene);
|
||||
const selectStarterPhase = new SelectStarterPhase(game.scene, GameModes.CLASSIC);
|
||||
game.scene.pushPhase(new EncounterPhase(game.scene, false));
|
||||
selectStarterPhase.initBattle(starters);
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(EncounterPhase).catch((error) => expect(error).toBe(EncounterPhase));
|
||||
await game.phaseInterceptor.mustRun(PostSummonPhase).catch((error) => expect(error).toBe(PostSummonPhase));
|
||||
}, 50000);
|
||||
|
||||
it("test remove random battle seed int", async() => {
|
||||
for (let i=0; i<10; i++) {
|
||||
const rand = game.scene.randBattleSeedInt(15);
|
||||
expect(rand).toBe(14);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
33
src/test/eggs/egg.test.ts
Normal file
33
src/test/eggs/egg.test.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {beforeAll, describe, expect, it} from "vitest";
|
||||
import BattleScene from "../../battle-scene";
|
||||
import { getLegendaryGachaSpeciesForTimestamp } from "#app/data/egg.js";
|
||||
import { Species } from "#app/data/enums/species.js";
|
||||
import Phaser from "phaser";
|
||||
|
||||
describe("getLegendaryGachaSpeciesForTimestamp", () => {
|
||||
|
||||
beforeAll(() => {
|
||||
new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return Arceus for the 10th of June", () => {
|
||||
const scene = new BattleScene();
|
||||
const timestamp = new Date(2024, 5, 10, 15, 0, 0, 0).getTime();
|
||||
const expectedSpecies = Species.ARCEUS;
|
||||
|
||||
const result = getLegendaryGachaSpeciesForTimestamp(scene, timestamp);
|
||||
|
||||
expect(result).toBe(expectedSpecies);
|
||||
});
|
||||
it("should return Arceus for the 10th of July", () => {
|
||||
const scene = new BattleScene();
|
||||
const timestamp = new Date(2024, 6, 10, 15, 0, 0, 0).getTime();
|
||||
const expectedSpecies = Species.ARCEUS;
|
||||
|
||||
const result = getLegendaryGachaSpeciesForTimestamp(scene, timestamp);
|
||||
|
||||
expect(result).toBe(expectedSpecies);
|
||||
});
|
||||
});
|
105
src/test/inputs/inputs.test.ts
Normal file
105
src/test/inputs/inputs.test.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import pad_xbox360 from "#app/configs/inputs/pad_xbox360";
|
||||
import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
|
||||
import InputsHandler from "#app/test/utils/inputsHandler";
|
||||
|
||||
|
||||
describe("Inputs", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let originalDocument: Document;
|
||||
|
||||
beforeAll(() => {
|
||||
originalDocument = window.document;
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
Object.defineProperty(window, "document", {
|
||||
value: originalDocument,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.inputsHandler = new InputsHandler(game.scene);
|
||||
});
|
||||
|
||||
it("Mobile - test touch holding for 1ms - 1 input", async () => {
|
||||
await game.inputsHandler.pressTouch("dpadUp", 1);
|
||||
expect(game.inputsHandler.log.length).toBe(1);
|
||||
});
|
||||
|
||||
it("Mobile - test touch holding for 200ms - 1 input", async () => {
|
||||
await game.inputsHandler.pressTouch("dpadUp", 200);
|
||||
expect(game.inputsHandler.log.length).toBe(1);
|
||||
});
|
||||
|
||||
it("Mobile - test touch holding for 300ms - 2 input", async () => {
|
||||
await game.inputsHandler.pressTouch("dpadUp", 300);
|
||||
expect(game.inputsHandler.log.length).toBe(2);
|
||||
});
|
||||
|
||||
it("Mobile - test touch holding for 1000ms - 4 input", async () => {
|
||||
await game.inputsHandler.pressTouch("dpadUp", 1000);
|
||||
expect(game.inputsHandler.log.length).toBe(4);
|
||||
});
|
||||
|
||||
it("keyboard - test input holding for 1ms - 1 input", async() => {
|
||||
await game.inputsHandler.pressKeyboardKey(cfg_keyboard_qwerty.deviceMapping.KEY_ARROW_UP, 1);
|
||||
expect(game.inputsHandler.log.length).toBe(1);
|
||||
});
|
||||
|
||||
it("keyboard - test input holding for 200ms - 1 input", async() => {
|
||||
await game.inputsHandler.pressKeyboardKey(cfg_keyboard_qwerty.deviceMapping.KEY_ARROW_UP, 200);
|
||||
expect(game.inputsHandler.log.length).toBe(1);
|
||||
});
|
||||
|
||||
it("keyboard - test input holding for 300ms - 2 input", async() => {
|
||||
await game.inputsHandler.pressKeyboardKey(cfg_keyboard_qwerty.deviceMapping.KEY_ARROW_UP, 300);
|
||||
expect(game.inputsHandler.log.length).toBe(2);
|
||||
});
|
||||
|
||||
it("keyboard - test input holding for 1000ms - 4 input", async() => {
|
||||
await game.inputsHandler.pressKeyboardKey(cfg_keyboard_qwerty.deviceMapping.KEY_ARROW_UP, 1000);
|
||||
expect(game.inputsHandler.log.length).toBe(4);
|
||||
});
|
||||
|
||||
it("keyboard - test input holding for 2000ms - 8 input", async() => {
|
||||
await game.inputsHandler.pressKeyboardKey(cfg_keyboard_qwerty.deviceMapping.KEY_ARROW_UP, 2000);
|
||||
expect(game.inputsHandler.log.length).toBe(8);
|
||||
});
|
||||
|
||||
it("gamepad - test input holding for 1ms - 1 input", async() => {
|
||||
await game.inputsHandler.pressGamepadButton(pad_xbox360.deviceMapping.RC_S, 1);
|
||||
expect(game.inputsHandler.log.length).toBe(1);
|
||||
});
|
||||
|
||||
it("gamepad - test input holding for 200ms - 1 input", async() => {
|
||||
await game.inputsHandler.pressGamepadButton(pad_xbox360.deviceMapping.RC_S, 200);
|
||||
expect(game.inputsHandler.log.length).toBe(1);
|
||||
});
|
||||
|
||||
it("gamepad - test input holding for 300ms - 2 input", async() => {
|
||||
await game.inputsHandler.pressGamepadButton(pad_xbox360.deviceMapping.RC_S, 300);
|
||||
expect(game.inputsHandler.log.length).toBe(2);
|
||||
});
|
||||
|
||||
it("gamepad - test input holding for 1000ms - 4 input", async() => {
|
||||
await game.inputsHandler.pressGamepadButton(pad_xbox360.deviceMapping.RC_S, 1000);
|
||||
expect(game.inputsHandler.log.length).toBe(4);
|
||||
});
|
||||
|
||||
it("gamepad - test input holding for 2000ms - 8 input", async() => {
|
||||
await game.inputsHandler.pressGamepadButton(pad_xbox360.deviceMapping.RC_S, 2000);
|
||||
expect(game.inputsHandler.log.length).toBe(8);
|
||||
});
|
||||
});
|
||||
|
80
src/test/items/toxic_orb.test.ts
Normal file
80
src/test/items/toxic_orb.test.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CommandPhase,
|
||||
EnemyCommandPhase,
|
||||
MessagePhase,
|
||||
TurnEndPhase,
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {StatusEffect} from "#app/data/status-effect";
|
||||
|
||||
|
||||
describe("Items - Toxic orb", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
const moveToUse = Moves.GROWTH;
|
||||
const oppMoveToUse = Moves.TACKLE;
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(2000);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([moveToUse]);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([oppMoveToUse, oppMoveToUse, oppMoveToUse, oppMoveToUse]);
|
||||
vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{
|
||||
name: "TOXIC_ORB",
|
||||
}]);
|
||||
});
|
||||
|
||||
it("TOXIC ORB", async() => {
|
||||
const moveToUse = Moves.GROWTH;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
expect(game.scene.modifiers[0].type.id).toBe("TOXIC_ORB");
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
// Select Attack
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
// Select Move Growth
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
|
||||
// will run the 13 phase from enemyCommandPhase to TurnEndPhase
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
||||
// Toxic orb should trigger here
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
const message = game.textInterceptor.getLatestMessage();
|
||||
expect(message).toContain("was badly poisoned by Toxic Orb");
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
const message2 = game.textInterceptor.getLatestMessage();
|
||||
expect(message2).toContain("is hurt");
|
||||
expect(message2).toContain("by poison");
|
||||
expect(game.scene.getParty()[0].status.effect).toBe(StatusEffect.TOXIC);
|
||||
}, 20000);
|
||||
});
|
42
src/test/lokalisation/french.test.ts
Normal file
42
src/test/lokalisation/french.test.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import {afterEach, beforeAll, describe, expect, it} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import i18next from "i18next";
|
||||
import {initI18n} from "#app/plugins/i18n";
|
||||
|
||||
describe("Lokalization - french", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
initI18n();
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
it("test bulbasaur name english", async () => {
|
||||
game = new GameManager(phaserGame);
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
]);
|
||||
expect(game.scene.getParty()[0].name).toBe("Bulbasaur");
|
||||
}, 20000);
|
||||
|
||||
it("test bulbasaure name french", async () => {
|
||||
const locale = "fr";
|
||||
i18next.changeLanguage(locale);
|
||||
localStorage.setItem("prLang", locale);
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
await game.startBattle([
|
||||
Species.BULBASAUR,
|
||||
]);
|
||||
expect(game.scene.getParty()[0].name).toBe("Bulbizarre");
|
||||
}, 20000);
|
||||
});
|
69
src/test/moves/growth.test.ts
Normal file
69
src/test/moves/growth.test.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CommandPhase,
|
||||
EnemyCommandPhase,
|
||||
TurnInitPhase,
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {Stat} from "#app/data/pokemon-stat";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {BattleStat} from "#app/data/battle-stat";
|
||||
|
||||
|
||||
describe("Moves - Growth", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
const moveToUse = Moves.GROWTH;
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.MOXIE);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(2000);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([moveToUse]);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
|
||||
});
|
||||
|
||||
it("GROWTH", async() => {
|
||||
const moveToUse = Moves.GROWTH;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
let battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[Stat.SPATK]).toBe(0);
|
||||
|
||||
const battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.SPATK]).toBe(0);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
||||
battleStatsPokemon = game.scene.getParty()[0].summonData.battleStats;
|
||||
expect(battleStatsPokemon[BattleStat.SPATK]).toBe(1);
|
||||
}, 20000);
|
||||
});
|
85
src/test/moves/tackle.test.ts
Normal file
85
src/test/moves/tackle.test.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CommandPhase,
|
||||
EnemyCommandPhase, TurnEndPhase,
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {Stat} from "#app/data/pokemon-stat";
|
||||
|
||||
|
||||
describe("Moves - Tackle", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
const moveToUse = Moves.TACKLE;
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.MAGIKARP);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(1);
|
||||
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(97);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([moveToUse]);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.GROWTH,Moves.GROWTH,Moves.GROWTH,Moves.GROWTH]);
|
||||
vi.spyOn(overrides, "NEVER_CRIT_OVERRIDE", "get").mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("TACKLE against ghost", async() => {
|
||||
const moveToUse = Moves.TACKLE;
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.GENGAR);
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
||||
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
||||
expect(hpLost).toBe(0);
|
||||
}, 20000);
|
||||
|
||||
it("TACKLE against not resistant", async() => {
|
||||
const moveToUse = Moves.TACKLE;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
game.scene.currentBattle.enemyParty[0].stats[Stat.DEF] = 50;
|
||||
game.scene.getParty()[0].stats[Stat.ATK] = 50;
|
||||
|
||||
|
||||
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
||||
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
||||
expect(hpLost).toBeGreaterThan(0);
|
||||
expect(hpLost).toBe(4);
|
||||
}, 20000);
|
||||
});
|
66
src/test/moves/tail_whip.test.ts
Normal file
66
src/test/moves/tail_whip.test.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import * as overrides from "#app/overrides";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
CommandPhase,
|
||||
EnemyCommandPhase,
|
||||
TurnInitPhase,
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
|
||||
import {Command} from "#app/ui/command-ui-handler";
|
||||
import {BattleStat} from "#app/data/battle-stat";
|
||||
|
||||
|
||||
describe("Moves - Tail whip", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
const moveToUse = Moves.TAIL_WHIP;
|
||||
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
|
||||
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
|
||||
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.INSOMNIA);
|
||||
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(2000);
|
||||
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([moveToUse]);
|
||||
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE,Moves.TACKLE,Moves.TACKLE,Moves.TACKLE]);
|
||||
});
|
||||
|
||||
it("TAIL_WHIP", async() => {
|
||||
const moveToUse = Moves.TAIL_WHIP;
|
||||
await game.startBattle([
|
||||
Species.MIGHTYENA,
|
||||
Species.MIGHTYENA,
|
||||
]);
|
||||
|
||||
let battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.DEF]).toBe(0);
|
||||
|
||||
game.onNextPrompt("CommandPhase", Mode.COMMAND, () => {
|
||||
game.scene.ui.setMode(Mode.FIGHT, (game.scene.getCurrentPhase() as CommandPhase).getFieldIndex());
|
||||
});
|
||||
game.onNextPrompt("CommandPhase", Mode.FIGHT, () => {
|
||||
const movePosition = getMovePosition(game.scene, 0, moveToUse);
|
||||
(game.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, movePosition, false);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
||||
battleStatsOpponent = game.scene.currentBattle.enemyParty[0].summonData.battleStats;
|
||||
expect(battleStatsOpponent[BattleStat.DEF]).toBe(-1);
|
||||
}, 20000);
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
import Phaser from "phaser";
|
||||
|
||||
export default new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
51
src/test/phases/phases.test.ts
Normal file
51
src/test/phases/phases.test.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import BattleScene from "#app/battle-scene.js";
|
||||
import { LoginPhase, TitlePhase, UnavailablePhase } from "#app/phases.js";
|
||||
import { Mode } from "#app/ui/ui.js";
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
|
||||
describe("Phases", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
let scene: BattleScene;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
scene = game.scene;
|
||||
});
|
||||
|
||||
describe("LoginPhase", () => {
|
||||
it("should start the login phase", async () => {
|
||||
const loginPhase = new LoginPhase(scene);
|
||||
loginPhase.start();
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MESSAGE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("TitlePhase", () => {
|
||||
it("should start the title phase", async () => {
|
||||
const titlePhase = new TitlePhase(scene);
|
||||
titlePhase.start();
|
||||
expect(scene.ui.getMode()).to.equal(Mode.MESSAGE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("UnavailablePhase", () => {
|
||||
it("should start the unavailable phase", async () => {
|
||||
const unavailablePhase = new UnavailablePhase(scene);
|
||||
unavailablePhase.start();
|
||||
expect(scene.ui.getMode()).to.equal(Mode.UNAVAILABLE);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,57 +0,0 @@
|
||||
import {describe, expect, it} from "vitest";
|
||||
import {getPokemonSpecies} from "#app/data/pokemon-species";
|
||||
import {PokemonMove} from "#app/field/pokemon";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {Moves} from "#app/data/enums/moves";
|
||||
import PokemonData from "#app/system/pokemon-data";
|
||||
|
||||
describe("some tests related to PokemonData and Species", () => {
|
||||
it("should create a species", () => {
|
||||
const species = getPokemonSpecies(Species.MEW);
|
||||
expect(species).not.toBeNull();
|
||||
});
|
||||
|
||||
it("should create a pokemon", () => {
|
||||
const pokemon = new PokemonData({
|
||||
species: Species.MEW,
|
||||
level: 1,
|
||||
});
|
||||
expect(pokemon).not.toBeNull();
|
||||
expect(pokemon.level).toEqual(1);
|
||||
expect(pokemon.species).toEqual(Species.MEW);
|
||||
});
|
||||
|
||||
it("should generate a moveset", () => {
|
||||
const pokemon = new PokemonData({
|
||||
species: Species.MEW,
|
||||
level: 1,
|
||||
});
|
||||
expect(pokemon.moveset[0].moveId).toBe(Moves.TACKLE);
|
||||
expect(pokemon.moveset[1].moveId).toBe(Moves.GROWL);
|
||||
});
|
||||
|
||||
it("should create an ennemypokemon", () => {
|
||||
const ennemyPokemon = new PokemonData({
|
||||
species: Species.MEWTWO,
|
||||
level: 100,
|
||||
});
|
||||
expect(ennemyPokemon).not.toBeNull();
|
||||
expect(ennemyPokemon.level).toEqual(100);
|
||||
expect(ennemyPokemon.species).toEqual(Species.MEWTWO);
|
||||
});
|
||||
|
||||
it("should create an ennemypokemon with specified moveset", () => {
|
||||
const ennemyPokemon = new PokemonData({
|
||||
species: Species.MEWTWO,
|
||||
level: 100,
|
||||
moveset: [
|
||||
new PokemonMove(Moves.ACID),
|
||||
new PokemonMove(Moves.ACROBATICS),
|
||||
new PokemonMove(Moves.FOCUS_ENERGY),
|
||||
]
|
||||
});
|
||||
expect(ennemyPokemon.moveset[0].moveId).toBe(Moves.ACID);
|
||||
expect(ennemyPokemon.moveset[1].moveId).toBe(Moves.ACROBATICS);
|
||||
expect(ennemyPokemon.moveset[2].moveId).toBe(Moves.FOCUS_ENERGY);
|
||||
});
|
||||
});
|
@ -5,8 +5,8 @@ import {
|
||||
getKeyWithKeycode,
|
||||
getKeyWithSettingName,
|
||||
} from "#app/configs/inputs/configHandler";
|
||||
import {MenuManip} from "#app/test/helpers/menuManip";
|
||||
import {InGameManip} from "#app/test/helpers/inGameManip";
|
||||
import {MenuManip} from "#app/test/settingMenu/helpers/menuManip";
|
||||
import {InGameManip} from "#app/test/settingMenu/helpers/inGameManip";
|
||||
import {Device} from "#app/enums/devices";
|
||||
import {InterfaceConfig} from "#app/inputs-controller";
|
||||
import cfg_keyboard_qwerty from "#app/configs/inputs/cfg_keyboard_qwerty";
|
@ -1,8 +1,8 @@
|
||||
import {beforeAll, describe, expect, it} from "vitest";
|
||||
import _masterlist from "../../public/images/pokemon/variant/_masterlist.json";
|
||||
import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import {getAppRootDir} from "#app/test/testUtils";
|
||||
import {getAppRootDir} from "#app/test/sprites/spritesUtils";
|
||||
|
||||
const deepCopy = (data) => {
|
||||
return JSON.parse(JSON.stringify(data));
|
612
src/test/ui/starter-select.test.ts
Normal file
612
src/test/ui/starter-select.test.ts
Normal file
@ -0,0 +1,612 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {
|
||||
EncounterPhase,
|
||||
SelectStarterPhase,
|
||||
TitlePhase,
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {GameModes} from "#app/game-mode";
|
||||
import StarterSelectUiHandler from "#app/ui/starter-select-ui-handler";
|
||||
import {Button} from "#app/enums/buttons";
|
||||
import OptionSelectUiHandler from "#app/ui/settings/option-select-ui-handler";
|
||||
import SaveSlotSelectUiHandler from "#app/ui/save-slot-select-ui-handler";
|
||||
import {OptionSelectItem} from "#app/ui/abstact-option-select-ui-handler";
|
||||
import {Gender} from "#app/data/gender";
|
||||
import {Nature} from "#app/data/nature";
|
||||
import {Abilities} from "#app/data/enums/abilities";
|
||||
import {allSpecies} from "#app/data/pokemon-species";
|
||||
|
||||
|
||||
describe("UI - Starter select", () => {
|
||||
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("Bulbasaur - shiny - variant 2 male", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
const caughtCount = Object.keys(game.scene.gameData.dexData).filter((key) => {
|
||||
const species = game.scene.gameData.dexData[key];
|
||||
return species.caughtAttr !== 0n;
|
||||
}).length;
|
||||
expect(caughtCount).toBe(Object.keys(allSpecies).length);
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||
expect(game.scene.getParty()[0].gender).toBe(Gender.MALE);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - shiny - variant 2 female hardy overgrow", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.CYCLE_GENDER);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||
expect(game.scene.getParty()[0].nature).toBe(Nature.HARDY);
|
||||
expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.OVERGROW);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - shiny - variant 2 female lonely cholorophyl", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.CYCLE_GENDER);
|
||||
handler.processInput(Button.CYCLE_NATURE);
|
||||
handler.processInput(Button.CYCLE_ABILITY);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||
expect(game.scene.getParty()[0].nature).toBe(Nature.LONELY);
|
||||
expect(game.scene.getParty()[0].getAbility().id).toBe(Abilities.CHLOROPHYLL);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - shiny - variant 2 female", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.CYCLE_GENDER);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||
expect(game.scene.getParty()[0].gender).toBe(Gender.FEMALE);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - not shiny", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.CYCLE_SHINY);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(false);
|
||||
expect(game.scene.getParty()[0].variant).toBe(0);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - shiny - variant 0", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.V);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(0);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - shiny - variant 1", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.V);
|
||||
handler.processInput(Button.V);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(1);
|
||||
}, 20000);
|
||||
|
||||
it("Bulbasaur - shiny - variant 1", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.V);
|
||||
handler.processInput(Button.V);
|
||||
handler.processInput(Button.V);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.SUBMIT);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.BULBASAUR);
|
||||
expect(game.scene.getParty()[0].shiny).toBe(true);
|
||||
expect(game.scene.getParty()[0].variant).toBe(2);
|
||||
}, 20000);
|
||||
|
||||
it("Check if first pokemon in party is caterpie from gen 1 and 1rd row, 3rd column ", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
let starterSelectUiHandler: StarterSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
starterSelectUiHandler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
starterSelectUiHandler.processInput(Button.SUBMIT);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(starterSelectUiHandler.starterGens[0]).toBe(0);
|
||||
expect(starterSelectUiHandler.starterCursors[0]).toBe(3);
|
||||
expect(starterSelectUiHandler.cursorObj.x).toBe(132 + 4 * 18);
|
||||
expect(starterSelectUiHandler.cursorObj.y).toBe(10);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.CATERPIE);
|
||||
}, 20000);
|
||||
|
||||
it("Check if first pokemon in party is nidoran_m from gen 1 and 2nd row, 4th column (cursor (9+4)-1) ", async() => {
|
||||
await game.importData("src/test/utils/saves/everything.prsv");
|
||||
await game.runToTitle();
|
||||
game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const currentPhase = game.scene.getCurrentPhase() as TitlePhase;
|
||||
currentPhase.gameMode = GameModes.CLASSIC;
|
||||
currentPhase.end();
|
||||
});
|
||||
await game.phaseInterceptor.mustRun(SelectStarterPhase).catch((error) => expect(error).toBe(SelectStarterPhase));
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
let options: OptionSelectItem[];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => {
|
||||
optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler;
|
||||
options = optionSelectUiHandler.getOptionsWithScroll();
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(options.some(option => option.label === "Add to Party")).toBe(true);
|
||||
expect(options.some(option => option.label === "Toggle IVs")).toBe(true);
|
||||
expect(options.some(option => option.label === "Manage Moves")).toBe(true);
|
||||
expect(options.some(option => option.label === "Use Candies")).toBe(true);
|
||||
expect(options.some(option => option.label === "Cancel")).toBe(true);
|
||||
optionSelectUiHandler.processInput(Button.ACTION);
|
||||
|
||||
let starterSelectUiHandler: StarterSelectUiHandler;
|
||||
await new Promise<void>((resolve) => {
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => {
|
||||
starterSelectUiHandler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
starterSelectUiHandler.processInput(Button.SUBMIT);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
expect(starterSelectUiHandler.starterGens[0]).toBe(0);
|
||||
expect(starterSelectUiHandler.starterCursors[0]).toBe(12);
|
||||
expect(starterSelectUiHandler.cursorObj.x).toBe(132 + 4 * 18);
|
||||
expect(starterSelectUiHandler.cursorObj.y).toBe(28);
|
||||
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.SAVE_SLOT, () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => {
|
||||
const handler = game.scene.ui.getHandler() as StarterSelectUiHandler;
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.NIDORAN_M);
|
||||
}, 20000);
|
||||
});
|
16
src/test/utils/TextInterceptor.ts
Normal file
16
src/test/utils/TextInterceptor.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export default class TextInterceptor {
|
||||
private scene;
|
||||
private logs = [];
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
scene.messageWrapper = this;
|
||||
}
|
||||
|
||||
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer): void {
|
||||
this.logs.push(text);
|
||||
}
|
||||
|
||||
getLatestMessage(): string {
|
||||
return this.logs[this.logs.length - 1];
|
||||
}
|
||||
}
|
50
src/test/utils/fakeMobile.html
Normal file
50
src/test/utils/fakeMobile.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html><body>
|
||||
<div id="touchControls">
|
||||
<div id="dpad" class="unselectable">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72">
|
||||
<path id="dpadUp" data-key="UP" d="M48,5.8C48,2.5,45.4,0,42,0H29.9C26.6,0,24,2.4,24,5.8V24h24V5.8z" />
|
||||
<path id="dpadRight" data-key="RIGHT" d="M66.2,24H48v24h18.2c3.3,0,5.8-2.7,5.8-6V29.9C72,26.5,69.5,24,66.2,24z" />
|
||||
<path id="dpadDown" data-key="DOWN" d="M24,66.3c0,3.3,2.6,5.7,5.9,5.7H42c3.3,0,6-2.4,6-5.7V48H24V66.3z" />
|
||||
<path id="dpadLeft" data-key="LEFT" d="M5.7,24C2.4,24,0,26.5,0,29.9V42c0,3.3,2.3,6,5.7,6H24V24H5.7z" />
|
||||
<rect id="dpadCenter" x="24" y="24" width="24" height="24" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div id="apad" class="unselectable">
|
||||
<div id="apadAction" class="apadCircBtn apadBtn" data-key="ACTION">
|
||||
<text id="apadLabelAction" class="apadLabel">A</text>
|
||||
</div>
|
||||
<div id="apadCancel" class="apadCircBtn apadBtn" data-key="CANCEL">
|
||||
<text id="apadLabelCancel" class="apadLabel">B</text>
|
||||
</div>
|
||||
<div class="apadBtnContainer apadRectBtnContainer">
|
||||
<div id="apadCycleShiny" class="apadSqBtn apadBtn" data-key="CYCLE_SHINY">
|
||||
<text class="apadLabel apadLabelSmall">R</text>
|
||||
</div>
|
||||
<div id="apadCycleVariant" class="apadSqBtn apadBtn" data-key="CYCLE_VARIANT">
|
||||
<text class="apadLabel apadLabelSmall">V</text>
|
||||
</div>
|
||||
<div id="apadStats" class="apadRectBtn apadBtn" data-key="STATS">
|
||||
<text class="apadLabel apadLabelSmall">C</text>
|
||||
</div>
|
||||
<div id="apadMenu" class="apadRectBtn apadBtn" data-key="MENU">
|
||||
<text class="apadLabel apadLabelSmall">Menu</text>
|
||||
</div>
|
||||
</div>
|
||||
<div class="apadBtnContainer apadSqBtnContainer">
|
||||
<div id="apadCycleForm" class="apadSqBtn apadBtn" data-key="CYCLE_FORM">
|
||||
<text class="apadLabel apadLabelSmall">F</text>
|
||||
</div>
|
||||
<div id="apadCycleGender" class="apadSqBtn apadBtn" data-key="CYCLE_GENDER">
|
||||
<text class="apadLabel apadLabelSmall">G</text>
|
||||
</div>
|
||||
<div id="apadCycleAbility" class="apadSqBtn apadBtn" data-key="CYCLE_ABILITY">
|
||||
<text class="apadLabel apadLabelSmall">E</text>
|
||||
</div>
|
||||
<div id="apadCycleNature" class="apadSqBtn apadBtn" data-key="CYCLE_NATURE">
|
||||
<text class="apadLabel apadLabelSmall">N</text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
223
src/test/utils/gameManager.ts
Normal file
223
src/test/utils/gameManager.ts
Normal file
@ -0,0 +1,223 @@
|
||||
import GameWrapper from "#app/test/utils/gameWrapper";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
import {generateStarter, waitUntil} from "#app/test/utils/gameManagerUtils";
|
||||
import {
|
||||
CheckSwitchPhase,
|
||||
CommandPhase,
|
||||
EncounterPhase,
|
||||
LoginPhase,
|
||||
PostSummonPhase,
|
||||
SelectGenderPhase,
|
||||
SelectStarterPhase,
|
||||
SummonPhase,
|
||||
TitlePhase,
|
||||
ToggleDoublePositionPhase,
|
||||
} from "#app/phases";
|
||||
import BattleScene from "#app/battle-scene.js";
|
||||
import PhaseInterceptor from "#app/test/utils/phaseInterceptor";
|
||||
import TextInterceptor from "#app/test/utils/TextInterceptor";
|
||||
import {expect} from "vitest";
|
||||
import {GameModes} from "#app/game-mode";
|
||||
import fs from "fs";
|
||||
import { AES, enc } from "crypto-js";
|
||||
import {updateUserInfo} from "#app/account";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {PlayerGender} from "#app/data/enums/player-gender";
|
||||
import {GameDataType} from "#app/data/enums/game-data-type";
|
||||
import InputsHandler from "#app/test/utils/inputsHandler";
|
||||
import {ExpNotification} from "#app/enums/exp-notification";
|
||||
|
||||
/**
|
||||
* Class to manage the game state and transitions between phases.
|
||||
*/
|
||||
export default class GameManager {
|
||||
public gameWrapper: GameWrapper;
|
||||
public scene: BattleScene;
|
||||
public phaseInterceptor: PhaseInterceptor;
|
||||
public textInterceptor: TextInterceptor;
|
||||
public inputsHandler: InputsHandler;
|
||||
|
||||
/**
|
||||
* Creates an instance of GameManager.
|
||||
* @param phaserGame - The Phaser game instance.
|
||||
* @param bypassLogin - Whether to bypass the login phase.
|
||||
*/
|
||||
constructor(phaserGame: Phaser.Game, bypassLogin: boolean = true) {
|
||||
BattleScene.prototype.randBattleSeedInt = (arg) => arg-1;
|
||||
this.gameWrapper = new GameWrapper(phaserGame, bypassLogin);
|
||||
this.scene = new BattleScene();
|
||||
this.phaseInterceptor = new PhaseInterceptor(this.scene);
|
||||
this.textInterceptor = new TextInterceptor(this.scene);
|
||||
this.gameWrapper.setScene(this.scene);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the game mode.
|
||||
* @param mode - The mode to set.
|
||||
*/
|
||||
setMode(mode: Mode) {
|
||||
this.scene.ui?.setMode(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the specified mode is set.
|
||||
* @param mode - The mode to wait for.
|
||||
* @returns A promise that resolves when the mode is set.
|
||||
*/
|
||||
waitMode(mode: Mode): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
await waitUntil(() => this.scene.ui?.getMode() === mode);
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the current phase.
|
||||
*/
|
||||
endPhase() {
|
||||
this.scene.getCurrentPhase().end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action to be executed on the next prompt.
|
||||
* @param phaseTarget - The target phase.
|
||||
* @param mode - The mode to wait for.
|
||||
* @param callback - The callback to execute.
|
||||
* @param expireFn - Optional function to determine if the prompt has expired.
|
||||
*/
|
||||
onNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn?: () => void) {
|
||||
this.phaseInterceptor.addToNextPrompt(phaseTarget, mode, callback, expireFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the game to the title phase.
|
||||
* @returns A promise that resolves when the title phase is reached.
|
||||
*/
|
||||
runToTitle(): Promise<void> {
|
||||
return new Promise(async(resolve) => {
|
||||
await this.phaseInterceptor.run(LoginPhase);
|
||||
this.onNextPrompt("SelectGenderPhase", Mode.OPTION_SELECT, () => {
|
||||
this.scene.gameData.gender = PlayerGender.MALE;
|
||||
this.endPhase();
|
||||
}, () => this.isCurrentPhase(TitlePhase));
|
||||
await this.phaseInterceptor.run(SelectGenderPhase, () => this.isCurrentPhase(TitlePhase));
|
||||
await this.phaseInterceptor.run(TitlePhase);
|
||||
this.scene.gameSpeed = 5;
|
||||
this.scene.moveAnimations = false;
|
||||
this.scene.showLevelUpStats = false;
|
||||
this.scene.expGainsSpeed = 3;
|
||||
this.scene.expParty = ExpNotification.SKIP;
|
||||
this.scene.hpBarSpeed = 3;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the game to the summon phase.
|
||||
* @param species - Optional array of species to summon.
|
||||
* @returns A promise that resolves when the summon phase is reached.
|
||||
*/
|
||||
runToSummon(species?: Species[]): Promise<void> {
|
||||
return new Promise(async(resolve) => {
|
||||
await this.runToTitle();
|
||||
this.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||
const starters = generateStarter(this.scene, species);
|
||||
const selectStarterPhase = new SelectStarterPhase(this.scene, GameModes.CLASSIC);
|
||||
this.scene.pushPhase(new EncounterPhase(this.scene, false));
|
||||
selectStarterPhase.initBattle(starters);
|
||||
});
|
||||
await this.phaseInterceptor.run(EncounterPhase);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a battle.
|
||||
* @param species - Optional array of species to start the battle with.
|
||||
* @returns A promise that resolves when the battle is started.
|
||||
*/
|
||||
startBattle(species?: Species[]): Promise<void> {
|
||||
return new Promise(async(resolve) => {
|
||||
await this.runToSummon(species);
|
||||
await this.phaseInterceptor.runFrom(PostSummonPhase).to(ToggleDoublePositionPhase);
|
||||
await this.phaseInterceptor.run(SummonPhase, () => this.isCurrentPhase(CheckSwitchPhase) || this.isCurrentPhase(PostSummonPhase));
|
||||
this.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||
this.setMode(Mode.MESSAGE);
|
||||
this.endPhase();
|
||||
}, () => this.isCurrentPhase(PostSummonPhase));
|
||||
this.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||
this.setMode(Mode.MESSAGE);
|
||||
this.endPhase();
|
||||
}, () => this.isCurrentPhase(PostSummonPhase));
|
||||
await this.phaseInterceptor.run(CheckSwitchPhase, () => this.isCurrentPhase(PostSummonPhase));
|
||||
await this.phaseInterceptor.run(CheckSwitchPhase, () => this.isCurrentPhase(PostSummonPhase));
|
||||
await this.phaseInterceptor.runFrom(PostSummonPhase).to(CommandPhase);
|
||||
await waitUntil(() => this.scene.ui?.getMode() === Mode.COMMAND);
|
||||
console.log("==================[New Turn]==================");
|
||||
expect(this.scene.ui?.getMode()).toBe(Mode.COMMAND);
|
||||
expect(this.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name);
|
||||
return resolve();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has won the battle.
|
||||
* @returns True if the player has won, otherwise false.
|
||||
*/
|
||||
isVictory() {
|
||||
return this.scene.currentBattle.enemyParty.every(pokemon => pokemon.isFainted());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current phase matches the target phase.
|
||||
* @param phaseTarget - The target phase.
|
||||
* @returns True if the current phase matches the target phase, otherwise false.
|
||||
*/
|
||||
isCurrentPhase(phaseTarget) {
|
||||
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
|
||||
return this.scene.getCurrentPhase().constructor.name === targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current mode matches the target mode.
|
||||
* @param mode - The target mode.
|
||||
* @returns True if the current mode matches the target mode, otherwise false.
|
||||
*/
|
||||
isCurrentMode(mode: Mode) {
|
||||
return this.scene.ui?.getMode() === mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the save data to import it in a test game.
|
||||
* @returns A promise that resolves with the exported save data.
|
||||
*/
|
||||
exportSaveToTest(): Promise<string> {
|
||||
return new Promise(async (resolve) => {
|
||||
await this.scene.gameData.saveAll(this.scene, true, true, true, true);
|
||||
this.scene.reset(true);
|
||||
await waitUntil(() => this.scene.ui?.getMode() === Mode.TITLE);
|
||||
await this.scene.gameData.tryExportData(GameDataType.SESSION, 0);
|
||||
await waitUntil(() => localStorage.hasOwnProperty("toExport"));
|
||||
return resolve(localStorage.getItem("toExport"));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports game data from a file.
|
||||
* @param path - The path to the data file.
|
||||
* @returns A promise that resolves with a tuple containing a boolean indicating success and an integer status code.
|
||||
*/
|
||||
async importData(path): Promise<[boolean, integer]> {
|
||||
const saveKey = "x0i2O7WRiANTqPmZ";
|
||||
const dataRaw = fs.readFileSync(path, {encoding: "utf8", flag: "r"});
|
||||
let dataStr = AES.decrypt(dataRaw, saveKey).toString(enc.Utf8);
|
||||
dataStr = this.scene.gameData.convertSystemDataStr(dataStr);
|
||||
const systemData = this.scene.gameData.parseSystemData(dataStr);
|
||||
const valid = !!systemData.dexData && !!systemData.timestamp;
|
||||
if (valid) {
|
||||
await updateUserInfo();
|
||||
await this.scene.gameData.initSystem(dataStr);
|
||||
}
|
||||
return updateUserInfo();
|
||||
}
|
||||
}
|
87
src/test/utils/gameManagerUtils.ts
Normal file
87
src/test/utils/gameManagerUtils.ts
Normal file
@ -0,0 +1,87 @@
|
||||
// Function to convert Blob to string
|
||||
import {getDailyRunStarters} from "#app/data/daily-run";
|
||||
import {Gender} from "#app/data/gender";
|
||||
import {Species} from "#app/data/enums/species";
|
||||
import {Starter} from "#app/ui/starter-select-ui-handler";
|
||||
import {GameModes, gameModes} from "#app/game-mode";
|
||||
import {getPokemonSpecies, getPokemonSpeciesForm} from "#app/data/pokemon-species";
|
||||
import {PlayerPokemon} from "#app/field/pokemon";
|
||||
|
||||
export function blobToString(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onloadend = () => {
|
||||
resolve(reader.result);
|
||||
};
|
||||
|
||||
reader.onerror = () => {
|
||||
reject(new Error("Error reading Blob as string"));
|
||||
};
|
||||
|
||||
reader.readAsText(blob);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function holdOn(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function generateStarter(scene, species?: Species[]) {
|
||||
const seed = "test";
|
||||
const starters = getTestRunStarters(scene, seed, species);
|
||||
const startingLevel = scene.gameMode.getStartingLevel();
|
||||
for (const starter of starters) {
|
||||
const starterProps = scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
||||
const starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0));
|
||||
const starterGender = starter.species.malePercent !== null
|
||||
? !starterProps.female ? Gender.MALE : Gender.FEMALE
|
||||
: Gender.GENDERLESS;
|
||||
const starterPokemon = scene.addPlayerPokemon(starter.species, startingLevel, starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, undefined, starter.nature);
|
||||
starter.moveset = starterPokemon.moveset;
|
||||
}
|
||||
return starters;
|
||||
}
|
||||
|
||||
function getTestRunStarters(scene, seed, species) {
|
||||
if (!species) {
|
||||
return getDailyRunStarters(scene, seed);
|
||||
}
|
||||
const starters: Starter[] = [];
|
||||
const startingLevel = gameModes[GameModes.CLASSIC].getStartingLevel();
|
||||
|
||||
for (const specie of species) {
|
||||
const starterSpeciesForm = getPokemonSpeciesForm(specie, 0);
|
||||
const starterSpecies = getPokemonSpecies(starterSpeciesForm.speciesId);
|
||||
const pokemon = new PlayerPokemon(scene, starterSpecies, startingLevel, undefined, 0, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
const starter: Starter = {
|
||||
species: starterSpecies,
|
||||
dexAttr: pokemon.getDexAttr(),
|
||||
abilityIndex: pokemon.abilityIndex,
|
||||
passive: false,
|
||||
nature: pokemon.getNature(),
|
||||
pokerus: pokemon.pokerus
|
||||
};
|
||||
starters.push(starter);
|
||||
}
|
||||
return starters;
|
||||
}
|
||||
|
||||
export function waitUntil(truth) {
|
||||
return new Promise(resolve => {
|
||||
const interval = setInterval(() => {
|
||||
if (truth()) {
|
||||
clearInterval(interval);
|
||||
resolve(true);
|
||||
}
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
export function getMovePosition(scene, pokemonIndex, moveIndex) {
|
||||
const playerPokemon = scene.getPlayerField()[pokemonIndex];
|
||||
const moveSet = playerPokemon.getMoveset();
|
||||
const index = moveSet.findIndex((move) => move.moveId === moveIndex);
|
||||
return index;
|
||||
}
|
252
src/test/utils/gameWrapper.ts
Normal file
252
src/test/utils/gameWrapper.ts
Normal file
@ -0,0 +1,252 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
import * as main from "#app/main";
|
||||
import fs from "fs";
|
||||
import InputManager = Phaser.Input.InputManager;
|
||||
import KeyboardManager = Phaser.Input.Keyboard.KeyboardManager;
|
||||
import KeyboardPlugin = Phaser.Input.Keyboard.KeyboardPlugin;
|
||||
import GamepadPlugin = Phaser.Input.Gamepad.GamepadPlugin;
|
||||
import EventEmitter = Phaser.Events.EventEmitter;
|
||||
import UpdateList = Phaser.GameObjects.UpdateList;
|
||||
import MockGraphics from "#app/test/utils/mocks/mocksContainer/mockGraphics";
|
||||
import MockTextureManager from "#app/test/utils/mocks/mockTextureManager";
|
||||
import Phaser from "phaser";
|
||||
import {blobToString} from "#app/test/utils/gameManagerUtils";
|
||||
import {vi} from "vitest";
|
||||
import mockLocalStorage from "#app/test/utils/mocks/mockLocalStorage";
|
||||
import mockConsoleLog from "#app/test/utils/mocks/mockConsoleLog";
|
||||
import MockLoader from "#app/test/utils/mocks/mockLoader";
|
||||
import {MockFetch} from "#app/test/utils/mocks/mockFetch";
|
||||
import * as Utils from "#app/utils";
|
||||
import InputText from "phaser3-rex-plugins/plugins/inputtext";
|
||||
import {MockClock} from "#app/test/utils/mocks/mockClock";
|
||||
import BattleScene from "#app/battle-scene.js";
|
||||
import {MoveAnim} from "#app/data/battle-anims";
|
||||
import Pokemon from "#app/field/pokemon";
|
||||
import * as battleScene from "#app/battle-scene";
|
||||
|
||||
Object.defineProperty(window, "localStorage", {
|
||||
value: mockLocalStorage(),
|
||||
});
|
||||
Object.defineProperty(window, "console", {
|
||||
value: mockConsoleLog(false),
|
||||
});
|
||||
|
||||
|
||||
InputText.prototype.setElement = () => null;
|
||||
InputText.prototype.resize = () => null;
|
||||
window.URL.createObjectURL = (blob: Blob) => {
|
||||
blobToString(blob).then((data: string) => {
|
||||
localStorage.setItem("toExport", data);
|
||||
})
|
||||
return null;
|
||||
};
|
||||
navigator.getGamepads = vi.fn().mockReturnValue([]);
|
||||
global.fetch = vi.fn(MockFetch);
|
||||
Utils.setCookie(Utils.sessionIdKey, 'fake_token');
|
||||
|
||||
|
||||
window.matchMedia = () => ({
|
||||
matches: false,
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Sets this object's position relative to another object with a given offset
|
||||
* @param guideObject {@linkcode Phaser.GameObjects.GameObject} to base the position off of
|
||||
* @param x The relative x position
|
||||
* @param y The relative y position
|
||||
*/
|
||||
const setPositionRelative = function (guideObject: any, x: number, y: number) {
|
||||
const offsetX = guideObject.width * (-0.5 + (0.5 - guideObject.originX));
|
||||
const offsetY = guideObject.height * (-0.5 + (0.5 - guideObject.originY));
|
||||
this.setPosition(guideObject.x + offsetX + x, guideObject.y + offsetY + y);
|
||||
};
|
||||
|
||||
Phaser.GameObjects.Container.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Sprite.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Image.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative;
|
||||
Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative;
|
||||
|
||||
|
||||
export default class GameWrapper {
|
||||
public game: Phaser.Game;
|
||||
public scene: BattleScene;
|
||||
|
||||
constructor(phaserGame: Phaser.Game, bypassLogin: boolean) {
|
||||
Phaser.Math.RND.sow([ 'test' ]);
|
||||
vi.spyOn(Utils, "apiFetch", "get").mockReturnValue(fetch);
|
||||
if (bypassLogin) {
|
||||
vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(true);
|
||||
}
|
||||
this.game = phaserGame;
|
||||
MoveAnim.prototype.getAnim = () => ({
|
||||
frames: {},
|
||||
});
|
||||
Pokemon.prototype.enableMask = () => null;
|
||||
localStorage.clear();
|
||||
}
|
||||
|
||||
setScene(scene: BattleScene) {
|
||||
this.scene = scene;
|
||||
this.injectMandatory();
|
||||
this.scene.preload && this.scene.preload();
|
||||
this.scene.create();
|
||||
}
|
||||
|
||||
injectMandatory() {
|
||||
this.game.config = {
|
||||
seed: ["test"],
|
||||
}
|
||||
this.scene.game = this.game;
|
||||
this.game.renderer = {
|
||||
maxTextures: -1,
|
||||
gl: {},
|
||||
deleteTexture: () => null,
|
||||
canvasToTexture: () => ({}),
|
||||
createCanvasTexture: () => ({}),
|
||||
pipelines: {
|
||||
add: () => null,
|
||||
},
|
||||
};
|
||||
this.scene.renderer = this.game.renderer;
|
||||
this.scene.children = {
|
||||
removeAll: () => null,
|
||||
};
|
||||
|
||||
this.scene.sound = {
|
||||
play: () => null,
|
||||
pause: () => null,
|
||||
setRate: () => null,
|
||||
add: () => this.scene.sound,
|
||||
get: () => this.scene.sound,
|
||||
getAllPlaying: () => [],
|
||||
manager: {
|
||||
game: this.game,
|
||||
},
|
||||
setVolume: () => null,
|
||||
stopByKey: () => null,
|
||||
on: (evt, callback) => callback(),
|
||||
key: "",
|
||||
};
|
||||
|
||||
this.scene.tweens = {
|
||||
add: (data) => {
|
||||
if (data.onComplete) {
|
||||
data.onComplete();
|
||||
}
|
||||
},
|
||||
getTweensOf: () => ([]),
|
||||
killTweensOf: () => ([]),
|
||||
chain: () => null,
|
||||
addCounter: (data) => {
|
||||
if (data.onComplete) {
|
||||
data.onComplete();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.scene.anims = this.game.anims;
|
||||
this.scene.cache = this.game.cache;
|
||||
this.scene.plugins = this.game.plugins;
|
||||
this.scene.registry = this.game.registry;
|
||||
this.scene.scale = this.game.scale;
|
||||
this.scene.textures = this.game.textures;
|
||||
this.scene.events = this.game.events;
|
||||
this.scene.manager = new InputManager(this.game, {});
|
||||
this.scene.manager.keyboard = new KeyboardManager(this.scene);
|
||||
this.scene.pluginEvents = new EventEmitter();
|
||||
this.scene.domContainer = {} as HTMLDivElement;
|
||||
this.scene.spritePipeline = {};
|
||||
this.scene.fieldSpritePipeline = {};
|
||||
this.scene.load = new MockLoader(this.scene);
|
||||
this.scene.sys = {
|
||||
queueDepthSort: () => null,
|
||||
anims: this.game.anims,
|
||||
game: this.game,
|
||||
textures: {
|
||||
addCanvas: () => ({
|
||||
get: () => ({ // this.frame in Text.js
|
||||
source: {},
|
||||
setSize: () => null,
|
||||
glTexture: () => ({
|
||||
spectorMetadata: {},
|
||||
}),
|
||||
}),
|
||||
})
|
||||
},
|
||||
cache: this.scene.load.cacheManager,
|
||||
scale: this.game.scale,
|
||||
// _scene.sys.scale = new ScaleManager(_scene);
|
||||
// events: {
|
||||
// on: () => null,
|
||||
// },
|
||||
events: new EventEmitter(),
|
||||
settings: {
|
||||
loader: {
|
||||
key: 'battle',
|
||||
}
|
||||
},
|
||||
input: this.game.input,
|
||||
};
|
||||
const mockTextureManager = new MockTextureManager(this.scene);
|
||||
this.scene.add = mockTextureManager.add;
|
||||
this.scene.sys.displayList = this.scene.add.displayList;
|
||||
this.scene.sys.updateList = new UpdateList(this.scene);
|
||||
this.scene.systems = this.scene.sys;
|
||||
this.scene.input = this.game.input;
|
||||
this.scene.scene = this.scene;
|
||||
this.scene.input.keyboard = new KeyboardPlugin(this.scene);
|
||||
this.scene.input.gamepad = new GamepadPlugin(this.scene);
|
||||
this.scene.cachedFetch = (url, init) => {
|
||||
return new Promise((resolve) => {
|
||||
// need to remove that if later we want to test battle-anims
|
||||
const newUrl = url.includes('./battle-anims/') ? prependPath('./battle-anims/tackle.json') : prependPath(url);
|
||||
let raw;
|
||||
try {
|
||||
raw = fs.readFileSync(newUrl, {encoding: "utf8", flag: "r"});
|
||||
} catch(e) {
|
||||
return resolve(createFetchBadResponse({}));
|
||||
}
|
||||
const data = JSON.parse(raw);
|
||||
const response = createFetchResponse(data);
|
||||
return resolve(response);
|
||||
});
|
||||
};
|
||||
this.scene.make = {
|
||||
graphics: (config) => new MockGraphics(mockTextureManager, config),
|
||||
rexTransitionImagePack: () => ({
|
||||
transit: () => null,
|
||||
}),
|
||||
};
|
||||
this.scene.time = new MockClock(this.scene);
|
||||
}
|
||||
}
|
||||
|
||||
function prependPath(originalPath) {
|
||||
const prefix = "public";
|
||||
if (originalPath.startsWith("./")) {
|
||||
return originalPath.replace("./", `${prefix}/`);
|
||||
}
|
||||
return originalPath;
|
||||
}
|
||||
// Simulate fetch response
|
||||
function createFetchResponse(data) {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: () => Promise.resolve(data),
|
||||
text: () => Promise.resolve(JSON.stringify(data)),
|
||||
};
|
||||
}
|
||||
// Simulate fetch response
|
||||
function createFetchBadResponse(data) {
|
||||
return {
|
||||
ok: false,
|
||||
status: 404,
|
||||
json: () => Promise.resolve(data),
|
||||
text: () => Promise.resolve(JSON.stringify(data)),
|
||||
};
|
||||
}
|
115
src/test/utils/inputsHandler.ts
Normal file
115
src/test/utils/inputsHandler.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import Phaser from "phaser";
|
||||
import {InputsController} from "#app/inputs-controller";
|
||||
import pad_xbox360 from "#app/configs/inputs/pad_xbox360";
|
||||
import {holdOn} from "#app/test/utils/gameManagerUtils";
|
||||
import {initTouchControls} from "#app/touch-controls";
|
||||
import { JSDOM } from "jsdom";
|
||||
import fs from "fs";
|
||||
|
||||
|
||||
export default class InputsHandler {
|
||||
private scene: BattleScene;
|
||||
private events: Phaser.Events.EventEmitter;
|
||||
private inputController: InputsController;
|
||||
public log = [];
|
||||
public logUp = [];
|
||||
private fakePad: Fakepad;
|
||||
private fakeMobile: FakeMobile;
|
||||
|
||||
constructor(scene: BattleScene) {
|
||||
this.scene = scene;
|
||||
this.inputController = this.scene.inputController;
|
||||
this.fakePad = new Fakepad(pad_xbox360);
|
||||
this.fakeMobile = new FakeMobile();
|
||||
this.scene.input.gamepad.gamepads.push(this.fakePad);
|
||||
this.init();
|
||||
}
|
||||
|
||||
pressTouch(button: string, duration: integer): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
this.fakeMobile.touchDown(button);
|
||||
await holdOn(duration);
|
||||
this.fakeMobile.touchUp(button);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
pressGamepadButton(button: integer, duration: integer): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
this.scene.input.gamepad.emit("down", this.fakePad, {index: button});
|
||||
await holdOn(duration);
|
||||
this.scene.input.gamepad.emit("up", this.fakePad, {index: button});
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
pressKeyboardKey(key: integer, duration: integer): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
this.scene.input.keyboard.emit("keydown", {keyCode: key});
|
||||
await holdOn(duration);
|
||||
this.scene.input.keyboard.emit("keyup", {keyCode: key});
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
init(): void {
|
||||
setInterval(() => {
|
||||
this.inputController.update();
|
||||
});
|
||||
initTouchControls(this.inputController.events);
|
||||
this.events = this.inputController.events;
|
||||
this.scene.input.gamepad.emit("connected", this.fakePad);
|
||||
this.listenInputs();
|
||||
}
|
||||
|
||||
listenInputs(): void {
|
||||
this.events.on("input_down", (event) => {
|
||||
this.log.push({type: "input_down", button: event.button});
|
||||
}, this);
|
||||
|
||||
this.events.on("input_up", (event) => {
|
||||
this.logUp.push({type: "input_up", button: event.button});
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
|
||||
class Fakepad extends Phaser.Input.Gamepad.Gamepad {
|
||||
public id: string;
|
||||
public index: number;
|
||||
|
||||
constructor(pad) {
|
||||
super(undefined, {...pad, buttons: pad.deviceMapping, axes: []});
|
||||
this.id = "xbox_360_fakepad";
|
||||
this.index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeMobile {
|
||||
constructor() {
|
||||
const fakeMobilePage = fs.readFileSync("./src/test/utils/fakeMobile.html", {encoding: "utf8", flag: "r"});
|
||||
const dom = new JSDOM(fakeMobilePage);
|
||||
Object.defineProperty(window, "document", {
|
||||
value: dom.window.document,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
touchDown(button: string) {
|
||||
const node = document.querySelector(`[data-key][id='${button}']`);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const event = new Event("touchstart");
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
|
||||
touchUp(button: string) {
|
||||
const node = document.querySelector(`[data-key][id='${button}']`);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const event = new Event("touchend");
|
||||
node.dispatchEvent(event);
|
||||
}
|
||||
}
|
79
src/test/utils/misc.test.ts
Normal file
79
src/test/utils/misc.test.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import {afterEach, beforeAll, beforeEach, describe, expect, it, vi} from "vitest";
|
||||
import Phaser from "phaser";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import {apiFetch} from "#app/utils";
|
||||
import {waitUntil} from "#app/test/utils/gameManagerUtils";
|
||||
|
||||
describe("Test misc", () => {
|
||||
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("test fetch mock async", async () => {
|
||||
const spy = vi.fn();
|
||||
await fetch("https://localhost:8080/account/info").then(response => {
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.ok).toBe(true);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
spy(); // Call the spy function
|
||||
expect(data).toEqual({"username":"greenlamp","lastSessionSlot":0});
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("test apifetch mock async", async () => {
|
||||
const spy = vi.fn();
|
||||
await apiFetch("https://localhost:8080/account/info").then(response => {
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.ok).toBe(true);
|
||||
return response.json();
|
||||
}).then(data => {
|
||||
spy(); // Call the spy function
|
||||
expect(data).toEqual({"username":"greenlamp","lastSessionSlot":0});
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("test fetch mock sync", async () => {
|
||||
const response = await fetch("https://localhost:8080/account/info");
|
||||
const data = await response.json();
|
||||
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.status).toBe(200);
|
||||
expect(data).toEqual({"username":"greenlamp","lastSessionSlot":0});
|
||||
});
|
||||
|
||||
it("test apifetch mock sync", async () => {
|
||||
const data = await game.scene.cachedFetch("./battle-anims/splishy-splash.json");
|
||||
expect(data).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it("testing wait phase queue", async () => {
|
||||
const fakeScene = {
|
||||
phaseQueue: [1, 2, 3] // Initially not empty
|
||||
};
|
||||
setTimeout(() => {
|
||||
fakeScene.phaseQueue = [];
|
||||
}, 500);
|
||||
const spy = vi.fn();
|
||||
await waitUntil(() => fakeScene.phaseQueue.length === 0).then(result => {
|
||||
expect(result).toBe(true);
|
||||
spy(); // Call the spy function
|
||||
});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
17
src/test/utils/mocks/mockClock.ts
Normal file
17
src/test/utils/mocks/mockClock.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import Clock = Phaser.Time.Clock;
|
||||
|
||||
|
||||
export class MockClock extends Clock {
|
||||
constructor(scene) {
|
||||
super(scene);
|
||||
setInterval(() => {
|
||||
/*
|
||||
To simulate frame update
|
||||
eventEmitter.on(SceneEvents.PRE_UPDATE, this.preUpdate, this);
|
||||
eventEmitter.on(SceneEvents.UPDATE, this.update, this);
|
||||
*/
|
||||
this.preUpdate(this.systems.game.loop.time, 100);
|
||||
this.update(this.systems.game.loop.time, 100);
|
||||
}, 100);
|
||||
}
|
||||
}
|
82
src/test/utils/mocks/mockConsoleLog.ts
Normal file
82
src/test/utils/mocks/mockConsoleLog.ts
Normal file
@ -0,0 +1,82 @@
|
||||
const MockConsoleLog = (_logDisabled= false, _phaseText=false) => {
|
||||
let logs = [];
|
||||
const logDisabled: boolean = _logDisabled;
|
||||
const phaseText: boolean = _phaseText;
|
||||
const originalLog = console.log;
|
||||
const originalError = console.error;
|
||||
const originalDebug = console.debug;
|
||||
const originalWarn = console.warn;
|
||||
const notified = [];
|
||||
|
||||
const blacklist = ["Phaser", "variant icon does not exist", "Texture \"%s\" not found"];
|
||||
const whitelist = ["Phase"];
|
||||
|
||||
return ({
|
||||
log(...args) {
|
||||
const argsStr = this.getStr(args);
|
||||
logs.push(argsStr);
|
||||
if (logDisabled && (!phaseText)) {
|
||||
return;
|
||||
}
|
||||
if ((phaseText && !whitelist.some((b) => argsStr.includes(b))) || blacklist.some((b) => argsStr.includes(b))) {
|
||||
return;
|
||||
}
|
||||
originalLog(args);
|
||||
},
|
||||
error(...args) {
|
||||
const argsStr = this.getStr(args);
|
||||
logs.push(argsStr);
|
||||
originalError(args); // Appelle le console.error originel
|
||||
},
|
||||
debug(...args) {
|
||||
const argsStr = this.getStr(args);
|
||||
logs.push(argsStr);
|
||||
if (logDisabled && (!phaseText)) {
|
||||
return;
|
||||
}
|
||||
if (!whitelist.some((b) => argsStr.includes(b)) || blacklist.some((b) => argsStr.includes(b))) {
|
||||
return;
|
||||
}
|
||||
originalDebug(args);
|
||||
},
|
||||
warn(...args) {
|
||||
const argsStr = this.getStr(args);
|
||||
logs.push(args);
|
||||
if (logDisabled && (!phaseText)) {
|
||||
return;
|
||||
}
|
||||
if (!whitelist.some((b) => argsStr.includes(b)) || blacklist.some((b) => argsStr.includes(b))) {
|
||||
return;
|
||||
}
|
||||
originalWarn(args);
|
||||
},
|
||||
notify(msg) {
|
||||
originalLog(msg);
|
||||
notified.push(msg);
|
||||
},
|
||||
getLogs() {
|
||||
return logs;
|
||||
},
|
||||
clearLogs() {
|
||||
logs = [];
|
||||
},
|
||||
getStr(...args) {
|
||||
return args.map(arg => {
|
||||
if (typeof arg === "object" && arg !== null) {
|
||||
// Handle objects including arrays
|
||||
return JSON.stringify(arg, (key, value) =>
|
||||
typeof value === "bigint" ? value.toString() : value
|
||||
);
|
||||
} else if (typeof arg === "bigint") {
|
||||
// Handle BigInt values
|
||||
return arg.toString();
|
||||
} else {
|
||||
// Handle all other types
|
||||
return arg.toString();
|
||||
}
|
||||
}).join(";");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default MockConsoleLog;
|
32
src/test/utils/mocks/mockFetch.ts
Normal file
32
src/test/utils/mocks/mockFetch.ts
Normal file
@ -0,0 +1,32 @@
|
||||
export const MockFetch = (input, init) => {
|
||||
const url = typeof input === "string" ? input : input.url;
|
||||
|
||||
let responseHandler;
|
||||
let responseText;
|
||||
|
||||
const handlers = {
|
||||
"account/info": {"username":"greenlamp","lastSessionSlot":0},
|
||||
"savedata/session": {},
|
||||
"savedata/system": {},
|
||||
"savedata/updateall": "",
|
||||
"daily/rankingpagecount": { data: 0 },
|
||||
"game/titlestats": {"playerCount":0,"battleCount":5},
|
||||
"daily/rankings": [],
|
||||
};
|
||||
|
||||
|
||||
for (const key of Object.keys(handlers)) {
|
||||
if (url.includes(key)) {
|
||||
responseHandler = async() => handlers[key];
|
||||
responseText = async() => handlers[key] ? JSON.stringify(handlers[key]) : handlers[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: responseHandler,
|
||||
text: responseText,
|
||||
});
|
||||
};
|
42
src/test/utils/mocks/mockLoader.ts
Normal file
42
src/test/utils/mocks/mockLoader.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import CacheManager = Phaser.Cache.CacheManager;
|
||||
|
||||
|
||||
export default class MockLoader {
|
||||
public cacheManager;
|
||||
constructor(scene) {
|
||||
this.cacheManager = new CacheManager(scene);
|
||||
}
|
||||
|
||||
once(event, callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
setBaseURL(url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
video() {
|
||||
return null;
|
||||
}
|
||||
|
||||
spritesheet(key, url, frameConfig) {
|
||||
}
|
||||
|
||||
audio(key, url) {
|
||||
|
||||
}
|
||||
|
||||
isLoading() {
|
||||
return false;
|
||||
}
|
||||
|
||||
start() {
|
||||
}
|
||||
|
||||
image() {
|
||||
|
||||
}
|
||||
|
||||
atlas(key, textureUrl, atlasUrl) {
|
||||
}
|
||||
}
|
27
src/test/utils/mocks/mockLocalStorage.ts
Normal file
27
src/test/utils/mocks/mockLocalStorage.ts
Normal file
@ -0,0 +1,27 @@
|
||||
const mockLocalStorage = (() => {
|
||||
let store = {} as Storage;
|
||||
|
||||
return {
|
||||
getItem(key: string) {
|
||||
return store[key];
|
||||
},
|
||||
|
||||
setItem(key: string, value: string) {
|
||||
store[key] = value;
|
||||
},
|
||||
|
||||
hasOwnProperty(key: string) {
|
||||
return store.hasOwnProperty(key);
|
||||
},
|
||||
|
||||
removeItem(key: string) {
|
||||
delete store[key];
|
||||
},
|
||||
|
||||
clear() {
|
||||
store = {} as Storage;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default mockLocalStorage;
|
85
src/test/utils/mocks/mockTextureManager.ts
Normal file
85
src/test/utils/mocks/mockTextureManager.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer";
|
||||
import MockSprite from "#app/test/utils/mocks/mocksContainer/mockSprite";
|
||||
import MockRectangle from "#app/test/utils/mocks/mocksContainer/mockRectangle";
|
||||
import MockNineslice from "#app/test/utils/mocks/mocksContainer/mockNineslice";
|
||||
import MockImage from "#app/test/utils/mocks/mocksContainer/mockImage";
|
||||
import MockText from "#app/test/utils/mocks/mocksContainer/mockText";
|
||||
import MockPolygon from "#app/test/utils/mocks/mocksContainer/mockPolygon";
|
||||
|
||||
|
||||
export default class MockTextureManager {
|
||||
private textures: Map<string, any>;
|
||||
private scene;
|
||||
public add;
|
||||
public displayList;
|
||||
public list = [];
|
||||
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.textures = new Map();
|
||||
this.displayList = new Phaser.GameObjects.DisplayList(scene);
|
||||
this.add = {
|
||||
container: this.container.bind(this),
|
||||
sprite: this.sprite.bind(this),
|
||||
tileSprite: this.sprite.bind(this),
|
||||
existing: this.existing.bind(this),
|
||||
rectangle: this.rectangle.bind(this),
|
||||
nineslice: this.nineslice.bind(this),
|
||||
image: this.image.bind(this),
|
||||
polygon: this.polygon.bind(this),
|
||||
text: this.text.bind(this),
|
||||
bitmapText: this.text.bind(this),
|
||||
displayList: this.displayList,
|
||||
};
|
||||
}
|
||||
|
||||
container(x, y) {
|
||||
const container = new MockContainer(this, x, y);
|
||||
this.list.push(container);
|
||||
return container;
|
||||
}
|
||||
|
||||
sprite(x,y, texture) {
|
||||
const sprite = new MockSprite(this, x, y, texture);
|
||||
this.list.push(sprite);
|
||||
return sprite;
|
||||
}
|
||||
|
||||
existing(obj) {
|
||||
// const whitelist = ["ArenaBase", "PlayerPokemon", "EnemyPokemon"];
|
||||
// const key = obj.constructor.name;
|
||||
// if (whitelist.includes(key) || obj.texture?.key?.includes("trainer_")) {
|
||||
// this.containers.push(obj);
|
||||
// }
|
||||
}
|
||||
|
||||
rectangle(x, y, width, height, fillColor) {
|
||||
const rectangle = new MockRectangle(this, x, y, width, height, fillColor);
|
||||
this.list.push(rectangle);
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
nineslice(x, y, texture, frame, width, height, leftWidth, rightWidth, topHeight, bottomHeight) {
|
||||
const nineSlice = new MockNineslice(this, x, y, texture, frame, width, height, leftWidth, rightWidth, topHeight, bottomHeight);
|
||||
this.list.push(nineSlice);
|
||||
return nineSlice;
|
||||
}
|
||||
|
||||
image(x, y, texture) {
|
||||
const image = new MockImage(this, x, y, texture);
|
||||
this.list.push(image);
|
||||
return image;
|
||||
}
|
||||
|
||||
text(x, y, content, styleOptions) {
|
||||
const text = new MockText(this, x, y, content, styleOptions);
|
||||
this.list.push(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
polygon(x, y, content, fillColor, fillAlpha) {
|
||||
const polygon = new MockPolygon(this, x, y, content, fillColor, fillAlpha);
|
||||
this.list.push(polygon);
|
||||
return polygon;
|
||||
}
|
||||
}
|
207
src/test/utils/mocks/mocksContainer/mockContainer.ts
Normal file
207
src/test/utils/mocks/mocksContainer/mockContainer.ts
Normal file
@ -0,0 +1,207 @@
|
||||
import MockTextureManager from "#app/test/utils/mocks/mockTextureManager";
|
||||
|
||||
export default class MockContainer {
|
||||
protected x;
|
||||
protected y;
|
||||
protected scene;
|
||||
protected width;
|
||||
protected height;
|
||||
protected visible;
|
||||
private alpha;
|
||||
private style;
|
||||
public frame;
|
||||
protected textureManager;
|
||||
public list = [];
|
||||
|
||||
constructor(textureManager: MockTextureManager, x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.frame = {};
|
||||
this.textureManager = textureManager;
|
||||
}
|
||||
setVisible(visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
once(event, callback, source) {
|
||||
}
|
||||
|
||||
off(event, callback, source) {
|
||||
}
|
||||
|
||||
removeFromDisplayList() {
|
||||
// same as remove or destroy
|
||||
}
|
||||
|
||||
addedToScene() {
|
||||
// This callback is invoked when this Game Object is added to a Scene.
|
||||
}
|
||||
|
||||
setSize(width, height) {
|
||||
// Sets the size of this Game Object.
|
||||
}
|
||||
|
||||
setMask() {
|
||||
/// Sets the mask that this Game Object will use to render with.
|
||||
}
|
||||
|
||||
setPositionRelative(source, x, y) {
|
||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||
}
|
||||
|
||||
setInteractive(hitArea?, callback?, dropZone?) {
|
||||
/// Sets the InteractiveObject to be a drop zone for a drag and drop operation.
|
||||
}
|
||||
setOrigin(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
setAlpha(alpha) {
|
||||
this.alpha = alpha;
|
||||
}
|
||||
|
||||
setFrame(frame, updateSize?: boolean, updateOrigin?: boolean) {
|
||||
// Sets the frame this Game Object will use to render with.
|
||||
}
|
||||
|
||||
setScale(scale) {
|
||||
// Sets the scale of this Game Object.
|
||||
}
|
||||
|
||||
setPosition(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
setX(x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
setY(y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
setShadow(shadowXpos, shadowYpos, shadowColor) {
|
||||
// Sets the shadow settings for this Game Object.
|
||||
}
|
||||
|
||||
setLineSpacing(lineSpacing) {
|
||||
// Sets the line spacing value of this Game Object.
|
||||
}
|
||||
|
||||
setText(text) {
|
||||
// Sets the text this Game Object will display.
|
||||
}
|
||||
|
||||
setAngle(angle) {
|
||||
// Sets the angle of this Game Object.
|
||||
}
|
||||
|
||||
setShadowOffset(offsetX, offsetY) {
|
||||
// Sets the shadow offset values.
|
||||
}
|
||||
|
||||
setWordWrapWidth(width) {
|
||||
// Sets the width (in pixels) to use for wrapping lines.
|
||||
}
|
||||
|
||||
setFontSize(fontSize) {
|
||||
// Sets the font size of this Game Object.
|
||||
}
|
||||
getBounds() {
|
||||
return { width: this.width, height: this.height };
|
||||
}
|
||||
|
||||
setColor(color) {
|
||||
// Sets the tint of this Game Object.
|
||||
}
|
||||
|
||||
setShadowColor(color) {
|
||||
// Sets the shadow color.
|
||||
}
|
||||
|
||||
setTint(color) {
|
||||
// Sets the tint of this Game Object.
|
||||
}
|
||||
|
||||
setStrokeStyle(thickness, color) {
|
||||
// Sets the stroke style for the graphics.
|
||||
return this;
|
||||
}
|
||||
|
||||
setDepth(depth) {
|
||||
// Sets the depth of this Game Object.
|
||||
}
|
||||
|
||||
setTexture(texture) {
|
||||
// Sets the texture this Game Object will use to render with.
|
||||
}
|
||||
|
||||
clearTint() {
|
||||
// Clears any previously set tint.
|
||||
}
|
||||
|
||||
sendToBack() {
|
||||
// Sends this Game Object to the back of its parent's display list.
|
||||
}
|
||||
|
||||
moveAbove(obj) {
|
||||
// Moves this Game Object to be above the given Game Object in the display list.
|
||||
}
|
||||
|
||||
moveBelow(obj) {
|
||||
// Moves this Game Object to be below the given Game Object in the display list.
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
// return this.phaserSprite.setName(name);
|
||||
}
|
||||
|
||||
bringToTop(obj) {
|
||||
// Brings this Game Object to the top of its parents display list.
|
||||
}
|
||||
|
||||
on(event, callback, source) {
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
// Adds a child to this Game Object.
|
||||
this.list.push(obj);
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
// Removes all Game Objects from this Container.
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
addAt(obj, index) {
|
||||
// Adds a Game Object to this Container at the given index.
|
||||
this.list.splice(index, 0, obj);
|
||||
}
|
||||
|
||||
remove(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
if (index !== -1) {
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
return index || -1;
|
||||
}
|
||||
|
||||
getAt(index) {
|
||||
return this.list[index];
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
}
|
96
src/test/utils/mocks/mocksContainer/mockGraphics.ts
Normal file
96
src/test/utils/mocks/mocksContainer/mockGraphics.ts
Normal file
@ -0,0 +1,96 @@
|
||||
export default class MockGraphics {
|
||||
private scene;
|
||||
public list = [];
|
||||
constructor(textureManager, config) {
|
||||
this.scene = textureManager.scene;
|
||||
}
|
||||
|
||||
fillStyle(color) {
|
||||
// Sets the fill style to be used by the fill methods.
|
||||
}
|
||||
|
||||
beginPath() {
|
||||
// Starts a new path by emptying the list of sub-paths. Call this method when you want to create a new path.
|
||||
}
|
||||
|
||||
fillRect(x, y, width, height) {
|
||||
// Adds a rectangle shape to the path which is filled when you call fill().
|
||||
}
|
||||
|
||||
createGeometryMask() {
|
||||
// Creates a geometry mask.
|
||||
}
|
||||
|
||||
setOrigin(x, y) {
|
||||
}
|
||||
|
||||
setAlpha(alpha) {
|
||||
}
|
||||
|
||||
setVisible(visible) {
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
}
|
||||
|
||||
once(event, callback, source) {
|
||||
}
|
||||
|
||||
removeFromDisplayList() {
|
||||
// same as remove or destroy
|
||||
}
|
||||
|
||||
addedToScene() {
|
||||
// This callback is invoked when this Game Object is added to a Scene.
|
||||
}
|
||||
|
||||
setPositionRelative(source, x, y) {
|
||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
setScale(scale) {
|
||||
// Sets the scale of this Game Object.
|
||||
}
|
||||
|
||||
off(event, callback, source) {
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
// Adds a child to this Game Object.
|
||||
this.list.push(obj);
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
// Removes all Game Objects from this Container.
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
addAt(obj, index) {
|
||||
// Adds a Game Object to this Container at the given index.
|
||||
this.list.splice(index, 0, obj);
|
||||
}
|
||||
|
||||
remove(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
if (index !== -1) {
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
return index || -1;
|
||||
}
|
||||
|
||||
getAt(index) {
|
||||
return this.list[index];
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this.list;
|
||||
}
|
||||
}
|
11
src/test/utils/mocks/mocksContainer/mockImage.ts
Normal file
11
src/test/utils/mocks/mocksContainer/mockImage.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer";
|
||||
|
||||
|
||||
export default class MockImage extends MockContainer {
|
||||
private texture;
|
||||
|
||||
constructor(textureManager, x, y, texture) {
|
||||
super(textureManager, x, y);
|
||||
this.texture = texture;
|
||||
}
|
||||
}
|
20
src/test/utils/mocks/mocksContainer/mockNineslice.ts
Normal file
20
src/test/utils/mocks/mocksContainer/mockNineslice.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer";
|
||||
|
||||
|
||||
export default class MockNineslice extends MockContainer {
|
||||
private texture;
|
||||
private leftWidth;
|
||||
private rightWidth;
|
||||
private topHeight;
|
||||
private bottomHeight;
|
||||
|
||||
constructor(textureManager, x, y, texture, frame, width, height, leftWidth, rightWidth, topHeight, bottomHeight) {
|
||||
super(textureManager, x, y);
|
||||
this.texture = texture;
|
||||
this.frame = frame;
|
||||
this.leftWidth = leftWidth;
|
||||
this.rightWidth = rightWidth;
|
||||
this.topHeight = topHeight;
|
||||
this.bottomHeight = bottomHeight;
|
||||
}
|
||||
}
|
9
src/test/utils/mocks/mocksContainer/mockPolygon.ts
Normal file
9
src/test/utils/mocks/mocksContainer/mockPolygon.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import MockContainer from "#app/test/utils/mocks/mocksContainer/mockContainer";
|
||||
|
||||
|
||||
export default class MockPolygon extends MockContainer {
|
||||
constructor(textureManager, x, y, content, fillColor, fillAlpha) {
|
||||
super(textureManager, x, y);
|
||||
}
|
||||
}
|
||||
|
74
src/test/utils/mocks/mocksContainer/mockRectangle.ts
Normal file
74
src/test/utils/mocks/mocksContainer/mockRectangle.ts
Normal file
@ -0,0 +1,74 @@
|
||||
export default class MockRectangle {
|
||||
private fillColor;
|
||||
private scene;
|
||||
public list = [];
|
||||
|
||||
constructor(textureManager, x, y, width, height, fillColor) {
|
||||
this.fillColor = fillColor;
|
||||
this.scene = textureManager.scene;
|
||||
}
|
||||
setOrigin(x, y) {
|
||||
}
|
||||
|
||||
setAlpha(alpha) {
|
||||
}
|
||||
setVisible(visible) {
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
}
|
||||
|
||||
once(event, callback, source) {
|
||||
}
|
||||
|
||||
removeFromDisplayList() {
|
||||
// same as remove or destroy
|
||||
}
|
||||
|
||||
addedToScene() {
|
||||
// This callback is invoked when this Game Object is added to a Scene.
|
||||
}
|
||||
|
||||
setPositionRelative(source, x, y) {
|
||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
// Adds a child to this Game Object.
|
||||
this.list.push(obj);
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
// Removes all Game Objects from this Container.
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
addAt(obj, index) {
|
||||
// Adds a Game Object to this Container at the given index.
|
||||
this.list.splice(index, 0, obj);
|
||||
}
|
||||
|
||||
remove(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
if (index !== -1) {
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
return index || -1;
|
||||
}
|
||||
|
||||
getAt(index) {
|
||||
return this.list[index];
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this.list;
|
||||
}
|
||||
}
|
205
src/test/utils/mocks/mocksContainer/mockSprite.ts
Normal file
205
src/test/utils/mocks/mocksContainer/mockSprite.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import Sprite = Phaser.GameObjects.Sprite;
|
||||
import Frame = Phaser.Textures.Frame;
|
||||
import Phaser from "phaser";
|
||||
|
||||
|
||||
export default class MockSprite {
|
||||
private phaserSprite;
|
||||
public pipelineData;
|
||||
public texture;
|
||||
public key;
|
||||
public frame;
|
||||
public textureManager;
|
||||
public scene;
|
||||
public anims;
|
||||
public list = [];
|
||||
constructor(textureManager, x, y, texture) {
|
||||
this.textureManager = textureManager;
|
||||
this.scene = textureManager.scene;
|
||||
Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive;
|
||||
// @ts-ignore
|
||||
Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture;
|
||||
Phaser.GameObjects.Sprite.prototype.setSizeToFrame = this.setSizeToFrame;
|
||||
Phaser.GameObjects.Sprite.prototype.setFrame = this.setFrame;
|
||||
// Phaser.GameObjects.Sprite.prototype.disable = this.disable;
|
||||
|
||||
// Phaser.GameObjects.Sprite.prototype.texture = { frameTotal: 1, get: () => null };
|
||||
this.phaserSprite = new Phaser.GameObjects.Sprite(textureManager.scene, x, y, texture);
|
||||
this.pipelineData = {};
|
||||
this.texture = {
|
||||
key: texture || "",
|
||||
};
|
||||
this.anims = {
|
||||
pause: () => null,
|
||||
};
|
||||
}
|
||||
|
||||
setTexture(key: string, frame?: string | number) {
|
||||
return this;
|
||||
}
|
||||
|
||||
setSizeToFrame(frame?: boolean | Frame): Sprite {
|
||||
return {} as Sprite;
|
||||
}
|
||||
|
||||
setPipeline(obj) {
|
||||
// Sets the pipeline of this Game Object.
|
||||
return this.phaserSprite.setPipeline(obj);
|
||||
}
|
||||
|
||||
off(event, callback, source) {
|
||||
}
|
||||
|
||||
setTintFill(color) {
|
||||
// Sets the tint fill color.
|
||||
return this.phaserSprite.setTintFill(color);
|
||||
}
|
||||
|
||||
setScale(scale) {
|
||||
return this.phaserSprite.setScale(scale);
|
||||
}
|
||||
|
||||
setOrigin(x, y) {
|
||||
return this.phaserSprite.setOrigin(x, y);
|
||||
}
|
||||
|
||||
setSize(width, height) {
|
||||
// Sets the size of this Game Object.
|
||||
return this.phaserSprite.setSize(width, height);
|
||||
}
|
||||
|
||||
once(event, callback, source) {
|
||||
return this.phaserSprite.once(event, callback, source);
|
||||
}
|
||||
|
||||
removeFromDisplayList() {
|
||||
// same as remove or destroy
|
||||
return this.phaserSprite.removeFromDisplayList();
|
||||
}
|
||||
|
||||
addedToScene() {
|
||||
// This callback is invoked when this Game Object is added to a Scene.
|
||||
return this.phaserSprite.addedToScene();
|
||||
}
|
||||
|
||||
setVisible(visible) {
|
||||
return this.phaserSprite.setVisible(visible);
|
||||
}
|
||||
|
||||
setPosition(x, y) {
|
||||
return this.phaserSprite.setPosition(x, y);
|
||||
}
|
||||
|
||||
stop() {
|
||||
return this.phaserSprite.stop();
|
||||
}
|
||||
|
||||
setInteractive(hitArea, hitAreaCallback, dropZone) {
|
||||
return null;
|
||||
}
|
||||
|
||||
on(event, callback, source) {
|
||||
return this.phaserSprite.on(event, callback, source);
|
||||
}
|
||||
|
||||
setAlpha(alpha) {
|
||||
return this.phaserSprite.setAlpha(alpha);
|
||||
}
|
||||
|
||||
setTint(color) {
|
||||
// Sets the tint of this Game Object.
|
||||
return this.phaserSprite.setTint(color);
|
||||
}
|
||||
|
||||
setFrame(frame, updateSize?: boolean, updateOrigin?: boolean) {
|
||||
// Sets the frame this Game Object will use to render with.
|
||||
this.frame = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
setPositionRelative(source, x, y) {
|
||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||
return this.phaserSprite.setPositionRelative(source, x, y);
|
||||
}
|
||||
|
||||
setCrop(x, y, width, height) {
|
||||
// Sets the crop size of this Game Object.
|
||||
return this.phaserSprite.setCrop(x, y, width, height);
|
||||
}
|
||||
|
||||
clearTint() {
|
||||
// Clears any previously set tint.
|
||||
return this.phaserSprite.clearTint();
|
||||
}
|
||||
|
||||
disableInteractive() {
|
||||
// Disables Interactive features of this Game Object.
|
||||
return null;
|
||||
}
|
||||
|
||||
apply() {
|
||||
return this.phaserSprite.apply();
|
||||
}
|
||||
|
||||
play() {
|
||||
// return this.phaserSprite.play();
|
||||
}
|
||||
|
||||
setPipelineData(key, value) {
|
||||
this.pipelineData[key] = value;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
return this.phaserSprite.destroy();
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
return this.phaserSprite.setName(name);
|
||||
}
|
||||
|
||||
setAngle(angle) {
|
||||
return this.phaserSprite.setAngle(angle);
|
||||
}
|
||||
|
||||
setMask() {
|
||||
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
// Adds a child to this Game Object.
|
||||
this.list.push(obj);
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
// Removes all Game Objects from this Container.
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
addAt(obj, index) {
|
||||
// Adds a Game Object to this Container at the given index.
|
||||
this.list.splice(index, 0, obj);
|
||||
}
|
||||
|
||||
remove(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
if (index !== -1) {
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
return index || -1;
|
||||
}
|
||||
|
||||
getAt(index) {
|
||||
return this.list[index];
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this.list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
265
src/test/utils/mocks/mocksContainer/mockText.ts
Normal file
265
src/test/utils/mocks/mocksContainer/mockText.ts
Normal file
@ -0,0 +1,265 @@
|
||||
import UI from "#app/ui/ui";
|
||||
|
||||
export default class MockText {
|
||||
private phaserText;
|
||||
private wordWrapWidth;
|
||||
private splitRegExp;
|
||||
private scene;
|
||||
private textureManager;
|
||||
public list = [];
|
||||
constructor(textureManager, x, y, content, styleOptions) {
|
||||
this.scene = textureManager.scene;
|
||||
this.textureManager = textureManager;
|
||||
// Phaser.GameObjects.TextStyle.prototype.setStyle = () => null;
|
||||
// Phaser.GameObjects.Text.prototype.updateText = () => null;
|
||||
// Phaser.Textures.TextureManager.prototype.addCanvas = () => {};
|
||||
UI.prototype.showText = this.showText;
|
||||
// super(scene, x, y);
|
||||
// this.phaserText = new Phaser.GameObjects.Text(scene, x, y, content, styleOptions);
|
||||
}
|
||||
|
||||
runWordWrap(text) {
|
||||
if (!text) {
|
||||
return "";
|
||||
}
|
||||
let result = "";
|
||||
this.splitRegExp = /(?:\r\n|\r|\n)/;
|
||||
const lines = text.split(this.splitRegExp);
|
||||
const lastLineIndex = lines.length - 1;
|
||||
const whiteSpaceWidth = 2;
|
||||
|
||||
for (let i = 0; i <= lastLineIndex; i++) {
|
||||
let spaceLeft = this.wordWrapWidth;
|
||||
const words = lines[i].split(" ");
|
||||
const lastWordIndex = words.length - 1;
|
||||
|
||||
for (let j = 0; j <= lastWordIndex; j++) {
|
||||
const word = words[j];
|
||||
const wordWidth = word.length * 2;
|
||||
let wordWidthWithSpace = wordWidth;
|
||||
|
||||
if (j < lastWordIndex) {
|
||||
wordWidthWithSpace += whiteSpaceWidth;
|
||||
}
|
||||
|
||||
if (wordWidthWithSpace > spaceLeft) {
|
||||
// Skip printing the newline if it's the first word of the line that is greater
|
||||
// than the word wrap width.
|
||||
if (j > 0) {
|
||||
result += "\n";
|
||||
spaceLeft = this.wordWrapWidth;
|
||||
}
|
||||
}
|
||||
|
||||
result += word;
|
||||
|
||||
if (j < lastWordIndex) {
|
||||
result += " ";
|
||||
spaceLeft -= wordWidthWithSpace;
|
||||
} else {
|
||||
spaceLeft -= wordWidth;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < lastLineIndex) {
|
||||
result += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
showText(text, delay, callback, callbackDelay, prompt, promptDelay) {
|
||||
this.scene.messageWrapper.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
setScale(scale) {
|
||||
// return this.phaserText.setScale(scale);
|
||||
}
|
||||
|
||||
setShadow(shadowXpos, shadowYpos, shadowColor) {
|
||||
// Sets the shadow settings for this Game Object.
|
||||
// return this.phaserText.setShadow(shadowXpos, shadowYpos, shadowColor);
|
||||
}
|
||||
|
||||
setLineSpacing(lineSpacing) {
|
||||
// Sets the line spacing value of this Game Object.
|
||||
// return this.phaserText.setLineSpacing(lineSpacing);
|
||||
}
|
||||
|
||||
setOrigin(x, y) {
|
||||
// return this.phaserText.setOrigin(x, y);
|
||||
}
|
||||
|
||||
once(event, callback, source) {
|
||||
// return this.phaserText.once(event, callback, source);
|
||||
}
|
||||
|
||||
off(event, callback, obj) {
|
||||
}
|
||||
|
||||
removedFromScene() {
|
||||
|
||||
}
|
||||
|
||||
addToDisplayList() {
|
||||
|
||||
}
|
||||
|
||||
setStroke(color, thickness) {
|
||||
// Sets the stroke color and thickness.
|
||||
// return this.phaserText.setStroke(color, thickness);
|
||||
}
|
||||
|
||||
removeFromDisplayList() {
|
||||
// same as remove or destroy
|
||||
// return this.phaserText.removeFromDisplayList();
|
||||
}
|
||||
|
||||
addedToScene() {
|
||||
// This callback is invoked when this Game Object is added to a Scene.
|
||||
// return this.phaserText.addedToScene();
|
||||
}
|
||||
|
||||
setVisible(visible) {
|
||||
// return this.phaserText.setVisible(visible);
|
||||
}
|
||||
|
||||
setY(y) {
|
||||
// return this.phaserText.setY(y);
|
||||
}
|
||||
|
||||
setX(x) {
|
||||
// return this.phaserText.setX(x);
|
||||
}
|
||||
|
||||
setText(text) {
|
||||
// Sets the text this Game Object will display.
|
||||
// return this.phaserText.setText(text);
|
||||
}
|
||||
|
||||
setAngle(angle) {
|
||||
// Sets the angle of this Game Object.
|
||||
// return this.phaserText.setAngle(angle);
|
||||
}
|
||||
|
||||
setPositionRelative(source, x, y) {
|
||||
/// Sets the position of this Game Object to be a relative position from the source Game Object.
|
||||
// return this.phaserText.setPositionRelative(source, x, y);
|
||||
}
|
||||
|
||||
setShadowOffset(offsetX, offsetY) {
|
||||
// Sets the shadow offset values.
|
||||
// return this.phaserText.setShadowOffset(offsetX, offsetY);
|
||||
}
|
||||
|
||||
setWordWrapWidth(width) {
|
||||
// Sets the width (in pixels) to use for wrapping lines.
|
||||
this.wordWrapWidth = width;
|
||||
}
|
||||
|
||||
setFontSize(fontSize) {
|
||||
// Sets the font size of this Game Object.
|
||||
// return this.phaserText.setFontSize(fontSize);
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
// return this.phaserText.getBounds();
|
||||
return {
|
||||
width: 1,
|
||||
};
|
||||
}
|
||||
|
||||
setColor(color) {
|
||||
// Sets the tint of this Game Object.
|
||||
// return this.phaserText.setColor(color);
|
||||
}
|
||||
|
||||
setShadowColor(color) {
|
||||
// Sets the shadow color.
|
||||
// return this.phaserText.setShadowColor(color);
|
||||
}
|
||||
|
||||
setTint(color) {
|
||||
// Sets the tint of this Game Object.
|
||||
// return this.phaserText.setTint(color);
|
||||
}
|
||||
|
||||
setStrokeStyle(thickness, color) {
|
||||
// Sets the stroke style for the graphics.
|
||||
// return this.phaserText.setStrokeStyle(thickness, color);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
// return this.phaserText.destroy();
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
setAlpha(alpha) {
|
||||
// return this.phaserText.setAlpha(alpha);
|
||||
}
|
||||
|
||||
setName(name) {
|
||||
// return this.phaserText.setName(name);
|
||||
}
|
||||
|
||||
setAlign(align) {
|
||||
// return this.phaserText.setAlign(align);
|
||||
}
|
||||
|
||||
setMask() {
|
||||
/// Sets the mask that this Game Object will use to render with.
|
||||
}
|
||||
|
||||
getBottomLeft() {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
}
|
||||
|
||||
getTopLeft() {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
// Adds a child to this Game Object.
|
||||
this.list.push(obj);
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
// Removes all Game Objects from this Container.
|
||||
this.list = [];
|
||||
}
|
||||
|
||||
addAt(obj, index) {
|
||||
// Adds a Game Object to this Container at the given index.
|
||||
this.list.splice(index, 0, obj);
|
||||
}
|
||||
|
||||
remove(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
if (index !== -1) {
|
||||
this.list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(obj) {
|
||||
const index = this.list.indexOf(obj);
|
||||
return index || -1;
|
||||
}
|
||||
|
||||
getAt(index) {
|
||||
return this.list[index];
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this.list;
|
||||
}
|
||||
}
|
279
src/test/utils/phaseInterceptor.ts
Normal file
279
src/test/utils/phaseInterceptor.ts
Normal file
@ -0,0 +1,279 @@
|
||||
import {
|
||||
BattleEndPhase,
|
||||
BerryPhase,
|
||||
CheckSwitchPhase, CommandPhase, DamagePhase, EggLapsePhase,
|
||||
EncounterPhase, EnemyCommandPhase, FaintPhase,
|
||||
LoginPhase, MessagePhase, MoveEffectPhase, MoveEndPhase, MovePhase, NewBattlePhase, NextEncounterPhase,
|
||||
PostSummonPhase,
|
||||
SelectGenderPhase, SelectModifierPhase,
|
||||
SelectStarterPhase, ShinySparklePhase, ShowAbilityPhase, StatChangePhase, SummonPhase,
|
||||
TitlePhase, ToggleDoublePositionPhase, TurnEndPhase, TurnInitPhase, TurnStartPhase, VictoryPhase
|
||||
} from "#app/phases";
|
||||
import {Mode} from "#app/ui/ui";
|
||||
|
||||
export default class PhaseInterceptor {
|
||||
public scene;
|
||||
public phases = {};
|
||||
public log;
|
||||
private onHold;
|
||||
private interval;
|
||||
private promptInterval;
|
||||
private intervalRun;
|
||||
private prompts;
|
||||
private phaseFrom;
|
||||
|
||||
/**
|
||||
* List of phases with their corresponding start methods.
|
||||
*/
|
||||
private PHASES = [
|
||||
[LoginPhase, this.startPhase],
|
||||
[TitlePhase, this.startPhase],
|
||||
[SelectGenderPhase, this.startPhase],
|
||||
[EncounterPhase, this.startPhase],
|
||||
[SelectStarterPhase, this.startPhase],
|
||||
[PostSummonPhase, this.startPhase],
|
||||
[SummonPhase, this.startPhase],
|
||||
[ToggleDoublePositionPhase, this.startPhase],
|
||||
[CheckSwitchPhase, this.startPhase],
|
||||
[ShowAbilityPhase, this.startPhase],
|
||||
[MessagePhase, this.startPhase],
|
||||
[TurnInitPhase, this.startPhase],
|
||||
[CommandPhase, this.startPhase],
|
||||
[EnemyCommandPhase, this.startPhase],
|
||||
[TurnStartPhase, this.startPhase],
|
||||
[MovePhase, this.startPhase],
|
||||
[MoveEffectPhase, this.startPhase],
|
||||
[DamagePhase, this.startPhase],
|
||||
[FaintPhase, this.startPhase],
|
||||
[BerryPhase, this.startPhase],
|
||||
[TurnEndPhase, this.startPhase],
|
||||
[BattleEndPhase, this.startPhase],
|
||||
[EggLapsePhase, this.startPhase],
|
||||
[SelectModifierPhase, this.startPhase],
|
||||
[NextEncounterPhase, this.startPhase],
|
||||
[NewBattlePhase, this.startPhase],
|
||||
[VictoryPhase, this.startPhase],
|
||||
[MoveEndPhase, this.startPhase],
|
||||
[StatChangePhase, this.startPhase],
|
||||
[ShinySparklePhase, this.startPhase],
|
||||
];
|
||||
|
||||
/**
|
||||
* Constructor to initialize the scene and properties, and to start the phase handling.
|
||||
* @param scene - The scene to be managed.
|
||||
*/
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.log = [];
|
||||
this.onHold = [];
|
||||
this.prompts = [];
|
||||
this.initPhases();
|
||||
this.startPromptHander();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the starting phase.
|
||||
* @param phaseFrom - The phase to start from.
|
||||
* @returns The instance of the PhaseInterceptor.
|
||||
*/
|
||||
runFrom(phaseFrom) {
|
||||
this.phaseFrom = phaseFrom;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to transition to a target phase.
|
||||
* @param phaseTo - The phase to transition to.
|
||||
* @returns A promise that resolves when the transition is complete.
|
||||
*/
|
||||
async to(phaseTo): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
await this.run(this.phaseFrom);
|
||||
this.phaseFrom = null;
|
||||
const targetName = typeof phaseTo === "string" ? phaseTo : phaseTo.name;
|
||||
this.intervalRun = setInterval(async () => {
|
||||
const currentPhase = this.onHold?.length && this.onHold[0];
|
||||
if (currentPhase && currentPhase.name !== targetName) {
|
||||
await this.run(currentPhase.name);
|
||||
} else if (currentPhase.name === targetName) {
|
||||
await this.run(currentPhase.name);
|
||||
clearInterval(this.intervalRun);
|
||||
return resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to run a phase with an optional skip function.
|
||||
* @param phaseTarget - The phase to run.
|
||||
* @param skipFn - Optional skip function.
|
||||
* @returns A promise that resolves when the phase is run.
|
||||
*/
|
||||
run(phaseTarget, skipFn?): Promise<void> {
|
||||
this.scene.moveAnimations = null; // Mandatory to avoid crash
|
||||
return new Promise(async (resolve) => {
|
||||
this.waitUntil(phaseTarget, skipFn).then(() => {
|
||||
const currentPhase = this.onHold.shift();
|
||||
currentPhase.call();
|
||||
resolve();
|
||||
}).catch(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to ensure a phase is run, to throw error on test if not.
|
||||
* @param phaseTarget - The phase to run.
|
||||
* @returns A promise that resolves when the phase is run.
|
||||
*/
|
||||
mustRun(phaseTarget): Promise<void> {
|
||||
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
|
||||
this.scene.moveAnimations = null; // Mandatory to avoid crash
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const interval = setInterval(async () => {
|
||||
const currentPhase = this.onHold?.length && this.onHold[0];
|
||||
if (currentPhase && currentPhase.name !== targetName) {
|
||||
reject(currentPhase);
|
||||
} else if (currentPhase && currentPhase.name === targetName) {
|
||||
clearInterval(interval);
|
||||
await this.run(phaseTarget);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to execute actions when about to run a phase. Does not run the phase, stop right before.
|
||||
* @param phaseTarget - The phase to run.
|
||||
* @param skipFn - Optional skip function.
|
||||
* @returns A promise that resolves when the phase is about to run.
|
||||
*/
|
||||
whenAboutToRun(phaseTarget, skipFn?): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
this.waitUntil(phaseTarget, skipFn).then(() => {
|
||||
resolve();
|
||||
}).catch(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to remove a phase from the list.
|
||||
* @param phaseTarget - The phase to remove.
|
||||
* @param skipFn - Optional skip function.
|
||||
* @returns A promise that resolves when the phase is removed.
|
||||
*/
|
||||
remove(phaseTarget, skipFn?): Promise<void> {
|
||||
return new Promise(async (resolve) => {
|
||||
this.waitUntil(phaseTarget, skipFn).then(() => {
|
||||
this.onHold.shift();
|
||||
this.scene.getCurrentPhase().end();
|
||||
resolve();
|
||||
}).catch(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to wait until a specific phase is reached.
|
||||
* @param phaseTarget - The phase to wait for.
|
||||
* @param skipFn - Optional skip function.
|
||||
* @returns A promise that resolves when the phase is reached.
|
||||
*/
|
||||
waitUntil(phaseTarget, skipFn?): Promise<void> {
|
||||
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.interval = setInterval(() => {
|
||||
const currentPhase = this.onHold?.length && this.onHold[0] && this.onHold[0].name;
|
||||
// if the currentPhase here is not filled, it means it's a phase we haven't added to the list
|
||||
if (currentPhase === targetName) {
|
||||
clearInterval(this.interval);
|
||||
return resolve();
|
||||
} else if (skipFn && skipFn()) {
|
||||
clearInterval(this.interval);
|
||||
return reject("Skipped phase");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to initialize phases and their corresponding methods.
|
||||
*/
|
||||
initPhases() {
|
||||
for (const [phase, method] of this.PHASES) {
|
||||
const originalStart = phase.prototype.start;
|
||||
this.phases[phase.name] = originalStart;
|
||||
phase.prototype.start = () => method.call(this, phase);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to start a phase and log it.
|
||||
* @param phase - The phase to start.
|
||||
*/
|
||||
startPhase(phase) {
|
||||
this.log.push(phase.name);
|
||||
const instance = this.scene.getCurrentPhase();
|
||||
this.onHold.push({
|
||||
name: phase.name,
|
||||
call: () => {
|
||||
this.phases[phase.name].apply(instance);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to start the prompt handler.
|
||||
*/
|
||||
startPromptHander() {
|
||||
this.promptInterval = setInterval(() => {
|
||||
if (this.prompts.length) {
|
||||
const actionForNextPrompt = this.prompts[0];
|
||||
const expireFn = actionForNextPrompt.expireFn && actionForNextPrompt.expireFn();
|
||||
const currentMode = this.scene.ui.getMode();
|
||||
const currentPhase = this.scene.getCurrentPhase().constructor.name;
|
||||
if (expireFn) {
|
||||
this.prompts.shift();
|
||||
} else if (currentMode === actionForNextPrompt.mode && currentPhase === actionForNextPrompt.phaseTarget) {
|
||||
this.prompts.shift().callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to add an action to the next prompt.
|
||||
* @param phaseTarget - The target phase for the prompt.
|
||||
* @param mode - The mode of the UI.
|
||||
* @param callback - The callback function to execute.
|
||||
* @param expireFn - The function to determine if the prompt has expired.
|
||||
*/
|
||||
addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn: () => void) {
|
||||
this.prompts.push({
|
||||
phaseTarget,
|
||||
mode,
|
||||
callback,
|
||||
expireFn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the original state of phases and clears intervals.
|
||||
*
|
||||
* This function iterates through all phases and resets their `start` method to the original
|
||||
* function stored in `this.phases`. Additionally, it clears the `promptInterval` and `interval`.
|
||||
*/
|
||||
restoreOg() {
|
||||
for (const [phase] of this.PHASES) {
|
||||
phase.prototype.start = this.phases[phase.name];
|
||||
}
|
||||
clearInterval(this.promptInterval);
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
}
|
1
src/test/utils/saves/everything.prsv
Normal file
1
src/test/utils/saves/everything.prsv
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user