From 2b4dc9a4f05a872330a4a82f4c02794b426d3ac3 Mon Sep 17 00:00:00 2001 From: Benjamin Odom Date: Sun, 5 May 2024 23:02:52 -0500 Subject: [PATCH 1/7] Fixes Struggle Softlock (#531) * Fixes Struggle Softlock Fixed an issue where moves marked as (N) would prevent you from being able to Struggle. Also fixes messaging so that a message shows when you are out of PP, Disabled, or trying to use an unimplemented move consistently. * Update phases.ts --- src/field/pokemon.ts | 2 +- src/locales/de/battle.ts | 1 + src/locales/en/battle.ts | 1 + src/locales/es/battle.ts | 1 + src/locales/fr/battle.ts | 1 + src/locales/it/battle.ts | 1 + src/phases.ts | 38 ++++++++++++++++++-------------------- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b20096513ef..1237f7bb55f 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3172,7 +3172,7 @@ export class PokemonMove { isUsable(pokemon: Pokemon, ignorePp?: boolean): boolean { if (this.moveId && pokemon.summonData?.disabledMove === this.moveId) return false; - return ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1; + return (ignorePp || this.ppUsed < this.getMovePp() || this.getMove().pp === -1) && !this.getMove().name.endsWith(' (N)'); } getMove(): Move { diff --git a/src/locales/de/battle.ts b/src/locales/de/battle.ts index 61191a1165a..b48aa1129b2 100644 --- a/src/locales/de/battle.ts +++ b/src/locales/de/battle.ts @@ -33,6 +33,7 @@ export const battle: SimpleTranslationEntries = { "learnMoveForgetSuccess": "{{pokemonName}} forgot how to\nuse {{moveName}}.", "levelCapUp": "Das Levellimit\nhat sich zu {{levelCap}} erhöht!", "moveNotImplemented": "{{moveName}} ist noch nicht implementiert und kann nicht ausgewählt werden.", + "moveNoPP": "There's no PP left for\nthis move!", "moveDisabled": "{{moveName}} ist deaktiviert!", "noPokeballForce": "Eine unsichtbare Kraft\nverhindert die Nutzung von Pokébällen.", "noPokeballTrainer": "Du kannst das Pokémon\neines anderen Trainers nicht fangen!", diff --git a/src/locales/en/battle.ts b/src/locales/en/battle.ts index 599672a1b19..e307a3a524a 100644 --- a/src/locales/en/battle.ts +++ b/src/locales/en/battle.ts @@ -33,6 +33,7 @@ export const battle: SimpleTranslationEntries = { "learnMoveForgetSuccess": "{{pokemonName}} forgot how to\nuse {{moveName}}.", "levelCapUp": "The level cap\nhas increased to {{levelCap}}!", "moveNotImplemented": "{{moveName}} is not yet implemented and cannot be selected.", + "moveNoPP": "There's no PP left for\nthis move!", "moveDisabled": "{{moveName}} is disabled!", "noPokeballForce": "An unseen force\nprevents using Poké Balls.", "noPokeballTrainer": "You can't catch\nanother trainer's Pokémon!", diff --git a/src/locales/es/battle.ts b/src/locales/es/battle.ts index 2d7087d6cba..f4bf22bb9e4 100644 --- a/src/locales/es/battle.ts +++ b/src/locales/es/battle.ts @@ -33,6 +33,7 @@ export const battle: SimpleTranslationEntries = { "learnMoveForgetSuccess": "{{pokemonName}} ha olvidado cómo utilizar {{moveName}}.", "levelCapUp": "¡Se ha incrementado el\nnivel máximo a {{levelCap}}!", "moveNotImplemented": "{{moveName}} aún no está implementado y no se puede seleccionar.", + "moveNoPP": "There's no PP left for\nthis move!", "moveDisabled": "!No puede usar {{moveName}} porque ha sido anulado!", "noPokeballForce": "Una fuerza misteriosa\nte impide usar Poké Balls.", "noPokeballTrainer": "¡No puedes atrapar a los\nPokémon de los demás!", diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index a4abf5239e3..2167ddd31ef 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -33,6 +33,7 @@ export const battle: SimpleTranslationEntries = { "learnMoveForgetSuccess": "{{pokemonName}} oublie comment\nutiliser {{moveName}}.", "levelCapUp": "La limite de niveau\na été augmentée à {{levelCap}} !", "moveNotImplemented": "{{moveName}} n’est pas encore implémenté et ne peut pas être sélectionné.", + "moveNoPP": "There's no PP left for\nthis move!", "moveDisabled": "{{moveName}} est sous entrave !", "noPokeballForce": "Une force mystérieuse\nempêche l’utilisation des Poké Balls.", "noPokeballTrainer": "Le Dresseur détourne la Ball\nVoler, c’est mal !", diff --git a/src/locales/it/battle.ts b/src/locales/it/battle.ts index 52a4ee66ccf..4b0afae1248 100644 --- a/src/locales/it/battle.ts +++ b/src/locales/it/battle.ts @@ -33,6 +33,7 @@ export const battle: SimpleTranslationEntries = { "learnMoveForgetSuccess": "{{pokemonName}} ha dimenticato la mossa\n{{moveName}}.", "levelCapUp": "Il livello massimo\nè aumentato a {{levelCap}}!", "moveNotImplemented": "{{moveName}} non è ancora implementata e non può essere selezionata.", + "moveNoPP": "There's no PP left for\nthis move!", "moveDisabled": "{{moveName}} è disabilitata!", "noPokeballForce": "Una forza misteriosa\nimpedisce l'uso dell Poké Ball.", "noPokeballTrainer": "Non puoi catturare\nPokémon di altri allenatori!", diff --git a/src/phases.ts b/src/phases.ts index 751cac166bf..fc6af354a2d 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1680,21 +1680,13 @@ export class CommandPhase extends FieldPhase { switch (command) { case Command.FIGHT: let useStruggle = false; - if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean) || (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) { + if (cursor === -1 || + playerPokemon.trySelectMove(cursor, args[0] as boolean) || + (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) { const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor].moveId : Moves.NONE : Moves.STRUGGLE; const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2]; - if (moveId) { - const move = playerPokemon.getMoveset()[cursor]; - if (move.getName().endsWith(' (N)')) { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t('battle:moveNotImplemented', { moveName: move.getName().slice(0, -4) }), null, () => { - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); - }, null, true); - return; - } - } else + if (!moveId) turnCommand.targets = [ this.fieldIndex ]; console.log(moveTargets, playerPokemon.name); if (moveTargets.targets.length <= 1 || moveTargets.multiple) @@ -1705,15 +1697,21 @@ export class CommandPhase extends FieldPhase { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; success = true; - } else if (cursor < playerPokemon.getMoveset().length) { + } + else if (cursor < playerPokemon.getMoveset().length) { const move = playerPokemon.getMoveset()[cursor]; - if (playerPokemon.summonData.disabledMove === move.moveId) { - this.scene.ui.setMode(Mode.MESSAGE); - this.scene.ui.showText(i18next.t('battle:moveDisabled', { moveName: move.getName() }), null, () => { - this.scene.ui.clearText(); - this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); - }, null, true); - } + this.scene.ui.setMode(Mode.MESSAGE); + + // Decides between a Disabled, Not Implemented, or No PP translation message + const errorMessage = + playerPokemon.summonData.disabledMove === move.moveId ? 'battle:moveDisabled' : + move.getName().endsWith(' (N)') ? 'battle:moveNotImplemented' : 'battle:moveNoPP'; + const moveName = move.getName().replace(' (N)', ''); // Trims off the indicator + + this.scene.ui.showText(i18next.t(errorMessage, { moveName: moveName }), null, () => { + this.scene.ui.clearText(); + this.scene.ui.setMode(Mode.FIGHT, this.fieldIndex); + }, null, true); } break; case Command.BALL: From 6016243caf003cac79c1072fcb45d7d6a3a98cc1 Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Sun, 5 May 2024 21:15:00 -0700 Subject: [PATCH 2/7] Lum berry should cure status and confusion if possible --- src/data/berry.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/berry.ts b/src/data/berry.ts index 1228bb54904..96b50caa932 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -114,7 +114,8 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { pokemon.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectHealText(pokemon.status.effect))); pokemon.resetStatus(); pokemon.updateInfo(); - } else if (pokemon.getTag(BattlerTagType.CONFUSED)) + } + if (pokemon.getTag(BattlerTagType.CONFUSED)) pokemon.lapseTag(BattlerTagType.CONFUSED); }; case BerryType.LIECHI: From bc8cb51dc141ac125b187cf9bfaed7ed891828d4 Mon Sep 17 00:00:00 2001 From: Jeremy B Date: Mon, 6 May 2024 00:27:56 -0500 Subject: [PATCH 3/7] Add Scrappy ability handling (#473) * partially implement scrappy * add minds eye handling also * remove unimplemented from minds eye --------- Co-authored-by: jbastyr Co-authored-by: Madmadness65 <59298170+Madmadness65@users.noreply.github.com> --- src/data/ability.ts | 47 +++++++++++++++++++++++++++++++------------- src/data/move.ts | 12 +++++------ src/field/pokemon.ts | 27 ++++++++++++++++--------- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index eec1240e1d3..c21b88832da 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -426,7 +426,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type) < 2) { + if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, attacker) < 2) { cancelled.value = true; (args[0] as Utils.NumberHolder).value = 0; return true; @@ -1794,7 +1794,7 @@ function getAnticipationCondition(): AbAttrCondition { for (let opponent of pokemon.getOpponents()) { for (let move of opponent.moveset) { // move is super effective - if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type) >= 2) { + if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent) >= 2) { return true; } // move is a OHKO @@ -1816,7 +1816,7 @@ function getAnticipationCondition(): AbAttrCondition { Type.FIRE, Type.WATER, Type.GRASS, Type.ELECTRIC, Type.PSYCHIC, Type.ICE, Type.DRAGON, Type.DARK][iv_val]; - if (pokemon.getAttackTypeEffectiveness(type) >= 2) { + if (pokemon.getAttackTypeEffectiveness(type, opponent) >= 2) { return true; } } @@ -2472,6 +2472,25 @@ export class NoFusionAbilityAbAttr extends AbAttr { } } +export class IgnoreTypeImmunityAbAttr extends AbAttr { + defenderType: Type; + allowedMoveTypes: Type[]; + + constructor(defenderType: Type, allowedMoveTypes: Type[]) { + super(true); + this.defenderType = defenderType; + this.allowedMoveTypes = allowedMoveTypes; + } + + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (this.defenderType !== (args[1] as Type)) { + return false; + } + + return this.allowedMoveTypes.some(type => type === (args[0] as Type)); + } +} + function applyAbAttrsInternal(attrType: { new(...args: any[]): TAttr }, pokemon: Pokemon, applyFunc: AbAttrApplyFunc, args: any[], isAsync: boolean = false, showAbilityInstant: boolean = false, quiet: boolean = false, passive: boolean = false): Promise { return new Promise(resolve => { @@ -2973,15 +2992,15 @@ export function initAbilities() { .attr(IgnoreOpponentStatChangesAbAttr) .ignorable(), new Ability(Abilities.TINTED_LENS, 4) - .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) <= 0.5, 2), + .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5, 2), new Ability(Abilities.FILTER, 4) - .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type) >= 2, 0.75) + .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) .ignorable(), new Ability(Abilities.SLOW_START, 4) .attr(PostSummonAddBattlerTagAbAttr, BattlerTagType.SLOW_START, 5), - new Ability(Abilities.SCRAPPY, 4) - .attr(IntimidateImmunityAbAttr) - .partial(), + new Ability(Abilities.SCRAPPY, 4) + .attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [Type.NORMAL, Type.FIGHTING]) + .attr(IntimidateImmunityAbAttr), new Ability(Abilities.STORM_DRAIN, 4) .attr(RedirectTypeMoveAbAttr, Type.WATER) .attr(TypeImmunityStatChangeAbAttr, Type.WATER, BattleStat.SPATK, 1) @@ -2990,7 +3009,7 @@ export function initAbilities() { .attr(BlockWeatherDamageAttr, WeatherType.HAIL) .attr(PostWeatherLapseHealAbAttr, 1, WeatherType.HAIL, WeatherType.SNOW), new Ability(Abilities.SOLID_ROCK, 4) - .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type) >= 2, 0.75) + .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) .ignorable(), new Ability(Abilities.SNOW_WARNING, 4) .attr(PostSummonWeatherChangeAbAttr, WeatherType.SNOW) @@ -3258,7 +3277,7 @@ export function initAbilities() { .attr(UnsuppressableAbilityAbAttr) .attr(NoFusionAbilityAbAttr), new Ability(Abilities.DISGUISE, 7) - .attr(PreDefendMovePowerToOneAbAttr, (target, user, move) => target.formIndex == 0 && target.getAttackTypeEffectiveness(move.type) > 0) + .attr(PreDefendMovePowerToOneAbAttr, (target, user, move) => target.formIndex == 0 && target.getAttackTypeEffectiveness(move.type, user) > 0) .attr(PostSummonFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) .attr(PostBattleInitFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) .attr(PostDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) @@ -3356,9 +3375,9 @@ export function initAbilities() { new Ability(Abilities.SHADOW_SHIELD, 7) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getHpRatio() === 1, 0.5), new Ability(Abilities.PRISM_ARMOR, 7) - .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type) >= 2, 0.75), + .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75), new Ability(Abilities.NEUROFORCE, 7) - .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2, 1.25), + .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), new Ability(Abilities.INTREPID_SWORD, 8) .attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true), new Ability(Abilities.DAUNTLESS_SHIELD, 8) @@ -3567,8 +3586,8 @@ export function initAbilities() { .attr(MoveAbilityBypassAbAttr, (pokemon, move: Move) => move.category === MoveCategory.STATUS) .partial(), new Ability(Abilities.MINDS_EYE, 9) - .ignorable() - .unimplemented(), + .attr(IgnoreTypeImmunityAbAttr, Type.GHOST, [Type.NORMAL, Type.FIGHTING]) + .ignorable(), // TODO: evasiveness bypass should not be ignored, but accuracy immunity should new Ability(Abilities.SUPERSWEET_SYRUP, 9) .unimplemented(), new Ability(Abilities.HOSPITALITY, 9) diff --git a/src/data/move.ts b/src/data/move.ts index 653ffc93c0a..afad81d4807 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -367,7 +367,7 @@ export class AttackMove extends Move { let attackScore = 0; - const effectiveness = target.getAttackTypeEffectiveness(this.type); + const effectiveness = target.getAttackTypeEffectiveness(this.type, user); attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2; if (attackScore) { if (this.category === MoveCategory.PHYSICAL) { @@ -738,7 +738,7 @@ export class SacrificialAttr extends MoveEffectAttr { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { if (user.isBoss()) return -20; - return Math.ceil(((1 - user.getHpRatio()) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type) - 0.5)); + return Math.ceil(((1 - user.getHpRatio()) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type, user) - 0.5)); } } @@ -776,7 +776,7 @@ export class HalfSacrificialAttr extends MoveEffectAttr { getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { if (user.isBoss()) return -10; - return Math.ceil(((1 - user.getHpRatio()/2) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type) - 0.5)); + return Math.ceil(((1 - user.getHpRatio()/2) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type, user) - 0.5)); } } @@ -2476,7 +2476,7 @@ export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultipli export class FlyingTypeMultiplierAttr extends VariableMoveTypeMultiplierAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const multiplier = args[0] as Utils.NumberHolder; - multiplier.value *= target.getAttackTypeEffectiveness(Type.FLYING); + multiplier.value *= target.getAttackTypeEffectiveness(Type.FLYING, user); return true; } } @@ -6453,9 +6453,9 @@ export function initMoves() { new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) .attr(TargetHalfHpDamageAttr), new AttackMove(Moves.COLLISION_COURSE, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1), + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 5461/4096 : 1), new AttackMove(Moves.ELECTRO_DRIFT, Type.ELECTRIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 9) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2 ? 5461/4096 : 1) .makesContact(), new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9) .unimplemented(), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 1237f7bb55f..36dc4265efc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagType } from "../data/enums/arena-tag-type"; import { Biome } from "../data/enums/biome"; -import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr } from '../data/ability'; +import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr } from '../data/ability'; import { Abilities } from "#app/data/enums/abilities"; import PokemonData from '../system/pokemon-data'; import Battle, { BattlerIndex } from '../battle'; @@ -880,7 +880,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getAttackMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier { const typeless = !!move.getMove().getAttrs(TypelessAttr).length; - const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type)); + const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type, source)); const cancelled = new Utils.BooleanHolder(false); if (!typeless) applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true); @@ -889,11 +889,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (!cancelled.value ? typeMultiplier.value : 0) as TypeDamageMultiplier; } - getAttackTypeEffectiveness(moveType: Type): TypeDamageMultiplier { + getAttackTypeEffectiveness(moveType: Type, source?: Pokemon): TypeDamageMultiplier { if (moveType === Type.STELLAR) return this.isTerastallized() ? 2 : 1; const types = this.getTypes(true, true); - let multiplier = getTypeDamageMultiplier(moveType, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(moveType, types[1]) : 1) * (types.length > 2 ? getTypeDamageMultiplier(moveType, types[2]) : 1) as TypeDamageMultiplier; + + const ignorableImmunities = source?.getAbility()?.getAttrs(IgnoreTypeImmunityAbAttr) || []; + const cancelled = new Utils.BooleanHolder(false); + + let multiplier = types.map(defType => + ignorableImmunities.some(attr => attr.apply(source, false, cancelled, [moveType, defType])) + ? 1 + : getTypeDamageMultiplier(moveType, defType) + ).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier; + // Handle strong winds lowering effectiveness of types super effective against pure flying if (this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && multiplier >= 2 && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) multiplier /= 2; @@ -904,12 +913,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const types = this.getTypes(true); const enemyTypes = pokemon.getTypes(true, true); const outspeed = (this.isActive(true) ? this.getBattleStat(Stat.SPD, pokemon) : this.getStat(Stat.SPD)) <= pokemon.getBattleStat(Stat.SPD, this); - let atkScore = pokemon.getAttackTypeEffectiveness(types[0]) * (outspeed ? 1.25 : 1); - let defScore = 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[0]), 0.25); + let atkScore = pokemon.getAttackTypeEffectiveness(types[0], this) * (outspeed ? 1.25 : 1); + let defScore = 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[0], pokemon), 0.25); if (types.length > 1) - atkScore *= pokemon.getAttackTypeEffectiveness(types[1]); + atkScore *= pokemon.getAttackTypeEffectiveness(types[1], this); if (enemyTypes.length > 1) - defScore *= (1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1]), 0.25)); + defScore *= (1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], pokemon), 0.25)); let hpDiffRatio = this.getHpRatio() + (1 - pokemon.getHpRatio()); if (outspeed) hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1); @@ -1262,7 +1271,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const cancelled = new Utils.BooleanHolder(false); const typeless = !!move.getAttrs(TypelessAttr).length; const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => types.includes((attr as StatusMoveTypeImmunityAttr).immuneType))) - ? this.getAttackTypeEffectiveness(type) + ? this.getAttackTypeEffectiveness(type, source) : 1); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); if (typeless) From a45da8a38284424074c45d5e53c066638567f2de Mon Sep 17 00:00:00 2001 From: Madmadness65 Date: Mon, 6 May 2024 00:31:28 -0500 Subject: [PATCH 4/7] Quick egg move fixes --- src/data/egg-moves.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/egg-moves.ts b/src/data/egg-moves.ts index 6dd26a7d547..0452297797d 100644 --- a/src/data/egg-moves.ts +++ b/src/data/egg-moves.ts @@ -137,7 +137,7 @@ export const speciesEggMoves = { [Species.SEEDOT]: [ Moves.SWORDS_DANCE, Moves.GRASSY_GLIDE, Moves.KOWTOW_CLEAVE, Moves.IVY_CUDGEL ], [Species.TAILLOW]: [ Moves.SWORDS_DANCE, Moves.FACADE, Moves.DRILL_RUN, Moves.EXTREME_SPEED ], [Species.WINGULL]: [ Moves.THUNDER, Moves.FLIP_TURN, Moves.DEFOG, Moves.STEAM_ERUPTION ], - [Species.RALTS]: [ Moves.BOOMBURST, Moves.BITTER_BLADE, Moves.PSYBLADE, Moves.VICTORY_DANCE ], + [Species.RALTS]: [ Moves.BOOMBURST, Moves.BITTER_BLADE, Moves.QUIVER_DANCE, Moves.VICTORY_DANCE ], [Species.SURSKIT]: [ Moves.ROOST, Moves.FIERY_DANCE, Moves.STICKY_WEB, Moves.BLEAKWIND_STORM ], [Species.SHROOMISH]: [ Moves.ACCELEROCK, Moves.TRAILBLAZE, Moves.STORM_THROW, Moves.SAPPY_SEED ], [Species.SLAKOTH]: [ Moves.FACADE, Moves.JUMP_KICK, Moves.KNOCK_OFF, Moves.SKILL_SWAP ], @@ -346,7 +346,7 @@ export const speciesEggMoves = { [Species.PANCHAM]: [ Moves.DRAIN_PUNCH, Moves.FAKE_OUT, Moves.BULLET_PUNCH, Moves.WICKED_BLOW ], [Species.FURFROU]: [ Moves.TIDY_UP, Moves.CRUNCH, Moves.COVET, Moves.MULTI_ATTACK ], [Species.ESPURR]: [ Moves.GLARE, Moves.MOONBLAST, Moves.FLAMETHROWER, Moves.PSYSTRIKE ], - [Species.HONEDGE]: [ Moves.RECOVER, Moves.POLTERGEIST, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], + [Species.HONEDGE]: [ Moves.TACHYON_CUTTER, Moves.POLTERGEIST, Moves.BITTER_BLADE, Moves.BEHEMOTH_BLADE ], [Species.SPRITZEE]: [ Moves.TRICK_ROOM, Moves.FOUL_PLAY, Moves.WISH, Moves.REVIVAL_BLESSING ], [Species.SWIRLIX]: [ Moves.BELLY_DRUM, Moves.SUCKER_PUNCH, Moves.SPIRIT_BREAK, Moves.SIZZLY_SLIDE ], [Species.INKAY]: [ Moves.POWER_TRIP, Moves.STORED_POWER, Moves.RECOVER, Moves.PSYCHO_BOOST ], @@ -485,7 +485,7 @@ export const speciesEggMoves = { [Species.SMOLIV]: [ Moves.STRENGTH_SAP, Moves.EARTH_POWER, Moves.CALM_MIND, Moves.BOOMBURST ], [Species.SQUAWKABILLY]: [ Moves.PARTING_SHOT, Moves.BULK_UP, Moves.FLARE_BLITZ, Moves.HEAD_CHARGE ], [Species.NACLI]: [ Moves.BODY_PRESS, Moves.SPIKES, Moves.CURSE, Moves.DIAMOND_STORM ], - [Species.CHARCADET]: [ Moves.SACRED_SWORD, Moves.PHOTON_GEYSER, Moves.RECOVER, Moves.SPECTRAL_THIEF ], + [Species.CHARCADET]: [ Moves.SACRED_SWORD, Moves.PHOTON_GEYSER, Moves.MOONBLAST, Moves.SPECTRAL_THIEF ], [Species.TADBULB]: [ Moves.PARABOLIC_CHARGE, Moves.SCALD, Moves.EARTH_POWER, Moves.ELECTRO_SHOT ], [Species.WATTREL]: [ Moves.NASTY_PLOT, Moves.TAILWIND, Moves.HEAT_WAVE, Moves.AEROBLAST ], [Species.MASCHIFF]: [ Moves.PARTING_SHOT, Moves.KNOCK_OFF, Moves.NUZZLE, Moves.COLLISION_COURSE ], From 29b80dbed85e02e37a97123180d5fad302edd78f Mon Sep 17 00:00:00 2001 From: gericocross <32669590+gericocross@users.noreply.github.com> Date: Mon, 6 May 2024 14:44:05 +0200 Subject: [PATCH 5/7] Fixed Grafaiai animation (#547) * Longer descriptions don't stuck shorter ones anymore * Fixed Grafaiai animation --- public/images/pokemon/exp/945.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/images/pokemon/exp/945.json b/public/images/pokemon/exp/945.json index 9456e435d28..31ba1c93090 100644 --- a/public/images/pokemon/exp/945.json +++ b/public/images/pokemon/exp/945.json @@ -146,14 +146,14 @@ "spriteSourceSize": { "x": 0, "y": 0, - "w": 3, - "h": 3 + "w": 66, + "h": 55 }, "frame": { - "x": 132, - "y": 0, - "w": 3, - "h": 3 + "x": 0, + "y": 112, + "w": 66, + "h": 55 } }, { From f92b563baaa9baecde58c01654ad2bc2ab1899c5 Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Sun, 5 May 2024 22:12:10 -0700 Subject: [PATCH 6/7] Hunger switch shouldn't trigger if terastallized --- src/data/ability.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index c21b88832da..07aecbdcc91 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -3453,7 +3453,8 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) - .attr(NoFusionAbilityAbAttr), + .attr(NoFusionAbilityAbAttr) + .condition((pokemon) => !pokemon.isTerastallized()), new Ability(Abilities.QUICK_DRAW, 8) .unimplemented(), new Ability(Abilities.UNSEEN_FIST, 8) From e3c1d08b378aa21417650223b85f5342959da578 Mon Sep 17 00:00:00 2001 From: Lugiad Date: Mon, 6 May 2024 11:50:50 +0200 Subject: [PATCH 7/7] Update French battle.ts + typo --- src/locales/fr/battle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index 2167ddd31ef..56ab692159d 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -23,7 +23,7 @@ export const battle: SimpleTranslationEntries = { "attackHitsCount": `Touché {{count}} fois !`, "expGain": "{{pokemonName}} gagne\n{{exp}} Points d’Exp !", "levelUp": "{{pokemonName}} monte au\nN. {{level}} !", - "learnMove": "{{pokemonName}} apprend \n{{moveName}} !", + "learnMove": "{{pokemonName}} apprend\n{{moveName}} !", "learnMovePrompt": "{{pokemonName}} veut apprendre\n{{moveName}}.", "learnMoveLimitReached": "Cependant, {{pokemonName}} connait\ndéjà quatre capacités.", "learnMoveReplaceQuestion": "Voulez-vous oublier une capacité\net la remplacer par {{moveName}} ?", @@ -33,7 +33,7 @@ export const battle: SimpleTranslationEntries = { "learnMoveForgetSuccess": "{{pokemonName}} oublie comment\nutiliser {{moveName}}.", "levelCapUp": "La limite de niveau\na été augmentée à {{levelCap}} !", "moveNotImplemented": "{{moveName}} n’est pas encore implémenté et ne peut pas être sélectionné.", - "moveNoPP": "There's no PP left for\nthis move!", + "moveNoPP": "Il n’y a plus de PP pour\ncette capacité !", "moveDisabled": "{{moveName}} est sous entrave !", "noPokeballForce": "Une force mystérieuse\nempêche l’utilisation des Poké Balls.", "noPokeballTrainer": "Le Dresseur détourne la Ball\nVoler, c’est mal !",