diff --git a/src/battle-phases.ts b/src/battle-phases.ts index bdceb1a8761..ac17d779e66 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -25,6 +25,7 @@ import { Gender } from "./data/gender"; import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; import { TempBattleStat } from "./data/temp-battle-stat"; import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag"; +import { ProtectStatAttr, applyPreStatChangeAbilityAttrs } from "./data/ability"; export class SelectStarterPhase extends BattlePhase { constructor(scene: BattleScene) { @@ -1080,9 +1081,10 @@ export class MoveAnimTestPhase extends BattlePhase { export class StatChangePhase extends PokemonPhase { private stats: BattleStat[]; + private selfTarget: boolean; private levels: integer; - constructor(scene: BattleScene, player: boolean, stats: BattleStat[], levels: integer) { + constructor(scene: BattleScene, player: boolean, selfTarget: boolean, stats: BattleStat[], levels: integer) { super(scene, player); const allStats = Utils.getEnumValues(BattleStat); @@ -1092,20 +1094,27 @@ export class StatChangePhase extends PokemonPhase { start() { const pokemon = this.getPokemon(); - + + const filteredStats = this.stats.filter(stat => { + const cancelled = new Utils.BooleanHolder(false); + + if (!this.selfTarget && this.levels < 0) + applyPreStatChangeAbilityAttrs(ProtectStatAttr, this.getPokemon(), stat, cancelled); + + return !cancelled.value; + }); + const battleStats = this.getPokemon().summonData.battleStats; - const relLevels = this.stats.map(stat => (this.levels >= 1 ? Math.min(battleStats[stat] + this.levels, 6) : Math.max(battleStats[stat] + this.levels, -6)) - battleStats[stat]); + const relLevels = filteredStats.map(stat => (this.levels >= 1 ? Math.min(battleStats[stat] + this.levels, 6) : Math.max(battleStats[stat] + this.levels, -6)) - battleStats[stat]); const end = () => { - const messages = this.getStatChangeMessages(relLevels); + const messages = this.getStatChangeMessages(filteredStats, relLevels); for (let message of messages) this.scene.queueMessage(message); - for (let stat of this.stats) + for (let stat of filteredStats) pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + this.levels, 6), -6); - console.log(pokemon.summonData.battleStats); - this.end(); }; @@ -1113,7 +1122,7 @@ export class StatChangePhase extends PokemonPhase { pokemon.enableMask(); const pokemonMaskSprite = pokemon.maskSprite; - const statSprite = this.scene.add.tileSprite((this.player ? 106 : 236) * 6, ((this.player ? 148 : 84) + (this.levels >= 1 ? 160 : 0)) * 6, 156, 316, 'battle_stats', this.stats.length > 1 ? 'mix' : BattleStat[this.stats[0]].toLowerCase()); + const statSprite = this.scene.add.tileSprite((this.player ? 106 : 236) * 6, ((this.player ? 148 : 84) + (this.levels >= 1 ? 160 : 0)) * 6, 156, 316, 'battle_stats', filteredStats.length > 1 ? 'mix' : BattleStat[filteredStats[0]].toLowerCase()); statSprite.setAlpha(0); statSprite.setScale(6); statSprite.setOrigin(0.5, 1); @@ -1150,11 +1159,11 @@ export class StatChangePhase extends PokemonPhase { end(); } - getStatChangeMessages(relLevels: integer[]): string[] { + getStatChangeMessages(stats: BattleStat[], relLevels: integer[]): string[] { const messages: string[] = []; - for (let s = 0; s < this.stats.length; s++) - messages.push(getPokemonMessage(this.getPokemon(), `'s ${getBattleStatName(this.stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`)); + for (let s = 0; s < stats.length; s++) + messages.push(getPokemonMessage(this.getPokemon(), `'s ${getBattleStatName(stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`)); return messages; } } diff --git a/src/data/ability.ts b/src/data/ability.ts index 419a374def8..9a3e1cfb6e2 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1,15 +1,129 @@ +import Pokemon, { PokemonMove } from "../pokemon"; +import { Type } from "./type"; +import * as Utils from "../utils"; +import { BattleStat } from "./battle-stat"; +import { StatChangePhase } from "../battle-phases"; + export class Ability { public id: Abilities; public name: string; public description: string; public generation: integer; + public attrs: AbilityAttr[]; constructor(id: Abilities, name: string, description: string, generation: integer) { this.id = id; this.name = name; this.description = description; this.generation = generation; + this.attrs = []; } + + getAttrs(attrType: { new(...args: any[]): AbilityAttr }): AbilityAttr[] { + return this.attrs.filter(a => a instanceof attrType); + } + + attr AbilityAttr>(AttrType: T, ...args: ConstructorParameters): Ability { + const attr = new AttrType(...args); + this.attrs.push(attr); + + return this; + } +} + +export abstract class AbilityAttr { } + +export class PreDefendAbilityAttr extends AbilityAttr { + applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return false; + } +} + +export class TypeImmunityAttr extends PreDefendAbilityAttr { + private immuneType: Type; + + constructor(immuneType: Type) { + super(); + + this.immuneType = immuneType; + } + + applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (move.getMove().type === this.immuneType) { + (args[0] as Utils.NumberHolder).value = 0; + return true; + } + + return false; + } +} + +class TypeImmunityStatChangeAttr extends TypeImmunityAttr { + private stat: BattleStat; + private levels: integer; + + constructor(immuneType: Type, stat: BattleStat, levels: integer) { + super(immuneType); + + this.stat = stat; + this.levels = levels; + } + + applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { + const ret = super.applyPreDefend(pokemon, attacker, move, cancelled, args); + + if (ret) { + cancelled.value = true; + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ this.stat ], this.levels)); + } + + return ret; + } +} + +export class PreStatChangeAbilityAttr extends AbilityAttr { + applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + return false; + } +} + +export class ProtectStatAttr extends PreStatChangeAbilityAttr { + private protectedStats: BattleStat[]; + + constructor(...stats: BattleStat[]) { + super(); + + this.protectedStats = stats; + } + + applyPreStatChange(pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (!this.protectedStats.length || this.protectedStats.indexOf(stat) > -1) { + cancelled.value = true; + return true; + } + + return false; + } +} + +export function applyPreDefendAbilityAttrs(attrType: { new(...args: any[]): PreDefendAbilityAttr }, + pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): void { + const ability = pokemon.getAbility(); + const attrs = ability.getAttrs(attrType) as PreDefendAbilityAttr[]; + for (let attr of attrs) { + if (attr.applyPreDefend(pokemon, attacker, move, cancelled, args)) + console.log('Applied', ability.name, attr); + } +} + +export function applyPreStatChangeAbilityAttrs(attrType: { new(...args: any[]): PreStatChangeAbilityAttr }, + pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]) { + const ability = pokemon.getAbility(); + const attrs = ability.getAttrs(attrType) as PreStatChangeAbilityAttr[]; + for (let attr of attrs) { + if (attr.applyPreStatChange(pokemon, stat, cancelled, args)) + console.log('Applied', ability.name, attr); + } } export enum Abilities { @@ -193,7 +307,8 @@ export const abilities = [ new Ability(Abilities.BATTLE_ARMOR, "Battle Armor (N)", "The POKéMON is protected against critical hits.", 3), new Ability(Abilities.BLAZE, "Blaze (N)", "Powers up FIRE-type moves in a pinch.", 3), new Ability(Abilities.CHLOROPHYLL, "Chlorophyll (N)", "Boosts the POKéMON's SPEED in sunshine.", 3), - new Ability(Abilities.CLEAR_BODY, "Clear Body (N)", "Prevents other POKéMON from lowering its stats.", 3), + new Ability(Abilities.CLEAR_BODY, "Clear Body", "Prevents other POKéMON from lowering its stats.", 3) + .attr(ProtectStatAttr), new Ability(Abilities.CLOUD_NINE, "Cloud Nine (N)", "Eliminates the effects of weather.", 3), new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3), new Ability(Abilities.COMPOUND_EYES, "Compound Eyes (N)", "The POKéMON's accuracy is boosted.", 3), @@ -209,15 +324,19 @@ export const abilities = [ new Ability(Abilities.GUTS, "Guts (N)", "Boosts ATTACK if there is a status problem.", 3), new Ability(Abilities.HUGE_POWER, "Huge Power (N)", "Raises the POKéMON's ATTACK stat.", 3), new Ability(Abilities.HUSTLE, "Hustle (N)", "Boosts the ATTACK stat, but lowers accuracy.", 3), - new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter (N)", "Prevents other POKéMON from lowering ATTACK stat.", 3), + new Ability(Abilities.HYPER_CUTTER, "Hyper Cutter", "Prevents other POKéMON from lowering ATTACK stat.", 3) + .attr(ProtectStatAttr, BattleStat.ATK), new Ability(Abilities.ILLUMINATE, "Illuminate (N)", "Raises the likelihood of meeting wild POKéMON.", 3), new Ability(Abilities.IMMUNITY, "Immunity (N)", "Prevents the POKéMON from getting poisoned.", 3), new Ability(Abilities.INNER_FOCUS, "Inner Focus (N)", "The POKéMON is protected from flinching.", 3), new Ability(Abilities.INSOMNIA, "Insomnia (N)", "Prevents the POKéMON from falling asleep.", 3), new Ability(Abilities.INTIMIDATE, "Intimidate (N)", "Lowers the foe's ATTACK stat.", 3), - new Ability(Abilities.KEEN_EYE, "Keen Eye (N)", "Prevents other POKéMON from lowering accuracy.", 3), - new Ability(Abilities.LEVITATE, "Levitate (N)", "Gives immunity to GROUND-type moves.", 3), - new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod (N)", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3), + new Ability(Abilities.KEEN_EYE, "Keen Eye", "Prevents other POKéMON from lowering accuracy.", 3) + .attr(ProtectStatAttr, BattleStat.ACC), + new Ability(Abilities.LEVITATE, "Levitate", "Gives immunity to GROUND-type moves.", 3) + .attr(TypeImmunityAttr, Type.FLYING), + new Ability(Abilities.LIGHTNING_ROD, "Lightning Rod", "Draws in all ELECTRIC-type moves to up SP. ATK.", 3) + .attr(TypeImmunityStatChangeAttr, Type.ELECTRIC, BattleStat.SPATK, 1), new Ability(Abilities.LIMBER, "Limber (N)", "The POKéMON is protected from paralysis.", 3), new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "Damages attackers using any draining move.", 3), new Ability(Abilities.MAGMA_ARMOR, "Magma Armor (N)", "Prevents the POKéMON from becoming frozen.", 3), @@ -262,7 +381,8 @@ export const abilities = [ new Ability(Abilities.VOLT_ABSORB, "Volt Absorb (N)", "Restores HP if hit by an ELECTRIC-type move.", 3), new Ability(Abilities.WATER_ABSORB, "Water Absorb (N)", "Restores HP if hit by a WATER-type move.", 3), new Ability(Abilities.WATER_VEIL, "Water Veil (N)", "Prevents the POKéMON from getting a burn.", 3), - new Ability(Abilities.WHITE_SMOKE, "White Smoke (N)", "Prevents other POKéMON from lowering its stats.", 3), + new Ability(Abilities.WHITE_SMOKE, "White Smoke", "Prevents other POKéMON from lowering its stats.", 3) + .attr(ProtectStatAttr), new Ability(Abilities.WONDER_GUARD, "Wonder Guard (N)", "Only supereffective moves will hit.", 3), new Ability(Abilities.ADAPTABILITY, "Adaptability (N)", "Powers up moves of the same type.", 4), new Ability(Abilities.AFTERMATH, "Aftermath (N)", "Damages the attacker landing the finishing hit.", 4), @@ -304,7 +424,8 @@ export const abilities = [ new Ability(Abilities.SOLID_ROCK, "Solid Rock (N)", "Reduces damage from super-effective attacks.", 4), new Ability(Abilities.STALL, "Stall (N)", "The POKéMON moves after all other POKéMON do.", 4), new Ability(Abilities.STEADFAST, "Steadfast (N)", "Raises SPEED each time the POKéMON flinches.", 4), - new Ability(Abilities.STORM_DRAIN, "Storm Drain (N)", "Draws in all WATER-type moves to up SP. ATK.", 4), + new Ability(Abilities.STORM_DRAIN, "Storm Drain", "Draws in all WATER-type moves to up SP. ATK.", 4) + .attr(TypeImmunityStatChangeAttr, Type.WATER, BattleStat.SPATK, 1), new Ability(Abilities.SUPER_LUCK, "Super Luck (N)", "Heightens the critical-hit ratios of moves.", 4), new Ability(Abilities.TANGLED_FEET, "Tangled Feet (N)", "Raises evasion if the POKéMON is confused.", 4), new Ability(Abilities.TECHNICIAN, "Technician (N)", "Powers up the POKéMON's weaker moves.", 4), @@ -312,7 +433,8 @@ export const abilities = [ new Ability(Abilities.UNAWARE, "Unaware (N)", "Ignores any stat changes in the POKéMON.", 4), new Ability(Abilities.UNBURDEN, "Unburden (N)", "Raises SPEED if a held item is used.", 4), new Ability(Abilities.ANALYTIC, "Analytic (N)", "Boosts move power when the POKéMON moves last.", 5), - new Ability(Abilities.BIG_PECKS, "Big Pecks (N)", "Protects the POKéMON from DEFENSE-lowering attacks.", 5), + new Ability(Abilities.BIG_PECKS, "Big Pecks", "Protects the POKéMON from DEFENSE-lowering attacks.", 5) + .attr(ProtectStatAttr, BattleStat.DEF), new Ability(Abilities.CONTRARY, "Contrary (N)", "Makes stat changes have an opposite effect.", 5), new Ability(Abilities.CURSED_BODY, "Cursed Body (N)", "May disable a move used on the POKéMON.", 5), new Ability(Abilities.DEFEATIST, "Defeatist (N)", "Lowers stats when HP drops below half.", 5), diff --git a/src/data/berry.ts b/src/data/berry.ts index c346c92c8d9..0494fbc6251 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -101,13 +101,13 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { case BerryType.APICOT: return (pokemon: Pokemon) => { const battleStat = (berryType - BerryType.LIECHI) as BattleStat; - pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), [ battleStat ], 1)); + pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ battleStat ], 1)); }; case BerryType.LANSAT: return (pokemon: Pokemon) => { pokemon.addTag(BattlerTagType.CRIT_BOOST); }; case BerryType.STARF: - return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), [ BattleStat.RAND ], 2)); + return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), true, [ BattleStat.RAND ], 2)); } } \ No newline at end of file diff --git a/src/data/move.ts b/src/data/move.ts index e109dfc31a8..fec43b5bb9b 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -9,7 +9,6 @@ import { Type } from "./type"; import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagType, ArenaTrapTag } from "./arena-tag"; -import { FlinchChanceModifier } from "../modifier/modifier"; export enum MoveCategory { PHYSICAL, @@ -1095,7 +1094,7 @@ export class StatChangeAttr extends MoveEffectAttr { if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) { const levels = this.getLevels(user); - user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.stats, levels)); + user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.selfTarget, this.stats, levels)); return true; } diff --git a/src/pokemon.ts b/src/pokemon.ts index 5e79f981573..c91c19a1236 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -23,6 +23,7 @@ import { WeatherType } from './data/weather'; import { TempBattleStat } from './data/temp-battle-stat'; import { WeakenMoveTypeTag } from './data/arena-tag'; import { Biome } from './data/biome'; +import { Abilities, Ability, TypeImmunityAttr, abilities, applyPreDefendAbilityAttrs } from './data/ability'; export default abstract class Pokemon extends Phaser.GameObjects.Container { public id: integer; @@ -345,6 +346,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getTypes().indexOf(type) > -1; } + getAbility(): Ability { + return abilities[this.species.getAbility(this.abilityIndex)]; + } + getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier { const types = this.getTypes(); return getTypeDamageMultiplier(moveType, types[0]) * (types.length ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier; @@ -479,70 +484,77 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { case MoveCategory.PHYSICAL: case MoveCategory.SPECIAL: const isPhysical = moveCategory === MoveCategory.PHYSICAL; + const cancelled = new Utils.BooleanHolder(false); const power = new Utils.NumberHolder(move.power); - const typeMultiplier = getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1); + const typeMultiplier = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, this.getSpeciesForm().type1) * (this.getSpeciesForm().type2 !== null ? getTypeDamageMultiplier(move.type, this.getSpeciesForm().type2) : 1)); const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type); - applyMoveAttrs(VariablePowerAttr, source, this, move, power); - this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power); - this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power); - const critLevel = new Utils.IntegerHolder(0); - applyMoveAttrs(HighCritAttr, source, this, move, critLevel); - this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); - if (source.getTag(BattlerTagType.CRIT_BOOST)) - critLevel.value += 2; - const critChance = Math.ceil(16 / Math.pow(2, critLevel.value)); - let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance)); - const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK); - const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF); - const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1; - const criticalMultiplier = isCritical ? 2 : 1; - damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier; - if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) - damage = Math.floor(damage / 2); - move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { - if (this.getTag(hta.tagType)) - damage *= 2; - }); + applyPreDefendAbilityAttrs(TypeImmunityAttr, this, source, battlerMove, cancelled, typeMultiplier); - const fixedDamage = new Utils.IntegerHolder(0); - applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); - if (damage && fixedDamage.value) { - damage = fixedDamage.value; - isCritical = false; - result = MoveResult.EFFECTIVE; - } + if (cancelled.value) + result = MoveResult.NO_EFFECT; + else { + applyMoveAttrs(VariablePowerAttr, source, this, move, power); + this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power); + this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, power); + const critLevel = new Utils.IntegerHolder(0); + applyMoveAttrs(HighCritAttr, source, this, move, critLevel); + this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); + if (source.getTag(BattlerTagType.CRIT_BOOST)) + critLevel.value += 2; + const critChance = Math.ceil(16 / Math.pow(2, critLevel.value)); + let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance)); + const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK); + const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF); + const stabMultiplier = source.species.type1 === move.type || (source.species.type2 !== null && source.species.type2 === move.type) ? 1.5 : 1; + const criticalMultiplier = isCritical ? 2 : 1; + damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier.value * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier; + if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) + damage = Math.floor(damage / 2); + move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => { + if (this.getTag(hta.tagType)) + damage *= 2; + }); - console.log('damage', damage, move.name, move.power, sourceAtk, targetDef); - - if (!result) { - if (typeMultiplier >= 2) - result = MoveResult.SUPER_EFFECTIVE; - else if (typeMultiplier >= 1) + const fixedDamage = new Utils.IntegerHolder(0); + applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage); + if (damage && fixedDamage.value) { + damage = fixedDamage.value; + isCritical = false; result = MoveResult.EFFECTIVE; - else if (typeMultiplier > 0) - result = MoveResult.NOT_VERY_EFFECTIVE; - else - result = MoveResult.NO_EFFECT; - } + } - if (damage) { - 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; - } + console.log('damage', damage, move.name, move.power, sourceAtk, targetDef); + + if (!result) { + if (typeMultiplier.value >= 2) + result = MoveResult.SUPER_EFFECTIVE; + else if (typeMultiplier.value >= 1) + result = MoveResult.EFFECTIVE; + else if (typeMultiplier.value > 0) + result = MoveResult.NOT_VERY_EFFECTIVE; + else + result = MoveResult.NO_EFFECT; + } - switch (result) { - case MoveResult.SUPER_EFFECTIVE: - this.scene.queueMessage('It\'s super effective!'); - break; - case MoveResult.NOT_VERY_EFFECTIVE: - this.scene.queueMessage('It\'s not very effective!'); - break; - case MoveResult.NO_EFFECT: - this.scene.queueMessage(`It doesn\'t affect ${this.name}!`); - break; + if (damage) { + 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) { + case MoveResult.SUPER_EFFECTIVE: + this.scene.queueMessage('It\'s super effective!'); + break; + case MoveResult.NOT_VERY_EFFECTIVE: + this.scene.queueMessage('It\'s not very effective!'); + break; + case MoveResult.NO_EFFECT: + this.scene.queueMessage(`It doesn\'t affect ${this.name}!`); + break; + } } break; case MoveCategory.STATUS: diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 821f2caad97..825418a00d2 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -94,6 +94,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityLabelText = addTextObject(this.scene, 6, 126, 'ABILITY:', TextStyle.SUMMARY, { fontSize: '64px' }); this.pokemonAbilityLabelText.setOrigin(0, 0); + this.pokemonAbilityLabelText.setVisible(false); this.starterSelectContainer.add(this.pokemonAbilityLabelText); this.pokemonAbilityText = addTextObject(this.scene, 38, 126, '', TextStyle.SUMMARY, { fontSize: '64px' }); @@ -454,11 +455,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (species && this.speciesStarterDexEntry) { this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 3)); this.pokemonNameText.setText(species.name.toUpperCase()); + this.pokemonAbilityLabelText.setVisible(true); this.setSpeciesDetails(species, !!this.speciesStarterDexEntry?.shiny, this.speciesStarterDexEntry?.formIndex, !!this.speciesStarterDexEntry?.female, this.speciesStarterDexEntry?.abilityIndex); } else { this.pokemonNumberText.setText(Utils.padInt(0, 3)); this.pokemonNameText.setText(species ? '???' : ''); + this.pokemonAbilityLabelText.setVisible(false); this.setSpeciesDetails(species, false, 0, false, 0); } @@ -563,7 +566,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.canCycleAbility = false; } - if (species.malePercent !== null) { + if (defaultDexEntry && species.malePercent !== null) { const gender = !female ? Gender.MALE : Gender.FEMALE; this.pokemonGenderText.setText(getGenderSymbol(gender)); this.pokemonGenderText.setColor(getGenderColor(gender)); @@ -571,12 +574,15 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else this.pokemonGenderText.setText(''); - const ability = this.lastSpecies.getAbility(abilityIndex); - this.pokemonAbilityText.setText(abilities[ability].name.toUpperCase()); + if (defaultDexEntry) { + const ability = this.lastSpecies.getAbility(abilityIndex); + this.pokemonAbilityText.setText(abilities[ability].name.toUpperCase()); - const isHidden = ability === this.lastSpecies.abilityHidden; - this.pokemonAbilityText.setColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD)); - this.pokemonAbilityText.setShadowColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true)); + const isHidden = ability === this.lastSpecies.abilityHidden; + this.pokemonAbilityText.setColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD)); + this.pokemonAbilityText.setShadowColor(getTextColor(!isHidden ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true)); + } else + this.pokemonAbilityText.setText(''); } else { this.pokemonGenderText.setText(''); this.pokemonAbilityText.setText('');