Merge branch 'beta' into hebrew-pr
This commit is contained in:
commit
3d03da1051
Binary file not shown.
Before Width: | Height: | Size: 441 B After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,440 @@
|
|||
{
|
||||
"textures": [
|
||||
{
|
||||
"image": "types_ca-ES.png",
|
||||
"format": "RGBA8888",
|
||||
"size": {
|
||||
"w": 32,
|
||||
"h": 280
|
||||
},
|
||||
"scale": 1,
|
||||
"frames": [
|
||||
{
|
||||
"filename": "unknown",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "bug",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 14,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "dark",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 28,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "dragon",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 42,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "electric",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 56,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "fairy",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 70,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "fighting",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 84,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "fire",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 98,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "flying",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 112,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "ghost",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 126,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "grass",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 140,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "ground",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 154,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "ice",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 168,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "normal",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 182,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "poison",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 196,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "psychic",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 210,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "rock",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 224,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "steel",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 238,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "water",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 252,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
},
|
||||
{
|
||||
"filename": "stellar",
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"sourceSize": {
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"spriteSourceSize": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
},
|
||||
"frame": {
|
||||
"x": 0,
|
||||
"y": 266,
|
||||
"w": 32,
|
||||
"h": 14
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"app": "https://www.codeandweb.com/texturepacker",
|
||||
"version": "3.0",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:f14cf47d9a8f1d40c8e03aa6ba00fff3:6fc4227b57a95d429a1faad4280f7ec8:5961efbfbf4c56b8745347e7a663a32f$"
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit 6b3f37cb351552721232f4dabefa17bddb5b9004
|
||||
Subproject commit cd4057af258b659ba2c1ed2778bb2793fa1f6141
|
|
@ -1064,7 +1064,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
|
|||
}
|
||||
|
||||
override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): void {
|
||||
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT });
|
||||
attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio));
|
||||
}
|
||||
|
||||
|
@ -3792,7 +3792,7 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
|||
if (!simulated) {
|
||||
const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name;
|
||||
globalScene.queueMessage(i18next.t("abilityTriggers:postWeatherLapseDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName }));
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), HitResult.OTHER);
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / (16 / this.damageFactor)), { result: HitResult.INDIRECT });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4084,7 +4084,7 @@ export class PostTurnHurtIfSleepingAbAttr extends PostTurnAbAttr {
|
|||
for (const opp of pokemon.getOpponents()) {
|
||||
if ((opp.status?.effect === StatusEffect.SLEEP || opp.hasAbility(Abilities.COMATOSE)) && !opp.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !opp.switchOutStatus) {
|
||||
if (!simulated) {
|
||||
opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), HitResult.OTHER);
|
||||
opp.damageAndUpdate(Utils.toDmgValue(opp.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
globalScene.queueMessage(i18next.t("abilityTriggers:badDreams", { pokemonName: getPokemonNameWithAffix(opp) }));
|
||||
}
|
||||
}
|
||||
|
@ -4567,7 +4567,7 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
|
|||
|
||||
override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void {
|
||||
if (!simulated) {
|
||||
attacker!.damageAndUpdate(Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker!.damageAndUpdate(Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT });
|
||||
attacker!.turnData.damageTaken += Utils.toDmgValue(attacker!.getMaxHp() * (1 / this.damageRatio));
|
||||
}
|
||||
}
|
||||
|
@ -4588,7 +4588,7 @@ export class PostFaintHPDamageAbAttr extends PostFaintAbAttr {
|
|||
override applyPostFaint(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker?: Pokemon, move?: Move, hitResult?: HitResult, ...args: any[]): void {
|
||||
if (move !== undefined && attacker !== undefined && !simulated) { //If the mon didn't die to indirect damage
|
||||
const damage = pokemon.turnData.attacksReceived[0].damage;
|
||||
attacker.damageAndUpdate((damage), HitResult.OTHER);
|
||||
attacker.damageAndUpdate((damage), { result: HitResult.INDIRECT });
|
||||
attacker.turnData.damageTaken += damage;
|
||||
}
|
||||
}
|
||||
|
@ -4989,7 +4989,7 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr {
|
|||
(args[0] as Utils.NumberHolder).value = this.multiplier;
|
||||
pokemon.removeTag(this.tagType);
|
||||
if (this.recoilDamageFunc) {
|
||||
pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER, false, false, true, true);
|
||||
pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), { result: HitResult.INDIRECT, ignoreSegments: true, ignoreFaintPhase: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -788,7 +788,7 @@ class SpikesTag extends ArenaTrapTag {
|
|||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
);
|
||||
pokemon.damageAndUpdate(damage, HitResult.OTHER);
|
||||
pokemon.damageAndUpdate(damage, { result: HitResult.INDIRECT });
|
||||
if (pokemon.turnData) {
|
||||
pokemon.turnData.damageTaken += damage;
|
||||
}
|
||||
|
@ -982,7 +982,7 @@ class StealthRockTag extends ArenaTrapTag {
|
|||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
);
|
||||
pokemon.damageAndUpdate(damage, HitResult.OTHER);
|
||||
pokemon.damageAndUpdate(damage, { result: HitResult.INDIRECT });
|
||||
if (pokemon.turnData) {
|
||||
pokemon.turnData.damageTaken += damage;
|
||||
}
|
||||
|
@ -1327,7 +1327,7 @@ class FireGrassPledgeTag extends ArenaTag {
|
|||
globalScene.unshiftPhase(
|
||||
new CommonAnimPhase(pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM),
|
||||
);
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8));
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
});
|
||||
|
||||
return super.lapse(arena);
|
||||
|
|
|
@ -1659,7 +1659,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
},
|
||||
[Biome.GRASS]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.BREEDER, TrainerType.SCHOOL_KID ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER, TrainerType.POKEFAN ],
|
||||
[BiomePoolTier.RARE]: [ TrainerType.BLACK_BELT ],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
[BiomePoolTier.ULTRA_RARE]: [],
|
||||
|
@ -1680,9 +1680,9 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
||||
},
|
||||
[Biome.METROPOLIS]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.CLERK, TrainerType.CYCLIST, TrainerType.OFFICER, TrainerType.WAITER, TrainerType.BEAUTY ],
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.CLERK, TrainerType.CYCLIST, TrainerType.OFFICER, TrainerType.WAITER ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.BREEDER, TrainerType.DEPOT_AGENT, TrainerType.GUITARIST ],
|
||||
[BiomePoolTier.RARE]: [ TrainerType.ARTIST ],
|
||||
[BiomePoolTier.RARE]: [ TrainerType.ARTIST, TrainerType.RICH_KID ],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
[BiomePoolTier.ULTRA_RARE]: [],
|
||||
[BiomePoolTier.BOSS]: [ TrainerType.WHITNEY, TrainerType.NORMAN, TrainerType.IONO, TrainerType.LARRY ],
|
||||
|
@ -1702,7 +1702,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
||||
},
|
||||
[Biome.SEA]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.SWIMMER, TrainerType.SAILOR ],
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.SAILOR, TrainerType.SWIMMER ],
|
||||
[BiomePoolTier.UNCOMMON]: [],
|
||||
[BiomePoolTier.RARE]: [],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
|
@ -1758,7 +1758,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
},
|
||||
[Biome.MOUNTAIN]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.BLACK_BELT, TrainerType.HIKER ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER, TrainerType.PILOT ],
|
||||
[BiomePoolTier.RARE]: [],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
[BiomePoolTier.ULTRA_RARE]: [],
|
||||
|
@ -1790,7 +1790,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
||||
},
|
||||
[Biome.DESERT]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.SCIENTIST, TrainerType.BACKPACKER ],
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.BACKPACKER, TrainerType.SCIENTIST ],
|
||||
[BiomePoolTier.UNCOMMON]: [],
|
||||
[BiomePoolTier.RARE]: [],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
|
@ -1812,8 +1812,8 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
||||
},
|
||||
[Biome.MEADOW]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.PARASOL_LADY, TrainerType.BEAUTY ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER, TrainerType.BREEDER, TrainerType.BAKER ],
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.BEAUTY, TrainerType.MUSICIAN, TrainerType.PARASOL_LADY ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.ACE_TRAINER, TrainerType.BAKER, TrainerType.BREEDER, TrainerType.POKEFAN ],
|
||||
[BiomePoolTier.RARE]: [],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
[BiomePoolTier.ULTRA_RARE]: [],
|
||||
|
@ -1967,7 +1967,7 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
},
|
||||
[Biome.SLUM]: {
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.BIKER, TrainerType.OFFICER, TrainerType.ROUGHNECK ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.BAKER ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.BAKER, TrainerType.HOOLIGANS ],
|
||||
[BiomePoolTier.RARE]: [],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
[BiomePoolTier.ULTRA_RARE]: [],
|
||||
|
@ -1988,8 +1988,8 @@ export const biomeTrainerPools: BiomeTrainerPools = {
|
|||
[BiomePoolTier.BOSS_ULTRA_RARE]: []
|
||||
},
|
||||
[Biome.ISLAND]: {
|
||||
[BiomePoolTier.COMMON]: [],
|
||||
[BiomePoolTier.UNCOMMON]: [],
|
||||
[BiomePoolTier.COMMON]: [ TrainerType.RICH_KID ],
|
||||
[BiomePoolTier.UNCOMMON]: [ TrainerType.RICH ],
|
||||
[BiomePoolTier.RARE]: [],
|
||||
[BiomePoolTier.SUPER_RARE]: [],
|
||||
[BiomePoolTier.ULTRA_RARE]: [],
|
||||
|
@ -7178,7 +7178,8 @@ export function initBiomes() {
|
|||
[ Biome.METROPOLIS, BiomePoolTier.COMMON ],
|
||||
[ Biome.MEADOW, BiomePoolTier.COMMON ],
|
||||
[ Biome.FAIRY_CAVE, BiomePoolTier.COMMON ]
|
||||
]],
|
||||
]
|
||||
],
|
||||
[ TrainerType.BIKER, [
|
||||
[ Biome.SLUM, BiomePoolTier.COMMON ]
|
||||
]
|
||||
|
@ -7208,7 +7209,8 @@ export function initBiomes() {
|
|||
],
|
||||
[ TrainerType.CLERK, [
|
||||
[ Biome.METROPOLIS, BiomePoolTier.COMMON ]
|
||||
]],
|
||||
]
|
||||
],
|
||||
[ TrainerType.CYCLIST, [
|
||||
[ Biome.PLAINS, BiomePoolTier.UNCOMMON ],
|
||||
[ Biome.METROPOLIS, BiomePoolTier.COMMON ]
|
||||
|
@ -7217,18 +7219,23 @@ export function initBiomes() {
|
|||
[ TrainerType.DANCER, []],
|
||||
[ TrainerType.DEPOT_AGENT, [
|
||||
[ Biome.METROPOLIS, BiomePoolTier.UNCOMMON ]
|
||||
]],
|
||||
]
|
||||
],
|
||||
[ TrainerType.DOCTOR, []],
|
||||
[ TrainerType.FIREBREATHER, [
|
||||
[ Biome.VOLCANO, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.FISHERMAN, [
|
||||
[ Biome.LAKE, BiomePoolTier.COMMON ],
|
||||
[ Biome.BEACH, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.RICH, []],
|
||||
[ TrainerType.GUITARIST, [
|
||||
[ Biome.METROPOLIS, BiomePoolTier.UNCOMMON ],
|
||||
[ Biome.POWER_PLANT, BiomePoolTier.COMMON ]
|
||||
]],
|
||||
]
|
||||
],
|
||||
[ TrainerType.HARLEQUIN, []],
|
||||
[ TrainerType.HIKER, [
|
||||
[ Biome.MOUNTAIN, BiomePoolTier.COMMON ],
|
||||
|
@ -7236,13 +7243,24 @@ export function initBiomes() {
|
|||
[ Biome.BADLANDS, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.HOOLIGANS, []],
|
||||
[ TrainerType.HOOLIGANS, [
|
||||
[ Biome.SLUM, BiomePoolTier.UNCOMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.HOOPSTER, []],
|
||||
[ TrainerType.INFIELDER, []],
|
||||
[ TrainerType.JANITOR, []],
|
||||
[ TrainerType.LINEBACKER, []],
|
||||
[ TrainerType.MAID, []],
|
||||
[ TrainerType.MUSICIAN, []],
|
||||
[ TrainerType.MUSICIAN, [
|
||||
[ Biome.MEADOW, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.HEX_MANIAC, [
|
||||
[ Biome.RUINS, BiomePoolTier.UNCOMMON ],
|
||||
[ Biome.GRAVEYARD, BiomePoolTier.UNCOMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.NURSERY_AIDE, []],
|
||||
[ TrainerType.OFFICER, [
|
||||
[ Biome.METROPOLIS, BiomePoolTier.COMMON ],
|
||||
|
@ -7256,8 +7274,15 @@ export function initBiomes() {
|
|||
[ Biome.MEADOW, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.PILOT, []],
|
||||
[ TrainerType.POKEFAN, []],
|
||||
[ TrainerType.PILOT, [
|
||||
[ Biome.MOUNTAIN, BiomePoolTier.UNCOMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.POKEFAN, [
|
||||
[ Biome.GRASS, BiomePoolTier.UNCOMMON ],
|
||||
[ Biome.MEADOW, BiomePoolTier.UNCOMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.PRESCHOOLER, []],
|
||||
[ TrainerType.PSYCHIC, [
|
||||
[ Biome.GRAVEYARD, BiomePoolTier.COMMON ],
|
||||
|
@ -7270,11 +7295,24 @@ export function initBiomes() {
|
|||
[ Biome.JUNGLE, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.RICH_KID, []],
|
||||
[ TrainerType.RICH, [
|
||||
[ Biome.ISLAND, BiomePoolTier.UNCOMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.RICH_KID, [
|
||||
[ Biome.METROPOLIS, BiomePoolTier.RARE ],
|
||||
[ Biome.ISLAND, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.ROUGHNECK, [
|
||||
[ Biome.SLUM, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.SAILOR, [
|
||||
[ Biome.SEA, BiomePoolTier.COMMON ],
|
||||
[ Biome.BEACH, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.SCIENTIST, [
|
||||
[ Biome.DESERT, BiomePoolTier.COMMON ],
|
||||
[ Biome.RUINS, BiomePoolTier.COMMON ]
|
||||
|
@ -7317,20 +7355,6 @@ export function initBiomes() {
|
|||
[ Biome.TOWN, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.HEX_MANIAC, [
|
||||
[ Biome.RUINS, BiomePoolTier.UNCOMMON ],
|
||||
[ Biome.GRAVEYARD, BiomePoolTier.UNCOMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.FIREBREATHER, [
|
||||
[ Biome.VOLCANO, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.SAILOR, [
|
||||
[ Biome.SEA, BiomePoolTier.COMMON ],
|
||||
[ Biome.BEACH, BiomePoolTier.COMMON ]
|
||||
]
|
||||
],
|
||||
[ TrainerType.BROCK, [
|
||||
[ Biome.CAVE, BiomePoolTier.BOSS ]
|
||||
]
|
||||
|
|
|
@ -757,7 +757,7 @@ export class ConfusedTag extends BattlerTag {
|
|||
((((2 * pokemon.level) / 5 + 2) * 40 * atk) / def / 50 + 2) * (pokemon.randSeedIntRange(85, 100) / 100),
|
||||
);
|
||||
globalScene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself"));
|
||||
pokemon.damageAndUpdate(damage);
|
||||
pokemon.damageAndUpdate(damage, { result: HitResult.CONFUSION });
|
||||
pokemon.battleData.hitCount++;
|
||||
(globalScene.getCurrentPhase() as MovePhase).cancel();
|
||||
}
|
||||
|
@ -818,7 +818,7 @@ export class DestinyBondTag extends BattlerTag {
|
|||
pokemonNameWithAffix2: getPokemonNameWithAffix(pokemon),
|
||||
}),
|
||||
);
|
||||
pokemon.damageAndUpdate(pokemon.hp, HitResult.ONE_HIT_KO, false, false, true);
|
||||
pokemon.damageAndUpdate(pokemon.hp, { result: HitResult.INDIRECT_KO, ignoreSegments: true });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -952,7 +952,7 @@ export class SeedTag extends BattlerTag {
|
|||
new CommonAnimPhase(source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED),
|
||||
);
|
||||
|
||||
const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8));
|
||||
const damage = pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false);
|
||||
globalScene.unshiftPhase(
|
||||
new PokemonHealPhase(
|
||||
|
@ -1029,7 +1029,7 @@ export class PowderTag extends BattlerTag {
|
|||
const cancelDamage = new BooleanHolder(false);
|
||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelDamage);
|
||||
if (!cancelDamage.value) {
|
||||
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), HitResult.OTHER);
|
||||
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||
}
|
||||
|
||||
// "When the flame touched the powder\non the Pokémon, it exploded!"
|
||||
|
@ -1082,7 +1082,7 @@ export class NightmareTag extends BattlerTag {
|
|||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||
|
||||
if (!cancelled.value) {
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4));
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1440,7 +1440,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
|
|||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||
|
||||
if (!cancelled.value) {
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8));
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1644,7 +1644,7 @@ export class ContactDamageProtectedTag extends ProtectedTag {
|
|||
if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) {
|
||||
const attacker = effectPhase.getPokemon();
|
||||
if (!attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) {
|
||||
attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
|
||||
attacker.damageAndUpdate(toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), { result: HitResult.INDIRECT });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1810,7 +1810,7 @@ export class PerishSongTag extends BattlerTag {
|
|||
}),
|
||||
);
|
||||
} else {
|
||||
pokemon.damageAndUpdate(pokemon.hp, HitResult.ONE_HIT_KO, false, true, true);
|
||||
pokemon.damageAndUpdate(pokemon.hp, { result: HitResult.INDIRECT_KO, ignoreSegments: true });
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -2240,7 +2240,7 @@ export class SaltCuredTag extends BattlerTag {
|
|||
|
||||
if (!cancelled.value) {
|
||||
const pokemonSteelOrWater = pokemon.isOfType(PokemonType.STEEL) || pokemon.isOfType(PokemonType.WATER);
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8));
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), { result: HitResult.INDIRECT });
|
||||
|
||||
globalScene.queueMessage(
|
||||
i18next.t("battlerTags:saltCuredLapse", {
|
||||
|
@ -2288,7 +2288,7 @@ export class CursedTag extends BattlerTag {
|
|||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||
|
||||
if (!cancelled.value) {
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4));
|
||||
pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 4), { result: HitResult.INDIRECT });
|
||||
globalScene.queueMessage(
|
||||
i18next.t("battlerTags:cursedLapse", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
|
||||
|
@ -2611,7 +2611,7 @@ export class GulpMissileTag extends BattlerTag {
|
|||
applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled);
|
||||
|
||||
if (!cancelled.value) {
|
||||
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), HitResult.OTHER);
|
||||
attacker.damageAndUpdate(Math.max(1, Math.floor(attacker.getMaxHp() / 4)), { result: HitResult.INDIRECT });
|
||||
}
|
||||
|
||||
if (this.tagType === BattlerTagType.GULP_MISSILE_ARROKUDA) {
|
||||
|
|
|
@ -18,9 +18,10 @@ import { TrainerType } from "#enums/trainer-type";
|
|||
import { Nature } from "#enums/nature";
|
||||
import type { Moves } from "#enums/moves";
|
||||
import { TypeColor, TypeShadow } from "#enums/color";
|
||||
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { pokemonFormChanges } from "./pokemon-forms";
|
||||
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
||||
|
||||
/** A constant for the default max cost of the starting party before a run */
|
||||
const DEFAULT_PARTY_MAX_COST = 10;
|
||||
|
@ -285,15 +286,9 @@ export abstract class Challenge {
|
|||
* @param _pokemon {@link PokemonSpecies} The pokemon to check the validity of.
|
||||
* @param _valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
|
||||
* @param _dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
|
||||
* @param _soft {@link boolean} If true, allow it if it could become a valid pokemon.
|
||||
* @returns {@link boolean} Whether this function did anything.
|
||||
*/
|
||||
applyStarterChoice(
|
||||
_pokemon: PokemonSpecies,
|
||||
_valid: Utils.BooleanHolder,
|
||||
_dexAttr: DexAttrProps,
|
||||
_soft = false,
|
||||
): boolean {
|
||||
applyStarterChoice(_pokemon: PokemonSpecies, _valid: Utils.BooleanHolder, _dexAttr: DexAttrProps): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -445,27 +440,8 @@ export class SingleGenerationChallenge extends Challenge {
|
|||
super(Challenges.SINGLE_GENERATION, 9);
|
||||
}
|
||||
|
||||
applyStarterChoice(
|
||||
pokemon: PokemonSpecies,
|
||||
valid: Utils.BooleanHolder,
|
||||
_dexAttr: DexAttrProps,
|
||||
soft = false,
|
||||
): boolean {
|
||||
const generations = [pokemon.generation];
|
||||
if (soft) {
|
||||
const speciesToCheck = [pokemon.speciesId];
|
||||
while (speciesToCheck.length) {
|
||||
const checking = speciesToCheck.pop();
|
||||
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
|
||||
pokemonEvolutions[checking].forEach(e => {
|
||||
speciesToCheck.push(e.speciesId);
|
||||
generations.push(getPokemonSpecies(e.speciesId).generation);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!generations.includes(this.value)) {
|
||||
applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder): boolean {
|
||||
if (pokemon.generation !== this.value) {
|
||||
valid.value = false;
|
||||
return true;
|
||||
}
|
||||
|
@ -739,41 +715,14 @@ export class SingleTypeChallenge extends Challenge {
|
|||
{ species: Species.CASTFORM, type: PokemonType.NORMAL, fusion: false },
|
||||
];
|
||||
// TODO: Find a solution for all Pokemon with this ssui issue, including Basculin and Burmy
|
||||
private static SPECIES_OVERRIDES: Species[] = [Species.MELOETTA];
|
||||
|
||||
constructor() {
|
||||
super(Challenges.SINGLE_TYPE, 18);
|
||||
}
|
||||
|
||||
override applyStarterChoice(
|
||||
pokemon: PokemonSpecies,
|
||||
valid: Utils.BooleanHolder,
|
||||
dexAttr: DexAttrProps,
|
||||
soft = false,
|
||||
): boolean {
|
||||
override applyStarterChoice(pokemon: PokemonSpecies, valid: Utils.BooleanHolder, dexAttr: DexAttrProps): boolean {
|
||||
const speciesForm = getPokemonSpeciesForm(pokemon.speciesId, dexAttr.formIndex);
|
||||
const types = [speciesForm.type1, speciesForm.type2];
|
||||
if (soft && !SingleTypeChallenge.SPECIES_OVERRIDES.includes(pokemon.speciesId)) {
|
||||
const speciesToCheck = [pokemon.speciesId];
|
||||
while (speciesToCheck.length) {
|
||||
const checking = speciesToCheck.pop();
|
||||
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
|
||||
pokemonEvolutions[checking].forEach(e => {
|
||||
speciesToCheck.push(e.speciesId);
|
||||
types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2);
|
||||
});
|
||||
}
|
||||
if (checking && pokemonFormChanges.hasOwnProperty(checking)) {
|
||||
pokemonFormChanges[checking].forEach(f1 => {
|
||||
getPokemonSpecies(checking).forms.forEach(f2 => {
|
||||
if (f1.formKey === f2.formKey) {
|
||||
types.push(f2.type1, f2.type2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!types.includes(this.value - 1)) {
|
||||
valid.value = false;
|
||||
return true;
|
||||
|
@ -1030,7 +979,6 @@ export class LowerStarterPointsChallenge extends Challenge {
|
|||
* @param pokemon {@link PokemonSpecies} The pokemon to check the validity of.
|
||||
* @param valid {@link Utils.BooleanHolder} A BooleanHolder, the value gets set to false if the pokemon isn't allowed.
|
||||
* @param dexAttr {@link DexAttrProps} The dex attributes of the pokemon.
|
||||
* @param soft {@link boolean} If true, allow it if it could become a valid pokemon.
|
||||
* @returns True if any challenge was successfully applied.
|
||||
*/
|
||||
export function applyChallenges(
|
||||
|
@ -1039,7 +987,6 @@ export function applyChallenges(
|
|||
pokemon: PokemonSpecies,
|
||||
valid: Utils.BooleanHolder,
|
||||
dexAttr: DexAttrProps,
|
||||
soft: boolean,
|
||||
): boolean;
|
||||
/**
|
||||
* Apply all challenges that modify available total starter points.
|
||||
|
@ -1222,7 +1169,7 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
|||
if (c.value !== 0) {
|
||||
switch (challengeType) {
|
||||
case ChallengeType.STARTER_CHOICE:
|
||||
ret ||= c.applyStarterChoice(args[0], args[1], args[2], args[3]);
|
||||
ret ||= c.applyStarterChoice(args[0], args[1], args[2]);
|
||||
break;
|
||||
case ChallengeType.STARTER_POINTS:
|
||||
ret ||= c.applyStarterPoints(args[0]);
|
||||
|
@ -1305,3 +1252,87 @@ export function initChallenges() {
|
|||
new FlipStatChallenge(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all challenges to the given starter (and form) to check its validity.
|
||||
* Differs from {@linkcode checkSpeciesValidForChallenge} which only checks form changes.
|
||||
* @param species - The {@linkcode PokemonSpecies} to check the validity of.
|
||||
* @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index.
|
||||
* @param soft - If `true`, allow it if it could become valid through evolution or form change.
|
||||
* @returns `true` if the species is considered valid.
|
||||
*/
|
||||
export function checkStarterValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
|
||||
if (!soft) {
|
||||
const isValidForChallenge = new Utils.BooleanHolder(true);
|
||||
applyChallenges(globalScene.gameMode, ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
|
||||
return isValidForChallenge.value;
|
||||
}
|
||||
// We check the validity of every evolution and form change, and require that at least one is valid
|
||||
const speciesToCheck = [species.speciesId];
|
||||
while (speciesToCheck.length) {
|
||||
const checking = speciesToCheck.pop();
|
||||
// Linter complains if we don't handle this
|
||||
if (!checking) {
|
||||
return false;
|
||||
}
|
||||
const checkingSpecies = getPokemonSpecies(checking);
|
||||
if (checkSpeciesValidForChallenge(checkingSpecies, props, true)) {
|
||||
return true;
|
||||
}
|
||||
if (checking && pokemonEvolutions.hasOwnProperty(checking)) {
|
||||
pokemonEvolutions[checking].forEach(e => {
|
||||
// Form check to deal with cases such as Basculin -> Basculegion
|
||||
// TODO: does this miss anything if checking forms of a stage 2 Pokémon?
|
||||
if (!e?.preFormKey || e.preFormKey === species.forms[props.formIndex].formKey) {
|
||||
speciesToCheck.push(e.speciesId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all challenges to the given species (and form) to check its validity.
|
||||
* Differs from {@linkcode checkStarterValidForChallenge} which also checks evolutions.
|
||||
* @param species - The {@linkcode PokemonSpecies} to check the validity of.
|
||||
* @param dexAttr - The {@linkcode DexAttrProps | dex attributes} of the species, including its form index.
|
||||
* @param soft - If `true`, allow it if it could become valid through a form change.
|
||||
* @returns `true` if the species is considered valid.
|
||||
*/
|
||||
function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrProps, soft: boolean) {
|
||||
const isValidForChallenge = new Utils.BooleanHolder(true);
|
||||
applyChallenges(globalScene.gameMode, ChallengeType.STARTER_CHOICE, species, isValidForChallenge, props);
|
||||
if (!soft || !pokemonFormChanges.hasOwnProperty(species.speciesId)) {
|
||||
return isValidForChallenge.value;
|
||||
}
|
||||
// If the form in props is valid, return true before checking other form changes
|
||||
if (soft && isValidForChallenge.value) {
|
||||
return true;
|
||||
}
|
||||
pokemonFormChanges[species.speciesId].forEach(f1 => {
|
||||
// Exclude form changes that require the mon to be on the field to begin with,
|
||||
// such as Castform
|
||||
if (!("item" in f1)) {
|
||||
return;
|
||||
}
|
||||
species.forms.forEach((f2, formIndex) => {
|
||||
if (f1.formKey === f2.formKey) {
|
||||
const formProps = { ...props };
|
||||
formProps.formIndex = formIndex;
|
||||
const isFormValidForChallenge = new Utils.BooleanHolder(true);
|
||||
applyChallenges(
|
||||
globalScene.gameMode,
|
||||
ChallengeType.STARTER_CHOICE,
|
||||
species,
|
||||
isFormValidForChallenge,
|
||||
formProps,
|
||||
);
|
||||
if (isFormValidForChallenge.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -312,6 +312,113 @@ export const trainerTypeDialogue: TrainerTypeDialogue = {
|
|||
victory: ["dialogue:sailor.victory.1", "dialogue:sailor.victory.2", "dialogue:sailor.victory.3"],
|
||||
},
|
||||
],
|
||||
[TrainerType.CLERK]: [
|
||||
{
|
||||
encounter: ["dialogue:clerk.encounter.1", "dialogue:clerk.encounter.2", "dialogue:clerk.encounter.3"],
|
||||
victory: ["dialogue:clerk.victory.1", "dialogue:clerk.victory.2", "dialogue:clerk.victory.3"],
|
||||
},
|
||||
{
|
||||
encounter: [
|
||||
"dialogue:clerk_female.encounter.1",
|
||||
"dialogue:clerk_female.encounter.2",
|
||||
"dialogue:clerk_female.encounter.3",
|
||||
],
|
||||
victory: [
|
||||
"dialogue:clerk_female.victory.1",
|
||||
"dialogue:clerk_female.victory.2",
|
||||
"dialogue:clerk_female.victory.3",
|
||||
],
|
||||
},
|
||||
],
|
||||
[TrainerType.HOOLIGANS]: [
|
||||
{
|
||||
encounter: ["dialogue:hooligans.encounter.1", "dialogue:hooligans.encounter.2"],
|
||||
victory: ["dialogue:hooligans.victory.1", "dialogue:hooligans.victory.2"],
|
||||
},
|
||||
],
|
||||
[TrainerType.MUSICIAN]: [
|
||||
{
|
||||
encounter: [
|
||||
"dialogue:musician.encounter.1",
|
||||
"dialogue:musician.encounter.2",
|
||||
"dialogue:musician.encounter.3",
|
||||
"dialogue:musician.encounter.4",
|
||||
],
|
||||
victory: ["dialogue:musician.victory.1", "dialogue:musician.victory.2", "dialogue:musician.victory.3"],
|
||||
},
|
||||
],
|
||||
[TrainerType.PILOT]: [
|
||||
{
|
||||
encounter: [
|
||||
"dialogue:pilot.encounter.1",
|
||||
"dialogue:pilot.encounter.2",
|
||||
"dialogue:pilot.encounter.3",
|
||||
"dialogue:pilot.encounter.4",
|
||||
],
|
||||
victory: [
|
||||
"dialogue:pilot.victory.1",
|
||||
"dialogue:pilot.victory.2",
|
||||
"dialogue:pilot.victory.3",
|
||||
"dialogue:pilot.victory.4",
|
||||
],
|
||||
},
|
||||
],
|
||||
[TrainerType.POKEFAN]: [
|
||||
{
|
||||
encounter: ["dialogue:pokefan.encounter.1", "dialogue:pokefan.encounter.2", "dialogue:pokefan.encounter.3"],
|
||||
victory: ["dialogue:pokefan.victory.1", "dialogue:pokefan.victory.2", "dialogue:pokefan.victory.3"],
|
||||
},
|
||||
{
|
||||
encounter: [
|
||||
"dialogue:pokefan_female.encounter.1",
|
||||
"dialogue:pokefan_female.encounter.2",
|
||||
"dialogue:pokefan_female.encounter.3",
|
||||
],
|
||||
victory: [
|
||||
"dialogue:pokefan_female.victory.1",
|
||||
"dialogue:pokefan_female.victory.2",
|
||||
"dialogue:pokefan_female.victory.3",
|
||||
],
|
||||
},
|
||||
],
|
||||
[TrainerType.RICH]: [
|
||||
{
|
||||
encounter: ["dialogue:rich.encounter.1", "dialogue:rich.encounter.2", "dialogue:rich.encounter.3"],
|
||||
victory: ["dialogue:rich.victory.1", "dialogue:rich.victory.2", "dialogue:rich.victory.3"],
|
||||
},
|
||||
{
|
||||
encounter: [
|
||||
"dialogue:rich_female.encounter.1",
|
||||
"dialogue:rich_female.encounter.2",
|
||||
"dialogue:rich_female.encounter.3",
|
||||
],
|
||||
victory: ["dialogue:rich_female.victory.1", "dialogue:rich_female.victory.2", "dialogue:rich_female.victory.3"],
|
||||
},
|
||||
],
|
||||
[TrainerType.RICH_KID]: [
|
||||
{
|
||||
encounter: ["dialogue:rich_kid.encounter.1", "dialogue:rich_kid.encounter.2", "dialogue:rich_kid.encounter.3"],
|
||||
victory: [
|
||||
"dialogue:rich_kid.victory.1",
|
||||
"dialogue:rich_kid.victory.2",
|
||||
"dialogue:rich_kid.victory.3",
|
||||
"dialogue:rich_kid.victory.4",
|
||||
],
|
||||
},
|
||||
{
|
||||
encounter: [
|
||||
"dialogue:rich_kid_female.encounter.1",
|
||||
"dialogue:rich_kid_female.encounter.2",
|
||||
"dialogue:rich_kid_female.encounter.3",
|
||||
],
|
||||
victory: [
|
||||
"dialogue:rich_kid_female.victory.1",
|
||||
"dialogue:rich_kid_female.victory.2",
|
||||
"dialogue:rich_kid_female.victory.3",
|
||||
"dialogue:rich_kid_female.victory.4",
|
||||
],
|
||||
},
|
||||
],
|
||||
[TrainerType.ROCKET_GRUNT]: [
|
||||
{
|
||||
encounter: [
|
||||
|
|
|
@ -3,144 +3,46 @@ import { Moves } from "#enums/moves";
|
|||
/** Set of moves that cannot be called by {@linkcode Moves.METRONOME Metronome} */
|
||||
export const invalidMetronomeMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.AFTER_YOU,
|
||||
Moves.APPLE_ACID,
|
||||
Moves.ARMOR_CANNON,
|
||||
Moves.ASSIST,
|
||||
Moves.ASTRAL_BARRAGE,
|
||||
Moves.AURA_WHEEL,
|
||||
Moves.BANEFUL_BUNKER,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BEHEMOTH_BASH,
|
||||
Moves.BEHEMOTH_BLADE,
|
||||
Moves.BELCH,
|
||||
Moves.BESTOW,
|
||||
Moves.BLAZING_TORQUE,
|
||||
Moves.BODY_PRESS,
|
||||
Moves.BRANCH_POKE,
|
||||
Moves.BREAKING_SWIPE,
|
||||
Moves.CELEBRATE,
|
||||
Moves.CHATTER,
|
||||
Moves.CHILLING_WATER,
|
||||
Moves.CHILLY_RECEPTION,
|
||||
Moves.CLANGOROUS_SOUL,
|
||||
Moves.COLLISION_COURSE,
|
||||
Moves.COMBAT_TORQUE,
|
||||
Moves.COMEUPPANCE,
|
||||
Moves.COPYCAT,
|
||||
Moves.COUNTER,
|
||||
Moves.COVET,
|
||||
Moves.CRAFTY_SHIELD,
|
||||
Moves.DECORATE,
|
||||
Moves.DESTINY_BOND,
|
||||
Moves.DETECT,
|
||||
Moves.DIAMOND_STORM,
|
||||
Moves.DOODLE,
|
||||
Moves.DOUBLE_IRON_BASH,
|
||||
Moves.DOUBLE_SHOCK,
|
||||
Moves.DRAGON_ASCENT,
|
||||
Moves.DRAGON_ENERGY,
|
||||
Moves.DRUM_BEATING,
|
||||
Moves.DYNAMAX_CANNON,
|
||||
Moves.ELECTRO_DRIFT,
|
||||
Moves.ENDURE,
|
||||
Moves.ETERNABEAM,
|
||||
Moves.FALSE_SURRENDER,
|
||||
Moves.FEINT,
|
||||
Moves.FIERY_WRATH,
|
||||
Moves.FILLET_AWAY,
|
||||
Moves.FLEUR_CANNON,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.FOLLOW_ME,
|
||||
Moves.FREEZE_SHOCK,
|
||||
Moves.FREEZING_GLARE,
|
||||
Moves.GLACIAL_LANCE,
|
||||
Moves.GRAV_APPLE,
|
||||
Moves.HELPING_HAND,
|
||||
Moves.HOLD_HANDS,
|
||||
Moves.HYPER_DRILL,
|
||||
Moves.HYPERSPACE_FURY,
|
||||
Moves.HYPERSPACE_HOLE,
|
||||
Moves.ICE_BURN,
|
||||
Moves.INSTRUCT,
|
||||
Moves.JET_PUNCH,
|
||||
Moves.JUNGLE_HEALING,
|
||||
Moves.KINGS_SHIELD,
|
||||
Moves.LIFE_DEW,
|
||||
Moves.LIGHT_OF_RUIN,
|
||||
Moves.MAKE_IT_RAIN,
|
||||
Moves.MAGICAL_TORQUE,
|
||||
Moves.MAT_BLOCK,
|
||||
Moves.ME_FIRST,
|
||||
Moves.METEOR_ASSAULT,
|
||||
Moves.METRONOME,
|
||||
Moves.MIMIC,
|
||||
Moves.MIND_BLOWN,
|
||||
Moves.MIRROR_COAT,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.MOONGEIST_BEAM,
|
||||
Moves.NATURE_POWER,
|
||||
Moves.NATURES_MADNESS,
|
||||
Moves.NOXIOUS_TORQUE,
|
||||
Moves.OBSTRUCT,
|
||||
Moves.ORDER_UP,
|
||||
Moves.ORIGIN_PULSE,
|
||||
Moves.OVERDRIVE,
|
||||
Moves.PHOTON_GEYSER,
|
||||
Moves.PLASMA_FISTS,
|
||||
Moves.POPULATION_BOMB,
|
||||
Moves.POUNCE,
|
||||
Moves.POWER_SHIFT,
|
||||
Moves.PRECIPICE_BLADES,
|
||||
Moves.PROTECT,
|
||||
Moves.PYRO_BALL,
|
||||
Moves.QUASH,
|
||||
Moves.QUICK_GUARD,
|
||||
Moves.RAGE_FIST,
|
||||
Moves.RAGE_POWDER,
|
||||
Moves.RAGING_BULL,
|
||||
Moves.RAGING_FURY,
|
||||
Moves.RELIC_SONG,
|
||||
Moves.REVIVAL_BLESSING,
|
||||
Moves.RUINATION,
|
||||
Moves.SALT_CURE,
|
||||
Moves.SECRET_SWORD,
|
||||
Moves.SHED_TAIL,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SILK_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SLEEP_TALK,
|
||||
Moves.SNAP_TRAP,
|
||||
Moves.SNARL,
|
||||
Moves.SNATCH,
|
||||
Moves.SNORE,
|
||||
Moves.SNOWSCAPE,
|
||||
Moves.SPECTRAL_THIEF,
|
||||
Moves.SPICY_EXTRACT,
|
||||
Moves.SPIKY_SHIELD,
|
||||
Moves.SPIRIT_BREAK,
|
||||
Moves.SPOTLIGHT,
|
||||
Moves.STEAM_ERUPTION,
|
||||
Moves.STEEL_BEAM,
|
||||
Moves.STRANGE_STEAM,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SUNSTEEL_STRIKE,
|
||||
Moves.SURGING_STRIKES,
|
||||
Moves.SWITCHEROO,
|
||||
Moves.TECHNO_BLAST,
|
||||
Moves.TERA_STARSTORM,
|
||||
Moves.THIEF,
|
||||
Moves.THOUSAND_ARROWS,
|
||||
Moves.THOUSAND_WAVES,
|
||||
Moves.THUNDER_CAGE,
|
||||
Moves.THUNDEROUS_KICK,
|
||||
Moves.TIDY_UP,
|
||||
Moves.TRAILBLAZE,
|
||||
Moves.TRANSFORM,
|
||||
Moves.TRICK,
|
||||
Moves.TWIN_BEAM,
|
||||
Moves.V_CREATE,
|
||||
Moves.WICKED_BLOW,
|
||||
Moves.WICKED_TORQUE,
|
||||
Moves.WIDE_GUARD,
|
||||
]);
|
||||
|
||||
|
@ -157,7 +59,6 @@ export const invalidAssistMoves: ReadonlySet<Moves> = new Set([
|
|||
Moves.CIRCLE_THROW,
|
||||
Moves.COPYCAT,
|
||||
Moves.COUNTER,
|
||||
Moves.COVET,
|
||||
Moves.DESTINY_BOND,
|
||||
Moves.DETECT,
|
||||
Moves.DIG,
|
||||
|
@ -192,7 +93,6 @@ export const invalidAssistMoves: ReadonlySet<Moves> = new Set([
|
|||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SWITCHEROO,
|
||||
Moves.THIEF,
|
||||
Moves.TRANSFORM,
|
||||
Moves.TRICK,
|
||||
Moves.WHIRLWIND,
|
||||
|
@ -208,7 +108,6 @@ export const invalidSleepTalkMoves: ReadonlySet<Moves> = new Set([
|
|||
Moves.COPYCAT,
|
||||
Moves.DIG,
|
||||
Moves.DIVE,
|
||||
Moves.DYNAMAX_CANNON,
|
||||
Moves.FREEZE_SHOCK,
|
||||
Moves.FLY,
|
||||
Moves.FOCUS_PUNCH,
|
||||
|
@ -238,15 +137,12 @@ export const invalidCopycatMoves: ReadonlySet<Moves> = new Set([
|
|||
Moves.ASSIST,
|
||||
Moves.BANEFUL_BUNKER,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BEHEMOTH_BASH,
|
||||
Moves.BEHEMOTH_BLADE,
|
||||
Moves.BESTOW,
|
||||
Moves.CELEBRATE,
|
||||
Moves.CHATTER,
|
||||
Moves.CIRCLE_THROW,
|
||||
Moves.COPYCAT,
|
||||
Moves.COUNTER,
|
||||
Moves.COVET,
|
||||
Moves.DESTINY_BOND,
|
||||
Moves.DETECT,
|
||||
Moves.DRAGON_TAIL,
|
||||
|
@ -274,8 +170,73 @@ export const invalidCopycatMoves: ReadonlySet<Moves> = new Set([
|
|||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SWITCHEROO,
|
||||
Moves.THIEF,
|
||||
Moves.TRANSFORM,
|
||||
Moves.TRICK,
|
||||
Moves.WHIRLWIND,
|
||||
]);
|
||||
|
||||
export const invalidMirrorMoveMoves: ReadonlySet<Moves> = new Set([
|
||||
Moves.ACUPRESSURE,
|
||||
Moves.AFTER_YOU,
|
||||
Moves.AROMATIC_MIST,
|
||||
Moves.BEAK_BLAST,
|
||||
Moves.BELCH,
|
||||
Moves.CHILLY_RECEPTION,
|
||||
Moves.COACHING,
|
||||
Moves.CONVERSION_2,
|
||||
Moves.COUNTER,
|
||||
Moves.CRAFTY_SHIELD,
|
||||
Moves.CURSE,
|
||||
Moves.DECORATE,
|
||||
Moves.DOODLE,
|
||||
Moves.DOOM_DESIRE,
|
||||
Moves.DRAGON_CHEER,
|
||||
Moves.ELECTRIC_TERRAIN,
|
||||
Moves.FINAL_GAMBIT,
|
||||
Moves.FLORAL_HEALING,
|
||||
Moves.FLOWER_SHIELD,
|
||||
Moves.FOCUS_PUNCH,
|
||||
Moves.FUTURE_SIGHT,
|
||||
Moves.GEAR_UP,
|
||||
Moves.GRASSY_TERRAIN,
|
||||
Moves.GRAVITY,
|
||||
Moves.GUARD_SPLIT,
|
||||
Moves.HAIL,
|
||||
Moves.HAZE,
|
||||
Moves.HEAL_PULSE,
|
||||
Moves.HELPING_HAND,
|
||||
Moves.HOLD_HANDS,
|
||||
Moves.INSTRUCT,
|
||||
Moves.ION_DELUGE,
|
||||
Moves.MAGNETIC_FLUX,
|
||||
Moves.MAT_BLOCK,
|
||||
Moves.ME_FIRST,
|
||||
Moves.MIMIC,
|
||||
Moves.MIRROR_COAT,
|
||||
Moves.MIRROR_MOVE,
|
||||
Moves.MIST,
|
||||
Moves.MISTY_TERRAIN,
|
||||
Moves.MUD_SPORT,
|
||||
Moves.PERISH_SONG,
|
||||
Moves.POWER_SPLIT,
|
||||
Moves.PSYCH_UP,
|
||||
Moves.PSYCHIC_TERRAIN,
|
||||
Moves.PURIFY,
|
||||
Moves.QUICK_GUARD,
|
||||
Moves.RAIN_DANCE,
|
||||
Moves.REFLECT_TYPE,
|
||||
Moves.ROLE_PLAY,
|
||||
Moves.ROTOTILLER,
|
||||
Moves.SANDSTORM,
|
||||
Moves.SHELL_TRAP,
|
||||
Moves.SKETCH,
|
||||
Moves.SNOWSCAPE,
|
||||
Moves.SPIT_UP,
|
||||
Moves.SPOTLIGHT,
|
||||
Moves.STRUGGLE,
|
||||
Moves.SUNNY_DAY,
|
||||
Moves.TEATIME,
|
||||
Moves.TRANSFORM,
|
||||
Moves.WATER_SPORT,
|
||||
Moves.WIDE_GUARD,
|
||||
]);
|
||||
|
|
|
@ -16,6 +16,7 @@ import type { AttackMoveResult, TurnMove } from "../../field/pokemon";
|
|||
import type Pokemon from "../../field/pokemon";
|
||||
import {
|
||||
EnemyPokemon,
|
||||
FieldPosition,
|
||||
HitResult,
|
||||
MoveResult,
|
||||
PlayerPokemon,
|
||||
|
@ -125,7 +126,7 @@ import { MoveTarget } from "#enums/MoveTarget";
|
|||
import { MoveFlags } from "#enums/MoveFlags";
|
||||
import { MoveEffectTrigger } from "#enums/MoveEffectTrigger";
|
||||
import { MultiHitType } from "#enums/MultiHitType";
|
||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidSleepTalkMoves } from "./invalid-moves";
|
||||
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves";
|
||||
|
||||
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||
type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
|
||||
|
@ -1646,7 +1647,7 @@ export class RecoilAttr extends MoveEffectAttr {
|
|||
return false;
|
||||
}
|
||||
|
||||
user.damageAndUpdate(recoilDamage, HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(recoilDamage, { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:hitWithRecoil", { pokemonName: getPokemonNameWithAffix(user) }));
|
||||
user.turnData.damageTaken += recoilDamage;
|
||||
|
||||
|
@ -1678,7 +1679,7 @@ export class SacrificialAttr extends MoveEffectAttr {
|
|||
* @returns true if the function succeeds
|
||||
**/
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
user.damageAndUpdate(user.hp, HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(user.hp, { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
user.turnData.damageTaken += user.hp;
|
||||
|
||||
return true;
|
||||
|
@ -1716,7 +1717,7 @@ export class SacrificialAttrOnHit extends MoveEffectAttr {
|
|||
return false;
|
||||
}
|
||||
|
||||
user.damageAndUpdate(user.hp, HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(user.hp, { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
user.turnData.damageTaken += user.hp;
|
||||
|
||||
return true;
|
||||
|
@ -1758,7 +1759,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr {
|
|||
// Check to see if the Pokemon has an ability that blocks non-direct damage
|
||||
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
|
||||
if (!cancelled.value) {
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:cutHpPowerUpMove", { pokemonName: getPokemonNameWithAffix(user) })); // Queue recoil message
|
||||
}
|
||||
return true;
|
||||
|
@ -1805,7 +1806,7 @@ export class AddSubstituteAttr extends MoveEffectAttr {
|
|||
}
|
||||
|
||||
const damageTaken = this.roundUp ? Math.ceil(user.getMaxHp() * this.hpCost) : Math.floor(user.getMaxHp() * this.hpCost);
|
||||
user.damageAndUpdate(damageTaken, HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(damageTaken, { result: HitResult.INDIRECT, ignoreSegments: true, ignoreFaintPhase: true });
|
||||
user.addTag(BattlerTagType.SUBSTITUTE, 0, move.id, user.id);
|
||||
return true;
|
||||
}
|
||||
|
@ -1955,7 +1956,7 @@ export class FlameBurstAttr extends MoveEffectAttr {
|
|||
return false;
|
||||
}
|
||||
|
||||
targetAlly.damageAndUpdate(Math.max(1, Math.floor(1 / 16 * targetAlly.getMaxHp())), HitResult.OTHER);
|
||||
targetAlly.damageAndUpdate(Math.max(1, Math.floor(1 / 16 * targetAlly.getMaxHp())), { result: HitResult.INDIRECT });
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3434,9 +3435,8 @@ export class CutHpStatStageBoostAttr extends StatStageChangeAttr {
|
|||
this.cutRatio = cutRatio;
|
||||
this.messageCallback = messageCallback;
|
||||
}
|
||||
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), HitResult.OTHER, false, true);
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / this.cutRatio), { result: HitResult.INDIRECT });
|
||||
user.updateInfo();
|
||||
const ret = super.apply(user, target, move, args);
|
||||
if (this.messageCallback) {
|
||||
|
@ -5328,7 +5328,7 @@ const crashDamageFunc = (user: Pokemon, move: Move) => {
|
|||
return false;
|
||||
}
|
||||
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), HitResult.OTHER, false, true);
|
||||
user.damageAndUpdate(Utils.toDmgValue(user.getMaxHp() / 2), { result: HitResult.INDIRECT });
|
||||
globalScene.queueMessage(i18next.t("moveTriggers:keptGoingAndCrashed", { pokemonName: getPokemonNameWithAffix(user) }));
|
||||
user.turnData.damageTaken += Utils.toDmgValue(user.getMaxHp() / 2);
|
||||
|
||||
|
@ -5649,7 +5649,7 @@ export class CurseAttr extends MoveEffectAttr {
|
|||
return false;
|
||||
}
|
||||
const curseRecoilDamage = Math.max(1, Math.floor(user.getMaxHp() / 2));
|
||||
user.damageAndUpdate(curseRecoilDamage, HitResult.OTHER, false, true, true);
|
||||
user.damageAndUpdate(curseRecoilDamage, { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
globalScene.queueMessage(
|
||||
i18next.t("battlerTags:cursedOnAdd", {
|
||||
pokemonNameWithAffix: getPokemonNameWithAffix(user),
|
||||
|
@ -6158,9 +6158,16 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
|||
|
||||
if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1) {
|
||||
const allyPokemon = user.getAlly();
|
||||
if (slotIndex <= 1) {
|
||||
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, pokemon.getFieldIndex(), slotIndex, false, false));
|
||||
} else if (allyPokemon.isFainted()) {
|
||||
// Handle cases where revived pokemon needs to get switched in on same turn
|
||||
if (allyPokemon.isFainted() || allyPokemon === pokemon) {
|
||||
// Enemy switch phase should be removed and replaced with the revived pkmn switching in
|
||||
globalScene.tryRemovePhase((phase: SwitchSummonPhase) => phase instanceof SwitchSummonPhase && phase.getPokemon() === pokemon);
|
||||
// If the pokemon being revived was alive earlier in the turn, cancel its move
|
||||
// (revived pokemon can't move in the turn they're brought back)
|
||||
globalScene.findPhase((phase: MovePhase) => phase.pokemon === pokemon)?.cancel();
|
||||
if (user.fieldPosition === FieldPosition.CENTER) {
|
||||
user.setFieldPosition(FieldPosition.LEFT);
|
||||
}
|
||||
globalScene.unshiftPhase(new SwitchSummonPhase(SwitchType.SWITCH, allyPokemon.getFieldIndex(), slotIndex, false, false));
|
||||
}
|
||||
}
|
||||
|
@ -6966,7 +6973,8 @@ export class CopyMoveAttr extends CallMoveAttr {
|
|||
getCondition(): MoveConditionFunc {
|
||||
return (user, target, move) => {
|
||||
if (this.mirrorMove) {
|
||||
return target.getMoveHistory().length !== 0;
|
||||
const lastMove = target.getLastXMoves()[0]?.move;
|
||||
return !!lastMove && !this.invalidMoves.has(lastMove);
|
||||
} else {
|
||||
const lastMove = globalScene.currentBattle.lastMove;
|
||||
return lastMove !== undefined && !this.invalidMoves.has(lastMove);
|
||||
|
@ -8562,7 +8570,7 @@ export function initMoves() {
|
|||
new SelfStatusMove(Moves.METRONOME, PokemonType.NORMAL, -1, 10, -1, 0, 1)
|
||||
.attr(RandomMoveAttr, invalidMetronomeMoves),
|
||||
new StatusMove(Moves.MIRROR_MOVE, PokemonType.FLYING, -1, 20, -1, 0, 1)
|
||||
.attr(CopyMoveAttr, true),
|
||||
.attr(CopyMoveAttr, true, invalidMirrorMoveMoves),
|
||||
new AttackMove(Moves.SELF_DESTRUCT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 200, 100, 5, -1, 0, 1)
|
||||
.attr(SacrificialAttr)
|
||||
.makesContact(false)
|
||||
|
|
|
@ -2141,7 +2141,15 @@ export const trainerConfigs: TrainerConfigs = {
|
|||
}),
|
||||
[TrainerType.HOOLIGANS]: new TrainerConfig(++t)
|
||||
.setDoubleOnly()
|
||||
.setMoneyMultiplier(1.5)
|
||||
.setEncounterBgm(TrainerType.ROUGHNECK)
|
||||
.setPartyTemplateFunc(() =>
|
||||
getWavePartyTemplate(
|
||||
trainerPartyTemplates.TWO_WEAK,
|
||||
trainerPartyTemplates.TWO_AVG,
|
||||
trainerPartyTemplates.ONE_AVG_ONE_STRONG,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(s => s.isOfType(PokemonType.POISON) || s.isOfType(PokemonType.DARK)),
|
||||
[TrainerType.HOOPSTER]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.CYCLIST),
|
||||
[TrainerType.INFIELDER]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.CYCLIST),
|
||||
|
@ -2149,7 +2157,14 @@ export const trainerConfigs: TrainerConfigs = {
|
|||
[TrainerType.LINEBACKER]: new TrainerConfig(++t).setMoneyMultiplier(1.2).setEncounterBgm(TrainerType.CYCLIST),
|
||||
[TrainerType.MAID]: new TrainerConfig(++t).setMoneyMultiplier(1.6).setEncounterBgm(TrainerType.RICH),
|
||||
[TrainerType.MUSICIAN]: new TrainerConfig(++t)
|
||||
.setEncounterBgm(TrainerType.ROUGHNECK)
|
||||
.setMoneyMultiplier(1.1)
|
||||
.setEncounterBgm(TrainerType.POKEFAN)
|
||||
.setPartyTemplates(
|
||||
trainerPartyTemplates.FOUR_WEAKER,
|
||||
trainerPartyTemplates.THREE_WEAK,
|
||||
trainerPartyTemplates.TWO_WEAK_ONE_AVG,
|
||||
trainerPartyTemplates.TWO_AVG,
|
||||
)
|
||||
.setSpeciesFilter(s => !!s.getLevelMoves().find(plm => plm[1] === Moves.SING)),
|
||||
[TrainerType.HEX_MANIAC]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(1.5)
|
||||
|
@ -2214,7 +2229,14 @@ export const trainerConfigs: TrainerConfigs = {
|
|||
) || s.getLevelMoves().some(plm => plm[1] === Moves.RAIN_DANCE),
|
||||
), // Mons with rain abilities or who learn Rain Dance by level
|
||||
[TrainerType.PILOT]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(1.75)
|
||||
.setEncounterBgm(TrainerType.CLERK)
|
||||
.setPartyTemplates(
|
||||
trainerPartyTemplates.THREE_WEAK,
|
||||
trainerPartyTemplates.TWO_WEAK_ONE_AVG,
|
||||
trainerPartyTemplates.TWO_AVG,
|
||||
trainerPartyTemplates.THREE_AVG,
|
||||
)
|
||||
.setSpeciesFilter(s => tmSpecies[Moves.FLY].indexOf(s.speciesId) > -1),
|
||||
[TrainerType.POKEFAN]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(1.4)
|
||||
|
@ -2230,7 +2252,8 @@ export const trainerConfigs: TrainerConfigs = {
|
|||
trainerPartyTemplates.FOUR_WEAK_SAME,
|
||||
trainerPartyTemplates.FIVE_WEAK,
|
||||
trainerPartyTemplates.SIX_WEAKER_SAME,
|
||||
),
|
||||
)
|
||||
.setSpeciesFilter(s => tmSpecies[Moves.HELPING_HAND].indexOf(s.speciesId) > -1),
|
||||
[TrainerType.PRESCHOOLER]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(0.2)
|
||||
.setEncounterBgm(TrainerType.YOUNGSTER)
|
||||
|
@ -2352,16 +2375,29 @@ export const trainerConfigs: TrainerConfigs = {
|
|||
[TrainerPoolTier.SUPER_RARE]: [Species.LARVESTA],
|
||||
}),
|
||||
[TrainerType.RICH]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(5)
|
||||
.setMoneyMultiplier(3.25)
|
||||
.setName("Gentleman")
|
||||
.setHasGenders("Madame")
|
||||
.setHasDouble("Rich Couple"),
|
||||
.setHasDouble("Rich Couple")
|
||||
.setPartyTemplates(
|
||||
trainerPartyTemplates.THREE_WEAK,
|
||||
trainerPartyTemplates.FOUR_WEAK,
|
||||
trainerPartyTemplates.TWO_WEAK_ONE_AVG,
|
||||
trainerPartyTemplates.THREE_AVG,
|
||||
)
|
||||
.setSpeciesFilter(s => s.isOfType(PokemonType.NORMAL) || s.isOfType(PokemonType.ELECTRIC)),
|
||||
[TrainerType.RICH_KID]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(3.75)
|
||||
.setMoneyMultiplier(2.5)
|
||||
.setName("Rich Boy")
|
||||
.setHasGenders("Lady")
|
||||
.setHasDouble("Rich Kids")
|
||||
.setEncounterBgm(TrainerType.RICH),
|
||||
.setEncounterBgm(TrainerType.RICH)
|
||||
.setPartyTemplates(
|
||||
trainerPartyTemplates.FOUR_WEAKER,
|
||||
trainerPartyTemplates.THREE_WEAK_SAME,
|
||||
trainerPartyTemplates.TWO_WEAK_SAME_ONE_AVG,
|
||||
)
|
||||
.setSpeciesFilter(s => s.baseTotal <= 460),
|
||||
[TrainerType.ROUGHNECK]: new TrainerConfig(++t)
|
||||
.setMoneyMultiplier(1.4)
|
||||
.setEncounterBgm(TrainerType.ROUGHNECK)
|
||||
|
|
|
@ -46,6 +46,7 @@ export default class DamageNumberHandler {
|
|||
case HitResult.NOT_VERY_EFFECTIVE:
|
||||
[textColor, shadowColor] = ["#f08030", "#c03028"];
|
||||
break;
|
||||
case HitResult.INDIRECT_KO:
|
||||
case HitResult.ONE_HIT_KO:
|
||||
[textColor, shadowColor] = ["#a040a0", "#483850"];
|
||||
break;
|
||||
|
|
|
@ -4465,11 +4465,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return result;
|
||||
}
|
||||
|
||||
// In case of fatal damage, this tag would have gotten cleared before we could lapse it.
|
||||
const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND);
|
||||
const grudgeTag = this.getTag(BattlerTagType.GRUDGE);
|
||||
|
||||
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
||||
// In case of fatal damage, this tag would have gotten cleared before we could lapse it.
|
||||
const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND);
|
||||
const grudgeTag = this.getTag(BattlerTagType.GRUDGE);
|
||||
|
||||
if (dmg) {
|
||||
this.lapseTags(BattlerTagLapseType.HIT);
|
||||
|
@ -4484,19 +4482,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
globalScene.applyModifiers(EnemyEndureChanceModifier, false, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* We explicitly require to ignore the faint phase here, as we want to show the messages
|
||||
* about the critical hit and the super effective/not very effective messages before the faint phase.
|
||||
*/
|
||||
const damage = this.damageAndUpdate(
|
||||
isBlockedBySubstitute ? 0 : dmg,
|
||||
result as DamageResult,
|
||||
isCritical,
|
||||
isOneHitKo,
|
||||
isOneHitKo,
|
||||
true,
|
||||
source,
|
||||
);
|
||||
/**
|
||||
* We explicitly require to ignore the faint phase here, as we want to show the messages
|
||||
* about the critical hit and the super effective/not very effective messages before the faint phase.
|
||||
*/
|
||||
const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg,
|
||||
{
|
||||
result: result as DamageResult,
|
||||
isCritical,
|
||||
ignoreFaintPhase: true,
|
||||
source
|
||||
});
|
||||
|
||||
if (damage > 0) {
|
||||
if (source.isPlayer()) {
|
||||
|
@ -4557,7 +4553,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
globalScene.unshiftPhase(
|
||||
new FaintPhase(
|
||||
this.getBattlerIndex(),
|
||||
isOneHitKo,
|
||||
false,
|
||||
destinyTag,
|
||||
grudgeTag,
|
||||
source,
|
||||
|
@ -4635,28 +4631,37 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
/**
|
||||
* Called by apply(), given the damage, adds a new DamagePhase and actually updates HP values, etc.
|
||||
* Checks for 'Indirect' HitResults to account for Endure/Reviver Seed applying correctly
|
||||
* @param damage integer - passed to damage()
|
||||
* @param result an enum if it's super effective, not very, etc.
|
||||
* @param critical boolean if move is a critical hit
|
||||
* @param isCritical boolean if move is a critical hit
|
||||
* @param ignoreSegments boolean, passed to damage() and not used currently
|
||||
* @param preventEndure boolean, ignore endure properties of pokemon, passed to damage()
|
||||
* @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage()
|
||||
* @returns integer of damage done
|
||||
*/
|
||||
damageAndUpdate(
|
||||
damage: number,
|
||||
result?: DamageResult,
|
||||
critical = false,
|
||||
ignoreSegments = false,
|
||||
preventEndure = false,
|
||||
ignoreFaintPhase = false,
|
||||
source?: Pokemon,
|
||||
damageAndUpdate(damage: number,
|
||||
{
|
||||
result = HitResult.EFFECTIVE,
|
||||
isCritical = false,
|
||||
ignoreSegments = false,
|
||||
ignoreFaintPhase = false,
|
||||
source = undefined,
|
||||
}:
|
||||
{
|
||||
result?: DamageResult,
|
||||
isCritical?: boolean,
|
||||
ignoreSegments?: boolean,
|
||||
ignoreFaintPhase?: boolean,
|
||||
source?: Pokemon,
|
||||
} = {}
|
||||
): number {
|
||||
const isIndirectDamage = [ HitResult.INDIRECT, HitResult.INDIRECT_KO ].includes(result);
|
||||
const damagePhase = new DamageAnimPhase(
|
||||
this.getBattlerIndex(),
|
||||
damage,
|
||||
result as DamageResult,
|
||||
critical,
|
||||
this.getBattlerIndex(),
|
||||
damage,
|
||||
result as DamageResult,
|
||||
isCritical
|
||||
);
|
||||
globalScene.unshiftPhase(damagePhase);
|
||||
if (this.switchOutStatus && source) {
|
||||
|
@ -4665,7 +4670,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
damage = this.damage(
|
||||
damage,
|
||||
ignoreSegments,
|
||||
preventEndure,
|
||||
isIndirectDamage,
|
||||
ignoreFaintPhase,
|
||||
);
|
||||
// Damage amount may have changed, but needed to be queued before calling damage function
|
||||
|
@ -5575,7 +5580,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
this.resetBattleSummonData();
|
||||
if (this.summonDataPrimer) {
|
||||
for (const k of Object.keys(this.summonData)) {
|
||||
for (const k of Object.keys(this.summonDataPrimer)) {
|
||||
if (this.summonDataPrimer[k]) {
|
||||
this.summonData[k] = this.summonDataPrimer[k];
|
||||
}
|
||||
|
@ -7711,8 +7716,10 @@ export enum HitResult {
|
|||
HEAL,
|
||||
FAIL,
|
||||
MISS,
|
||||
OTHER,
|
||||
INDIRECT,
|
||||
IMMUNE,
|
||||
CONFUSION,
|
||||
INDIRECT_KO,
|
||||
}
|
||||
|
||||
export type DamageResult =
|
||||
|
@ -7720,7 +7727,9 @@ export type DamageResult =
|
|||
| HitResult.SUPER_EFFECTIVE
|
||||
| HitResult.NOT_VERY_EFFECTIVE
|
||||
| HitResult.ONE_HIT_KO
|
||||
| HitResult.OTHER;
|
||||
| HitResult.CONFUSION
|
||||
| HitResult.INDIRECT_KO
|
||||
| HitResult.INDIRECT;
|
||||
|
||||
/** Interface containing the results of a damage calculation for a given move */
|
||||
export interface DamageCalculationResult {
|
||||
|
|
|
@ -250,7 +250,7 @@ export class LoadingScene extends SceneBase {
|
|||
this.loadAtlas("statuses", "");
|
||||
this.loadAtlas("types", "");
|
||||
}
|
||||
const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"];
|
||||
const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN", "zh-TW", "ca-ES"];
|
||||
if (lang && availableLangs.includes(lang)) {
|
||||
this.loadImage(`pkmnday2025event-${lang}`, "events");
|
||||
} else {
|
||||
|
|
|
@ -5,8 +5,7 @@ import { allMoves } from "#app/data/moves/move";
|
|||
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
|
||||
import { type FormChangeItem, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
||||
import { getStatusEffectHealText } from "#app/data/status-effect";
|
||||
import type { PlayerPokemon } from "#app/field/pokemon";
|
||||
import Pokemon from "#app/field/pokemon";
|
||||
import Pokemon, { type PlayerPokemon } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import Overrides from "#app/overrides";
|
||||
import { EvolutionPhase } from "#app/phases/evolution-phase";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { applyAbAttrs, RunSuccessAbAttr } from "#app/data/ability";
|
||||
import { applyAbAttrs, applyPreLeaveFieldAbAttrs, PreLeaveFieldAbAttr, RunSuccessAbAttr } from "#app/data/ability";
|
||||
import { Stat } from "#app/enums/stat";
|
||||
import { StatusEffect } from "#app/enums/status-effect";
|
||||
import type { PlayerPokemon, EnemyPokemon } from "#app/field/pokemon";
|
||||
|
@ -29,6 +29,8 @@ export class AttemptRunPhase extends PokemonPhase {
|
|||
applyAbAttrs(RunSuccessAbAttr, playerPokemon, null, false, escapeChance);
|
||||
|
||||
if (playerPokemon.randSeedInt(100) < escapeChance.value && !this.forceFailEscape) {
|
||||
enemyField.forEach(enemyPokemon => applyPreLeaveFieldAbAttrs(PreLeaveFieldAbAttr, enemyPokemon));
|
||||
|
||||
globalScene.playSound("se/flee");
|
||||
globalScene.queueMessage(i18next.t("battle:runAwaySuccess"), null, true, 500);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ export class DamageAnimPhase extends PokemonPhase {
|
|||
start() {
|
||||
super.start();
|
||||
|
||||
if (this.damageResult === HitResult.ONE_HIT_KO) {
|
||||
if (this.damageResult === HitResult.ONE_HIT_KO || this.damageResult === HitResult.INDIRECT_KO) {
|
||||
if (globalScene.moveAnimations) {
|
||||
globalScene.toggleInvert(true);
|
||||
}
|
||||
|
@ -42,9 +42,11 @@ export class DamageAnimPhase extends PokemonPhase {
|
|||
applyDamage() {
|
||||
switch (this.damageResult) {
|
||||
case HitResult.EFFECTIVE:
|
||||
case HitResult.CONFUSION:
|
||||
globalScene.playSound("se/hit");
|
||||
break;
|
||||
case HitResult.SUPER_EFFECTIVE:
|
||||
case HitResult.INDIRECT_KO:
|
||||
case HitResult.ONE_HIT_KO:
|
||||
globalScene.playSound("se/hit_strong");
|
||||
break;
|
||||
|
@ -57,7 +59,7 @@ export class DamageAnimPhase extends PokemonPhase {
|
|||
globalScene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical);
|
||||
}
|
||||
|
||||
if (this.damageResult !== HitResult.OTHER && this.amount > 0) {
|
||||
if (this.damageResult !== HitResult.INDIRECT && this.amount > 0) {
|
||||
const flashTimer = globalScene.time.addEvent({
|
||||
delay: 100,
|
||||
repeat: 5,
|
||||
|
|
|
@ -258,7 +258,7 @@ export class FaintPhase extends PokemonPhase {
|
|||
} else {
|
||||
// Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase
|
||||
enemy.hp++;
|
||||
globalScene.unshiftPhase(new DamageAnimPhase(enemy.getBattlerIndex(), 0, HitResult.OTHER));
|
||||
globalScene.unshiftPhase(new DamageAnimPhase(enemy.getBattlerIndex(), 0, HitResult.INDIRECT));
|
||||
this.end();
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -3,7 +3,6 @@ import type { BattlerIndex } from "#app/battle";
|
|||
import { CommonAnim } from "#app/data/battle-anims";
|
||||
import { getStatusEffectHealText } from "#app/data/status-effect";
|
||||
import { StatusEffect } from "#app/enums/status-effect";
|
||||
import type { DamageResult } from "#app/field/pokemon";
|
||||
import { HitResult } from "#app/field/pokemon";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
import { HealingBoosterModifier } from "#app/modifier/modifier";
|
||||
|
@ -79,7 +78,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
|
|||
}
|
||||
const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
|
||||
if (healAmount.value < 0) {
|
||||
pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL as DamageResult);
|
||||
pokemon.damageAndUpdate(healAmount.value * -1, { result: HitResult.INDIRECT });
|
||||
healAmount.value = 0;
|
||||
}
|
||||
// Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock)
|
||||
|
|
|
@ -195,6 +195,10 @@ export class SummonPhase extends PartyMemberPokemonPhase {
|
|||
pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 });
|
||||
pokemon.getSprite().clearTint();
|
||||
pokemon.resetSummonData();
|
||||
// necessary to stay transformed during wild waves
|
||||
if (pokemon.summonData?.speciesForm) {
|
||||
pokemon.loadAssets(false);
|
||||
}
|
||||
globalScene.time.delayedCall(1000, () => this.end());
|
||||
},
|
||||
});
|
||||
|
|
|
@ -66,7 +66,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
|
|||
const damage = Utils.toDmgValue(pokemon.getMaxHp() / 16);
|
||||
|
||||
globalScene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct?
|
||||
pokemon.damageAndUpdate(damage, HitResult.EFFECTIVE, false, false, true);
|
||||
pokemon.damageAndUpdate(damage, { result: HitResult.INDIRECT, ignoreSegments: true });
|
||||
};
|
||||
|
||||
this.executeForAll((pokemon: Pokemon) => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { globalScene } from "#app/global-scene";
|
|||
import type { Gender } from "../data/gender";
|
||||
import type { Nature } from "#enums/nature";
|
||||
import type { PokeballType } from "#enums/pokeball";
|
||||
import { getPokemonSpecies } from "../data/pokemon-species";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species";
|
||||
import { Status } from "../data/status-effect";
|
||||
import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon";
|
||||
import { TrainerSlot } from "../data/trainer-config";
|
||||
|
@ -14,6 +14,7 @@ import { Moves } from "#enums/moves";
|
|||
import type { Species } from "#enums/species";
|
||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||
import type { PokemonType } from "#enums/pokemon-type";
|
||||
import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms";
|
||||
|
||||
export default class PokemonData {
|
||||
public id: number;
|
||||
|
@ -63,6 +64,7 @@ export default class PokemonData {
|
|||
public bossSegments?: number;
|
||||
|
||||
public summonData: PokemonSummonData;
|
||||
public summonDataSpeciesFormIndex: number;
|
||||
|
||||
/** Data that can customize a Pokemon in non-standard ways from its Species */
|
||||
public customPokemonData: CustomPokemonData;
|
||||
|
@ -145,8 +147,9 @@ export default class PokemonData {
|
|||
this.moveset = sourcePokemon.moveset;
|
||||
if (!forHistory) {
|
||||
this.status = sourcePokemon.status;
|
||||
if (this.player) {
|
||||
if (this.player && sourcePokemon.summonData) {
|
||||
this.summonData = sourcePokemon.summonData;
|
||||
this.summonDataSpeciesFormIndex = this.getSummonDataSpeciesFormIndex();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -170,6 +173,8 @@ export default class PokemonData {
|
|||
this.summonData.ability = source.summonData.ability;
|
||||
this.summonData.moveset = source.summonData.moveset?.map(m => PokemonMove.loadMove(m));
|
||||
this.summonData.types = source.summonData.types;
|
||||
this.summonData.speciesForm = source.summonData.speciesForm;
|
||||
this.summonDataSpeciesFormIndex = source.summonDataSpeciesFormIndex;
|
||||
|
||||
if (source.summonData.tags) {
|
||||
this.summonData.tags = source.summonData.tags?.map(t => loadBattlerTag(t));
|
||||
|
@ -213,8 +218,28 @@ export default class PokemonData {
|
|||
this,
|
||||
);
|
||||
if (this.summonData) {
|
||||
// when loading from saved session, recover summonData.speciesFrom and form index species object
|
||||
// used to stay transformed on reload session
|
||||
if (this.summonData.speciesForm) {
|
||||
this.summonData.speciesForm = getPokemonSpeciesForm(
|
||||
this.summonData.speciesForm.speciesId,
|
||||
this.summonDataSpeciesFormIndex,
|
||||
);
|
||||
}
|
||||
ret.primeSummonData(this.summonData);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to save summon data species form index
|
||||
* Necessary in case the pokemon is transformed
|
||||
* to reload the correct form
|
||||
*/
|
||||
getSummonDataSpeciesFormIndex(): number {
|
||||
if (this.summonData.speciesForm) {
|
||||
return this.summonData.speciesForm.formIndex;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -948,10 +948,10 @@ export function setSetting(setting: string, value: number): boolean {
|
|||
label: "日本語",
|
||||
handler: () => changeLocaleHandler("ja"),
|
||||
},
|
||||
// {
|
||||
// label: "Català",
|
||||
// handler: () => changeLocaleHandler("ca-ES")
|
||||
// },
|
||||
{
|
||||
label: "Català",
|
||||
handler: () => changeLocaleHandler("ca-ES")
|
||||
},
|
||||
{
|
||||
label: "עברית",
|
||||
handler: () => changeLocaleHandler("he")
|
||||
|
|
|
@ -574,9 +574,7 @@ export default class PartyUiHandler extends MessageUiHandler {
|
|||
form: pokemon.formIndex,
|
||||
female: pokemon.gender === Gender.FEMALE,
|
||||
};
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, pokemon.species, pokemon.formIndex, attributes).then(() =>
|
||||
this.clearOptions(),
|
||||
);
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, pokemon.species, attributes).then(() => this.clearOptions());
|
||||
return true;
|
||||
} else if (option === PartyOption.UNPAUSE_EVOLUTION) {
|
||||
this.clearOptions();
|
||||
|
|
|
@ -20,7 +20,6 @@ import { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, normalForm } from
|
|||
import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters";
|
||||
import { starterPassiveAbilities } from "#app/data/balance/passives";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { GameModes } from "#app/game-mode";
|
||||
import type { DexEntry, StarterAttributes } from "#app/system/game-data";
|
||||
import { AbilityAttr, DexAttr } from "#app/system/game-data";
|
||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||
|
@ -33,7 +32,6 @@ import { Egg } from "#app/data/egg";
|
|||
import Overrides from "#app/overrides";
|
||||
import { SettingKeyboard } from "#app/system/settings/settings-keyboard";
|
||||
import { Passive as PassiveAttr } from "#enums/passive";
|
||||
import * as Challenge from "#app/data/challenge";
|
||||
import MoveInfoOverlay from "#app/ui/move-info-overlay";
|
||||
import PokedexInfoOverlay from "#app/ui/pokedex-info-overlay";
|
||||
import { getEggTierForSpecies } from "#app/data/egg";
|
||||
|
@ -51,7 +49,6 @@ import {
|
|||
BooleanHolder,
|
||||
getLocalizedSpriteKey,
|
||||
isNullOrUndefined,
|
||||
NumberHolder,
|
||||
padInt,
|
||||
rgbHexToRgba,
|
||||
toReadableString,
|
||||
|
@ -242,6 +239,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
private starterAttributes: StarterAttributes;
|
||||
private savedStarterAttributes: StarterAttributes;
|
||||
|
||||
private previousSpecies: PokemonSpecies[];
|
||||
private previousStarterAttributes: StarterAttributes[];
|
||||
|
||||
protected blockInput = false;
|
||||
protected blockInputOverlay = false;
|
||||
|
||||
|
@ -656,6 +656,9 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
|
||||
// Filter bar sits above everything, except the message box
|
||||
this.starterSelectContainer.bringToTop(this.starterSelectMessageBoxContainer);
|
||||
|
||||
this.previousSpecies = [];
|
||||
this.previousStarterAttributes = [];
|
||||
}
|
||||
|
||||
show(args: any[]): boolean {
|
||||
|
@ -668,14 +671,14 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
return false;
|
||||
}
|
||||
this.species = args[0];
|
||||
this.formIndex = args[1] ?? 0;
|
||||
this.savedStarterAttributes = args[2] ?? {
|
||||
this.savedStarterAttributes = args[1] ?? {
|
||||
shiny: false,
|
||||
female: true,
|
||||
variant: 0,
|
||||
form: 0,
|
||||
};
|
||||
this.filteredIndices = args[3] ?? null;
|
||||
this.formIndex = this.savedStarterAttributes.form ?? 0;
|
||||
this.filteredIndices = args[2] ?? null;
|
||||
this.starterSetup();
|
||||
|
||||
this.moveInfoOverlay.clear(); // clear this when removing a menu; the cancel button doesn't seem to trigger this automatically on controllers
|
||||
|
@ -1091,8 +1094,19 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
if (this.statsMode) {
|
||||
this.toggleStatsMode(false);
|
||||
success = true;
|
||||
} else if (this.previousSpecies.length > 0) {
|
||||
this.blockInput = true;
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT).then(() => {
|
||||
const species = this.previousSpecies.pop();
|
||||
const starterAttributes = this.previousStarterAttributes.pop();
|
||||
this.moveInfoOverlay.clear();
|
||||
this.clearText();
|
||||
ui.setModeForceTransition(Mode.POKEDEX_PAGE, species, starterAttributes);
|
||||
success = true;
|
||||
});
|
||||
this.blockInput = false;
|
||||
} else {
|
||||
this.getUi().revertMode();
|
||||
ui.revertMode();
|
||||
success = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -1507,6 +1521,8 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
? (preSpecies ?? this.species).getFormNameToDisplay(preFormIndex, true)
|
||||
: (preSpecies ?? this.species).getExpandedSpeciesName(),
|
||||
handler: () => {
|
||||
this.previousSpecies.push(this.species);
|
||||
this.previousStarterAttributes.push({ ...this.savedStarterAttributes });
|
||||
const newSpecies = allSpecies.find(
|
||||
species => species.speciesId === pokemonPrevolutions[pre.speciesId],
|
||||
);
|
||||
|
@ -1522,7 +1538,7 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
this.savedStarterAttributes.form = newFormIndex;
|
||||
this.moveInfoOverlay.clear();
|
||||
this.clearText();
|
||||
ui.setMode(Mode.POKEDEX_PAGE, newSpecies, newFormIndex, this.savedStarterAttributes);
|
||||
ui.setMode(Mode.POKEDEX_PAGE, newSpecies, this.savedStarterAttributes);
|
||||
return true;
|
||||
},
|
||||
onHover: () => this.showText(conditionText),
|
||||
|
@ -1558,11 +1574,13 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
: (evoSpecies ?? this.species).getExpandedSpeciesName(),
|
||||
style: isCaughtEvo && isFormCaughtEvo ? TextStyle.WINDOW : TextStyle.SHADOW_TEXT,
|
||||
handler: () => {
|
||||
this.previousSpecies.push(this.species);
|
||||
this.previousStarterAttributes.push({ ...this.savedStarterAttributes });
|
||||
this.starterAttributes.form = newFormIndex;
|
||||
this.savedStarterAttributes.form = newFormIndex;
|
||||
this.moveInfoOverlay.clear();
|
||||
this.clearText();
|
||||
ui.setMode(Mode.POKEDEX_PAGE, evoSpecies, newFormIndex, this.savedStarterAttributes);
|
||||
ui.setMode(Mode.POKEDEX_PAGE, evoSpecies, this.savedStarterAttributes);
|
||||
return true;
|
||||
},
|
||||
onHover: () => this.showText(conditionText),
|
||||
|
@ -1598,6 +1616,8 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
label: label,
|
||||
style: isFormCaught ? TextStyle.WINDOW : TextStyle.SHADOW_TEXT,
|
||||
handler: () => {
|
||||
this.previousSpecies.push(this.species);
|
||||
this.previousStarterAttributes.push({ ...this.savedStarterAttributes });
|
||||
const newSpecies = this.species;
|
||||
const newFormIndex = this.species.forms.find(f => f.formKey === bf.formKey)?.formIndex;
|
||||
this.starterAttributes.form = newFormIndex;
|
||||
|
@ -1607,7 +1627,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
ui.setMode(
|
||||
Mode.POKEDEX_PAGE,
|
||||
newSpecies,
|
||||
newFormIndex,
|
||||
this.savedStarterAttributes,
|
||||
this.filteredIndices,
|
||||
);
|
||||
|
@ -1958,6 +1977,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
case Button.LEFT:
|
||||
this.blockInput = true;
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT).then(() => {
|
||||
// Always go back to first selection after scrolling around
|
||||
if (this.previousSpecies.length === 0) {
|
||||
this.previousSpecies.push(this.species);
|
||||
this.previousStarterAttributes.push({ ...this.savedStarterAttributes });
|
||||
}
|
||||
let newSpecies: PokemonSpecies;
|
||||
if (this.filteredIndices) {
|
||||
const index = this.filteredIndices.findIndex(id => id === this.species.speciesId);
|
||||
|
@ -1979,7 +2003,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
ui.setModeForceTransition(
|
||||
Mode.POKEDEX_PAGE,
|
||||
newSpecies,
|
||||
newFormIndex,
|
||||
this.savedStarterAttributes,
|
||||
this.filteredIndices,
|
||||
);
|
||||
|
@ -1988,6 +2011,11 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
break;
|
||||
case Button.RIGHT:
|
||||
ui.setModeWithoutClear(Mode.OPTION_SELECT).then(() => {
|
||||
// Always go back to first selection after scrolling around
|
||||
if (this.previousSpecies.length === 0) {
|
||||
this.previousSpecies.push(this.species);
|
||||
this.previousStarterAttributes.push({ ...this.savedStarterAttributes });
|
||||
}
|
||||
let newSpecies: PokemonSpecies;
|
||||
if (this.filteredIndices) {
|
||||
const index = this.filteredIndices.findIndex(id => id === this.species.speciesId);
|
||||
|
@ -2009,7 +2037,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
ui.setModeForceTransition(
|
||||
Mode.POKEDEX_PAGE,
|
||||
newSpecies,
|
||||
newFormIndex,
|
||||
this.savedStarterAttributes,
|
||||
this.filteredIndices,
|
||||
);
|
||||
|
@ -2128,22 +2155,6 @@ export default class PokedexPageUiHandler extends MessageUiHandler {
|
|||
}
|
||||
}
|
||||
|
||||
getValueLimit(): number {
|
||||
const valueLimit = new NumberHolder(0);
|
||||
switch (globalScene.gameMode.modeId) {
|
||||
case GameModes.ENDLESS:
|
||||
case GameModes.SPLICED_ENDLESS:
|
||||
valueLimit.value = 15;
|
||||
break;
|
||||
default:
|
||||
valueLimit.value = 10;
|
||||
}
|
||||
|
||||
Challenge.applyChallenges(globalScene.gameMode, Challenge.ChallengeType.STARTER_POINTS, valueLimit);
|
||||
|
||||
return valueLimit.value;
|
||||
}
|
||||
|
||||
setCursor(cursor: number): boolean {
|
||||
const ret = super.setCursor(cursor);
|
||||
|
||||
|
|
|
@ -1125,7 +1125,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
|
|||
} else if (this.showingTray) {
|
||||
if (button === Button.ACTION) {
|
||||
const formIndex = this.trayForms[this.trayCursor].formIndex;
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, formIndex, { form: formIndex }, this.filteredIndices);
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, { form: formIndex }, this.filteredIndices);
|
||||
success = true;
|
||||
} else {
|
||||
const numberOfForms = this.trayContainers.length;
|
||||
|
@ -1174,7 +1174,7 @@ export default class PokedexUiHandler extends MessageUiHandler {
|
|||
}
|
||||
} else {
|
||||
if (button === Button.ACTION) {
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, 0, null, this.filteredIndices);
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, null, this.filteredIndices);
|
||||
success = true;
|
||||
} else {
|
||||
switch (button) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { CandyUpgradeNotificationChangedEvent } from "#app/events/battle-scene";
|
||||
import { BattleSceneEventType } from "#app/events/battle-scene";
|
||||
import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import { pokemonEvolutions, pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions";
|
||||
import type { Variant } from "#app/data/variant";
|
||||
import { getVariantTint, getVariantIcon } from "#app/data/variant";
|
||||
import { argbFromRgba } from "@material/material-color-utilities";
|
||||
|
@ -19,7 +19,7 @@ import { pokemonFormChanges } from "#app/data/pokemon-forms";
|
|||
import type { LevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balance/pokemon-level-moves";
|
||||
import type PokemonSpecies from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species";
|
||||
import { allSpecies, getPokemonSpecies, getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species";
|
||||
import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters";
|
||||
import { PokemonType } from "#enums/pokemon-type";
|
||||
import { GameModes } from "#app/game-mode";
|
||||
|
@ -80,6 +80,7 @@ import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
|||
import { achvs } from "#app/system/achv";
|
||||
import * as Utils from "../utils";
|
||||
import type { GameObjects } from "phaser";
|
||||
import { checkStarterValidForChallenge } from "#app/data/challenge";
|
||||
|
||||
export type StarterSelectCallback = (starters: Starter[]) => void;
|
||||
|
||||
|
@ -149,7 +150,7 @@ const languageSettings: { [key: string]: LanguageSetting } = {
|
|||
instructionTextSize: "38px",
|
||||
},
|
||||
"ca-ES": {
|
||||
starterInfoTextSize: "56px",
|
||||
starterInfoTextSize: "52px",
|
||||
instructionTextSize: "38px",
|
||||
},
|
||||
"he":{
|
||||
|
@ -1764,21 +1765,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
const species = starter.species;
|
||||
const [isDupe] = this.isInParty(species);
|
||||
const starterCost = globalScene.gameData.getSpeciesStarterValue(species.speciesId);
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isValidForChallenge = checkStarterValidForChallenge(
|
||||
species,
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)),
|
||||
this.isPartyValid(),
|
||||
);
|
||||
const isCaught = globalScene.gameData.dexData[species.speciesId].caughtAttr;
|
||||
return (
|
||||
!isDupe &&
|
||||
isValidForChallenge.value &&
|
||||
currentPartyValue + starterCost <= this.getValueLimit() &&
|
||||
isCaught
|
||||
!isDupe && isValidForChallenge && currentPartyValue + starterCost <= this.getValueLimit() && isCaught
|
||||
);
|
||||
});
|
||||
if (validStarters.length === 0) {
|
||||
|
@ -1865,16 +1859,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
const ui = this.getUi();
|
||||
let options: any[] = []; // TODO: add proper type
|
||||
|
||||
const [isDupe, removeIndex]: [boolean, number] = this.isInParty(this.lastSpecies); // checks to see if the pokemon is a duplicate; if it is, returns the index that will be removed
|
||||
const [isDupe, removeIndex]: [boolean, number] = this.isInParty(this.lastSpecies);
|
||||
|
||||
const isPartyValid = this.isPartyValid();
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isValidForChallenge = checkStarterValidForChallenge(
|
||||
this.lastSpecies,
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(
|
||||
this.lastSpecies,
|
||||
this.getCurrentDexProps(this.lastSpecies.speciesId),
|
||||
|
@ -1892,11 +1881,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
const newCost = globalScene.gameData.getSpeciesStarterValue(this.lastSpecies.speciesId);
|
||||
if (
|
||||
!isDupe &&
|
||||
isValidForChallenge.value &&
|
||||
isValidForChallenge &&
|
||||
currentPartyValue + newCost <= this.getValueLimit() &&
|
||||
this.starterSpecies.length < PLAYER_PARTY_MAX_SIZE
|
||||
) {
|
||||
// this checks to make sure the pokemon doesn't exist in your party, it's valid for the challenge and that it won't go over the cost limit; if it meets all these criteria it will add it to your party
|
||||
options = [
|
||||
{
|
||||
label: i18next.t("starterSelectUiHandler:addToParty"),
|
||||
|
@ -1906,7 +1894,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
globalScene.gameData.getSpeciesStarterValue(this.lastSpecies.speciesId),
|
||||
true,
|
||||
);
|
||||
if (!isDupe && isValidForChallenge.value && isOverValueLimit) {
|
||||
if (!isDupe && isValidForChallenge && isOverValueLimit) {
|
||||
const cursorObj = this.starterCursorObjs[this.starterSpecies.length];
|
||||
cursorObj.setVisible(true);
|
||||
cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y);
|
||||
|
@ -2340,7 +2328,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
form: starterAttributes.form,
|
||||
female: starterAttributes.female,
|
||||
};
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, starterAttributes.form, attributes);
|
||||
ui.setOverlayMode(Mode.POKEDEX_PAGE, this.lastSpecies, attributes);
|
||||
});
|
||||
return true;
|
||||
},
|
||||
|
@ -2997,32 +2985,27 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
/* Here we are making a fake form index dex props for challenges
|
||||
* Since some pokemon rely on forms to be valid (i.e. blaze tauros for fire challenges), we make a fake form and dex props to use in the challenge
|
||||
*/
|
||||
if (!species.forms[i].isStarterSelectable) {
|
||||
continue;
|
||||
}
|
||||
const tempFormProps = BigInt(Math.pow(2, i)) * DexAttr.DEFAULT_FORM;
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isValidForChallenge = checkStarterValidForChallenge(
|
||||
container.species,
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(species, tempFormProps),
|
||||
true,
|
||||
);
|
||||
allFormsValid = allFormsValid || isValidForChallenge.value;
|
||||
allFormsValid = allFormsValid || isValidForChallenge;
|
||||
}
|
||||
} else {
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isValidForChallenge = checkStarterValidForChallenge(
|
||||
container.species,
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(
|
||||
species,
|
||||
globalScene.gameData.getSpeciesDefaultDexAttr(container.species, false, true),
|
||||
),
|
||||
true,
|
||||
);
|
||||
allFormsValid = isValidForChallenge.value;
|
||||
allFormsValid = isValidForChallenge;
|
||||
}
|
||||
if (allFormsValid) {
|
||||
this.validStarterContainers.push(container);
|
||||
|
@ -3855,15 +3838,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
this.pokemonSprite.setVisible(!this.statsMode);
|
||||
}
|
||||
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
species,
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor),
|
||||
!!this.starterSpecies.length,
|
||||
);
|
||||
const currentFilteredContainer = this.filteredStarterContainers.find(
|
||||
p => p.species.speciesId === species.speciesId,
|
||||
);
|
||||
|
@ -4237,20 +4211,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
globalScene.time.delayedCall(fixedInt(500), () => this.tryUpdateValue());
|
||||
return false;
|
||||
}
|
||||
let isPartyValid: boolean = this.isPartyValid(); // this checks to see if the party is valid
|
||||
let isPartyValid: boolean = this.isPartyValid();
|
||||
if (addingToParty) {
|
||||
// this does a check to see if the pokemon being added is valid; if so, it will update the isPartyValid boolean
|
||||
const isNewPokemonValid = new BooleanHolder(true);
|
||||
const species = this.filteredStarterContainers[this.cursor].species;
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isNewPokemonValid = checkStarterValidForChallenge(
|
||||
species,
|
||||
isNewPokemonValid,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)),
|
||||
false,
|
||||
);
|
||||
isPartyValid = isPartyValid || isNewPokemonValid.value;
|
||||
isPartyValid = isPartyValid || isNewPokemonValid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4274,12 +4243,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
* If speciesStarterDexEntry?.caughtAttr is true, this species registered in stater.
|
||||
* we change to can AddParty value to true since the user has enough cost to choose this pokemon and this pokemon registered too.
|
||||
*/
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isValidForChallenge = checkStarterValidForChallenge(
|
||||
this.allSpecies[s],
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(
|
||||
this.allSpecies[s],
|
||||
this.getCurrentDexProps(this.allSpecies[s].speciesId),
|
||||
|
@ -4287,7 +4252,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
isPartyValid,
|
||||
);
|
||||
|
||||
const canBeChosen = remainValue >= speciesStarterValue && isValidForChallenge.value;
|
||||
const canBeChosen = remainValue >= speciesStarterValue && isValidForChallenge;
|
||||
|
||||
const isPokemonInParty = this.isInParty(this.allSpecies[s])[0]; // this will get the valud of isDupe from isInParty. This will let us see if the pokemon in question is in our party already so we don't grey out the sprites if they're invalid
|
||||
|
||||
|
@ -4421,17 +4386,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||
isPartyValid(): boolean {
|
||||
let canStart = false;
|
||||
for (let s = 0; s < this.starterSpecies.length; s++) {
|
||||
const isValidForChallenge = new BooleanHolder(true);
|
||||
const species = this.starterSpecies[s];
|
||||
Challenge.applyChallenges(
|
||||
globalScene.gameMode,
|
||||
Challenge.ChallengeType.STARTER_CHOICE,
|
||||
const isValidForChallenge = checkStarterValidForChallenge(
|
||||
species,
|
||||
isValidForChallenge,
|
||||
globalScene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId)),
|
||||
false,
|
||||
);
|
||||
canStart = canStart || isValidForChallenge.value;
|
||||
canStart = canStart || isValidForChallenge;
|
||||
}
|
||||
return canStart;
|
||||
}
|
||||
|
|
|
@ -465,6 +465,7 @@ export function hasAllLocalizedSprites(lang?: string): boolean {
|
|||
case "pt-BR":
|
||||
case "ko":
|
||||
case "ja":
|
||||
case "ca-ES":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { PokeballType } from "#app/enums/pokeball";
|
||||
import { WeatherType } from "#app/enums/weather-type";
|
||||
import type { CommandPhase } from "#app/phases/command-phase";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
|
@ -131,4 +133,20 @@ describe("Abilities - Desolate Land", () => {
|
|||
|
||||
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.HARSH_SUN);
|
||||
});
|
||||
|
||||
it("should lift after fleeing from a wild pokemon", async () => {
|
||||
game.override
|
||||
.enemyAbility(Abilities.DESOLATE_LAND)
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.HARSH_SUN);
|
||||
|
||||
vi.spyOn(game.scene.getPlayerPokemon()!, "randSeedInt").mockReturnValue(0);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).not.toBe(WeatherType.HARSH_SUN);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -127,4 +127,63 @@ describe("Abilities - Imposter", () => {
|
|||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
|
||||
it("should persist transformed attributes across reloads", async () => {
|
||||
game.override.moveset([Moves.ABSORB]);
|
||||
|
||||
await game.classicMode.startBattle([Species.DITTO]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
await game.toNextWave();
|
||||
|
||||
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||
|
||||
await game.reload.reloadSession();
|
||||
|
||||
const playerReloaded = game.scene.getPlayerPokemon()!;
|
||||
const playerMoveset = player.getMoveset();
|
||||
|
||||
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
|
||||
expect(playerReloaded.getAbility()).toBe(enemy.getAbility());
|
||||
expect(playerReloaded.getGender()).toBe(enemy.getGender());
|
||||
|
||||
expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP));
|
||||
for (const s of EFFECTIVE_STATS) {
|
||||
expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false));
|
||||
}
|
||||
|
||||
expect(playerMoveset.length).toEqual(1);
|
||||
expect(playerMoveset[0]?.moveId).toEqual(Moves.SPLASH);
|
||||
});
|
||||
|
||||
it("should stay transformed with the correct form after reload", async () => {
|
||||
game.override.moveset([Moves.ABSORB]);
|
||||
game.override.enemySpecies(Species.UNOWN);
|
||||
await game.classicMode.startBattle([Species.DITTO]);
|
||||
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// change form
|
||||
enemy.species.forms[5];
|
||||
enemy.species.formIndex = 5;
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
await game.toNextWave();
|
||||
|
||||
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||
|
||||
await game.reload.reloadSession();
|
||||
|
||||
const playerReloaded = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
|
||||
expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { BattlerIndex } from "#app/battle";
|
||||
import type { CommandPhase } from "#app/phases/command-phase";
|
||||
import { Command } from "#app/ui/command-ui-handler";
|
||||
import { PostSummonWeatherChangeAbAttr } from "#app/data/ability";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
|
@ -157,6 +159,22 @@ describe("Abilities - Neutralizing Gas", () => {
|
|||
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should deactivate after fleeing from a wild pokemon", async () => {
|
||||
game.override
|
||||
.enemyAbility(Abilities.NEUTRALIZING_GAS)
|
||||
.ability(Abilities.BALL_FETCH);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeDefined();
|
||||
|
||||
vi.spyOn(game.scene.getPlayerPokemon()!, "randSeedInt").mockReturnValue(0);
|
||||
|
||||
const commandPhase = game.scene.getCurrentPhase() as CommandPhase;
|
||||
commandPhase.handleCommand(Command.RUN, 0);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.arena.getTag(ArenaTagType.NEUTRALIZING_GAS)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("should not activate abilities of pokemon no longer on the field", async () => {
|
||||
game.override
|
||||
.battleType("single")
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
import { BattlerIndex } from "#app/battle";
|
||||
import { allMoves } from "#app/data/moves/move";
|
||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||
import type { PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("Items - Reviver Seed", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([ Moves.SPLASH, Moves.TACKLE, Moves.ENDURE ])
|
||||
.ability(Abilities.BALL_FETCH)
|
||||
.battleType("single")
|
||||
.disableCrits()
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.startingHeldItems([{ name: "REVIVER_SEED" }])
|
||||
.enemyHeldItems([{ name: "REVIVER_SEED" }])
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
vi.spyOn(allMoves[Moves.SHEER_COLD], "accuracy", "get").mockReturnValue(100);
|
||||
vi.spyOn(allMoves[Moves.LEECH_SEED], "accuracy", "get").mockReturnValue(100);
|
||||
vi.spyOn(allMoves[Moves.WHIRLPOOL], "accuracy", "get").mockReturnValue(100);
|
||||
vi.spyOn(allMoves[Moves.WILL_O_WISP], "accuracy", "get").mockReturnValue(100);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ moveType: "Special Move", move: Moves.WATER_GUN },
|
||||
{ moveType: "Physical Move", move: Moves.TACKLE },
|
||||
{ moveType: "Fixed Damage Move", move: Moves.SEISMIC_TOSS },
|
||||
{ moveType: "Final Gambit", move: Moves.FINAL_GAMBIT },
|
||||
{ moveType: "Counter", move: Moves.COUNTER },
|
||||
{ moveType: "OHKO", move: Moves.SHEER_COLD }
|
||||
])("should activate the holder's reviver seed from a $moveType", async ({ move }) => {
|
||||
game.override
|
||||
.enemyLevel(100)
|
||||
.startingLevel(1)
|
||||
.enemyMoveset(move);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.FEEBAS ]);
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
player.damageAndUpdate(player.hp - 1);
|
||||
|
||||
const reviverSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier;
|
||||
vi.spyOn(reviverSeed, "apply");
|
||||
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(player.isFainted()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should activate the holder's reviver seed from confusion self-hit", async () => {
|
||||
game.override
|
||||
.enemyLevel(1)
|
||||
.startingLevel(100)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.FEEBAS ]);
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
player.damageAndUpdate(player.hp - 1);
|
||||
player.addTag(BattlerTagType.CONFUSED, 3);
|
||||
|
||||
const reviverSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier;
|
||||
vi.spyOn(reviverSeed, "apply");
|
||||
|
||||
vi.spyOn(player, "randSeedInt").mockReturnValue(0); // Force confusion self-hit
|
||||
game.move.select(Moves.TACKLE);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(player.isFainted()).toBeFalsy();
|
||||
});
|
||||
|
||||
// Damaging opponents tests
|
||||
it.each([
|
||||
{ moveType: "Damaging Move Chip Damage", move: Moves.SALT_CURE },
|
||||
{ moveType: "Chip Damage", move: Moves.LEECH_SEED },
|
||||
{ moveType: "Trapping Chip Damage", move: Moves.WHIRLPOOL },
|
||||
{ moveType: "Status Effect Damage", move: Moves.WILL_O_WISP },
|
||||
{ moveType: "Weather", move: Moves.SANDSTORM },
|
||||
])("should not activate the holder's reviver seed from $moveType", async ({ move }) => {
|
||||
game.override
|
||||
.enemyLevel(1)
|
||||
.startingLevel(100)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.moveset(move)
|
||||
.enemyMoveset(Moves.ENDURE);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
enemy.damageAndUpdate(enemy.hp - 1);
|
||||
|
||||
game.move.select(move);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(enemy.isFainted()).toBeTruthy();
|
||||
});
|
||||
|
||||
// Self-damage tests
|
||||
it.each([
|
||||
{ moveType: "Recoil", move: Moves.DOUBLE_EDGE },
|
||||
{ moveType: "Self-KO", move: Moves.EXPLOSION },
|
||||
{ moveType: "Self-Deduction", move: Moves.CURSE },
|
||||
{ moveType: "Liquid Ooze", move: Moves.GIGA_DRAIN },
|
||||
])("should not activate the holder's reviver seed from $moveType", async ({ move }) => {
|
||||
game.override
|
||||
.enemyLevel(100)
|
||||
.startingLevel(1)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.moveset(move)
|
||||
.enemyAbility(Abilities.LIQUID_OOZE)
|
||||
.enemyMoveset(Moves.SPLASH);
|
||||
await game.classicMode.startBattle([ Species.GASTLY, Species.FEEBAS ]);
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
player.damageAndUpdate(player.hp - 1);
|
||||
|
||||
const playerSeed = player.getHeldItems()[0] as PokemonInstantReviveModifier;
|
||||
vi.spyOn(playerSeed, "apply");
|
||||
|
||||
game.move.select(move);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(player.isFainted()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should not activate the holder's reviver seed from Destiny Bond fainting", async () => {
|
||||
game.override
|
||||
.enemyLevel(100)
|
||||
.startingLevel(1)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.moveset(Moves.DESTINY_BOND)
|
||||
.startingHeldItems([]) // reset held items to nothing so user doesn't revive and not trigger Destiny Bond
|
||||
.enemyMoveset(Moves.TACKLE);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.FEEBAS ]);
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
player.damageAndUpdate(player.hp - 1);
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.select(Moves.DESTINY_BOND);
|
||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(enemy.isFainted()).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -22,7 +22,7 @@ describe("Moves - Endure", () => {
|
|||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([Moves.THUNDER, Moves.BULLET_SEED, Moves.TOXIC])
|
||||
.moveset([ Moves.THUNDER, Moves.BULLET_SEED, Moves.TOXIC, Moves.SHEER_COLD ])
|
||||
.ability(Abilities.SKILL_LINK)
|
||||
.startingLevel(100)
|
||||
.battleType("single")
|
||||
|
@ -50,16 +50,37 @@ describe("Moves - Endure", () => {
|
|||
expect(game.scene.getEnemyPokemon()!.hp).toBe(1);
|
||||
});
|
||||
|
||||
it("shouldn't prevent fainting from indirect damage", async () => {
|
||||
game.override.enemyLevel(100);
|
||||
await game.classicMode.startBattle([Species.ARCEUS]);
|
||||
|
||||
it("should let the pokemon survive against OHKO moves", async () => {
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP ]);
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
enemy.hp = 2;
|
||||
|
||||
game.move.select(Moves.TOXIC);
|
||||
await game.phaseInterceptor.to("VictoryPhase");
|
||||
game.move.select(Moves.SHEER_COLD);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(enemy.isFainted()).toBe(true);
|
||||
expect(enemy.isFainted()).toBeFalsy();
|
||||
});
|
||||
|
||||
// comprehensive indirect damage test copied from Reviver Seed test
|
||||
it.each([
|
||||
{ moveType: "Damaging Move Chip Damage", move: Moves.SALT_CURE },
|
||||
{ moveType: "Chip Damage", move: Moves.LEECH_SEED },
|
||||
{ moveType: "Trapping Chip Damage", move: Moves.WHIRLPOOL },
|
||||
{ moveType: "Status Effect Damage", move: Moves.TOXIC },
|
||||
{ moveType: "Weather", move: Moves.SANDSTORM },
|
||||
])("should not prevent fainting from $moveType", async ({ move }) => {
|
||||
game.override
|
||||
.enemyLevel(1)
|
||||
.startingLevel(100)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.moveset(move)
|
||||
.enemyMoveset(Moves.ENDURE);
|
||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.FEEBAS ]);
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
enemy.damageAndUpdate(enemy.hp - 1);
|
||||
|
||||
game.move.select(move);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(enemy.isFainted()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -114,4 +114,25 @@ describe("Moves - Revival Blessing", () => {
|
|||
expect(feebas.hp).toBe(toDmgValue(0.5 * feebas.getMaxHp()));
|
||||
expect(game.scene.getPlayerField()[0]).toBe(feebas);
|
||||
});
|
||||
|
||||
it("should not summon multiple pokemon to the same slot when reviving the enemy ally in doubles", async () => {
|
||||
game.override
|
||||
.battleType("double")
|
||||
.enemyMoveset([ Moves.REVIVAL_BLESSING ])
|
||||
.moveset([ Moves.SPLASH ])
|
||||
.startingWave(25); // 2nd rival battle - must have 3+ pokemon
|
||||
await game.classicMode.startBattle([ Species.ARCEUS, Species.GIRATINA ]);
|
||||
|
||||
const enemyFainting = game.scene.getEnemyField()[0];
|
||||
|
||||
game.move.select(Moves.SPLASH, 0);
|
||||
game.move.select(Moves.SPLASH, 1);
|
||||
await game.killPokemon(enemyFainting);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
await game.toNextTurn();
|
||||
// If there are incorrectly two switch phases into this slot, the fainted pokemon will end up in slot 3
|
||||
// Make sure it's still in slot 1
|
||||
expect(game.scene.getEnemyParty()[0]).toBe(enemyFainting);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
|||
import { Moves } from "#enums/moves";
|
||||
import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { BattlerIndex } from "#app/battle";
|
||||
|
||||
// TODO: Add more tests once Transform is fully implemented
|
||||
describe("Moves - Transform", () => {
|
||||
|
@ -58,7 +59,7 @@ describe("Moves - Transform", () => {
|
|||
}
|
||||
|
||||
const playerMoveset = player.getMoveset();
|
||||
const enemyMoveset = player.getMoveset();
|
||||
const enemyMoveset = enemy.getMoveset();
|
||||
|
||||
expect(playerMoveset.length).toBe(enemyMoveset.length);
|
||||
for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) {
|
||||
|
@ -127,4 +128,71 @@ describe("Moves - Transform", () => {
|
|||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
|
||||
it("should persist transformed attributes across reloads", async () => {
|
||||
game.override.enemyMoveset([]).moveset([]);
|
||||
|
||||
await game.classicMode.startBattle([Species.DITTO]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
game.move.changeMoveset(player, Moves.TRANSFORM);
|
||||
game.move.changeMoveset(enemy, Moves.MEMENTO);
|
||||
|
||||
game.move.select(Moves.TRANSFORM);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.toNextWave();
|
||||
|
||||
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||
|
||||
await game.reload.reloadSession();
|
||||
|
||||
const playerReloaded = game.scene.getPlayerPokemon()!;
|
||||
const playerMoveset = player.getMoveset();
|
||||
|
||||
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
|
||||
expect(playerReloaded.getAbility()).toBe(enemy.getAbility());
|
||||
expect(playerReloaded.getGender()).toBe(enemy.getGender());
|
||||
|
||||
expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP));
|
||||
for (const s of EFFECTIVE_STATS) {
|
||||
expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false));
|
||||
}
|
||||
|
||||
expect(playerMoveset.length).toEqual(1);
|
||||
expect(playerMoveset[0]?.moveId).toEqual(Moves.MEMENTO);
|
||||
});
|
||||
|
||||
it("should stay transformed with the correct form after reload", async () => {
|
||||
game.override.enemyMoveset([]).moveset([]);
|
||||
game.override.enemySpecies(Species.DARMANITAN);
|
||||
|
||||
await game.classicMode.startBattle([Species.DITTO]);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
|
||||
// change form
|
||||
enemy.species.forms[1];
|
||||
enemy.species.formIndex = 1;
|
||||
|
||||
game.move.changeMoveset(player, Moves.TRANSFORM);
|
||||
game.move.changeMoveset(enemy, Moves.MEMENTO);
|
||||
|
||||
game.move.select(Moves.TRANSFORM);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.toNextWave();
|
||||
|
||||
expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase");
|
||||
expect(game.scene.currentBattle.waveIndex).toBe(2);
|
||||
|
||||
await game.reload.reloadSession();
|
||||
|
||||
const playerReloaded = game.scene.getPlayerPokemon()!;
|
||||
|
||||
expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);
|
||||
expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue