Compare commits

...

9 Commits

Author SHA1 Message Date
Frederico Santos
c40717fd33
Merge pull request #3617 from KimJeongSun/fix-only-variant-form-bugs
[Hotfix] Fixed a bug occurring when only single rare/epic variant or a single rare form in Starter UI
2024-08-18 07:43:11 +01:00
KimJeongSun
69c1389ec4 add fix setting code to prevent form/variant bug when default form/variant setting is wrong.
in addition, that fix code include gender fix, so i revert old gender fix.
update wrong log message.
2024-08-18 15:12:20 +09:00
NightKev
0e6c2952ca
Make Disguise properly reset form on arena reset when fainted (#3612) 2024-08-18 05:05:04 +01:00
Mumble
192aa63635
[Bug][Hotfix] Final Boss MBH no longer transferrable (#3611)
* Should fix it.

* typedocs fixes

---------

Co-authored-by: Frutescens <info@laptop>
2024-08-18 04:28:06 +01:00
cam
abced6cf02
[Sprite] Lumineon female spritesheet fix (#3608)
fix from Vari
2024-08-18 04:17:21 +01:00
cam
5f6cb6ce00
[Sprite] Floette animation fix - Xatu female variant fix (#3610)
* 177-178 icons, variant icons

* 178 icons

* [HotFix][Sprite] Xatu female variants added

* [HotFix][Sprite] Floette json: update to match images

* Xatu variant- reverted _masterlist

reverted _masterlist.json
added anim files for both female variants

* [fix] Xatu female: show variants, added back sprite anim

Edited _masterlist on correct keys
added anim json for back sprites
2024-08-18 04:14:25 +01:00
AJ Fontaine
0e92366cac
Fixed egg moves being relearnable in daily runs (#3604) 2024-08-18 02:23:02 +01:00
innerthunder
8704723c9c
Fix missing form change logic for Cramorant (#3603) 2024-08-18 02:22:21 +01:00
Mumble
5ede6a54c6
[Hotfix] End Biome Catch Problems (#3605)
* Needs more testing.

* removed debugging

---------

Co-authored-by: Frutescens <info@laptop>
2024-08-18 01:09:28 +01:00
44 changed files with 30432 additions and 2820 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2513,7 +2513,6 @@
], ],
"meta": { "meta": {
"app": "https://www.codeandweb.com/texturepacker", "app": "https://www.codeandweb.com/texturepacker",
"version": "3.0", "version": "3.0"
"smartupdate": "$TexturePacker:SmartUpdate:edb2df3a947401efb05329a2c96d5d73:f256d83ef4df17c17958acc6e0432ab0:bad05b37c157676604256a043511a6a2$"
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -3815,6 +3815,11 @@
1, 1,
1 1
], ],
"178": [
0,
2,
2
],
"185": [ "185": [
0, 0,
1, 1,
@ -7833,6 +7838,11 @@
1, 1,
1 1
], ],
"178": [
0,
2,
2
],
"185": [ "185": [
0, 0,
1, 1,

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -5,7 +5,7 @@ import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species"; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
import { Constructor } from "#app/utils"; import { Constructor } from "#app/utils";
import * as Utils from "./utils"; import * as Utils from "./utils";
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier"; import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
import { PokeballType } from "./data/pokeball"; import { PokeballType } from "./data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
import { Phase } from "./phase"; import { Phase } from "./phase";
@ -37,7 +37,7 @@ import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
import { addUiThemeOverrides } from "./ui/ui-theme"; import { addUiThemeOverrides } from "./ui/ui-theme";
import PokemonData from "./system/pokemon-data"; import PokemonData from "./system/pokemon-data";
import { Nature } from "./data/nature"; import { Nature } from "./data/nature";
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem } from "./data/pokemon-forms"; import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem, SpeciesFormChange } from "./data/pokemon-forms";
import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase"; import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase";
import { getTypeRgb } from "./data/type"; import { getTypeRgb } from "./data/type";
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
@ -2579,7 +2579,7 @@ export default class BattleScene extends SceneBase {
// in case this is NECROZMA, determine which forms this // in case this is NECROZMA, determine which forms this
const matchingFormChangeOpts = pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon)); const matchingFormChangeOpts = pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon));
let matchingFormChange; let matchingFormChange: SpeciesFormChange | null;
if (pokemon.species.speciesId === Species.NECROZMA && matchingFormChangeOpts.length > 1) { if (pokemon.species.speciesId === Species.NECROZMA && matchingFormChangeOpts.length > 1) {
// Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves. // Ultra Necrozma is changing its form back, so we need to figure out into which form it devolves.
const formChangeItemModifiers = (this.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]).filter(m => m.active).map(m => m.formChangeItem); const formChangeItemModifiers = (this.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]).filter(m => m.active).map(m => m.formChangeItem);
@ -2666,7 +2666,9 @@ export default class BattleScene extends SceneBase {
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
this.fadeOutBgm(Utils.fixedInt(2000), false); this.fadeOutBgm(Utils.fixedInt(2000), false);
this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, undefined, () => { this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, undefined, () => {
this.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true); const finalBossMBH = getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as TurnHeldItemTransferModifier;
finalBossMBH.setTransferrableFalse();
this.addEnemyModifier(finalBossMBH, false, true);
pokemon.generateAndPopulateMoveset(1); pokemon.generateAndPopulateMoveset(1);
this.setFieldScale(0.75); this.setFieldScale(0.75);
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);

View File

@ -5039,6 +5039,7 @@ export function initAbilities() {
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
(pokemon) => Math.floor(pokemon.getMaxHp() / 8)) (pokemon) => Math.floor(pokemon.getMaxHp() / 8))
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
.bypassFaint()
.ignorable(), .ignorable(),
new Ability(Abilities.BATTLE_BOND, 7) new Ability(Abilities.BATTLE_BOND, 7)
.attr(PostVictoryFormChangeAbAttr, () => 2) .attr(PostVictoryFormChangeAbAttr, () => 2)
@ -5191,6 +5192,7 @@ export function initAbilities() {
.attr(FormBlockDamageAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, .attr(FormBlockDamageAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE,
(pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName })) (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }))
.attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostBattleInitFormChangeAbAttr, () => 0)
.bypassFaint()
.ignorable(), .ignorable(),
new Ability(Abilities.POWER_SPOT, 8) new Ability(Abilities.POWER_SPOT, 8)
.attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL], 1.3), .attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL], 1.3),

