Damage-related fixes and changes

Apply damage numbers to all damage and heal; fix some damage-related bugs
This commit is contained in:
Flashfyre 2024-03-01 09:35:36 -05:00
parent 716d8853a3
commit 1f6a6f4621
7 changed files with 103 additions and 65 deletions

View File

@ -905,8 +905,8 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
if (pokemon.getHpRatio() < 1) { if (pokemon.getHpRatio() < 1) {
const scene = pokemon.scene; const scene = pokemon.scene;
scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby its ${pokemon.getAbility()}!`)); scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby its ${pokemon.getAbility()}!`));
scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.OTHER)); const damage = pokemon.damage(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor)));
pokemon.damage(Math.ceil(pokemon.getMaxHp() / (16 / this.damageFactor))); scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), damage, HitResult.OTHER));
return true; return true;
} }

View File

@ -170,10 +170,10 @@ class SpikesTag extends ArenaTrapTag {
activateTrap(pokemon: Pokemon): boolean { activateTrap(pokemon: Pokemon): boolean {
if ((!pokemon.isOfType(Type.FLYING) || pokemon.getTag(BattlerTagType.IGNORE_FLYING) || pokemon.scene.arena.getTag(ArenaTagType.GRAVITY))) { if ((!pokemon.isOfType(Type.FLYING) || pokemon.getTag(BattlerTagType.IGNORE_FLYING) || pokemon.scene.arena.getTag(ArenaTagType.GRAVITY))) {
const damageHpRatio = 1 / (10 - 2 * this.layers); 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.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!'));
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.OTHER)); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage), HitResult.OTHER));
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
return true; return true;
} }
@ -266,9 +266,9 @@ class StealthRockTag extends ArenaTrapTag {
} }
if (damageHpRatio) { if (damageHpRatio) {
const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio);
pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`); pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`);
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.OTHER)); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage), HitResult.OTHER));
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
} }
return false; return false;

View File

@ -179,8 +179,7 @@ export class ConfusedTag extends BattlerTag {
const def = pokemon.getBattleStat(Stat.DEF); 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)); 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.queueMessage('It hurt itself in its\nconfusion!');
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage)));
pokemon.damage(damage);
pokemon.battleData.hitCount++; pokemon.battleData.hitCount++;
(pokemon.scene.getCurrentPhase() as MovePhase).cancel(); (pokemon.scene.getCurrentPhase() as MovePhase).cancel();
} }
@ -264,9 +263,9 @@ export class SeedTag extends BattlerTag {
if (source) { if (source) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED));
const damage = Math.max(Math.floor(pokemon.getMaxHp() / 8), 1); const damage = pokemon.damage(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex()));
pokemon.damage(damage); 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)); 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 pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.CURSE)); // TODO: Update animation type
const damage = Math.ceil(pokemon.getMaxHp() / 4); const damage = Math.ceil(pokemon.getMaxHp() / 4);
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage)));
pokemon.damage(damage);
} }
return ret; return ret;
@ -473,8 +471,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, this.commonAnim)); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, this.commonAnim));
const damage = Math.ceil(pokemon.getMaxHp() / 8); const damage = Math.ceil(pokemon.getMaxHp() / 8);
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex())); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(damage)));
pokemon.damage(damage);
} }
return ret; return ret;
@ -617,10 +614,8 @@ export class PerishSongTag extends BattlerTag {
if (ret) if (ret)
pokemon.scene.queueMessage(getPokemonMessage(pokemon, `\'s perish count fell to ${this.turnCount}.`)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, `\'s perish count fell to ${this.turnCount}.`));
else { else
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), HitResult.ONE_HIT_KO)); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.damage(pokemon.hp, true, true), HitResult.ONE_HIT_KO));
pokemon.damage(pokemon.hp, true, true);
}
return ret; return ret;
} }

View File

