diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 71b2afe60..201f991d9 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -1150,8 +1150,8 @@ export class WeatherEffectPhase extends CommonAnimPhase { if (this.weather.isDamaging()) { const inflictDamage = (pokemon: Pokemon) => { this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); - pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16)); this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer())); + pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16)); }; const playerPokemon = this.scene.getPlayerPokemon(); diff --git a/src/data/battler-tag.ts b/src/data/battler-tag.ts index 5dc4abf82..af21f88b0 100644 --- a/src/data/battler-tag.ts +++ b/src/data/battler-tag.ts @@ -177,9 +177,9 @@ export class ConfusedTag extends BattlerTag { const atk = pokemon.getBattleStat(Stat.ATK); const def = pokemon.getBattleStat(Stat.DEF); const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * ((Utils.randInt(15) + 85) / 100)); - pokemon.damage(damage); pokemon.scene.queueMessage('It hurt itself in its\nconfusion!'); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.damage(damage); (pokemon.scene.getCurrentPhase() as MovePhase).cancel(); } } @@ -206,8 +206,8 @@ export class SeedTag extends BattlerTag { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, !pokemon.isPlayer(), CommonAnim.LEECH_SEED)); const damage = Math.max(Math.floor(pokemon.getMaxHp() / 8), 1); - pokemon.damage(damage); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.damage(damage); pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, !pokemon.isPlayer(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by LEECH SEED!'), false, true)); } @@ -240,8 +240,8 @@ export class NightmareTag extends BattlerTag { pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CURSE)); // TODO: Update animation type const damage = Math.ceil(pokemon.getMaxHp() / 4); - pokemon.damage(damage); pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer())); + pokemon.damage(damage); } return ret; diff --git a/src/data/move.ts b/src/data/move.ts index a9f5fad17..8b7e1f8ce 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -746,9 +746,9 @@ export class RecoilAttr extends MoveEffectAttr { return false; const recoilDamage = Math.max(Math.floor(user.turnData.damageDealt / 4), 1); - user.damage(recoilDamage); user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), MoveResult.OTHER)); user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!')); + user.damage(recoilDamage); return true; } @@ -763,8 +763,8 @@ export class SacrificialAttr extends MoveEffectAttr { if (!super.apply(user, target, move, args)) return false; - user.damage(user.getMaxHp()); user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), MoveResult.OTHER)); + user.damage(user.getMaxHp()); return true; } @@ -959,7 +959,7 @@ export class ClearWeatherAttr extends MoveEffectAttr { export class OneHitKOAttr extends MoveHitEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - target.hp = 0; + target.damage(target.hp, true); user.scene.queueMessage('It\'s a one-hit KO!'); return true; } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 8b45b09db..b27b3f936 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -533,6 +533,9 @@ const modifierTypes = { BERRY_POUCH: () => new ModifierType('BERRY POUCH', 'Adds a 25% chance that a used berry will not be consumed', (type, _args) => new Modifiers.PreserveBerryModifier(type)), + FOCUS_BAND: () => new PokemonHeldItemModifierType('FOCUS BAND', 'Adds a 10% chance to survive with 1 HP after being damaged enough to faint', + (type, args) => new Modifiers.SurviveDamageModifier(type, (args[0] as Pokemon).id)), + LEFTOVERS: () => new PokemonHeldItemModifierType('LEFTOVERS', 'Heals 1/16 of a POKéMON\'s maximum HP every turn', (type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)), SHELL_BELL: () => new PokemonHeldItemModifierType('SHELL BELL', 'Heals 1/8 of a POKéMON\'s dealt damage', @@ -613,6 +616,7 @@ const modifierPool = { new WeightedModifierType(modifierTypes.CANDY_JAR, 3), //new WeightedModifierType(modifierTypes.OVAL_CHARM, 1), new WeightedModifierType(modifierTypes.HEALING_CHARM, 1), + new WeightedModifierType(modifierTypes.FOCUS_BAND, 3), new WeightedModifierType(modifierTypes.LEFTOVERS, 2), new WeightedModifierType(modifierTypes.SHELL_BELL, 2), new WeightedModifierType(modifierTypes.BERRY_POUCH, 3), @@ -641,6 +645,7 @@ const enemyModifierPool = { ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), [ModifierTier.ULTRA]: [ new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 5), + new WeightedModifierType(modifierTypes.FOCUS_BAND, 2), new WeightedModifierType(modifierTypes.LUCKY_EGG, 2), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), [ModifierTier.MASTER]: [ diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 50132bcf4..eb43dfe7a 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -349,6 +349,41 @@ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier { } } +export class SurviveDamageModifier extends PokemonHeldItemModifier { + constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { + super(type, pokemonId, stackCount); + } + + matchType(modifier: Modifier) { + return modifier instanceof SurviveDamageModifier; + } + + clone() { + return new SurviveDamageModifier(this.type, this.pokemonId, this.stackCount); + } + + shouldApply(args: any[]): boolean { + return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.BooleanHolder; + } + + apply(args: any[]): boolean { + const pokemon = args[0] as Pokemon; + const surviveDamage = args[1] as Utils.BooleanHolder; + + if (!surviveDamage.value && (this.getStackCount() === this.getMaxStackCount() || Utils.randInt(10) < this.getStackCount())) { + surviveDamage.value = true; + + pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`)); + } + + return true; + } + + getMaxStackCount(): number { + return 5; + } +} + export class TurnHealModifier extends PokemonHeldItemModifier { constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, pokemonId, stackCount); @@ -374,7 +409,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier { } getMaxStackCount(): number { - return 16; + return 4; } } @@ -403,7 +438,7 @@ export class HitHealModifier extends PokemonHeldItemModifier { } getMaxStackCount(): number { - return 16; + return 4; } } @@ -733,6 +768,10 @@ export class HealingBoosterModifier extends PersistentModifier { return true; } + + getMaxStackCount(): number { + return 4; + } } export class ExpBoosterModifier extends PersistentModifier { diff --git a/src/pokemon.ts b/src/pokemon.ts index a232315da..83c410f35 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -8,7 +8,7 @@ import * as Utils from './utils'; import { Type, getTypeDamageMultiplier } from './data/type'; import { getLevelTotalExp } from './data/exp'; import { Stat } from './data/pokemon-stat'; -import { AttackTypeBoosterModifier, PokemonBaseStatModifier, ShinyRateBoosterModifier, TempBattleStatBoosterModifier } from './modifier/modifier'; +import { AttackTypeBoosterModifier, PokemonBaseStatModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier'; import { PokeballType } from './data/pokeball'; import { Gender } from './data/gender'; import { initMoveAnim, loadMoveAnimAssets } from './data/battle-anims'; @@ -522,11 +522,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (damage) { - this.damage(damage); - source.turnData.damageDealt += damage; - this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult)) + this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult)); if (isCritical) this.scene.queueMessage('A critical hit!'); + this.damage(damage); + source.turnData.damageDealt += damage; } switch (result) { @@ -549,10 +549,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return result; } - damage(damage: integer): void { + damage(damage: integer, preventEndure?: boolean): void { if (!this.hp) return; + if (this.hp > 1 && this.hp - damage <= 0 && !preventEndure) { + const surviveDamage = new Utils.BooleanHolder(false); + this.scene.applyModifiers(SurviveDamageModifier, this.isPlayer(), this, surviveDamage); + if (surviveDamage.value) + damage = this.hp - 1; + } + this.hp = Math.max(this.hp - damage, 0); if (!this.hp) { this.scene.pushPhase(new FaintPhase(this.scene, this.isPlayer()));