diff --git a/src/data/move.ts b/src/data/move.ts index 54f81a48055..ee346e44167 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5,7 +5,7 @@ import { EncoreTag, HelpingHandTag, SemiInvulnerableTag, TypeBoostTag } from "./ import { getPokemonMessage, getPokemonNameWithAffix } from "../messages"; import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon"; import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects} from "./status-effect"; -import { Type } from "./type"; +import { getTypeResistances, Type } from "./type"; import { Constructor } from "#app/utils"; import * as Utils from "../utils"; import { WeatherType } from "./weather"; @@ -4804,10 +4804,8 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { } const firstMoveType = target.getMoveset()[0].getMove().type; - user.summonData.types = [ firstMoveType ]; - - user.scene.queueMessage(getPokemonMessage(user, ` transformed\ninto to the ${Utils.toReadableString(Type[firstMoveType])} type!`)); + user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: Utils.toReadableString(Type[firstMoveType])})); return true; } @@ -5651,6 +5649,62 @@ export class hitsSameTypeAttr extends VariableMoveTypeMultiplierAttr { } } +/** + * Attribute used for Conversion 2, to convert the user's type to a random type that resists the target's last used move. + * Fails if the user already has ALL types that resist the target's last used move. + * Fails if the opponent has not used a move yet + * Fails if the type is unknown or stellar + * + * TODO: + * If a move has its type changed (e.g. {@linkcode Moves.HIDDEN_POWER}), it will check the new type. + */ +export class ResistLastMoveTypeAttr extends MoveEffectAttr { + constructor() { + super(true); + } + /** + * User changes its type to a random type that resists the target's last used move + * @param {Pokemon} user Pokemon that used the move and will change types + * @param {Pokemon} target Opposing pokemon that recently used a move + * @param {Move} move Move being used + * @param {any[]} args Unused + * @returns {boolean} true if the function succeeds + */ + apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (!super.apply(user, target, move, args)) { + return false; + } + + const [targetMove] = target.getLastXMoves(1); // target's most recent move + if (!targetMove) { + return false; + } + + const moveData = allMoves[targetMove.move]; + if (moveData.type === Type.STELLAR || moveData.type === Type.UNKNOWN) { + return false; + } + const userTypes = user.getTypes(); + const validTypes = getTypeResistances(moveData.type).filter(t => !userTypes.includes(t)); // valid types are ones that are not already the user's types + if (!validTypes.length) { + return false; + } + const type = validTypes[user.randSeedInt(validTypes.length)]; + user.summonData.types = [ type ]; + user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: Utils.toReadableString(Type[type])})); + user.updateInfo(); + + return true; + } + + getCondition(): MoveConditionFunc { + return (user, target, move) => { + const moveHistory = target.getLastXMoves(); + return !!moveHistory.length; + }; + } +} + const unknownTypeCondition: MoveConditionFunc = (user, target, move) => !user.getTypes().includes(Type.UNKNOWN); export type MoveTargetSet = { @@ -6198,7 +6252,8 @@ export function initMoves() { new AttackMove(Moves.FLAIL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, 0, 2) .attr(LowHpPowerAttr), new StatusMove(Moves.CONVERSION_2, Type.NORMAL, -1, 30, -1, 0, 2) - .unimplemented(), + .attr(ResistLastMoveTypeAttr) + .partial(), // Checks the move's original typing and not if its type is changed through some other means new AttackMove(Moves.AEROBLAST, Type.FLYING, MoveCategory.SPECIAL, 100, 95, 5, -1, 0, 2) .attr(HighCritAttr), new StatusMove(Moves.COTTON_SPORE, Type.GRASS, 100, 40, -1, 0, 2) diff --git a/src/data/type.ts b/src/data/type.ts index c92416afca9..cf3bc85ab48 100644 --- a/src/data/type.ts +++ b/src/data/type.ts @@ -501,6 +501,55 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer): } } +/** + * Retrieve the types resisting a given type + * @returns An array populated with Types, or an empty array if no resistances exist (Unknown or Stellar type) + */ +export function getTypeResistances(type: integer): Type[] { + switch (type) { + case Type.NORMAL: + return [Type.ROCK, Type.STEEL, Type.GHOST]; + case Type.FIGHTING: + return [Type.FLYING, Type.POISON, Type.BUG, Type.PSYCHIC, Type.FAIRY, Type.GHOST]; + case Type.FLYING: + return [Type.ROCK, Type.ELECTRIC, Type.STEEL]; + case Type.POISON: + return [Type.POISON, Type.GROUND, Type.ROCK, Type.GHOST, Type.STEEL]; + case Type.GROUND: + return [Type.BUG, Type.GRASS, Type.FLYING]; + case Type.ROCK: + return [Type.FIGHTING, Type.GROUND, Type.STEEL]; + case Type.BUG: + return [Type.FIGHTING, Type.FLYING, Type.POISON, Type.GHOST, Type.STEEL, Type.FIRE, Type.FAIRY]; + case Type.GHOST: + return [Type.DARK, Type.NORMAL]; + case Type.STEEL: + return [Type.STEEL, Type.FIRE, Type.WATER, Type.ELECTRIC]; + case Type.FIRE: + return [Type.ROCK, Type.FIRE, Type.WATER, Type.DRAGON]; + case Type.WATER: + return [Type.WATER, Type.GRASS, Type.DRAGON]; + case Type.GRASS: + return [Type.FLYING, Type.POISON, Type.BUG, Type.STEEL, Type.FIRE, Type.GRASS, Type.DRAGON]; + case Type.ELECTRIC: + return [Type.GRASS, Type.ELECTRIC, Type.DRAGON, Type.GROUND]; + case Type.PSYCHIC: + return [Type.STEEL, Type.PSYCHIC]; + case Type.ICE: + return [Type.STEEL, Type.FIRE, Type.WATER, Type.ICE]; + case Type.DRAGON: + return [Type.STEEL, Type.FAIRY]; + case Type.DARK: + return [Type.FIGHTING, Type.DARK, Type.FAIRY]; + case Type.FAIRY: + return [Type.POISON, Type.STEEL, Type.FIRE]; + case Type.UNKNOWN: + case Type.STELLAR: + default: + return []; + } +} + /** * Retrieve the color corresponding to a specific damage multiplier * @returns A color or undefined if the default color should be used diff --git a/src/locales/de/battle.ts b/src/locales/de/battle.ts index 29f478f1856..74ddede5c89 100644 --- a/src/locales/de/battle.ts +++ b/src/locales/de/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "{{stats}} von {{pokemonNameWithAffix}} sinken drastisch!", "statWontGoAnyLower_one": "{{stats}} von {{pokemonNameWithAffix}} kann nicht weiter sinken!", "statWontGoAnyLower_other": "{{stats}} von {{pokemonNameWithAffix}} können nicht weiter sinken!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "ppReduced": "{{moveName}} von {{targetName}} wird um {{reduction}} AP reduziert!", "retryBattle": "Möchtest du vom Beginn des Kampfes neustarten?", "unlockedSomething": "{{unlockedThing}} wurde freigeschaltet.", diff --git a/src/locales/en/battle.ts b/src/locales/en/battle.ts index 5752633779e..d7a1f6ccf90 100644 --- a/src/locales/en/battle.ts +++ b/src/locales/en/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "retryBattle": "Would you like to retry from the start of the battle?", "unlockedSomething": "{{unlockedThing}}\nhas been unlocked.", "congratulations": "Congratulations!", diff --git a/src/locales/es/battle.ts b/src/locales/es/battle.ts index ef4bd4da282..d0efa369b8d 100644 --- a/src/locales/es/battle.ts +++ b/src/locales/es/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han bajado muchísimo!", "statWontGoAnyLower_one": "¡El {{stats}} de {{pokemonNameWithAffix}} no puede bajar más!", "statWontGoAnyLower_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} no pueden bajar más!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "retryBattle": "Would you like to retry from the start of the battle?", "unlockedSomething": "{{unlockedThing}}\nhas been unlocked.", "congratulations": "Congratulations!", diff --git a/src/locales/fr/battle.ts b/src/locales/fr/battle.ts index 4523993017b..0ce9d73c70e 100644 --- a/src/locales/fr/battle.ts +++ b/src/locales/fr/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "{{stats}} de {{pokemonNameWithAffix}}\nbaissent énormément !", "statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}}\nne peut plus baisser !", "statWontGoAnyLower_other": "{{stats}} de {{pokemonNameWithAffix}}\nne peuvent plus baisser !", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "ppReduced": "Les PP de la capacité {{moveName}}\nde {{targetName}} baissent de {{reduction}} !", "retryBattle": "Voulez-vous réessayer depuis le début du combat ?", "unlockedSomething": "{{unlockedThing}}\na été débloqué.", diff --git a/src/locales/it/battle.ts b/src/locales/it/battle.ts index e77faca10aa..7ae8ec9f397 100644 --- a/src/locales/it/battle.ts +++ b/src/locales/it/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}'s {{stats}} non può diminuire più di così!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "retryBattle": "Vuoi riprovare dall'inizio della lotta?", "unlockedSomething": "{{unlockedThing}}\nè stato/a sbloccato/a.", "congratulations": "Congratulazioni!", diff --git a/src/locales/ko/battle.ts b/src/locales/ko/battle.ts index be9fb05cebe..60bbc067062 100644 --- a/src/locales/ko/battle.ts +++ b/src/locales/ko/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "{{pokemonNameWithAffix}}의\n{{stats}}[[가]] 매우 크게 떨어졌다!", "statWontGoAnyLower_one": "{{pokemonNameWithAffix}}의\n{{stats}}[[는]] 더 떨어지지 않는다!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}의\n{{stats}}[[는]] 더 떨어지지 않는다!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "retryBattle": "이 배틀의 처음부터 재도전하시겠습니까?", "unlockedSomething": "{{unlockedThing}}[[가]]\n해금되었다.", "congratulations": "축하합니다!", diff --git a/src/locales/pt_BR/battle.ts b/src/locales/pt_BR/battle.ts index 39433f65556..9f6db75ca79 100644 --- a/src/locales/pt_BR/battle.ts +++ b/src/locales/pt_BR/battle.ts @@ -89,6 +89,7 @@ export const battle: SimpleTranslationEntries = { "statSeverelyFell_other": "{{stats}} de {{pokemonNameWithAffix}} diminuíram severamente!", "statWontGoAnyLower_one": "{{stats}} de {{pokemonNameWithAffix}} não vai mais diminuir!", "statWontGoAnyLower_other": "{{stats}} de {{pokemonNameWithAffix}} não vão mais diminuir!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "ppReduced": "O PP do movimento {{moveName}} de\n{{targetName}} foi reduzido em {{reduction}}!", "retryBattle": "Você gostaria de tentar novamente desde o início da batalha?", "unlockedSomething": "{{unlockedThing}}\nfoi desbloqueado.", diff --git a/src/locales/zh_CN/battle.ts b/src/locales/zh_CN/battle.ts index d541eed5765..af9f7ac6cbb 100644 --- a/src/locales/zh_CN/battle.ts +++ b/src/locales/zh_CN/battle.ts @@ -81,6 +81,7 @@ export const battle: SimpleTranslationEntries = { "statHarshlyFell_other": "{{pokemonNameWithAffix}}的 {{stats}}大幅降低了!", "statSeverelyFell_other": "{{pokemonNameWithAffix}}的 {{stats}}极大幅降低了!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}的 {{stats}}已经无法再降低了!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "ppReduced": "降低了{{targetName}}的\n{{moveName}}的PP{{reduction}}点!", "retryBattle": "你要从对战开始时重试么?", "unlockedSomething": "{{unlockedThing}}\n已解锁。", diff --git a/src/locales/zh_TW/battle.ts b/src/locales/zh_TW/battle.ts index 15ffbcb0536..75457b34c07 100644 --- a/src/locales/zh_TW/battle.ts +++ b/src/locales/zh_TW/battle.ts @@ -78,6 +78,7 @@ export const battle: SimpleTranslationEntries = { "statHarshlyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} harshly fell!", "statSeverelyFell_other": "{{pokemonNameWithAffix}}'s {{stats}} severely fell!", "statWontGoAnyLower_other": "{{pokemonNameWithAffix}}'s {{stats}} won't go any lower!", + "transformedIntoType": "{{pokemonName}} transformed\ninto the {{type}} type!", "ppReduced": "降低了 {{targetName}} 的\n{{moveName}} 的PP{{reduction}}點!", "retryBattle": "Would you like to retry from the start of the battle?", "unlockedSomething": "{{unlockedThing}}\nhas been unlocked.",