@ -532,9 +532,8 @@ export class RecoilAttr extends MoveEffectAttr {
if (!recoilDamage) if (!recoilDamage)
return false; 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.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!'));
user.damage(recoilDamage, true);
return true; return true;
} }
@ -553,8 +552,7 @@ export class SacrificialAttr extends MoveEffectAttr {
if (!super.apply(user, target, move, args)) if (!super.apply(user, target, move, args))
return false; return false;
user.scene.unshiftPhase(new DamagePhase(user.scene, user.getBattlerIndex(), HitResult.OTHER)); user.scene.unshiftPhase(new DamagePhase(user.scene, user.getBattlerIndex(), user.damage(user.hp, true, true), HitResult.OTHER));
user.damage(user.getMaxHp(), true, true);
return true; return true;
} }
@ -1085,7 +1083,9 @@ export class HalfHpStatMaxAttr extends StatChangeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(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(() => { user.updateInfo().then(() => {
const ret = super.apply(user, target, move, args); const ret = super.apply(user, target, move, args);
user.scene.queueMessage(getPokemonMessage(user, ` cut its own hp\nand maximized its ${getBattleStatName(this.stats[0])}!`)); 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<boolean> { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(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(() => { user.updateInfo().then(() => {
const ret = super.apply(user, target, move, args); const ret = super.apply(user, target, move, args);
resolve(ret); resolve(ret);
@ -1128,16 +1130,26 @@ export class HpSplitAttr extends MoveEffectAttr {
const infoUpdates = []; const infoUpdates = [];
const hpValue = Math.floor((target.hp + user.hp) / 2); const hpValue = Math.floor((target.hp + user.hp) / 2);
if (user.hp < hpValue) if (user.hp < hpValue) {
user.heal(hpValue - user.hp); const healing = user.heal(hpValue - user.hp);
else if (user.hp > hpValue) if (healing)
user.damage(user.hp - hpValue, true); 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()); infoUpdates.push(user.updateInfo());
if (target.hp < hpValue) if (target.hp < hpValue) {
target.heal(hpValue - target.hp); const healing = target.heal(hpValue - target.hp);
else if (target.hp > hpValue) if (healing)
target.damage(target.hp - hpValue, true); 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()); infoUpdates.push(target.updateInfo());
return Promise.all(infoUpdates).then(() => resolve(true)); 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 TypelessAttr extends MoveAttr { }
export class DisableMoveAttr extends MoveEffectAttr { export class DisableMoveAttr extends MoveEffectAttr {
@ -2280,7 +2299,7 @@ export function initMoves() {
.attr(MultiHitAttr, MultiHitType._2), .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.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) 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), .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) 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), .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) 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), .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) 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), .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) 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), .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 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 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) 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), .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.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) 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.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.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) 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) 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(), .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), 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),

View File

