[Move] Implements Conversion 2 (#2943)

* Creates function to get type resistances, implements conversion 2

* Removes unimplemented tag, adds condition for move history to exist

* Cleans up type selection, creates i18n entries for typeChanged

* Uses typeChanged i18n in Conversion move

* More detailed docs, early return with stellar/unknown type

* Adds note that it wont track type-changing moves properly

* Rephrases doc description, adds partial since it can't track type-changing moves

* Updates localization, removes typeChanged entry to use move-trigger entry

* Missed locale de entry in last commit

* Adds comment for reason of .partial()

* Fixes localization error due to revert, removes improper merge conflict from prior commit
This commit is contained in:
schmidtc1 2024-07-15 13:12:35 -04:00 committed by GitHub
parent 21f2e6981c
commit b215de1628
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 118 additions and 5 deletions

View File

@ -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)

View File

@ -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

View File

@ -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.",

View File

@ -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!",

View File

@ -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!",

View File

@ -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é.",

View File

@ -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!",

View File

@ -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": "축하합니다!",

View File

@ -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.",

View File

@ -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已解锁。",

View File

@ -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.",