View File

@ -837,6 +837,8 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)), new SpeciesFormChange(Species.CRAMORANT, "", "gorging", new SpeciesFormChangeManualTrigger, true, new SpeciesFormChangeCondition(p => p.getHpRatio() < .5)),
new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true), new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeManualTrigger, true),
new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true), new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeManualTrigger, true),
new SpeciesFormChange(Species.CRAMORANT, "gulping", "", new SpeciesFormChangeActiveTrigger(false), true),
new SpeciesFormChange(Species.CRAMORANT, "gorging", "", new SpeciesFormChangeActiveTrigger(false), true),
] ]
}; };

View File

@ -922,7 +922,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/ */
getLearnableLevelMoves(): Moves[] { getLearnableLevelMoves(): Moves[] {
let levelMoves = this.getLevelMoves(1, true).map(lm => lm[1]); let levelMoves = this.getLevelMoves(1, true).map(lm => lm[1]);
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge()) { if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) {
levelMoves = this.getUnlockedEggMoves().concat(levelMoves); levelMoves = this.getUnlockedEggMoves().concat(levelMoves);
} }
return levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm)); return levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm));

View File

@ -62,7 +62,7 @@ export class GameMode implements GameModeConfig {
* @returns true if the game mode has that challenge * @returns true if the game mode has that challenge
*/ */
hasChallenge(challenge: Challenges): boolean { hasChallenge(challenge: Challenges): boolean {
return this.challenges.some(c => c.id === challenge); return this.challenges.some(c => c.id === challenge && c.value !== 0);
} }
/** /**

View File

@ -2338,7 +2338,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier {
* @see {@linkcode modifierTypes[MINI_BLACK_HOLE]} * @see {@linkcode modifierTypes[MINI_BLACK_HOLE]}
*/ */
export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
readonly isTransferrable: boolean = true; isTransferrable: boolean = true;
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
super(type, pokemonId, stackCount); super(type, pokemonId, stackCount);
} }
@ -2362,6 +2362,10 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
getMaxHeldItemCount(pokemon: Pokemon): integer { getMaxHeldItemCount(pokemon: Pokemon): integer {
return 1; return 1;
} }
setTransferrableFalse(): void {
this.isTransferrable = false;
}
} }
/** /**

View File

@ -2034,7 +2034,8 @@ export class CommandPhase extends FieldPhase {
} }
break; break;
case Command.BALL: case Command.BALL:
if (this.scene.arena.biomeType === Biome.END && (!this.scene.gameMode.isClassic || this.scene.gameMode.isFreshStartChallenge() || (this.scene.getEnemyField().filter(p => p.isActive(true)).some(p => !p.scene.gameData.dexData[p.species.speciesId].caughtAttr) && this.scene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarters).length - 1))) { const notInDex = (this.scene.getEnemyField().filter(p => p.isActive(true)).some(p => !p.scene.gameData.dexData[p.species.speciesId].caughtAttr) && this.scene.gameData.getStarterCount(d => !!d.caughtAttr) < Object.keys(speciesStarters).length - 1);
if (this.scene.arena.biomeType === Biome.END && (!this.scene.gameMode.isClassic || this.scene.gameMode.isFreshStartChallenge() || notInDex )) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => { this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => {

View File

@ -2,12 +2,12 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { getMovePosition } from "#test/utils/gameManagerUtils"; import { getMovePosition } from "#test/utils/gameManagerUtils";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Abilities } from "#enums/abilities";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { StatusEffect } from "#app/data/status-effect.js"; import { StatusEffect } from "#app/data/status-effect.js";
import { MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases.js"; import { CommandPhase, MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases.js";
import { BattleStat } from "#app/data/battle-stat.js"; import { BattleStat } from "#app/data/battle-stat.js";
import { SPLASH_ONLY } from "../utils/testUtils"; import { SPLASH_ONLY } from "../utils/testUtils";
import { Mode } from "#app/ui/ui.js";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -38,7 +38,7 @@ describe("Abilities - Disguise", () => {
game.override.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); game.override.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]);
}, TIMEOUT); }, TIMEOUT);
it("takes no damage from attacking move and transforms to Busted form, taking 1/8 max HP damage from the disguise breaking", async () => { it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => {
await game.startBattle(); await game.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!; const mimikyu = game.scene.getEnemyPokemon()!;
@ -134,17 +134,30 @@ describe("Abilities - Disguise", () => {
expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.formIndex).toBe(bustedForm);
}, TIMEOUT); }, TIMEOUT);
it("reverts to Disguised on arena reset", async () => { it("persists form change when wave changes with no arena reset", async () => {
game.override.startingWave(4); game.override.starterSpecies(0);
game.override.starterForms({
[Species.MIMIKYU]: bustedForm
});
await game.startBattle([Species.FURRET, Species.MIMIKYU]);
const mimikyu = game.scene.getParty()[1]!;
expect(mimikyu.formIndex).toBe(bustedForm);
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
await game.doKillOpponents();
await game.toNextWave();
expect(mimikyu.formIndex).toBe(bustedForm);
}, TIMEOUT);
it("reverts to Disguised form on arena reset", async () => {
game.override.startingWave(4);
game.override.starterSpecies(Species.MIMIKYU); game.override.starterSpecies(Species.MIMIKYU);
game.override.starterForms({ game.override.starterForms({
[Species.MIMIKYU]: bustedForm [Species.MIMIKYU]: bustedForm
}); });
game.override.enemySpecies(Species.MAGIKARP);
game.override.enemyAbility(Abilities.BALL_FETCH);
await game.startBattle(); await game.startBattle();
const mimikyu = game.scene.getPlayerPokemon()!; const mimikyu = game.scene.getPlayerPokemon()!;
@ -153,10 +166,41 @@ describe("Abilities - Disguise", () => {
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
await game.doKillOpponents(); await game.doKillOpponents();
await game.phaseInterceptor.to(TurnEndPhase); await game.toNextWave();
game.doSelectModifier();
await game.phaseInterceptor.to(TurnInitPhase);
expect(mimikyu.formIndex).toBe(disguisedForm); expect(mimikyu.formIndex).toBe(disguisedForm);
}, TIMEOUT); }, TIMEOUT);
it("reverts to Disguised form on biome change when fainted", async () => {
game.override.startingWave(10);
game.override.starterSpecies(0);
game.override.starterForms({
[Species.MIMIKYU]: bustedForm
});
await game.startBattle([Species.MIMIKYU, Species.FURRET]);
const mimikyu1 = game.scene.getPlayerPokemon()!;
expect(mimikyu1.formIndex).toBe(bustedForm);
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
await game.killPokemon(mimikyu1);
game.doSelectPartyPokemon(1);
await game.toNextTurn();
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
await game.doKillOpponents();
game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => { // TODO: Make tests run in set mode instead of switch mode
game.setMode(Mode.MESSAGE);
game.endPhase();
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase));
game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
game.setMode(Mode.MESSAGE);
game.endPhase();
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase));
await game.phaseInterceptor.to("PartyHealPhase");
expect(mimikyu1.formIndex).toBe(disguisedForm);
}, TIMEOUT);
}); });

View File

@ -84,6 +84,21 @@ describe("Abilities - Gulp Missile", () => {
expect(cramorant.formIndex).toBe(GORGING_FORM); expect(cramorant.formIndex).toBe(GORGING_FORM);
}); });
it("changes to base form when switched out after Surf or Dive is used", async () => {
await game.startBattle([Species.CRAMORANT, Species.MAGIKARP]);
const cramorant = game.scene.getPlayerPokemon()!;
game.doAttack(getMovePosition(game.scene, 0, Moves.SURF));
await game.toNextTurn();
game.doSwitchPokemon(1);
await game.toNextTurn(); // form change is delayed until after end of turn
expect(cramorant.formIndex).toBe(NORMAL_FORM);
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeUndefined();
expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeUndefined();
});
it("changes form during Dive's charge turn", async () => { it("changes form during Dive's charge turn", async () => {
await game.startBattle([Species.CRAMORANT]); await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!; const cramorant = game.scene.getPlayerPokemon()!;

View File

@ -15,6 +15,7 @@ import {
MovePhase, MovePhase,
NewBattlePhase, NewBattlePhase,
NextEncounterPhase, NextEncounterPhase,
PartyHealPhase,
PostSummonPhase, PostSummonPhase,
SelectGenderPhase, SelectGenderPhase,
SelectModifierPhase, SelectModifierPhase,
@ -92,6 +93,7 @@ export default class PhaseInterceptor {
[QuietFormChangePhase, this.startPhase], [QuietFormChangePhase, this.startPhase],
[SwitchPhase, this.startPhase], [SwitchPhase, this.startPhase],
[SwitchSummonPhase, this.startPhase], [SwitchSummonPhase, this.startPhase],
[PartyHealPhase, this.startPhase],
]; ];
private endBySetMode = [ private endBySetMode = [

View File

@ -2916,14 +2916,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const isCaught = this.scene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0); const isCaught = this.scene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0);
const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3); const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3);
const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2); const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2);
const isDefaultVariantCaught = !!(isCaught & DexAttr.DEFAULT_VARIANT);
const isVariantCaught = !!(isCaught & DexAttr.SHINY); const isVariantCaught = !!(isCaught & DexAttr.SHINY);
const isMaleCaught = !!(isCaught & DexAttr.MALE); const isMaleCaught = !!(isCaught & DexAttr.MALE);
const isFemaleCaught = !!(isCaught & DexAttr.FEMALE); const isFemaleCaught = !!(isCaught & DexAttr.FEMALE);
if (!dexEntry.caughtAttr) { const starterAttributes = this.starterPreferences[species.speciesId];
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)); const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId));
const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species); const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
if (!dexEntry.caughtAttr) {
if (shiny === undefined || shiny !== props.shiny) { if (shiny === undefined || shiny !== props.shiny) {
shiny = props.shiny; shiny = props.shiny;
} }
@ -2942,6 +2946,83 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (natureIndex === undefined || natureIndex !== defaultNature) { if (natureIndex === undefined || natureIndex !== defaultNature) {
natureIndex = defaultNature; natureIndex = defaultNature;
} }
} else {
// compare current shiny, formIndex, female, variant, abilityIndex, natureIndex with the caught ones
// if the current ones are not caught, we need to find the next caught ones
if (shiny) {
if (!(isVariantCaught || isVariant2Caught || isVariant3Caught)) {
shiny = false;
starterAttributes.shiny = false;
variant = 0;
starterAttributes.variant = 0;
} else {
shiny = true;
starterAttributes.shiny = true;
if (variant === 0 && !isDefaultVariantCaught) {
if (isVariant2Caught) {
variant = 1;
starterAttributes.variant = 1;
} else if (isVariant3Caught) {
variant = 2;
starterAttributes.variant = 2;
} else {
variant = 0;
starterAttributes.variant = 0;
}
} else if (variant === 1 && !isVariant2Caught) {
if (isVariantCaught) {
variant = 0;
starterAttributes.variant = 0;
} else if (isVariant3Caught) {
variant = 2;
starterAttributes.variant = 2;
} else {
variant = 0;
starterAttributes.variant = 0;
}
} else if (variant === 2 && !isVariant3Caught) {
if (isVariantCaught) {
variant = 0;
starterAttributes.variant = 0;
} else if (isVariant2Caught) {
variant = 1;
starterAttributes.variant = 1;
} else {
variant = 0;
starterAttributes.variant = 0;
}
}
}
}
if (female) {
if (!isFemaleCaught) {
female = false;
starterAttributes.female = false;
}
} else {
if (!isMaleCaught) {
female = true;
starterAttributes.female = true;
}
}
if (species.forms) {
const formCount = species.forms.length;
let newFormIndex = formIndex??0;
if (species.forms[newFormIndex]) {
const isValidForm = species.forms[newFormIndex].isStarterSelectable && dexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex);
if (!isValidForm) {
do {
newFormIndex = (newFormIndex + 1) % formCount;
if (species.forms[newFormIndex].isStarterSelectable && dexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex)) {
break;
}
} while (newFormIndex !== props.formIndex);
formIndex = newFormIndex;
starterAttributes.form = formIndex;
}
}
}
} }
this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default?
@ -2993,12 +3074,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
} }
if (dexEntry.caughtAttr && species.malePercent !== null) { if (dexEntry.caughtAttr && species.malePercent !== null) {
let gender: Gender; const gender = !female ? Gender.MALE : Gender.FEMALE;
if ((female && isFemaleCaught) || (!female && !isMaleCaught)) {
gender = Gender.FEMALE;
} else {
gender = Gender.MALE;
}
this.pokemonGenderText.setText(getGenderSymbol(gender)); this.pokemonGenderText.setText(getGenderSymbol(gender));
this.pokemonGenderText.setColor(getGenderColor(gender)); this.pokemonGenderText.setColor(getGenderColor(gender));
this.pokemonGenderText.setShadowColor(getGenderColor(gender, true)); this.pokemonGenderText.setShadowColor(getGenderColor(gender, true));
@ -3479,7 +3555,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
checkIconId(icon: Phaser.GameObjects.Sprite, species: PokemonSpecies, female: boolean, formIndex: number, shiny: boolean, variant: number) { checkIconId(icon: Phaser.GameObjects.Sprite, species: PokemonSpecies, female: boolean, formIndex: number, shiny: boolean, variant: number) {
if (icon.frame.name !== species.getIconId(female, formIndex, shiny, variant)) { if (icon.frame.name !== species.getIconId(female, formIndex, shiny, variant)) {
console.log(`${species.name}'s variant icon does not exist. Replacing with default.`); console.log(`${species.name}'s icon ${icon.frame.name} does not match getIconId with female: ${female}, formIndex: ${formIndex}, shiny: ${shiny}, variant: ${variant}`);
icon.setTexture(species.getIconAtlasKey(formIndex, false, variant)); icon.setTexture(species.getIconAtlasKey(formIndex, false, variant));
icon.setFrame(species.getIconId(female, formIndex, false, variant)); icon.setFrame(species.getIconId(female, formIndex, false, variant));
} }