@ -1,5 +1,5 @@
import { TextStyle, addTextObject } from "../ui/text"; import { TextStyle, addTextObject } from "../ui/text";
import Pokemon, { AttackMoveResult, HitResult } from "./pokemon"; import Pokemon, { DamageResult, HitResult } from "./pokemon";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { BattlerIndex } from "../battle"; import { BattlerIndex } from "../battle";
@ -10,17 +10,21 @@ export default class DamageNumberHandler {
this.damageNumbers = new Map(); 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; const scene = target.scene;
if (!scene.damageNumbersMode)
return;
const battlerIndex = target.getBattlerIndex(); const battlerIndex = target.getBattlerIndex();
const baseScale = target.getSpriteScale() / 6; 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.setOrigin(0.5, 1);
damageNumber.setScale(baseScale); damageNumber.setScale(baseScale);
let [ textColor, shadowColor ] = [ null, null ]; let [ textColor, shadowColor ] = [ null, null ];
switch (result.result) { switch (result) {
case HitResult.SUPER_EFFECTIVE: case HitResult.SUPER_EFFECTIVE:
[ textColor, shadowColor ] = [ '#f8d030', '#b8a038' ]; [ textColor, shadowColor ] = [ '#f8d030', '#b8a038' ];
break; break;
@ -30,6 +34,9 @@ export default class DamageNumberHandler {
case HitResult.ONE_HIT_KO: case HitResult.ONE_HIT_KO:
[ textColor, shadowColor ] = [ '#a040a0', '#483850' ]; [ textColor, shadowColor ] = [ '#a040a0', '#483850' ];
break; break;
case HitResult.HEAL:
[ textColor, shadowColor ] = [ '#78c850', '#588040' ];
break;
default: default:
[ textColor, shadowColor ] = [ '#ffffff', '#636363' ]; [ textColor, shadowColor ] = [ '#ffffff', '#636363' ];
break; break;
@ -38,7 +45,7 @@ export default class DamageNumberHandler {
if (textColor) if (textColor)
damageNumber.setColor(textColor); damageNumber.setColor(textColor);
if (shadowColor) { if (shadowColor) {
if (result.critical) { if (critical) {
damageNumber.setShadowOffset(0, 0); damageNumber.setShadowOffset(0, 0);
damageNumber.setStroke(shadowColor, 12); damageNumber.setStroke(shadowColor, 12);
} else } else

View File

@ -1123,11 +1123,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage); this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
if (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) if (isCritical)
this.scene.queueMessage('A critical hit!'); this.scene.queueMessage('A critical hit!');
this.scene.setPhaseQueueSplice(); this.scene.setPhaseQueueSplice();
damage.value = this.damage(damage.value);
if (source.isPlayer()) { if (source.isPlayer()) {
this.scene.validateAchvs(DamageAchv, damage); this.scene.validateAchvs(DamageAchv, damage);
if (damage.value > this.scene.gameData.gameStats.highestDamage) if (damage.value > this.scene.gameData.gameStats.highestDamage)
@ -1137,8 +1137,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.battleData.hitCount++; this.battleData.hitCount++;
const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id }; const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id };
this.turnData.attacksReceived.unshift(attackResult); this.turnData.attacksReceived.unshift(attackResult);
if (damage.value && this.scene.damageNumbersMode)
this.scene.damageNumberHandler.add(this, attackResult);
if (source.isPlayer() && !this.isPlayer()) if (source.isPlayer() && !this.isPlayer())
this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, damage) this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, damage)
} }
@ -2344,7 +2342,7 @@ export class EnemyPokemon extends Pokemon {
const hpThreshold = segmentSize * s; const hpThreshold = segmentSize * s;
const roundedHpThreshold = Math.round(hpThreshold); const roundedHpThreshold = Math.round(hpThreshold);
if (this.hp >= roundedHpThreshold) { if (this.hp >= roundedHpThreshold) {
if (this.hp - damage < roundedHpThreshold) { if (this.hp - damage <= roundedHpThreshold) {
const hpRemainder = this.hp - roundedHpThreshold; const hpRemainder = this.hp - roundedHpThreshold;
let segmentsBypassed = 0; let segmentsBypassed = 0;
while (this.canBypassBossSegments(segmentsBypassed + 1) && (damage - hpRemainder) >= Math.round(segmentSize * Math.pow(2, segmentsBypassed + 1))) { 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, ONE_HIT_KO,
NO_EFFECT, NO_EFFECT,
STATUS, STATUS,
HEAL,
FAIL, FAIL,
MISS, MISS,
OTHER OTHER

View File

@ -2394,9 +2394,10 @@ export class WeatherEffectPhase extends CommonAnimPhase {
if (cancelled.value) if (cancelled.value)
return; return;
const damage = Math.ceil(pokemon.getMaxHp() / 16);
this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon));
this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.getBattlerIndex())); this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.getBattlerIndex(), pokemon.damage(damage)));
pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16));
}; };
this.executeForAll((pokemon: Pokemon) => { this.executeForAll((pokemon: Pokemon) => {
@ -2458,19 +2459,22 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) { if (pokemon?.isActive(true) && pokemon.status && pokemon.status.isPostTurn()) {
pokemon.status.incrementTurn(); pokemon.status.incrementTurn();
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => {
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))); this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect)));
let damage: integer = 0;
switch (pokemon.status.effect) { switch (pokemon.status.effect) {
case StatusEffect.POISON: case StatusEffect.POISON:
case StatusEffect.BURN: case StatusEffect.BURN:
pokemon.damage(Math.max(pokemon.getMaxHp() >> 3, 1)); damage = Math.max(pokemon.getMaxHp() >> 3, 1);
break; break;
case StatusEffect.TOXIC: case StatusEffect.TOXIC:
pokemon.damage(Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1)); damage = Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1);
break; break;
} }
pokemon.updateInfo().then(() => this.end()); 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 } else
this.end(); this.end();
} }
@ -2512,12 +2516,16 @@ export class MessagePhase extends Phase {
} }
export class DamagePhase extends PokemonPhase { export class DamagePhase extends PokemonPhase {
private amount: integer;
private damageResult: DamageResult; 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); super(scene, battlerIndex);
this.amount = amount;
this.damageResult = damageResult || HitResult.EFFECTIVE; this.damageResult = damageResult || HitResult.EFFECTIVE;
this.critical = critical;
} }
start() { start() {
@ -2549,6 +2557,9 @@ export class DamagePhase extends PokemonPhase {
break; break;
} }
if (this.amount)
this.scene.damageNumberHandler.add(this.getPokemon(), this.amount, this.damageResult, this.critical);
if (this.damageResult !== HitResult.OTHER) { if (this.damageResult !== HitResult.OTHER) {
const flashTimer = this.scene.time.addEvent({ const flashTimer = this.scene.time.addEvent({
delay: 100, delay: 100,
@ -2685,10 +2696,15 @@ export class FaintPhase extends PokemonPhase {
case BattleSpec.FINAL_BOSS: case BattleSpec.FINAL_BOSS:
if (!this.player) { if (!this.player) {
const enemy = this.getPokemon(); 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()); 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); this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value)); const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
healAmount.value = pokemon.heal(healAmount.value); healAmount.value = pokemon.heal(healAmount.value);
if (healAmount.value)
this.scene.damageNumberHandler.add(pokemon, healAmount.value, HitResult.HEAL);
if (pokemon.isPlayer()) { if (pokemon.isPlayer()) {
this.scene.validateAchvs(HealAchv, healAmount); this.scene.validateAchvs(HealAchv, healAmount);
if (healAmount.value > this.scene.gameData.gameStats.highestHeal) if (healAmount.value > this.scene.gameData.gameStats.highestHeal)