Compare commits

..

No commits in common. "c40717fd33f57575a6c49cb9f1931776e41f2240" and "2b853bae2588daa6d1bf52b11fb665d38b521663" have entirely different histories.

44 changed files with 2752 additions and 30364 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.8 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: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2513,6 +2513,7 @@
],
"meta": {
"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.2 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

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 { Constructor } from "#app/utils";
import * as Utils from "./utils";
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems } from "./modifier/modifier";
import { PokeballType } from "./data/pokeball";
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
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 PokemonData from "./system/pokemon-data";
import { Nature } from "./data/nature";
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem, SpeciesFormChange } from "./data/pokemon-forms";
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem } from "./data/pokemon-forms";
import { FormChangePhase, QuietFormChangePhase } from "./form-change-phase";
import { getTypeRgb } from "./data/type";
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
const matchingFormChangeOpts = pokemonFormChanges[pokemon.species.speciesId].filter(fc => fc.findTrigger(formChangeTriggerType) && fc.canChange(pokemon));
let matchingFormChange: SpeciesFormChange | null;
let matchingFormChange;
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.
const formChangeItemModifiers = (this.findModifiers(m => m instanceof PokemonFormChangeItemModifier && m.pokemonId === pokemon.id) as PokemonFormChangeItemModifier[]).filter(m => m.active).map(m => m.formChangeItem);
@ -2666,9 +2666,7 @@ export default class BattleScene extends SceneBase {
if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) {
this.fadeOutBgm(Utils.fixedInt(2000), false);
this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, undefined, () => {
const finalBossMBH = getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as TurnHeldItemTransferModifier;
finalBossMBH.setTransferrableFalse();
this.addEnemyModifier(finalBossMBH, false, true);
this.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true);
pokemon.generateAndPopulateMoveset(1);
this.setFieldScale(0.75);
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);

View File

@ -5039,7 +5039,6 @@ export function initAbilities() {
(pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }),
(pokemon) => Math.floor(pokemon.getMaxHp() / 8))
.attr(PostBattleInitFormChangeAbAttr, () => 0)
.bypassFaint()
.ignorable(),
new Ability(Abilities.BATTLE_BOND, 7)
.attr(PostVictoryFormChangeAbAttr, () => 2)
@ -5192,7 +5191,6 @@ export function initAbilities() {
.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 }))
.attr(PostBattleInitFormChangeAbAttr, () => 0)
.bypassFaint()
.ignorable(),
new Ability(Abilities.POWER_SPOT, 8)
.attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL], 1.3),

View File

@ -837,8 +837,6 @@ export const pokemonFormChanges: PokemonFormChanges = {
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, "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[] {
let levelMoves = this.getLevelMoves(1, true).map(lm => lm[1]);
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) {
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge()) {
levelMoves = this.getUnlockedEggMoves().concat(levelMoves);
}
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
*/
hasChallenge(challenge: Challenges): boolean {
return this.challenges.some(c => c.id === challenge && c.value !== 0);
return this.challenges.some(c => c.id === challenge);
}
/**

View File

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

View File

@ -2034,8 +2034,7 @@ export class CommandPhase extends FieldPhase {
}
break;
case Command.BALL:
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 )) {
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))) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE);
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 { getMovePosition } from "#test/utils/gameManagerUtils";
import { Moves } from "#enums/moves";
import { Abilities } from "#enums/abilities";
import { Species } from "#enums/species";
import { StatusEffect } from "#app/data/status-effect.js";
import { CommandPhase, MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases.js";
import { MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases.js";
import { BattleStat } from "#app/data/battle-stat.js";
import { SPLASH_ONLY } from "../utils/testUtils";
import { Mode } from "#app/ui/ui.js";
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]);
}, TIMEOUT);
it("takes no damage from attacking move and transforms to Busted form, takes 1/8 max HP damage from the disguise breaking", async () => {
it("takes no damage from attacking move and transforms to Busted form, taking 1/8 max HP damage from the disguise breaking", async () => {
await game.startBattle();
const mimikyu = game.scene.getEnemyPokemon()!;
@ -134,30 +134,17 @@ describe("Abilities - Disguise", () => {
expect(mimikyu.formIndex).toBe(bustedForm);
}, TIMEOUT);
it("persists form change when wave changes with no arena reset", async () => {
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 () => {
it("reverts to Disguised on arena reset", async () => {
game.override.startingWave(4);
game.override.starterSpecies(Species.MIMIKYU);
game.override.starterForms({
[Species.MIMIKYU]: bustedForm
});
game.override.enemySpecies(Species.MAGIKARP);
game.override.enemyAbility(Abilities.BALL_FETCH);
await game.startBattle();
const mimikyu = game.scene.getPlayerPokemon()!;
@ -166,41 +153,10 @@ describe("Abilities - Disguise", () => {
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
await game.doKillOpponents();
await game.toNextWave();
await game.phaseInterceptor.to(TurnEndPhase);
game.doSelectModifier();
await game.phaseInterceptor.to(TurnInitPhase);
expect(mimikyu.formIndex).toBe(disguisedForm);
}, 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,21 +84,6 @@ describe("Abilities - Gulp Missile", () => {
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 () => {
await game.startBattle([Species.CRAMORANT]);
const cramorant = game.scene.getPlayerPokemon()!;

View File

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

View File

@ -2916,18 +2916,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
const isCaught = this.scene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0);
const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3);
const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2);
const isDefaultVariantCaught = !!(isCaught & DexAttr.DEFAULT_VARIANT);
const isVariantCaught = !!(isCaught & DexAttr.SHINY);
const isMaleCaught = !!(isCaught & DexAttr.MALE);
const isFemaleCaught = !!(isCaught & DexAttr.FEMALE);
const starterAttributes = this.starterPreferences[species.speciesId];
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId));
const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
if (!dexEntry.caughtAttr) {
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId));
const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
if (shiny === undefined || shiny !== props.shiny) {
shiny = props.shiny;
}
@ -2946,83 +2942,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
if (natureIndex === undefined || 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?
@ -3074,7 +2993,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
}
if (dexEntry.caughtAttr && species.malePercent !== null) {
const gender = !female ? Gender.MALE : Gender.FEMALE;
let gender: Gender;
if ((female && isFemaleCaught) || (!female && !isMaleCaught)) {
gender = Gender.FEMALE;
} else {
gender = Gender.MALE;
}
this.pokemonGenderText.setText(getGenderSymbol(gender));
this.pokemonGenderText.setColor(getGenderColor(gender));
this.pokemonGenderText.setShadowColor(getGenderColor(gender, true));
@ -3555,7 +3479,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
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)) {
console.log(`${species.name}'s icon ${icon.frame.name} does not match getIconId with female: ${female}, formIndex: ${formIndex}, shiny: ${shiny}, variant: ${variant}`);
console.log(`${species.name}'s variant icon does not exist. Replacing with default.`);
icon.setTexture(species.getIconAtlasKey(formIndex, false, variant));
icon.setFrame(species.getIconId(female, formIndex, false, variant));
}