From 1f6a6f4621c14e759109234a8d1e32b338ba25c9 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Fri, 1 Mar 2024 09:35:36 -0500 Subject: [PATCH] Damage-related fixes and changes Apply damage numbers to all damage and heal; fix some damage-related bugs --- src/data/ability.ts | 4 +-- src/data/arena-tag.ts | 8 ++--- src/data/battler-tags.ts | 21 +++++------- src/data/move.ts | 55 ++++++++++++++++++++---------- src/field/damage-number-handler.ts | 17 ++++++--- src/field/pokemon.ts | 9 +++-- src/phases.ts | 54 +++++++++++++++++++---------- 7 files changed, 103 insertions(+), 65 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 063b8c070da..3a529ce0f59 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -905,8 +905,8 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr { if (pokemon.getHpRatio() < 1) { const scene = pokemon.scene; scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby its ${pokemon.getAbility()}!`)); - scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.OTHER)); - pokemon.damage(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor))); + const damage = pokemon.damage(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor))); + scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), damage, HitResult.OTHER)); return true; } diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index bf6c2c4ad82..e2a51d1a8d0 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -170,10 +170,10 @@ class SpikesTag extends ArenaTrapTag { activateTrap(pokemon: Pokemon): boolean { if ((!pokemon.isOfType(Type.FLYING) || pokemon.getTag(BattlerTagType.IGNORE_FLYING) || pokemon.scene.arena.getTag(ArenaTagType.GRAVITY))) { 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.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.OTHER)); - pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio)); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage), HitResult.OTHER)); return true; } @@ -266,9 +266,9 @@ class StealthRockTag extends ArenaTrapTag { } if (damageHpRatio) { + const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.OTHER)); - pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio)); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage), HitResult.OTHER)); } return false; diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index bb52f234c43..d85942f462f 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -179,8 +179,7 @@ export class ConfusedTag extends BattlerTag { const def = pokemon.getBattleStat(Stat.DEF); const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); pokemon.scene.queueMessage('It hurt itself in its\nconfusion!'); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); - pokemon.damage(damage); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage))); pokemon.battleData.hitCount++; (pokemon.scene.getCurrentPhase() as MovePhase).cancel(); } @@ -264,9 +263,9 @@ export class SeedTag extends BattlerTag { if (source) { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); - const damage = Math.max(Math.floor(pokemon.getMaxHp() / 8), 1); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); - pokemon.damage(damage); + const damage = pokemon.damage(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1)); + + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), damage)); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true)); } } @@ -304,8 +303,7 @@ export class NightmareTag extends BattlerTag { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE)); // TODO: Update animation type const damage = Math.ceil(pokemon.getMaxHp() / 4); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); - pokemon.damage(damage); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage))); } return ret; @@ -473,8 +471,7 @@ export abstract class DamagingTrapTag extends TrappedTag { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, this.commonAnim)); const damage = Math.ceil(pokemon.getMaxHp() / 8); - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); - pokemon.damage(damage); + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage))); } return ret; @@ -617,10 +614,8 @@ export class PerishSongTag extends BattlerTag { if (ret) pokemon.scene.queueMessage(getPokemonMessage(pokemon, `\'s perish count fell to ${this.turnCount}.`)); - else { - pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.ONE_HIT_KO)); - pokemon.damage(pokemon.hp, true, true); - } + else + pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(pokemon.hp, true, true), HitResult.ONE_HIT_KO)); return ret; } diff --git a/src/data/move.ts b/src/data/move.ts index 6a5cff9eafb..908392f8483 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -532,9 +532,8 @@ export class RecoilAttr extends MoveEffectAttr { if (!recoilDamage) return false; - user.scene.unshiftPhase(new DamagePhase(user.scene, user.getBattlerIndex(), HitResult.OTHER)); + user.scene.unshiftPhase(new DamagePhase(user.scene, user.getBattlerIndex(), user.damage(recoilDamage, true), HitResult.OTHER)); user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!')); - user.damage(recoilDamage, true); return true; } @@ -553,8 +552,7 @@ export class SacrificialAttr extends MoveEffectAttr { if (!super.apply(user, target, move, args)) return false; - user.scene.unshiftPhase(new DamagePhase(user.scene, user.getBattlerIndex(), HitResult.OTHER)); - user.damage(user.getMaxHp(), true, true); + user.scene.unshiftPhase(new DamagePhase(user.scene, user.getBattlerIndex(), user.damage(user.hp, true, true), HitResult.OTHER)); return true; } @@ -1085,7 +1083,9 @@ export class HalfHpStatMaxAttr extends StatChangeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - user.damage(Math.floor(user.getMaxHp() / 2)); + const damage = user.damage(Math.floor(user.getMaxHp() / 2), true); + if (damage) + user.scene.damageNumberHandler.add(user, damage); user.updateInfo().then(() => { const ret = super.apply(user, target, move, args); user.scene.queueMessage(getPokemonMessage(user, ` cut its own hp\nand maximized its ${getBattleStatName(this.stats[0])}!`)); @@ -1106,7 +1106,9 @@ export class CutHpStatBoostAttr extends StatChangeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - user.damage(Math.floor(user.getMaxHp() / 3)); + const damage = user.damage(Math.floor(user.getMaxHp() / 3), true); + if (damage) + user.scene.damageNumberHandler.add(user, damage); user.updateInfo().then(() => { const ret = super.apply(user, target, move, args); resolve(ret); @@ -1128,16 +1130,26 @@ export class HpSplitAttr extends MoveEffectAttr { const infoUpdates = []; const hpValue = Math.floor((target.hp + user.hp) / 2); - if (user.hp < hpValue) - user.heal(hpValue - user.hp); - else if (user.hp > hpValue) - user.damage(user.hp - hpValue, true); + if (user.hp < hpValue) { + const healing = user.heal(hpValue - user.hp); + if (healing) + user.scene.damageNumberHandler.add(user, healing, HitResult.HEAL); + } else if (user.hp > hpValue) { + const damage = user.damage(user.hp - hpValue, true); + if (damage) + user.scene.damageNumberHandler.add(user, damage); + } infoUpdates.push(user.updateInfo()); - if (target.hp < hpValue) - target.heal(hpValue - target.hp); - else if (target.hp > hpValue) - target.damage(target.hp - hpValue, true); + if (target.hp < hpValue) { + const healing = target.heal(hpValue - target.hp); + if (healing) + user.scene.damageNumberHandler.add(user, healing, HitResult.HEAL); + } else if (target.hp > hpValue) { + const damage = target.damage(target.hp - hpValue, true); + if (damage) + target.scene.damageNumberHandler.add(target, damage); + } infoUpdates.push(target.updateInfo()); return Promise.all(infoUpdates).then(() => resolve(true)); @@ -1397,6 +1409,13 @@ export class MissEffectAttr extends MoveAttr { } } +const halveHpMissEffectFunc = (user: Pokemon, move: Move) => { + const damage = user.damage(Math.floor(user.getMaxHp() / 2)); + if (damage) + user.scene.damageNumberHandler.add(user, damage, HitResult.OTHER); + return true; +}; + export class TypelessAttr extends MoveAttr { } export class DisableMoveAttr extends MoveEffectAttr { @@ -2280,7 +2299,7 @@ export function initMoves() { .attr(MultiHitAttr, MultiHitType._2), new AttackMove(Moves.MEGA_KICK, "Mega Kick", Type.NORMAL, MoveCategory.PHYSICAL, 120, 75, 5, -1, "The target is attacked by a kick launched with muscle-packed power.", -1, 0, 1), new AttackMove(Moves.JUMP_KICK, "Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, "The user jumps up high, then strikes with a kick. If the kick misses, the user hurts itself.", -1, 0, 1) - .attr(MissEffectAttr, (user: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; }) + .attr(MissEffectAttr, halveHpMissEffectFunc) .condition(failOnGravityCondition), new AttackMove(Moves.ROLLING_KICK, "Rolling Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, -1, "The user lashes out with a quick, spinning kick. This may also make the target flinch.", 30, 0, 1) .attr(FlinchAttr), @@ -2547,7 +2566,7 @@ export function initMoves() { new SelfStatusMove(Moves.SOFT_BOILED, "Soft-Boiled", Type.NORMAL, -1, 5, -1, "The user restores its own HP by up to half of its max HP.", -1, 0, 1) .attr(HealAttr, 0.5), new AttackMove(Moves.HIGH_JUMP_KICK, "High Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, -1, "The target is attacked with a knee kick from a jump. If it misses, the user is hurt instead.", -1, 0, 1) - .attr(MissEffectAttr, (user: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; }) + .attr(MissEffectAttr, halveHpMissEffectFunc) .condition(failOnGravityCondition), new StatusMove(Moves.GLARE, "Glare", Type.NORMAL, 100, 30, -1, "The user intimidates the target with the pattern on its belly to cause paralysis.", -1, 0, 1) .attr(StatusEffectAttr, StatusEffect.PARALYSIS), @@ -4120,7 +4139,7 @@ export function initMoves() { new AttackMove(Moves.TERA_BLAST, "Tera Blast (P)", Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, "If the user has Terastallized, it unleashes energy of its Tera Type. This move inflicts damage using the Attack or Sp. Atk stat-whichever is higher for the user.", -1, 0, 9), new SelfStatusMove(Moves.SILK_TRAP, "Silk Trap (N)", Type.BUG, -1, 10, -1, "The user spins a silken trap, protecting itself from damage while lowering the Speed stat of any attacker that makes direct contact.", -1, 4, 9), new AttackMove(Moves.AXE_KICK, "Axe Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, -1, "The user attacks by kicking up into the air and slamming its heel down upon the target. This may also confuse the target. If it misses, the user takes damage instead.", 30, 0, 9) - .attr(MissEffectAttr, (user: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; }) + .attr(MissEffectAttr, halveHpMissEffectFunc) .attr(ConfuseAttr), new AttackMove(Moves.LAST_RESPECTS, "Last Respects (P)", Type.GHOST, MoveCategory.PHYSICAL, 50, 100, 10, -1, "The user attacks to avenge its allies. The more defeated allies there are in the user's party, the greater the move's power.", -1, 0, 9), new AttackMove(Moves.LUMINA_CRASH, "Lumina Crash", Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, "The user attacks by unleashing a peculiar light that even affects the mind. This also harshly lowers the target's Sp. Def stat.", 100, 0, 9) @@ -4250,7 +4269,7 @@ export function initMoves() { new AttackMove(Moves.ALLURING_VOICE, "Alluring Voice (P)", Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, "The user attacks the target using its angelic voice. This also confuses the target if its stats have been boosted during the turn.", -1, 0, 9), new AttackMove(Moves.TEMPER_FLARE, "Temper Flare (P)", Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, "Spurred by desperation, the user attacks the target. This move's power is doubled if the user's previous move failed.", -1, 0, 9), new AttackMove(Moves.SUPERCELL_SLAM, "Supercell Slam", Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, "The user electrifies its body and drops onto the target to inflict damage. If this move misses, the user takes damage instead.", -1, 0, 9) - .attr(MissEffectAttr, (user: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; }), + .attr(MissEffectAttr, halveHpMissEffectFunc), new AttackMove(Moves.PSYCHIC_NOISE, "Psychic Noise (P)", Type.PSYCHIC, MoveCategory.SPECIAL, 75, 100, 10, -1, "The user attacks the target with unpleasant sound waves. For two turns, the target is prevented from recovering HP through moves, Abilities, or held items.", -1, 0, 9) .soundBased(), new AttackMove(Moves.UPPER_HAND, "Upper Hand (P)", Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, -1, "The user reacts to the target's movement and strikes with the heel of its palm, making the target flinch. This move fails if the target is not readying a priority move.", -1, 0, 9), diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index 69e30931721..f4a3d570e19 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -1,5 +1,5 @@ import { TextStyle, addTextObject } from "../ui/text"; -import Pokemon, { AttackMoveResult, HitResult } from "./pokemon"; +import Pokemon, { DamageResult, HitResult } from "./pokemon"; import * as Utils from "../utils"; import { BattlerIndex } from "../battle"; @@ -10,17 +10,21 @@ export default class DamageNumberHandler { this.damageNumbers = new Map(); } - add(target: Pokemon, result: AttackMoveResult): void { + add(target: Pokemon, amount: integer, result: DamageResult | HitResult.HEAL = HitResult.EFFECTIVE, critical: boolean = false): void { const scene = target.scene; + + if (!scene.damageNumbersMode) + return; + const battlerIndex = target.getBattlerIndex(); const baseScale = target.getSpriteScale() / 6; - const damageNumber = addTextObject(scene, target.x, -(scene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, Utils.formatStat(result.damage, true), TextStyle.SUMMARY); + const damageNumber = addTextObject(scene, target.x, -(scene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, Utils.formatStat(amount, true), TextStyle.SUMMARY); damageNumber.setOrigin(0.5, 1); damageNumber.setScale(baseScale); let [ textColor, shadowColor ] = [ null, null ]; - switch (result.result) { + switch (result) { case HitResult.SUPER_EFFECTIVE: [ textColor, shadowColor ] = [ '#f8d030', '#b8a038' ]; break; @@ -30,6 +34,9 @@ export default class DamageNumberHandler { case HitResult.ONE_HIT_KO: [ textColor, shadowColor ] = [ '#a040a0', '#483850' ]; break; + case HitResult.HEAL: + [ textColor, shadowColor ] = [ '#78c850', '#588040' ]; + break; default: [ textColor, shadowColor ] = [ '#ffffff', '#636363' ]; break; @@ -38,7 +45,7 @@ export default class DamageNumberHandler { if (textColor) damageNumber.setColor(textColor); if (shadowColor) { - if (result.critical) { + if (critical) { damageNumber.setShadowOffset(0, 0); damageNumber.setStroke(shadowColor, 12); } else diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 9b97e5fe74d..0602e288182 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1123,11 +1123,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); if (damage) { - this.scene.unshiftPhase(new DamagePhase(this.scene, this.getBattlerIndex(), result as DamageResult)); + damage.value = this.damage(damage.value); + this.scene.unshiftPhase(new DamagePhase(this.scene, this.getBattlerIndex(), damage.value, result as DamageResult, isCritical)); if (isCritical) this.scene.queueMessage('A critical hit!'); this.scene.setPhaseQueueSplice(); - damage.value = this.damage(damage.value); if (source.isPlayer()) { this.scene.validateAchvs(DamageAchv, damage); if (damage.value > this.scene.gameData.gameStats.highestDamage) @@ -1137,8 +1137,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.battleData.hitCount++; const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id }; this.turnData.attacksReceived.unshift(attackResult); - if (damage.value && this.scene.damageNumbersMode) - this.scene.damageNumberHandler.add(this, attackResult); if (source.isPlayer() && !this.isPlayer()) this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, damage) } @@ -2344,7 +2342,7 @@ export class EnemyPokemon extends Pokemon { const hpThreshold = segmentSize * s; const roundedHpThreshold = Math.round(hpThreshold); if (this.hp >= roundedHpThreshold) { - if (this.hp - damage < roundedHpThreshold) { + if (this.hp - damage <= roundedHpThreshold) { const hpRemainder = this.hp - roundedHpThreshold; let segmentsBypassed = 0; while (this.canBypassBossSegments(segmentsBypassed + 1) && (damage - hpRemainder) >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))) { @@ -2536,6 +2534,7 @@ export enum HitResult { ONE_HIT_KO, NO_EFFECT, STATUS, + HEAL, FAIL, MISS, OTHER diff --git a/src/phases.ts b/src/phases.ts index 36a8345e151..cf2c2727e32 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -2394,9 +2394,10 @@ export class WeatherEffectPhase extends CommonAnimPhase { if (cancelled.value) return; + const damage = Math.ceil(pokemon.getMaxHp() / 16); + this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); - this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.getBattlerIndex())); - pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16)); + this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.getBattlerIndex(), pokemon.damage(damage))); }; this.executeForAll((pokemon: Pokemon) => { @@ -2458,19 +2459,22 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { const pokemon = this.getPokemon(); if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) { pokemon.status.incrementTurn(); - new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => { - this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))); - switch (pokemon.status.effect) { - case StatusEffect.POISON: - case StatusEffect.BURN: - pokemon.damage(Math.max(pokemon.getMaxHp() >> 3, 1)); - break; - case StatusEffect.TOXIC: - pokemon.damage(Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1)); - break; - } - pokemon.updateInfo().then(() => this.end()); - }); + this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))); + let damage: integer = 0; + switch (pokemon.status.effect) { + case StatusEffect.POISON: + case StatusEffect.BURN: + damage = Math.max(pokemon.getMaxHp() >> 3, 1); + break; + case StatusEffect.TOXIC: + damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 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(); } @@ -2512,12 +2516,16 @@ export class MessagePhase extends Phase { } export class DamagePhase extends PokemonPhase { + private amount: integer; private damageResult: DamageResult; + private critical: boolean; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, damageResult?: DamageResult) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, amount: integer, damageResult?: DamageResult, critical: boolean = false) { super(scene, battlerIndex); + this.amount = amount; this.damageResult = damageResult || HitResult.EFFECTIVE; + this.critical = critical; } start() { @@ -2549,6 +2557,9 @@ export class DamagePhase extends PokemonPhase { break; } + if (this.amount) + this.scene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical); + if (this.damageResult !== HitResult.OTHER) { const flashTimer = this.scene.time.addEvent({ delay: 100, @@ -2685,10 +2696,15 @@ export class FaintPhase extends PokemonPhase { case BattleSpec.FINAL_BOSS: if (!this.player) { const enemy = this.getPokemon(); - if (enemy.formIndex) { + if (enemy.formIndex) this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].secondStageWin, enemy.species.name, null, () => this.doFaint()); - return true; + else { + // Final boss' HP threshold has been bypassed; cancel faint and force check for 2nd phase + enemy.hp++; + this.scene.unshiftPhase(new DamagePhase(this.scene, enemy.getBattlerIndex(), 0, HitResult.OTHER)); + this.end(); } + return true; } } @@ -3310,6 +3326,8 @@ export class PokemonHealPhase extends CommonAnimPhase { this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier); const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); healAmount.value = pokemon.heal(healAmount.value); + if (healAmount.value) + this.scene.damageNumberHandler.add(pokemon, healAmount.value, HitResult.HEAL); if (pokemon.isPlayer()) { this.scene.validateAchvs(HealAchv, healAmount); if (healAmount.value > this.scene.gameData.gameStats.highestHeal)