diff --git a/public/images/pokemon/back/1017-cornerstone-mask-tera.json b/public/images/pokemon/back/1017-cornerstone-mask-tera.json index 1aec2048cbb..1424d2555e9 100644 --- a/public/images/pokemon/back/1017-cornerstone-mask-tera.json +++ b/public/images/pokemon/back/1017-cornerstone-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-cornerstone-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 94, + "h": 94 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { - "x": 0, + "x": 1, "y": 2, - "w": 144, - "h": 140 + "w": 94, + "h": 92 }, "frame": { "x": 0, "y": 0, - "w": 144, - "h": 140 + "w": 94, + "h": 92 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:dcbefd71783ecd9e243d3426e927b845:1f7ab2bf8ba8848c6f73ec7bc9e81921:19983e2c44c76def68513841019a938a$" + "smartupdate": "$TexturePacker:SmartUpdate:7445fc98c3d4d220190fb1c24e70d3ab:1f7ab2bf8ba8848c6f73ec7bc9e81921:19983e2c44c76def68513841019a938a$" } } diff --git a/public/images/pokemon/back/1017-cornerstone-mask-tera.png b/public/images/pokemon/back/1017-cornerstone-mask-tera.png index 80f34d6b529..9a612041335 100644 Binary files a/public/images/pokemon/back/1017-cornerstone-mask-tera.png and b/public/images/pokemon/back/1017-cornerstone-mask-tera.png differ diff --git a/public/images/pokemon/back/1017-hearthflame-mask-tera.json b/public/images/pokemon/back/1017-hearthflame-mask-tera.json index 06dc6aa0c2f..83bc9e280ba 100644 --- a/public/images/pokemon/back/1017-hearthflame-mask-tera.json +++ b/public/images/pokemon/back/1017-hearthflame-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-hearthflame-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { "x": 3, "y": 0, - "w": 136, - "h": 144 + "w": 89, + "h": 96 }, "frame": { "x": 0, "y": 0, - "w": 136, - "h": 144 + "w": 89, + "h": 96 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:e85adb80edf2c01ef6f95faf83de58ce:c7ed7dc68c465d2334a81deca3c86664:a668acdf23dbfab4355fc0c90e8f5362$" + "smartupdate": "$TexturePacker:SmartUpdate:f9e0b5ff9ac2b57d131d6f27661d1aaa:c7ed7dc68c465d2334a81deca3c86664:a668acdf23dbfab4355fc0c90e8f5362$" } } diff --git a/public/images/pokemon/back/1017-hearthflame-mask-tera.png b/public/images/pokemon/back/1017-hearthflame-mask-tera.png index cce6529d2fd..3a50be4b00b 100644 Binary files a/public/images/pokemon/back/1017-hearthflame-mask-tera.png and b/public/images/pokemon/back/1017-hearthflame-mask-tera.png differ diff --git a/public/images/pokemon/back/1017-teal-mask-tera.json b/public/images/pokemon/back/1017-teal-mask-tera.json index 9e729c7dce4..36959768e16 100644 --- a/public/images/pokemon/back/1017-teal-mask-tera.json +++ b/public/images/pokemon/back/1017-teal-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-teal-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { - "x": 0, + "x": 1, "y": 0, - "w": 144, - "h": 144 + "w": 94, + "h": 96 }, "frame": { "x": 0, "y": 0, - "w": 144, - "h": 144 + "w": 94, + "h": 96 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:a84c3152ca2c84a0dccb8d85180893c0:c65956e68f88f806b48f40f2e2b3aefc:9f6debc3cc730be60b9cc5260e70873a$" + "smartupdate": "$TexturePacker:SmartUpdate:d3820bf4209f076190e819598a6ec8d7:c65956e68f88f806b48f40f2e2b3aefc:9f6debc3cc730be60b9cc5260e70873a$" } } diff --git a/public/images/pokemon/back/1017-teal-mask-tera.png b/public/images/pokemon/back/1017-teal-mask-tera.png index 81f2c8f6f37..496a0833655 100644 Binary files a/public/images/pokemon/back/1017-teal-mask-tera.png and b/public/images/pokemon/back/1017-teal-mask-tera.png differ diff --git a/public/images/pokemon/back/1017-wellspring-mask-tera.json b/public/images/pokemon/back/1017-wellspring-mask-tera.json index bf277369098..25fd3a76487 100644 --- a/public/images/pokemon/back/1017-wellspring-mask-tera.json +++ b/public/images/pokemon/back/1017-wellspring-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-wellspring-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { "x": 0, - "y": 8, - "w": 144, - "h": 130 + "y": 6, + "w": 96, + "h": 85 }, "frame": { "x": 0, "y": 0, - "w": 144, - "h": 130 + "w": 96, + "h": 85 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5d527aed445f4210bf0f76a29b0718ef:b9a6688aea29bea33c6b1b518f1da693:f4f8b58743ad897a5774e4ca3d3eff03$" + "smartupdate": "$TexturePacker:SmartUpdate:689ff4514c071d220bf0457da26850d5:b9a6688aea29bea33c6b1b518f1da693:f4f8b58743ad897a5774e4ca3d3eff03$" } } diff --git a/public/images/pokemon/back/1017-wellspring-mask-tera.png b/public/images/pokemon/back/1017-wellspring-mask-tera.png index 12760861152..19d71054927 100644 Binary files a/public/images/pokemon/back/1017-wellspring-mask-tera.png and b/public/images/pokemon/back/1017-wellspring-mask-tera.png differ diff --git a/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.json b/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.json index 2c3aa11447e..9f07320e1d6 100644 --- a/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.json +++ b/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-cornerstone-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 94, + "h": 94 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { - "x": 0, + "x": 1, "y": 2, - "w": 144, - "h": 140 + "w": 94, + "h": 92 }, "frame": { "x": 0, "y": 0, - "w": 144, - "h": 140 + "w": 94, + "h": 92 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:2c54519746e1aca46c247179ac5bef04:c40781d9ba7317d9195c41c46a2b386c:19983e2c44c76def68513841019a938a$" + "smartupdate": "$TexturePacker:SmartUpdate:33c1ac8f12234ff41459d7738281c92f:c40781d9ba7317d9195c41c46a2b386c:19983e2c44c76def68513841019a938a$" } } diff --git a/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.png b/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.png index 9b2e0a3ecca..0d2883d2371 100644 Binary files a/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.png and b/public/images/pokemon/back/shiny/1017-cornerstone-mask-tera.png differ diff --git a/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.json b/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.json index 088c009733e..21d1d17f3d6 100644 --- a/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.json +++ b/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-hearthflame-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { "x": 3, "y": 0, - "w": 136, - "h": 144 + "w": 89, + "h": 96 }, "frame": { "x": 0, "y": 0, - "w": 136, - "h": 144 + "w": 89, + "h": 96 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:8b38cc19c7295a495d9e91ae10718e62:75bb98cbf7a7074508abbada1dbfc94c:a668acdf23dbfab4355fc0c90e8f5362$" + "smartupdate": "$TexturePacker:SmartUpdate:84ef49fd74c1e18eea1fbf1ccbdf3667:75bb98cbf7a7074508abbada1dbfc94c:a668acdf23dbfab4355fc0c90e8f5362$" } } diff --git a/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.png b/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.png index 7c71afcf0eb..34c88846677 100644 Binary files a/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.png and b/public/images/pokemon/back/shiny/1017-hearthflame-mask-tera.png differ diff --git a/public/images/pokemon/back/shiny/1017-teal-mask-tera.json b/public/images/pokemon/back/shiny/1017-teal-mask-tera.json index 1ee4ee8fb40..ffc0657b642 100644 --- a/public/images/pokemon/back/shiny/1017-teal-mask-tera.json +++ b/public/images/pokemon/back/shiny/1017-teal-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-teal-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { - "x": 0, + "x": 1, "y": 0, - "w": 144, - "h": 144 + "w": 94, + "h": 96 }, "frame": { "x": 0, "y": 0, - "w": 144, - "h": 144 + "w": 94, + "h": 96 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5395c9c1661c7d728723b2c2a457aa66:b5288f0e4ffc7614f30c6b606d36647b:9f6debc3cc730be60b9cc5260e70873a$" + "smartupdate": "$TexturePacker:SmartUpdate:46c5307225fee0ace2c1e637ebc20c5b:b5288f0e4ffc7614f30c6b606d36647b:9f6debc3cc730be60b9cc5260e70873a$" } } diff --git a/public/images/pokemon/back/shiny/1017-teal-mask-tera.png b/public/images/pokemon/back/shiny/1017-teal-mask-tera.png index 81b8987e1f6..d118d314c9a 100644 Binary files a/public/images/pokemon/back/shiny/1017-teal-mask-tera.png and b/public/images/pokemon/back/shiny/1017-teal-mask-tera.png differ diff --git a/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.json b/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.json index 56a854a2ab3..b0bf6414fb6 100644 --- a/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.json +++ b/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.json @@ -4,30 +4,30 @@ "image": "1017-wellspring-mask-tera.png", "format": "RGBA8888", "size": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, - "scale": 0.5, + "scale": 0.333, "frames": [ { "filename": "0001.png", "rotated": false, "trimmed": false, "sourceSize": { - "w": 144, - "h": 144 + "w": 96, + "h": 96 }, "spriteSourceSize": { "x": 0, - "y": 8, - "w": 144, - "h": 130 + "y": 6, + "w": 96, + "h": 85 }, "frame": { "x": 0, "y": 0, - "w": 144, - "h": 130 + "w": 96, + "h": 85 } } ] @@ -36,6 +36,6 @@ "meta": { "app": "https://www.codeandweb.com/texturepacker", "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:a1bf5253f2d6ae6d8c33f2be9d591228:12d1e5242ab1c5174bbe31202f0e13e8:f4f8b58743ad897a5774e4ca3d3eff03$" + "smartupdate": "$TexturePacker:SmartUpdate:2cd8c5adc90abae707acf2863021a2ef:12d1e5242ab1c5174bbe31202f0e13e8:f4f8b58743ad897a5774e4ca3d3eff03$" } } diff --git a/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.png b/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.png index c935b62f482..00b0f7e7566 100644 Binary files a/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.png and b/public/images/pokemon/back/shiny/1017-wellspring-mask-tera.png differ diff --git a/src/data/ability.ts b/src/data/ability.ts index ffa7e169783..923a34ecdea 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -155,6 +155,42 @@ export class PostBattleInitFormChangeAbAttr extends PostBattleInitAbAttr { } } +export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { + private stats: BattleStat[]; + private levels: integer; + private selfTarget: boolean; + + constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean) { + super(); + + this.stats = typeof(stats) === 'number' + ? [ stats as BattleStat ] + : stats as BattleStat[]; + this.levels = levels; + this.selfTarget = !!selfTarget; + } + + applyPostBattleInit(pokemon: Pokemon, args: any[]): boolean { + const statChangePhases: StatChangePhase[] = []; + + if (this.selfTarget) + statChangePhases.push(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, this.stats, this.levels)); + else { + for (let opponent of pokemon.getOpponents()) + statChangePhases.push(new StatChangePhase(pokemon.scene, opponent.getBattlerIndex(), false, this.stats, this.levels)); + } + + for (let statChangePhase of statChangePhases) { + if (!this.selfTarget && !statChangePhase.getPokemon().summonData) + pokemon.scene.pushPhase(statChangePhase); // TODO: This causes the ability bar to be shown at the wrong time + else + pokemon.scene.unshiftPhase(statChangePhase); + } + + return true; + } +} + type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: PokemonMove) => boolean; export class PreDefendAbAttr extends AbAttr { @@ -977,9 +1013,16 @@ export class BlockCritAbAttr extends AbAttr { } } +export class BlockNonDirectDamageAbAttr extends AbAttr { + apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean { + cancelled.value = true; + return true; + } +} + export class BlockOneHitKOAbAttr extends AbAttr { apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean { - cancelled.value = false; + cancelled.value = true; return true; } } @@ -2200,7 +2243,8 @@ export function initAbilities() { new Ability(Abilities.QUICK_FEET, "Quick Feet (N)", "Boosts the Speed stat if the Pokémon has a status condition.", 4), new Ability(Abilities.NORMALIZE, "Normalize (N)", "All the Pokémon's moves become Normal type. The power of those moves is boosted a little.", 4), new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits when attacking.", 4), - new Ability(Abilities.MAGIC_GUARD, "Magic Guard (N)", "The Pokémon only takes damage from attacks.", 4), + new Ability(Abilities.MAGIC_GUARD, "Magic Guard", "The Pokémon only takes damage from attacks.", 4) + .attr(BlockNonDirectDamageAbAttr), new Ability(Abilities.NO_GUARD, "No Guard (N)", "The Pokémon employs no-guard tactics to ensure incoming and outgoing attacks always land.", 4), new Ability(Abilities.STALL, "Stall (N)", "The Pokémon moves after all other Pokémon do.", 4), new Ability(Abilities.TECHNICIAN, "Technician", "Powers up the Pokémon's weaker moves.", 4) @@ -2597,10 +2641,14 @@ export function initAbilities() { new Ability(Abilities.SUPERSWEET_SYRUP, "Supersweet Syrup (N)", "A sickly sweet scent spreads across the field the first time the Pokémon enters a battle, lowering the evasiveness of opposing Pokémon.", 9), new Ability(Abilities.HOSPITALITY, "Hospitality (N)", "When the Pokémon enters a battle, it showers its ally with hospitality, restoring a small amount of the ally's HP.", 9), new Ability(Abilities.TOXIC_CHAIN, "Toxic Chain (N)", "The power of the Pokémon's toxic chain may badly poison any target the Pokémon hits with a move.", 9), - new Ability(Abilities.EMBODY_ASPECT_TEAL, "Embody Aspect (N)", "The Pokémon's heart fills with memories, causing the Teal Mask to shine and the Pokémon's Speed stat to be boosted.", 9), - new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, "Embody Aspect (N)", "The Pokémon's heart fills with memories, causing the Wellspring Mask to shine and the Pokémon's Sp. Def stat to be boosted.", 9), - new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, "Embody Aspect (N)", "The Pokémon's heart fills with memories, causing the Hearthflame Mask to shine and the Pokémon's Attack stat to be boosted.", 9), - new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, "Embody Aspect (N)", "The Pokémon's heart fills with memories, causing the Cornerstone Mask to shine and the Pokémon's Defense stat to be boosted.", 9), + new Ability(Abilities.EMBODY_ASPECT_TEAL, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Teal Mask to shine and the Pokémon's Speed stat to be boosted.", 9) + .attr(PostBattleInitStatChangeAbAttr, BattleStat.SPD, 1, true), + new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Wellspring Mask to shine and the Pokémon's Sp. Def stat to be boosted.", 9) + .attr(PostBattleInitStatChangeAbAttr, BattleStat.SPDEF, 1, true), + new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Hearthflame Mask to shine and the Pokémon's Attack stat to be boosted.", 9) + .attr(PostBattleInitStatChangeAbAttr, BattleStat.ATK, 1, true), + new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, "Embody Aspect", "The Pokémon's heart fills with memories, causing the Cornerstone Mask to shine and the Pokémon's Defense stat to be boosted.", 9) + .attr(PostBattleInitStatChangeAbAttr, BattleStat.DEF, 1, true), new Ability(Abilities.TERA_SHIFT, "Tera Shift", "When the Pokémon enters a battle, it absorbs the energy around itself and transforms into its Terastal Form.", 9) .attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1), new Ability(Abilities.TERA_SHELL, "Tera Shell (N)", "The Pokémon's shell contains the powers of each type. All damage-dealing moves that hit the Pokémon when its HP is full will not be very effective.", 9) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index ac2b54cad86..b34205db3f1 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -9,6 +9,7 @@ import { StatusEffect } from "./status-effect"; import { BattlerIndex } from "../battle"; import { Moves } from "./enums/moves"; import { ArenaTagType } from "./enums/arena-tag-type"; +import { BlockNonDirectDamageAbAttr, applyAbAttrs } from "./ability"; export enum ArenaTagSide { BOTH, @@ -172,12 +173,17 @@ class SpikesTag extends ArenaTrapTag { activateTrap(pokemon: Pokemon): boolean { if (pokemon.isGrounded()) { - const damageHpRatio = 1 / (10 - 2 * this.layers); - const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!')); - pokemon.damageAndUpdate(damage, HitResult.OTHER); - return true; + if (!cancelled.value) { + const damageHpRatio = 1 / (10 - 2 * this.layers); + const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!')); + pokemon.damageAndUpdate(damage, HitResult.OTHER); + return true; + } } return false; @@ -293,6 +299,12 @@ class StealthRockTag extends ArenaTrapTag { } activateTrap(pokemon: Pokemon): boolean { + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + + if (cancelled.value) + return false; + const damageHpRatio = this.getDamageHpRatio(pokemon); if (damageHpRatio) { diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 6fff284d8b9..9a3ab9be3b8 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -8,7 +8,7 @@ import * as Utils from "../utils"; import { Moves } from "./enums/moves"; import { ChargeAttr, MoveFlags, allMoves } from "./move"; import { Type } from "./type"; -import { Abilities, FlinchEffectAbAttr, applyAbAttrs } from "./ability"; +import { Abilities, BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, applyAbAttrs } from "./ability"; import { BattlerTagType } from "./enums/battler-tag-type"; import { TerrainType } from "./terrain"; import { WeatherType } from "./weather"; @@ -280,10 +280,15 @@ export class SeedTag extends BattlerTag { if (ret) { const source = pokemon.getOpponents().find(o => o.getBattlerIndex() === this.sourceIndex); if (source) { - pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); - const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); - pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true)); + if (!cancelled.value) { + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); + + const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true)); + } } } @@ -319,7 +324,11 @@ export class NightmareTag extends BattlerTag { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is locked\nin a Nightmare!')); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE)); // TODO: Update animation type - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4)); + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + + if (!cancelled.value) + pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 4)); } return ret; @@ -506,7 +515,11 @@ export abstract class DamagingTrapTag extends TrappedTag { pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby ${this.getMoveName()}!`)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, this.commonAnim)); - pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8)) + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + + if (!cancelled.value) + pokemon.damageAndUpdate(Math.ceil(pokemon.getMaxHp() / 8)) } return ret; @@ -924,10 +937,15 @@ export class SaltCuredTag extends BattlerTag { if (ret) { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE)); - const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER); - pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1)); + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); - pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by ${this.getMoveName()}!`)); + if (!cancelled.value) { + const pokemonSteelOrWater = pokemon.isOfType(Type.STEEL) || pokemon.isOfType(Type.WATER); + pokemon.damageAndUpdate(Math.max(Math.floor(pokemonSteelOrWater ? pokemon.getMaxHp() / 4 : pokemon.getMaxHp() / 8), 1)); + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by ${this.getMoveName()}!`)); + } } return ret; diff --git a/src/data/move.ts b/src/data/move.ts index 637c04cba4b..f10513548ff 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -12,7 +12,7 @@ import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagType } from "./enums/arena-tag-type"; -import { Abilities, ProtectAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs } from "./ability"; +import { Abilities, ProtectAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr } from "./ability"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import { BattlerIndex } from "../battle"; import { Stat } from "./pokemon-stat"; @@ -441,6 +441,14 @@ export class StatusMoveTypeImmunityAttr extends MoveAttr { } } +export class IgnoreOpponentStatChangesAttr extends MoveAttr { + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + (args[0] as Utils.IntegerHolder).value = 0; + + return true; + } +} + export class HighCritAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { (args[0] as Utils.IntegerHolder).value++; @@ -626,6 +634,10 @@ export class RecoilAttr extends MoveEffectAttr { if (!recoilDamage) return false; + applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); + if (cancelled.value) + return false; + user.damageAndUpdate(recoilDamage, HitResult.OTHER, false, true); user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!')); @@ -1817,6 +1829,11 @@ export class MissEffectAttr extends MoveAttr { } const halveHpMissEffectFunc = (user: Pokemon, move: Move) => { + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled); + if (cancelled.value) + return false; + const damage = user.damage(Math.floor(user.getMaxHp() / 2)); if (damage) user.scene.damageNumberHandler.add(user, damage, HitResult.OTHER); @@ -3915,7 +3932,7 @@ export function initMoves() { .attr(StatChangeAttr, BattleStat.SPDEF, -2) .ballBombMove(), new AttackMove(Moves.FOUL_PLAY, "Foul Play", Type.DARK, MoveCategory.PHYSICAL, 95, 100, 15, "The user turns the target's power against it. The higher the target's Attack stat, the greater the damage it deals.", -1, 0, 5) - .attr(TargetAtkUserAtkAttr), + .attr(TargetAtkUserAtkAttr), new StatusMove(Moves.SIMPLE_BEAM, "Simple Beam (N)", Type.NORMAL, 100, 15, "The user's mysterious psychic wave changes the target's Ability to Simple.", -1, 0, 5), new StatusMove(Moves.ENTRAINMENT, "Entrainment (N)", Type.NORMAL, 100, 15, "The user dances with an odd rhythm that compels the target to mimic it, making the target's Ability the same as the user's.", -1, 0, 5), new StatusMove(Moves.AFTER_YOU, "After You (N)", Type.NORMAL, -1, 15, "The user helps the target and makes it use its move right after the user.", -1, 0, 5) @@ -3925,7 +3942,8 @@ export function initMoves() { new AttackMove(Moves.ECHOED_VOICE, "Echoed Voice", Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, "The user attacks the target with an echoing voice. If this move is used every turn, its power is increased.", -1, 0, 5) .attr(ConsecutiveUseMultiBasePowerAttr, 5, false) .soundBased(), - new AttackMove(Moves.CHIP_AWAY, "Chip Away (P)", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, "Looking for an opening, the user strikes consistently. The target's stat changes don't affect this attack's damage.", -1, 0, 5), + new AttackMove(Moves.CHIP_AWAY, "Chip Away", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, "Looking for an opening, the user strikes consistently. The target's stat changes don't affect this attack's damage.", -1, 0, 5) + .attr(IgnoreOpponentStatChangesAttr), new AttackMove(Moves.CLEAR_SMOG, "Clear Smog (P)", Type.POISON, MoveCategory.SPECIAL, 50, -1, 15, "The user attacks the target by throwing a clump of special mud. All stat changes are returned to normal.", -1, 0, 5), new AttackMove(Moves.STORED_POWER, "Stored Power (P)", Type.PSYCHIC, MoveCategory.SPECIAL, 20, 100, 10, "The user attacks the target with stored power. The more the user's stats are raised, the greater the move's power.", -1, 0, 5), new StatusMove(Moves.QUICK_GUARD, "Quick Guard (N)", Type.FIGHTING, -1, 15, "The user protects itself and its allies from priority moves.", -1, 3, 5) @@ -3998,7 +4016,8 @@ export function initMoves() { .attr(FlinchAttr), new AttackMove(Moves.HORN_LEECH, "Horn Leech", Type.GRASS, MoveCategory.PHYSICAL, 75, 100, 10, "The user drains the target's energy with its horns. The user's HP is restored by half the damage taken by the target.", -1, 0, 5) .attr(HitHealAttr), - new AttackMove(Moves.SACRED_SWORD, "Sacred Sword (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 15, "The user attacks by slicing with a long horn. The target's stat changes don't affect this attack's damage.", -1, 0, 5) + new AttackMove(Moves.SACRED_SWORD, "Sacred Sword", Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 15, "The user attacks by slicing with a long horn. The target's stat changes don't affect this attack's damage.", -1, 0, 5) + .attr(IgnoreOpponentStatChangesAttr) .slicingMove(), new AttackMove(Moves.RAZOR_SHELL, "Razor Shell", Type.WATER, MoveCategory.PHYSICAL, 75, 95, 10, "The user cuts its target with sharp shells. This may also lower the target's Defense stat.", 50, 0, 5) .attr(StatChangeAttr, BattleStat.DEF, -1) @@ -4262,7 +4281,8 @@ export function initMoves() { .attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER), new AttackMove(Moves.SPIRIT_SHACKLE, "Spirit Shackle (P)", Type.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, "The user attacks while simultaneously stitching the target's shadow to the ground to prevent the target from escaping.", -1, 0, 7) .makesContact(false), - new AttackMove(Moves.DARKEST_LARIAT, "Darkest Lariat (P)", Type.DARK, MoveCategory.PHYSICAL, 85, 100, 10, "The user swings both arms and hits the target. The target's stat changes don't affect this attack's damage.", -1, 0, 7), + new AttackMove(Moves.DARKEST_LARIAT, "Darkest Lariat", Type.DARK, MoveCategory.PHYSICAL, 85, 100, 10, "The user swings both arms and hits the target. The target's stat changes don't affect this attack's damage.", -1, 0, 7) + .attr(IgnoreOpponentStatChangesAttr), new AttackMove(Moves.SPARKLING_ARIA, "Sparkling Aria", Type.WATER, MoveCategory.SPECIAL, 90, 100, 10, "The user bursts into song, emitting many bubbles. Any Pokémon suffering from a burn will be healed by the touch of these bubbles.", -1, 0, 7) .attr(HealStatusEffectAttr, false, StatusEffect.BURN) .soundBased() diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index f5664efc15b..1a76a3600d7 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -637,7 +637,15 @@ export const pokemonFormChanges: PokemonFormChanges = { [Species.OGERPON]: [ new SpeciesFormChange(Species.OGERPON, 'teal-mask', 'wellspring-mask', new SpeciesFormChangeItemTrigger(FormChangeItem.WELLSPRING_MASK)), new SpeciesFormChange(Species.OGERPON, 'teal-mask', 'hearthflame-mask', new SpeciesFormChangeItemTrigger(FormChangeItem.HEARTHFLAME_MASK)), - new SpeciesFormChange(Species.OGERPON, 'teal-mask', 'cornerstone-mask', new SpeciesFormChangeItemTrigger(FormChangeItem.CORNERSTONE_MASK)) + new SpeciesFormChange(Species.OGERPON, 'teal-mask', 'cornerstone-mask', new SpeciesFormChangeItemTrigger(FormChangeItem.CORNERSTONE_MASK)), + new SpeciesFormChange(Species.OGERPON, 'teal-mask', 'teal-mask-tera', new SpeciesFormChangeManualTrigger(), true), //When holding a Grass Tera Shard + new SpeciesFormChange(Species.OGERPON, 'teal-mask-tera', 'teal-mask', new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Grass Tera Shard + new SpeciesFormChange(Species.OGERPON, 'wellspring-mask', 'wellspring-mask-tera', new SpeciesFormChangeManualTrigger(), true), //When holding a Water Tera Shard + new SpeciesFormChange(Species.OGERPON, 'wellspring-mask-tera', 'wellspring-mask', new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Water Tera Shard + new SpeciesFormChange(Species.OGERPON, 'hearthflame-mask', 'hearthflame-mask-tera', new SpeciesFormChangeManualTrigger(), true), //When holding a Fire Tera Shard + new SpeciesFormChange(Species.OGERPON, 'hearthflame-mask-tera', 'hearthflame-mask', new SpeciesFormChangeManualTrigger(), true), //When no longer holding a Fire Tera Shard + new SpeciesFormChange(Species.OGERPON, 'cornerstone-mask', 'cornerstone-mask-tera', new SpeciesFormChangeManualTrigger(), true), //When holding a Rock Tera Shard + new SpeciesFormChange(Species.OGERPON, 'cornerstone-mask-tera', 'cornerstone-mask', new SpeciesFormChangeManualTrigger(), true) //When no longer holding a Rock Tera Shard ], [Species.TERAPAGOS]: [ new SpeciesFormChange(Species.TERAPAGOS, '', 'terastal', new SpeciesFormChangeManualTrigger(), true) diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 467bfca98fd..4ab51f1aba1 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -555,7 +555,8 @@ export const trainerConfigs: TrainerConfigs = { [TrainerPoolTier.ULTRA_RARE]: [ Species.KUBFU ] }), [TrainerType.BREEDER]: new TrainerConfig(++t).setMoneyMultiplier(1.325).setEncounterBgm(TrainerType.POKEFAN).setHasGenders().setHasDouble('Breeders') - .setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.FOUR_WEAKER, trainerPartyTemplates.FIVE_WEAKER, trainerPartyTemplates.SIX_WEAKER)), + .setPartyTemplateFunc(scene => getWavePartyTemplate(scene, trainerPartyTemplates.FOUR_WEAKER, trainerPartyTemplates.FIVE_WEAKER, trainerPartyTemplates.SIX_WEAKER)) + .setSpeciesFilter(s => s.baseTotal < 450), [TrainerType.CLERK]: new TrainerConfig(++t).setHasGenders().setHasDouble('Colleagues').setEncounterBgm(TrainerType.CLERK) .setPartyTemplates(trainerPartyTemplates.TWO_WEAK, trainerPartyTemplates.THREE_WEAK, trainerPartyTemplates.ONE_AVG, trainerPartyTemplates.TWO_AVG, trainerPartyTemplates.TWO_WEAK_ONE_AVG) .setSpeciesPools({ diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index aef6cae01c5..839b54844a7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2,7 +2,7 @@ import Phaser from 'phaser'; import BattleScene, { ABILITY_OVERRIDE, AnySound, MOVE_OVERRIDE, OPP_ABILITY_OVERRIDE, OPP_MOVE_OVERRIDE } from '../battle-scene'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from '../ui/battle-info'; import { Moves } from "../data/enums/moves"; -import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr } from "../data/move"; +import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr } from "../data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from '../data/pokemon-species'; import * as Utils from '../utils'; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from '../data/type'; @@ -481,13 +481,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.stats[stat]; } - getBattleStat(stat: Stat, opponent?: Pokemon): integer { + getBattleStat(stat: Stat, opponent?: Pokemon, move?: Move): integer { if (stat === Stat.HP) return this.getStat(Stat.HP); const battleStat = (stat - 1) as BattleStat; const statLevel = new Utils.IntegerHolder(this.summonData.battleStats[battleStat]); - if (opponent) + if (opponent) { applyAbAttrs(IgnoreOpponentStatChangesAbAttr, opponent, null, statLevel); + if (move) + applyMoveAttrs(IgnoreOpponentStatChangesAttr, this, opponent, move, statLevel); + } if (this.isPlayer()) this.scene.applyModifiers(TempBattleStatBoosterModifier, this.isPlayer(), battleStat as integer as TempBattleStat, statLevel); const statValue = new Utils.NumberHolder(this.getStat(stat)); @@ -1098,7 +1101,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this)); - const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source)); + const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move)); const criticalMultiplier = isCritical ? 2 : 1; const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier) === 0; const sourceTypes = source.getTypes(); diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index ccc38f2aba0..6c9c4b093e4 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -525,7 +525,8 @@ export class FormChangeItemModifierType extends PokemonModifierType implements G constructor(formChangeItem: FormChangeItem) { super(Utils.toReadableString(FormChangeItem[formChangeItem]), `Causes certain Pokémon to change form`, (_type, args) => new Modifiers.PokemonFormChangeItemModifier(this, (args[0] as PlayerPokemon).id, formChangeItem, true), (pokemon: PlayerPokemon) => { - if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && !!pokemonFormChanges[pokemon.species.speciesId].find(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger))) + if (pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) && !!pokemonFormChanges[pokemon.species.speciesId].find(fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger) + && (fc.trigger as SpeciesFormChangeItemTrigger).item === this.formChangeItem)) return null; return PartyUiHandler.NoEffectMessage; diff --git a/src/phases.ts b/src/phases.ts index 8ab5990b2f1..82ec03c2929 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2,7 +2,7 @@ import BattleScene, { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE, bypassLog import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; import * as Utils from './utils'; import { Moves } from "./data/enums/moves"; -import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr } from "./data/move"; +import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr } from "./data/move"; import { Mode } from './ui/ui'; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; @@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get import { TempBattleStat } from "./data/temp-battle-stat"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagType } from "./data/enums/arena-tag-type"; -import { Abilities, CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr } from "./data/ability"; +import { Abilities, CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr } from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; @@ -2313,6 +2313,7 @@ export class MoveEffectPhase extends PokemonPhase { const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]); applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel); applyAbAttrs(IgnoreOpponentStatChangesAbAttr, this.getUserPokemon(), null, targetEvasionLevel); + applyMoveAttrs(IgnoreOpponentStatChangesAttr, this.getUserPokemon(), target, this.move.getMove(), targetEvasionLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, this.player, TempBattleStat.ACC, userAccuracyLevel); const rand = this.getUserPokemon().randSeedInt(100, 1); @@ -2550,6 +2551,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { const cancelled = new Utils.BooleanHolder(false); applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (cancelled.value) return; @@ -2619,24 +2621,32 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { const pokemon = this.getPokemon(); if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) { pokemon.status.incrementTurn(); - this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))); - let damage: integer = 0; - switch (pokemon.status.effect) { - case StatusEffect.POISON: - damage = Math.max(pokemon.getMaxHp() >> 3, 1); - break; - case StatusEffect.TOXIC: - damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1); - break; - case StatusEffect.BURN: - damage = Math.max(pokemon.getMaxHp() >> 4, 1); - break; - } - if (damage) { - this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage)); - pokemon.updateInfo(); - } - new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end()); + const cancelled = new Utils.BooleanHolder(false); + applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + + console.log(cancelled.value) + + if (!cancelled.value) { + this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))); + let damage: integer = 0; + switch (pokemon.status.effect) { + case StatusEffect.POISON: + damage = Math.max(pokemon.getMaxHp() >> 3, 1); + break; + case StatusEffect.TOXIC: + damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1); + break; + case StatusEffect.BURN: + damage = Math.max(pokemon.getMaxHp() >> 4, 1); + break; + } + if (damage) { + this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage)); + pokemon.updateInfo(); + } + new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => this.end()); + } else + this.end(); } else this.end(); } diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 570d35fad3e..ba71e91ef5c 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -72,7 +72,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private genMode: boolean; private statsMode: boolean; private dexAttrCursor: bigint = 0n; - private natureCursor: integer = 0; + private natureCursor: integer = -1; private genCursor: integer = 0; private genScrollCursor: integer = 0; private starterMoveset: StarterMoveset; @@ -1022,19 +1022,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, abilityIndex: integer, natureIndex: integer, forSeen: boolean = false): void { const oldProps = species ? this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null; + const oldNatureIndex = this.natureCursor > -1 ? this.natureCursor : this.scene.gameData.getSpeciesDefaultNature(species); this.dexAttrCursor = 0n; + this.natureCursor = -1; if (species) { this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps.female)) ? DexAttr.MALE : DexAttr.FEMALE; this.dexAttrCursor |= (abilityIndex !== undefined ? !abilityIndex : !(abilityIndex = oldProps.abilityIndex)) ? DexAttr.ABILITY_1 : species.ability2 && abilityIndex === 1 ? DexAttr.ABILITY_2 : DexAttr.ABILITY_HIDDEN; this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps.formIndex)); - if (natureIndex === undefined) - natureIndex = this.scene.gameData.getSpeciesDefaultNature(species); + this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex); } - this.natureCursor = natureIndex; - this.pokemonSprite.setVisible(false); if (this.assetLoadCancelled) {