Merge branch 'beta' into restrictionMoves

This commit is contained in:
Mumble 2024-09-23 12:37:09 -07:00 committed by GitHub
commit 145167f3a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 2003 additions and 1134 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -4317,6 +4317,10 @@ export class ReduceBerryUseThresholdAbAttr extends AbAttr {
}
}
/**
* Ability attribute used for abilites that change the ability owner's weight
* Used for Heavy Metal (doubling weight) and Light Metal (halving weight)
*/
export class WeightMultiplierAbAttr extends AbAttr {
private multiplier: integer;

View File

@ -2299,6 +2299,36 @@ export class TarShotTag extends BattlerTag {
}
}
/**
* Battler Tag that keeps track of how many times the user has Autotomized
* Each count of Autotomization reduces the weight by 100kg
*/
export class AutotomizedTag extends BattlerTag {
public autotomizeCount: number = 0;
constructor(sourceMove: Moves = Moves.AUTOTOMIZE) {
super(BattlerTagType.AUTOTOMIZED, BattlerTagLapseType.CUSTOM, 1, sourceMove);
}
/**
* Adds an autotomize count to the Pokemon. Each stack reduces weight by 100kg
* If the Pokemon is over 0.1kg it also displays a message.
* @param pokemon The Pokemon that is being autotomized
*/
onAdd(pokemon: Pokemon): void {
const minWeight = 0.1;
if (pokemon.getWeight() > minWeight) {
pokemon.scene.queueMessage(i18next.t("battlerTags:autotomizeOnAdd", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)
}));
}
this.autotomizeCount += 1;
}
onOverlap(pokemon: Pokemon): void {
this.onAdd(pokemon);
}
}
export class SubstituteTag extends BattlerTag {
/** The substitute's remaining HP. If HP is depleted, the Substitute fades. */
public hp: number;
@ -2680,6 +2710,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
return new GorillaTacticsTag();
case BattlerTagType.SUBSTITUTE:
return new SubstituteTag(sourceMove, sourceId);
case BattlerTagType.AUTOTOMIZED:
return new AutotomizedTag();
case BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON:
return new MysteryEncounterPostSummonTag();
case BattlerTagType.HEAL_BLOCK:

View File

@ -172,11 +172,9 @@ export abstract class Challenge {
* @param overrideValue {@link integer} The value to check for. If undefined, gets the current value.
* @returns {@link string} The localised name for the current value.
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return i18next.t(`challenges:${this.geti18nKey()}.value.${this.value}`);
getValue(overrideValue?: number): string {
const value = overrideValue ?? this.value;
return i18next.t(`challenges:${this.geti18nKey()}.value.${value}`);
}
/**
@ -184,11 +182,9 @@ export abstract class Challenge {
* @param overrideValue {@link integer} The value to check for. If undefined, gets the current value.
* @returns {@link string} The localised description for the current value.
*/
getDescription(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
return `${i18next.t([`challenges:${this.geti18nKey()}.desc.${this.value}`, `challenges:${this.geti18nKey()}.desc`])}`;
getDescription(overrideValue?: number): string {
const value = overrideValue ?? this.value;
return `${i18next.t([`challenges:${this.geti18nKey()}.desc.${value}`, `challenges:${this.geti18nKey()}.desc`])}`;
}
/**
@ -511,14 +507,12 @@ export class SingleGenerationChallenge extends Challenge {
* @param {value} overrideValue The value to check for. If undefined, gets the current value.
* @returns {string} The localised name for the current value.
*/
getValue(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
if (this.value === 0) {
getValue(overrideValue?: number): string {
const value = overrideValue ?? this.value;
if (value === 0) {
return i18next.t("settings:off");
}
return i18next.t(`starterSelectUiHandler:gen${this.value}`);
return i18next.t(`starterSelectUiHandler:gen${value}`);
}
/**
@ -526,14 +520,12 @@ export class SingleGenerationChallenge extends Challenge {
* @param {value} overrideValue The value to check for. If undefined, gets the current value.
* @returns {string} The localised description for the current value.
*/
getDescription(overrideValue?: integer): string {
if (overrideValue === undefined) {
overrideValue = this.value;
}
if (this.value === 0) {
getDescription(overrideValue?: number): string {
const value = overrideValue ?? this.value;
if (value === 0) {
return i18next.t("challenges:singleGeneration.desc_default");
}
return i18next.t("challenges:singleGeneration.desc", { gen: i18next.t(`challenges:singleGeneration.gen_${this.value}`) });
return i18next.t("challenges:singleGeneration.desc", { gen: i18next.t(`challenges:singleGeneration.gen_${value}`) });
}

View File

@ -5174,31 +5174,29 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
}
export class ForceSwitchOutAttr extends MoveEffectAttr {
private user: boolean;
private batonPass: boolean;
constructor(user?: boolean, batonPass?: boolean) {
constructor(
private selfSwitch: boolean = false,
private batonPass: boolean = false
) {
super(false, MoveEffectTrigger.POST_APPLY, false, true);
this.user = !!user;
this.batonPass = !!batonPass;
}
isBatonPass() {
return this.batonPass;
}
// TODO: Why is this a Promise?
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
return new Promise(resolve => {
// Check if the move category is not STATUS or if the switch out condition is not met
if (!this.getSwitchOutCondition()(user, target, move)) {
return resolve(false);
}
// Move the switch out logic inside the conditional block
// This ensures that the switch out only happens when the conditions are met
const switchOutTarget = this.user ? user : target;
if (switchOutTarget instanceof PlayerPokemon) {
// Move the switch out logic inside the conditional block
// This ensures that the switch out only happens when the conditions are met
const switchOutTarget = this.selfSwitch ? user : target;
if (switchOutTarget instanceof PlayerPokemon) {
switchOutTarget.leaveField(!this.batonPass);
if (switchOutTarget.hp > 0) {
@ -5207,41 +5205,43 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
} else {
resolve(false);
}
return;
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) {
// Switch out logic for trainer battles
return;
} else if (user.scene.currentBattle.battleType !== BattleType.WILD) {
// Switch out logic for trainer battles
switchOutTarget.leaveField(!this.batonPass);
if (switchOutTarget.hp > 0) {
// for opponent switching out
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), (user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), false, this.batonPass, false), MoveEndPhase);
if (switchOutTarget.hp > 0) {
// for opponent switching out
user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(),
(user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
false, this.batonPass, false), MoveEndPhase);
}
} else {
// Switch out logic for everything else (eg: WILD battles)
switchOutTarget.leaveField(false);
} else {
// Switch out logic for everything else (eg: WILD battles)
switchOutTarget.leaveField(false);
if (switchOutTarget.hp) {
user.scene.queueMessage(i18next.t("moveTriggers:fled", {pokemonName: getPokemonNameWithAffix(switchOutTarget)}), null, true, 500);
if (switchOutTarget.hp) {
user.scene.queueMessage(i18next.t("moveTriggers:fled", {pokemonName: getPokemonNameWithAffix(switchOutTarget)}), null, true, 500);
// in double battles redirect potential moves off fled pokemon
if (switchOutTarget.scene.currentBattle.double) {
const allyPokemon = switchOutTarget.getAlly();
switchOutTarget.scene.redirectPokemonMoves(switchOutTarget, allyPokemon);
}
}
}
if (!switchOutTarget.getAlly()?.isActive(true)) {
user.scene.clearEnemyHeldItemModifiers();
if (!switchOutTarget.getAlly()?.isActive(true)) {
user.scene.clearEnemyHeldItemModifiers();
if (switchOutTarget.hp) {
user.scene.pushPhase(new BattleEndPhase(user.scene));
user.scene.pushPhase(new NewBattlePhase(user.scene));
}
}
}
if (switchOutTarget.hp) {
user.scene.pushPhase(new BattleEndPhase(user.scene));
user.scene.pushPhase(new NewBattlePhase(user.scene));
}
}
}
resolve(true);
});
resolve(true);
});
}
getCondition(): MoveConditionFunc {
@ -5256,29 +5256,33 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
getSwitchOutCondition(): MoveConditionFunc {
return (user, target, move) => {
const switchOutTarget = (this.user ? user : target);
const switchOutTarget = (this.selfSwitch ? user : target);
const player = switchOutTarget instanceof PlayerPokemon;
if (!this.user && move.hitsSubstitute(user, target)) {
return false;
if (!this.selfSwitch) {
if (move.hitsSubstitute(user, target)) {
return false;
}
const blockedByAbility = new Utils.BooleanHolder(false);
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
return !blockedByAbility.value;
}
if (!this.user && move.category === MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr))) {
return false;
}
if (!player && !user.scene.currentBattle.battleType) {
if (!player && user.scene.currentBattle.battleType === BattleType.WILD) {
if (this.batonPass) {
return false;
}
// Don't allow wild opponents to flee on the boss stage since it can ruin a run early on
if (!(user.scene.currentBattle.waveIndex % 10)) {
if (user.scene.currentBattle.waveIndex % 10 === 0) {
return false;
}
}
const party = player ? user.scene.getParty() : user.scene.getEnemyParty();
return (!player && !user.scene.currentBattle.battleType) || party.filter(p => p.isAllowedInBattle() && (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
return (!player && !user.scene.currentBattle.battleType)
|| party.filter(p => p.isAllowedInBattle()
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
};
}
@ -5286,8 +5290,8 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
if (!user.scene.getEnemyParty().find(p => p.isActive() && !p.isOnField())) {
return -20;
}
let ret = this.user ? Math.floor((1 - user.getHpRatio()) * 20) : super.getUserBenefitScore(user, target, move);
if (this.user && this.batonPass) {
let ret = this.selfSwitch ? Math.floor((1 - user.getHpRatio()) * 20) : super.getUserBenefitScore(user, target, move);
if (this.selfSwitch && this.batonPass) {
const statStageTotal = user.getStatStages().reduce((s: integer, total: integer) => total += s, 0);
ret = ret / 2 + (Phaser.Tweens.Builders.GetEaseFunction("Sine.easeOut")(Math.min(Math.abs(statStageTotal), 10) / 10) * (statStageTotal >= 0 ? 10 : -10));
}
@ -8105,7 +8109,7 @@ export function initMoves() {
.attr(MovePowerMultiplierAttr, (user, target, move) => target.status && (target.status.effect === StatusEffect.POISON || target.status.effect === StatusEffect.TOXIC) ? 2 : 1),
new SelfStatusMove(Moves.AUTOTOMIZE, Type.STEEL, -1, 15, -1, 0, 5)
.attr(StatStageChangeAttr, [ Stat.SPD ], 2, true)
.partial(),
.attr(AddBattlerTagAttr, BattlerTagType.AUTOTOMIZED, true),
new SelfStatusMove(Moves.RAGE_POWDER, Type.BUG, -1, 20, -1, 2, 5)
.powderMove()
.attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true),

View File

@ -339,7 +339,7 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig:
loadEnemyAssets.push(enemyPokemon.loadAssets());
console.log(enemyPokemon.name, enemyPokemon.species.speciesId, enemyPokemon.stats);
console.log(`Pokemon: ${enemyPokemon.name}`, `Species ID: ${enemyPokemon.species.speciesId}`, `Stats: ${enemyPokemon.stats}`, `Ability: ${enemyPokemon.getAbility().name}`, `Passive Ability: ${enemyPokemon.getPassiveAbility().name}`);
});
scene.pushPhase(new MysteryEncounterBattlePhase(scene, partyConfig.disableSwitch));

View File

@ -2067,7 +2067,7 @@ export const trainerConfigs: TrainerConfigs = {
p.setBoss(true, 2);
p.generateAndPopulateMoveset();
p.pokeball = PokeballType.ULTRA_BALL;
p.formIndex = Utils.randSeedInt(5, 1); // Shock, Burn, Chill, or Douse Drive
p.formIndex = Utils.randSeedInt(4, 1); // Shock, Burn, Chill, or Douse Drive
}))
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BASCULEGION, Species.JELLICENT ], TrainerSlot.TRAINER, true, p => {
p.generateAndPopulateMoveset();

View File

@ -79,6 +79,7 @@ export enum BattlerTagType {
TAR_SHOT = "TAR_SHOT",
BURNED_UP = "BURNED_UP",
DOUBLE_SHOCKED = "DOUBLE_SHOCKED",
AUTOTOMIZED = "AUTOTOMIZED",
MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON",
HEAL_BLOCK = "HEAL_BLOCK",
TORMENT = "TORMENT",

View File

@ -17,7 +17,7 @@ import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
import { Status, StatusEffect, getRandomStatus } from "../data/status-effect";
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "../data/pokemon-evolutions";
import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "../data/tms";
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag } from "../data/battler-tags";
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag } from "../data/battler-tags";
import { WeatherType } from "../data/weather";
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "../data/arena-tag";
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs } from "../data/ability";
@ -1427,11 +1427,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
}
/**
* Gets the weight of the Pokemon with subtractive modifiers (Autotomize) happening first
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
* @returns the kg of the Pokemon (minimum of 0.1)
*/
getWeight(): number {
const weight = new Utils.NumberHolder(this.species.weight);
const autotomizedTag = this.getTag(AutotomizedTag);
let weightRemoved = 0;
if (!Utils.isNullOrUndefined(autotomizedTag)) {
weightRemoved = 100 * autotomizedTag!.autotomizeCount;
}
const minWeight = 0.1;
const weight = new Utils.NumberHolder(this.species.weight - weightRemoved);
// This will trigger the ability overlay so only call this function when necessary
applyAbAttrs(WeightMultiplierAbAttr, this, null, false, weight);
return weight.value;
return Math.max(minWeight, weight.value);
}
/**

View File

@ -76,5 +76,6 @@
"substituteOnRemove": "{{pokemonNameWithAffix}}'s substitute faded!",
"tormentOnAdd": "{{pokemonNameWithAffix}} was subjected to torment!",
"tauntOnAdd":"{{pokemonNameWithAffix}} fell for the taunt!",
"imprisonOnAdd":"{{pokemonNameWithAffix}} sealed the opponents move(s)!"
"imprisonOnAdd":"{{pokemonNameWithAffix}} sealed the opponents move(s)!",
"autotomizeOnAdd": "{{pokemonNameWIthAffix}} became nimble!"
}

View File

@ -1 +1,3 @@
{}
{
"generation": "Generación {{generation}}"
}

View File

@ -1,91 +1,101 @@
{
"bossAppeared": "¡{{bossName}} te corta el paso!",
"trainerAppeared": "¡{{trainerName}}\nte desafía!",
"trainerAppearedDouble": "¡{{trainerName}}\nquieren luchar!",
"trainerSendOut": "¡{{trainerName}} saca a\n{{pokemonName}}!",
"singleWildAppeared": Un {{pokemonName}} salvaje te corta el paso!",
"multiWildAppeared": Un {{pokemonName1}} y un {{pokemonName2}} salvajes\nte cortan el paso!",
"playerComeBack": "¡{{pokemonName}}, ven aquí!",
"singleWildAppeared": {{pokemonName}} salvaje te corta el paso!",
"multiWildAppeared": {{pokemonName1}} y {{pokemonName2}} salvajes\nte cortan el paso!",
"playerComeBack": "¡{{pokemonName}}, vuelve!",
"trainerComeBack": "¡{{trainerName}} retira a {{pokemonName}} del combate!",
"playerGo": "¡Adelante, {{pokemonName}}!",
"trainerGo": "¡{{trainerName}} saca a {{pokemonName}}!",
"switchQuestion": "¿Quieres cambiar a\n{{pokemonName}}?",
"switchQuestion": "¿Quieres cambiar de {{pokemonName}}?",
"trainerDefeated": "¡Has derrotado a\n{{trainerName}}!",
"moneyWon": "¡Has ganado\n₽{{moneyAmount}} por vencer!",
"pokemonCaught": "¡{{pokemonName}} atrapado!",
"addedAsAStarter": "{{pokemonName}} ha sido añadido\na tus iniciales!",
"partyFull": "Tu equipo esta completo.\n¿Quieres liberar un Pokémon para meter a {{pokemonName}}?",
"moneyWon": "¡Has ganado\n{{moneyAmount}}₽ por vencer!",
"moneyPickedUp": "¡Has recogido {{moneyAmount}}₽!",
"pokemonCaught": "¡{{pokemonName}} ha sido atrapado!",
"addedAsAStarter": "¡{{pokemonName}} ha sido añadido a tus iniciales!",
"partyFull": "Tu equipo está completo.\n¿Quieres liberar a un Pokémon para quedarte con {{pokemonName}}?",
"pokemon": "Pokémon",
"sendOutPokemon": Adelante, {{pokemonName}}!",
"sendOutPokemon": Vamos, {{pokemonName}}!",
"hitResultCriticalHit": "¡Un golpe crítico!",
"hitResultSuperEffective": "¡Es supereficaz!",
"hitResultNotVeryEffective": "No es muy eficaz…",
"hitResultNoEffect": "No afecta a {{pokemonName}}!",
"hitResultNoEffect": "No afecta a {{pokemonName}}...",
"hitResultImmune": "¡No afecta a {{pokemonName}}!",
"hitResultOneHitKO": "¡KO en 1 golpe!",
"hitResultOneHitKO": "¡KO de un golpe!",
"attackFailed": "¡Pero ha fallado!",
"attackMissed": "¡{{pokemonNameWithAffix}}\nha evitado el ataque!",
"attackHitsCount": "N.º de golpes: {{count}}.",
"rewardGain": "¡Has obtenido\n{{modifierName}}!",
"rewardGain": "¡Has conseguido\n{{modifierName}}!",
"expGain": "{{pokemonName}} ha ganado\n{{exp}} puntos de experiencia.",
"levelUp": "¡{{pokemonName}} ha subido al \nNv. {{level}}!",
"levelUp": "¡{{pokemonName}} ha subido a\nNv. {{level}}!",
"learnMove": "¡{{pokemonName}} ha aprendido {{moveName}}!",
"learnMovePrompt": "{{pokemonName}} quiere aprender\n{{moveName}}.",
"learnMoveLimitReached": "Pero, {{pokemonName}} ya conoce\ncuatro movimientos.",
"learnMovePrompt": "{{pokemonName}} quiere aprender {{moveName}}.",
"learnMoveLimitReached": "Pero {{pokemonName}} ya conoce\ncuatro movimientos.",
"learnMoveReplaceQuestion": "¿Quieres sustituir uno de sus movimientos por {{moveName}}?",
"learnMoveStopTeaching": "¿Prefieres que no aprenda\n{{moveName}}?",
"learnMoveNotLearned": "{{pokemonName}} no ha aprendido {{moveName}}.",
"learnMoveForgetQuestion": "¿Qué movimiento quieres que olvide?",
"learnMoveForgetQuestion": "¿Qué movimiento debería olvidar?",
"learnMoveForgetSuccess": "{{pokemonName}} ha olvidado cómo utilizar {{moveName}}.",
"countdownPoof": "@d{32}1, @d{15}2, @d{15}y@d{15}… @d{15}… @d{15}… @d{15}@s{se/pb_bounce_1}¡Puf!",
"learnMoveAnd": "Y…",
"countdownPoof": "@d{32}1, @d{15}2, @d{15}3@d{15}… @d{15}… @d{15}… @d{15}@s{se/pb_bounce_1}Y...",
"learnMoveAnd": "¡Puf!",
"levelCapUp": "¡Se ha incrementado el\nnivel máximo a {{levelCap}}!",
"moveNotImplemented": "{{moveName}} aún no está implementado y no se puede seleccionar.",
"moveNoPP": "¡No hay suficientes PP\npara este movimiento!",
"moveDisabled": "!No puede usar {{moveName}} porque ha sido anulado!",
"moveNoPP": "¡No quedan PP para este movimiento!",
"moveDisabled": "¡{{moveName}} está anulado!",
"disableInterruptedMove": "¡Se ha anulado el movimiento {{moveName}}\nde {{pokemonNameWithAffix}}!",
"noPokeballForce": "Una fuerza misteriosa\nte impide usar Poké Balls.",
"noPokeballTrainer": "¡No puedes atrapar a los\nPokémon de los demás!",
"noPokeballMulti": "¡No se pueden lanzar Poké Balls\ncuando hay más de un Pokémon!",
"noPokeballStrong": "¡Este Pokémon es demasiado fuerte para ser capturado!\nNecesitas bajarle los PS primero!",
"noPokeballTrainer": "¡No es bien capturar los\nPokémon de los demás!",
"noPokeballMulti": "¡No puedes lanzar Poké Balls\ncuando hay más de un Pokémon!",
"noPokeballStrong": "Este Pokémon es demasiado fuerte para ser capturado.\n¡Baja sus PS!",
"noEscapeForce": "Una fuerza misteriosa\nte impide huir.",
"noEscapeTrainer": "¡No puedes huir de los\ncombates contra Entrenadores!",
"noEscapePokemon": El movimiento {{moveName}} de {{pokemonName}} impide la huida!",
"noEscapeTrainer": "¡No puedes huir de los\ncombates contra entrenadores!",
"noEscapePokemon": {{moveName}} de {{pokemonName}} no te deja huir!",
"runAwaySuccess": "¡Escapas sin problemas!",
"runAwayCannotEscape": "¡No has podido escapar!",
"escapeVerbSwitch": "cambiar",
"escapeVerbFlee": "huir",
"notDisabled": El movimiento {{moveName}} de {{pokemonName}}\nya no está anulado!",
"turnEndHpRestore": "Los PS de {{pokemonName}} fueron restaurados.",
"notDisabled": {{moveName}} de {{pokemonName}} ya no está anulado!",
"turnEndHpRestore": "{{pokemonName}} ha recuperado PS.",
"hpIsFull": "¡Los PS de {{pokemonName}}\nestán al máximo!",
"skipItemQuestion": "¿Estás seguro de que no quieres coger un objeto?",
"itemStackFull": "El máximo número de {{fullItemName}} ha sido alcanzado. Recibirás {{itemName}} en su lugar.",
"itemStackFull": "No tienes sitio para más {{fullItemName}}. Recibirás {{itemName}} en su lugar.",
"eggHatching": "¿Y esto?",
"ivScannerUseQuestion": "¿Quieres usar el Escáner de IVs en {{pokemonName}}?",
"wildPokemonWithAffix": "El {{pokemonName}} salvaje",
"foePokemonWithAffix": "El {{pokemonName}} enemigo",
"useMove": "¡{{pokemonNameWithAffix}} usó {{moveName}}!",
"drainMessage": {{pokemonName}} tuvo su\nenergía absorbida!",
"drainMessage": La energía de {{pokemonName}}\nha sido absorbida!",
"regainHealth": "¡{{pokemonName}} recuperó\nPS!",
"stealEatBerry": "¡{{pokemonName}} robó la {{berryName}}\nde {{targetName}} y se la comió!",
"ppHealBerry": "¡{{pokemonNameWithAffix}} recuperó algunos PP de {{moveName}}\ngracias a su {{berryName}}!",
"hpHealBerry": "¡{{pokemonNameWithAffix}} recuperó sus PS gracias a su {{berryName}}!",
"fainted": "¡{{pokemonNameWithAffix}} se debilitó!",
"statsAnd": "y",
"stats": "Las estadísticas",
"statRose_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha subido!",
"statRose_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han subido!",
"statSharplyRose_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha subido mucho!",
"statSharplyRose_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han subido mucho!",
"statRoseDrastically_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha subido muchísimo!",
"statRoseDrastically_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han subido muchísimo!",
"statWontGoAnyHigher_one": "¡El {{stats}} de {{pokemonNameWithAffix}} no puede subir más!",
"statWontGoAnyHigher_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} no pueden subir más!",
"statFell_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha bajado!",
"statFell_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han bajado!",
"statHarshlyFell_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha bajado mucho!",
"statHarshlyFell_other": "¡{{stats}} de\n{{pokemonNameWithAffix}} han bajado mucho!",
"statSeverelyFell_one": "¡El {{stats}} de {{pokemonNameWithAffix}} ha bajado muchísimo!",
"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!",
"statRose_one": "¡{{stats}} de {{pokemonNameWithAffix}} ha aumentado!",
"statRose_other": "¡{{stats}} de {{pokemonNameWithAffix}} ha aumentado!",
"statSharplyRose_one": "¡{{stats}} de {{pokemonNameWithAffix}} ha aumentado mucho!",
"statSharplyRose_other": "¡{{stats}} de {{pokemonNameWithAffix}} ha aumentado mucho!",
"statRoseDrastically_one": "¡{{stats}} de {{pokemonNameWithAffix}} ha aumentado muchísimo!",
"statRoseDrastically_other": "¡{{stats}} de {{pokemonNameWithAffix}} ha aumentado muchísimo!",
"statWontGoAnyHigher_one": "¡{{stats}} de {{pokemonNameWithAffix}} no puede aumentar más!",
"statWontGoAnyHigher_other": "¡{{stats}} de {{pokemonNameWithAffix}} no puede aumentar más!",
"statFell_one": "¡{{stats}} de {{pokemonNameWithAffix}} ha disminuido!",
"statFell_other": "¡{{stats}} de {{pokemonNameWithAffix}} ha disminuido!",
"statHarshlyFell_one": "¡{{stats}} de {{pokemonNameWithAffix}} ha disminuido mucho!",
"statHarshlyFell_other": "¡{{stats}} de {{pokemonNameWithAffix}} ha disminuido mucho!",
"statSeverelyFell_one": "¡{{stats}} de {{pokemonNameWithAffix}} ha disminuido muchísimo!",
"statSeverelyFell_other": "¡{{stats}} de {{pokemonNameWithAffix}} ha disminuido muchísimo!",
"statWontGoAnyLower_one": "¡{{stats}} de {{pokemonNameWithAffix}} no puede disminuir más!",
"statWontGoAnyLower_other": "¡{{stats}} de {{pokemonNameWithAffix}} no puede disminuir más!",
"transformedIntoType": "¡{{pokemonName}} ha cambiado a tipo {{type}}!",
"retryBattle": "¿Quieres reintentar este combate?",
"unlockedSomething": "¡Has desbloqueado {{unlockedThing}}!",
"congratulations": "¡Enhorabuena!",
"beatModeFirstTime": "¡{{speciesName}} ha completado el modo {{gameMode}} por primera vez!\n¡Has conseguido {{newModifier}}!",
"ppReduced": "¡El movimiento {{moveName}} de {{targetName}} ha perdido {{reduction}} PP!",
"battlerTagsHealBlock": "¡{{pokemonNameWithAffix}} no puede restaurar sus PS!",
"battlerTagsHealBlockOnRemove": "¡{{pokemonNameWithAffix}} ya puede recuperar PS!"
}

View File

@ -1,5 +1,6 @@
{
"title": "Parámetros de Desafíos",
"title": "Parámetros de desafíos",
"illegalEvolution": "¡{{pokemon}} ha evolucionado! ¡No puede ser parte del desafío!",
"singleGeneration": {
"name": "Monogeneración",
"desc": "Solo puedes usar Pokémon de {{gen}} generación.",
@ -14,16 +15,22 @@
"gen_8": "octava",
"gen_9": "novena"
},
"freshStart": {
"name": "Nuevo Comienzo",
"desc": "Sólo puedes usar Pokémon iniciales originales, como si acabases de empezar PokéRogue.",
"value.0": "Desact.",
"value.1": "Activo"
},
"singleType": {
"name": "Monotipo",
"desc": "Solo puedes usar Pokémon with the {{type}} type.",
"desc_default": "Solo puedes usar Pokémon del tipo elegido."
},
"inverseBattle": {
"name": "Combate Inverso",
"name": "Lucha Inversa",
"shortName": "Inverso",
"desc": "La efectividad de los tipos es invertida. No hay inmunidades entre tipos.\nEste reto deshabilita logros de otros retos.",
"value.0": "Desactivado",
"value.1": "Activado"
"desc": "Las relaciones entre tipos son invertidas y ningún tipo es inmune a otro.\nDesactiva el resto de desafíos.",
"value.0": "Desact.",
"value.1": "Activo"
}
}
}

View File

@ -1,7 +1,8 @@
{
"start": "Listo",
"luckIndicator": "Suerte:",
"shinyOnHover": "Shiny",
"commonShiny": "Común",
"rareShiny": "Raro",
"epicShiny": "Épico"
}
}

View File

@ -1 +1,42 @@
{}
{
"stats": "Estadísticas",
"playTime": "Tiempo jugado",
"totalBattles": "Batallas totales",
"starters": "Iniciales",
"shinyStarters": "Shinies iniciales",
"speciesSeen": "Especies vistas",
"speciesCaught": "Especies capturadas",
"ribbonsOwned": "Cintas obtenidas",
"classicRuns": "Partidas clásicas",
"classicWins": "Victorias clásicas",
"dailyRunAttempts": "Intentos del reto diario",
"dailyRunWins": "Victorias del reto diario",
"endlessRuns": "Partidas Infinitas",
"highestWaveEndless": "Oleada récord (infinito)",
"highestMoney": "Dinero récord",
"highestDamage": "Daño máximo",
"highestHPHealed": "Máximos PS curados",
"pokemonEncountered": "Pokémon encontrados",
"pokemonDefeated": "Pokémon derrotados",
"pokemonCaught": "Pokémon capturados",
"eggsHatched": "Huevos eclosionados",
"subLegendsSeen": "Sublegendarios vistos",
"subLegendsCaught": "Sub-legs. capturados",
"subLegendsHatched": "Sub-legs. eclosionados",
"legendsSeen": "Legendarios vistos",
"legendsCaught": "Legendarios capturados",
"legendsHatched": "Legendarios eclosionados",
"mythicalsSeen": "Míticos vistos",
"mythicalsCaught": "Singulares capturados",
"mythicalsHatched": "Singulares eclosionados",
"shiniesSeen": "Shinies vistos",
"shiniesCaught": "Shinies capturados",
"shiniesHatched": "Shinies eclosionados",
"pokemonFused": "Pokémons fusionados",
"trainersDefeated": "Entrenadores derrotados",
"eggsPulled": "Huevos canjeados",
"rareEggsPulled": "Huevos raros canjeados",
"epicEggsPulled": "Huevos épicos canjeados",
"legendaryEggsPulled": "Huevos legendarios canjeados",
"manaphyEggsPulled": "Huevos Manaphy canjeados"
}

View File

@ -3,26 +3,27 @@
"ACHIEVEMENTS": "Logros",
"STATS": "Estadísticas",
"RUN_HISTORY": "Historial de partida",
"EGG_LIST": "Lista de Huevos",
"EGG_GACHA": "Gacha de Huevos",
"MANAGE_DATA": "Gestionar Datos",
"EGG_LIST": "Lista de huevos",
"EGG_GACHA": "Lotería Oval",
"MANAGE_DATA": "Gestionar datos",
"COMMUNITY": "Comunidad",
"SAVE_AND_QUIT": "Guardar y Salir",
"LOG_OUT": "Cerrar Sesión",
"SAVE_AND_QUIT": "Guardar y salir",
"LOG_OUT": "Cerrar sesión",
"slot": "Ranura {{slotNumber}}",
"importSession": "Importar Sesión",
"importSession": "Importar sesión",
"importSlotSelect": "Selecciona una ranura para importar.",
"exportSession": "Exportar Sesión",
"exportSession": "Exportar sesión",
"exportSlotSelect": "Selecciona una ranura para exportar.",
"importRunHistory":"Importar Historial de partida",
"exportRunHistory":"Exportar Historial de partida",
"importData": "Importar Datos",
"exportData": "Exportar Datos",
"importRunHistory": "Importar historial de partida",
"exportRunHistory": "Exportar historial de partida",
"importData": "Importar datos",
"exportData": "Exportar datos",
"consentPreferences": "Consentimiento de datos",
"linkDiscord": "Conectar Discord",
"unlinkDiscord": "Desconectar Discord",
"linkGoogle": "Conectar Google",
"unlinkGoogle": "Desconectar Google",
"cancel": "Cancelar",
"losingProgressionWarning": "Perderás cualquier progreso desde el inicio de la batalla. ¿Continuar?"
}
"losingProgressionWarning": "Perderás cualquier progreso desde el inicio de la batalla. ¿Continuar?",
"noEggs": "¡No hay huevos incubándose\nahora mismo!"
}

View File

@ -1,8 +1,37 @@
{
"hitWithRecoil": "¡{{pokemonName}} también\nse ha hecho daño!",
"cutHpPowerUpMove": "¡{{pokemonName}} sacrifica sus PS para mejorar su movimiento!",
"absorbedElectricity": "¡{{pokemonName}} está acumulando electricidad!",
"switchedStatChanges": "¡{{pokemonName}} intercambió los cambios de características con el objetivo!",
"switchedTwoStatChanges": "{{pokemonName}} ha intercambiado los cambios en {{firstStat}} y {{secondStat}} con los del objetivo!",
"switchedStat": "{{pokemonName}} cambia su {{stat}} por la de su objetivo!",
"sharedGuard": "{{pokemonName}} suma su capacidad defensiva a la del objetivo y la reparte equitativamente!",
"sharedPower": "{{pokemonName}} suma su capacidad ofensiva a la del objetivo y la reparte equitativamente!",
"goingAllOutForAttack": "¡{{pokemonName}} lo ha dado todo!",
"regainedHealth": "¡{{pokemonName}} ha recuperado PS!",
"keptGoingAndCrashed": "¡{{pokemonName}} ha fallado y se ha caído al suelo!",
"fled": "¡{{pokemonName}} ha huido!",
"cannotBeSwitchedOut": "¡{{pokemonName}} no puede dejar el combate!",
"swappedAbilitiesWithTarget": "¡{{pokemonName}} ha intercambiado su habilidad con la de su objetivo!",
"coinsScatteredEverywhere": "¡Hay monedas por todas partes!",
"attackedByItem": "¡{{pokemonName}} sufre daño por su {{itemName}}!",
"whippedUpAWhirlwind": "¡{{pokemonName}} se prepara para lanzar una borrasca!",
"flewUpHigh": "¡{{pokemonName}} voló alto!",
"tookInSunlight": "¡{{pokemonName}} ha absorbido luz solar!",
"dugAHole": "¡{{pokemonName}} se ha ocultado bajo tierra!",
"loweredItsHead": "¡{{pokemonName}} ha agachado la cabeza!",
"isGlowing": "¡Un intenso halo rodea a {{pokemonName}}!",
"bellChimed": "Ha repicado una campana.",
"foresawAnAttack": "¡{{pokemonName}} ha previsto el ataque!",
"isTighteningFocus": "¡{{pokemonName}} está reforzando su concentración!",
"hidUnderwater": "¡{{pokemonName}} se ha ocultado bajo el agua!",
"soothingAromaWaftedThroughArea": "Un aroma balsámico flota en el aire...",
"sprangUp": "¡{{pokemonName}} ha saltado muy alto!",
"choseDoomDesireAsDestiny": "¡{{pokemonName}} ha elegido Deseo Oculto para el futuro!",
"vanishedInstantly": "¡{{pokemonName}} ha desaparecido en un abrir y cerrar de ojos!",
"tookTargetIntoSky": "¡{{pokemonName}} se ha llevado a {{targetName}} por los aires!",
"becameCloakedInFreezingLight": "¡Una luz fría envuelve a {{pokemonName}}!",
"becameCloakedInFreezingAir": "¡Una ráfaga gélida envuelve a {{pokemonName}}!",
"isChargingPower": "¡{{pokemonName}} está acumulando energía!",
"burnedItselfOut": "¡El fuego interior de {{pokemonName}} se ha extinguido!",
"startedHeatingUpBeak": "¡{{pokemonName}} empieza\na calentar su pico!",
@ -10,8 +39,33 @@
"isOverflowingWithSpacePower": "¡{{pokemonName}} rebosa\nenergía cósmica!",
"usedUpAllElectricity": "¡{{pokemonName}} ha descargado toda su electricidad!",
"stoleItem": "¡{{pokemonName}} robó el objeto\n{{itemName}} de {{targetName}}!",
"incineratedItem": "¡{{pokemonName}} ha incinerado la {{itemName}} de {{targetName}}!",
"knockedOffItem": "¡{{itemName}} de {{targetName}} ha caído al suelo!",
"tookMoveAttack": "¡{{pokemonName}} ha sido alcanzado por {{moveName}}!",
"cutOwnHpAndMaximizedStat": "¡{{pokemonName}} ha sacrificado algunos PS y ha aumentado su {{statName}} al máximo!",
"copiedStatChanges": "¡{{pokemonName}} ha copiado los cambios de características de\n{{targetName}}!",
"magnitudeMessage": "Magnitud: {{magnitude}}!",
"tookAimAtTarget": "¡{{pokemonName}} tiene a {{targetName}} en su punto de mira!",
"transformedIntoType": "¡{{pokemonName}} ha cambiado a tipo {{typeName}}!",
"copiedMove": "¡{{pokemonName}} ha copiado {{moveName}}!",
"sketchedMove": "¡{{pokemonName}} ha usado Esquema para copiar {{moveName}}!",
"acquiredAbility": "¡La habilidad de {{pokemonName}} ha cambiado a {{abilityName}}!",
"copiedTargetAbility": "¡{{pokemonName}} ha copiado la habilidad {{abilityName}} de {{targetName}}!",
"transformedIntoTarget": "¡{{pokemonName}} se ha transformado en {{targetName}}!",
"tryingToTakeFoeDown": "¡{{pokemonName}} intenta que el rival sufra su mismo destino!",
"addType": "¡{{pokemonName}} ahora también es de tipo {{typeName}}!",
"cannotUseMove": "¡{{pokemonName}} no puede usar {{moveName}}!",
"healHp": "¡{{pokemonName}} recuperó sus PS!",
"sacrificialFullRestore": "¡El deseo de curación se ha hecho realidad para {{pokemonName}}!",
"invertStats": "¡Se han invertido los cambios de características de {{pokemonName}}!",
"resetStats": "¡Se han anulado todos los cambios de características de\t{{pokemonName}}!",
"statEliminated": "¡Los cambios en estadísticas fueron eliminados!",
"faintCountdown": "{{pokemonName}}\nse debilitará dentro de {{turnCount}} turnos.",
"copyType": "¡{{pokemonName}} ahora es del mismo tipo que\n{{targetPokemonName}}!",
"suppressAbilities": "¡Se ha anulado la habilidad de {{pokemonName}}!",
"revivalBlessing": "¡{{pokemonName}} ha revivido!",
"swapArenaTags": "¡{{pokemonName}} ha intercambiado los efectos del terreno de combate!",
"exposedMove": "¡{{pokemonName}} ha identificado\n{{targetPokemonName}}!",
"safeguard": "¡{{targetName}} está protegido por Velo Sagrado!",
"afterYou": "¡{{pokemonName}} ha decidido aprovechar la oportunidad!"
}

File diff suppressed because it is too large Load Diff

View File

@ -2,5 +2,6 @@
"moveset": "Movimientos",
"gender": "Género:",
"ability": "Habilid:",
"nature": "Natur:"
}
"nature": "Natur:",
"form": "Forma:"
}

View File

@ -1,5 +1,6 @@
{
"confirmStartTeam": "¿Comenzar con estos Pokémon?",
"confirmExit": "¿Quieres salir?",
"invalidParty": "¡Este equipo no es válido!",
"gen1": "I",
"gen2": "II",
@ -11,21 +12,24 @@
"gen8": "VIII",
"gen9": "IX",
"growthRate": "Crecimiento:",
"ability": "Habilid:",
"ability": "Habili:",
"passive": "Pasiva:",
"nature": "Natur:",
"eggMoves": "Mov. Huevo",
"addToParty": "Añadir al Equipo",
"removeFromParty": "Excluir del Equipo",
"removeFromParty": "Quitar del Equipo",
"toggleIVs": "Mostrar IVs",
"manageMoves": "Cambiar movs.",
"manageNature": "Cambiar natur.",
"addToFavorites": "Añadir a Favoritos",
"removeFromFavorites": "Quitar de Favoritos",
"useCandies": "Usar Caramelos",
"selectNature": "Elige Natur.",
"selectMoveSwapOut": "Elige el movimiento que sustituir.",
"selectMoveSwapWith": "Elige el movimiento que sustituirá a",
"unlockPassive": "Añadir Pasiva",
"reduceCost": "Reducir Coste",
"sameSpeciesEgg": "Comprar Huevo",
"cycleShiny": ": Shiny",
"cycleForm": ": Forma",
"cycleGender": ": Género",
@ -38,4 +42,4 @@
"locked": "Bloqueado",
"disabled": "No disponible",
"uncaught": "No capturado"
}
}

View File

@ -1,7 +1,7 @@
{
"ace_trainer": "Entrenador Guay",
"ace_trainer_female": "Entrenadora Guay",
"ace_duo": "Pareja Guay",
"ace_trainer": "Entrenador guay",
"ace_trainer_female": "Entrenadora guay",
"ace_duo": "Pareja guay",
"artist": "Artista",
"artist_female": "Artista",
"backers": "Hinchas",
@ -19,7 +19,7 @@
"breeders": "Criadores",
"clerk": "Empresario",
"clerk_female": "Oficinista",
"colleagues": "Colegas Oficina",
"colleagues": "Colegas oficina",
"crush_kin": "Luchadores",
"cyclist": "Ciclista",
"cyclist_female": "Ciclista",
@ -46,13 +46,13 @@
"linebacker": "Quarterback",
"maid": "Criada",
"madame": "Señora",
"medical_team": "Equipo Médico",
"medical_team": "Equipo médico",
"musician": "Cantautor",
"hex_maniac": "Bruja",
"nurse": "Enfermera",
"nursery_aide": "Seño",
"officer": "Policía",
"parasol_lady": "Dama Parasol",
"parasol_lady": "Dama parasol",
"pilot": "Piloto",
"pokéfan": "Pokéfan",
"pokéfan_female": "Pokéfan",
@ -63,22 +63,26 @@
"psychic": "Médium",
"psychic_female": "Mentalista",
"psychics": "Pareja Médium",
"pokémon_ranger": "Pokémon Ranger",
"pokémon_ranger_female": "Pokéguarda",
"pokémon_rangers": "Pokéguardas",
"ranger": "Guarda",
"restaurant_staff": "Personal Restaurante",
"rich": "Bien",
"rich_female": "Bien",
"rich_boy": "Niño Bien",
"rich_couple": "Pareja Bien",
"rich_kid": "Niño Bien",
"rich_kid_female": "Niña Bien",
"rich_kids": "Niños Bien",
"rich_boy": "Niño bien",
"rich_couple": "Pareja bien",
"rich_kid": "Niño bien",
"rich_kid_female": "Niña bien",
"rich_kids": "Niños bien",
"roughneck": "Calvo",
"sailor": "Marinero",
"scientist": "Científico",
"scientist_female": "Científica",
"scientists": "Científicos",
"smasher": "Tenista",
"snow_worker": "Operario Nieve",
"snow_worker_female": "Operaria Nieve",
"snow_worker": "Operario nieve",
"snow_worker_female": "Operaria nieve",
"striker": "Delantero",
"school_kid": "Colegial",
"school_kid_female": "Colegial",
@ -89,16 +93,40 @@
"twins": "Gemelas",
"veteran": "Veterano",
"veteran_female": "Veterana",
"veteran_duo": "Dúo Veterano",
"veteran_duo": "Dúo veterano",
"waiter": "Camarero",
"waitress": "Camarera",
"worker": "Operario",
"worker_female": "Operaria",
"workers": "Operarios",
"youngster": "Joven",
"rocket_grunt": "Recluta Rocket",
"rocket_grunts": "Reclutas Rocket",
"rocket_grunt_female": "Recluta Rocket",
"magma_grunt": "Recluta Magma",
"magma_grunt_female": "Recluta Magma",
"magma_grunts": "Reclutas Magma",
"aqua_grunt": "Recluta Aqua",
"aqua_grunt_female": "Recluta Aqua",
"aqua_grunts": "Reclutas Aqua",
"galactic_grunt": "Recluta Galaxia",
"galactic_grunt_female": "Recluta Galaxia",
"galactic_grunts": "Reclutas Galaxia",
"plasma_grunt": "Recluta Plasma",
"plasma_grunt_female": "Recluta Plasma",
"plasma_grunts": "Reclutas Plasma",
"flare_grunt": "Recluta Flare",
"flare_grunt_female": "Recluta Flare",
"flare_grunts": "Reclutas Flare",
"aether_grunt": "Empleado de la Fundación Æther",
"aether_grunt_female": "Empleada de la Fundación Æther",
"aether_grunts": "Empleados de la Fundación Æther",
"skull_grunt": "Recluta Skull",
"skull_grunt_female": "Recluta Skull",
"skull_grunts": "Reclutas Skull",
"macro_grunt": "Entrenador Macrocosmos",
"macro_grunt_female": "Entrenadora Macrocosmos",
"macro_grunts": "Entrenadores Macrocosmos",
"star_grunt": "Star Grunt",
"star_grunt_female": "Star Grunt",
"star_grunts": "Star Grunts"

View File

@ -3,38 +3,38 @@
"elite_four_female": "Alto Mando",
"gym_leader": "Líder de gimnasio",
"gym_leader_female": "Líder de gimnasio",
"gym_leader_double": "Líderes de Gimnasio",
"gym_leader_double": "Líderes de gimnasio",
"champion": "Campeón",
"champion_female": "Campeona",
"champion_double": "Campeones",
"rival": "Rival",
"professor": "Profesor",
"frontier_brain": "As del Frente Batalla",
"rocket_boss": "Team Rocket Boss",
"magma_boss": "Team Magma Boss",
"aqua_boss": "Team Aqua Boss",
"galactic_boss": "Team Galactic Boss",
"plasma_boss": "Team Plasma Boss",
"flare_boss": "Team Flare Boss",
"rocket_boss": "Jefe del Team Rocket",
"magma_boss": "Jefe del Equipo Magma",
"aqua_boss": "Jefe del Equipo Aqua",
"galactic_boss": "Jefe del Equipo Galaxia",
"plasma_boss": "Jefe del Equipo Plasma",
"flare_boss": "Jefe del Team Flare",
"aether_boss": "Presidente Æther",
"skull_boss": "Team Skull Boss",
"macro_boss": "Macro Cosmos President",
"skull_boss": "Jefe del Team Skull",
"macro_boss": "Presidente de Macrocosmos",
"star_boss": "Team Star Leader",
"rocket_admin": "Team Rocket Admin",
"rocket_admin_female": "Team Rocket Admin",
"magma_admin": "Team Magma Admin",
"magma_admin_female": "Team Magma Admin",
"aqua_admin": "Team Aqua Admin",
"aqua_admin_female": "Team Aqua Admin",
"galactic_commander": "Team Galactic Commander",
"galactic_commander_female": "Team Galactic Commander",
"plasma_sage": "Team Plasma Sage",
"plasma_admin": "Team Plasma Admin",
"flare_admin": "Team Flare Admin",
"flare_admin_female": "Team Flare Admin",
"rocket_admin": "Admin. del Team Rocket",
"rocket_admin_female": "Admin. del Team Rocket",
"magma_admin": "Admin. del Equipo Magma",
"magma_admin_female": "Admin. del Equipo Magma",
"aqua_admin": "Admin. del Equipo Aqua",
"aqua_admin_female": "Admin. del Equipo Aqua",
"galactic_commander": "Comand. del Equipo Galaxia",
"galactic_commander_female": "Comand. del Equipo Galaxia",
"plasma_sage": "Sabio del Equipo Plasma",
"plasma_admin": "Admin. del Equipo Plasma",
"flare_admin": "Admin. del Team Flare",
"flare_admin_female": "Admin. del Team Flare",
"aether_admin": "Director de la Fundación Æther",
"skull_admin": "Team Skull Admin",
"macro_admin": "Macro Cosmos",
"skull_admin": "Admin. del Team Skull",
"macro_admin": "Admin. de Macrocosmos",
"star_admin": "Team Star Squad Boss"
}

View File

@ -1,5 +1,5 @@
{
"intro": "¡Bienvenido/a a PokéRogue! Este es un fangame de Pokémon centrado en el combate con elementos roguelite.\n$Este juego no está monetizado y no reclamamos ningún derecho de propiedad sobre Pokémon ni sobre ninguno de\n$los recursos con copyright utilizados.\n$El juego está en desarrollo, pero es completamente jugable.\nPara reportar bugs, por favor, hazlo en nuestra\n$comunidad de Discord.\n$Si el juego va lento, por favor, asegúrate de que tengas activada la opción 'Aceleración de gráficos' en los\n$ajustes de tu navegador.",
"intro": "¡Bienvenido/a a PokéRogue! Este es un fangame de Pokémon centrado en el combate con elementos roguelite.\n$Este juego no está monetizado y no reclamamos ningún derecho de propiedad sobre Pokémon ni sobre ninguno de\n$los recursos con copyright utilizados.\n$El juego está en desarrollo, pero es completamente jugable.\n$Por favor, reporta los errores que veas en nuestro Discord oficial.\n$Si el juego va lento, por favor, asegúrate de que tengas activada la opción 'Aceleración de gráficos' en los\n$ajustes de tu navegador.",
"accessMenu": "Para acceder al menú, pulsa M o Escape cuando\ntengas el control.\n$El menú contiene los ajustes y otras funciones.",
"menu": "Desde este menú podrás acceder a los ajustes.\n$Podrás cambiar la velocidad del juego, el estilo de la ventana y demás.\n$Hay más opciones, ¡así que pruébalas todas!",
"starterSelect": "En esta pantalla, podrás elegir tus iniciales presionando Z\no Espacio. Estos serán tus miembros de equipo al comenzar.\n$Cada inicial tiene un valor. Tu equipo puede contener hasta 6\nmiembros mientras el valor total no pase de 10.\n$También puedes elegir su género, habilidad y forma\ndependiendo de las variantes que hayas conseguido.\n$Los IVs de los iniciales corresponderán al valor más alto de\nlos Pokémon de la misma especie que hayas obtenido.\n$¡Así que intenta conseguir muchos Pokémon de la misma\nespecie!",
@ -7,4 +7,4 @@
"statChange": "Los cambios de estadísticas se mantienen entre combates\nmientras que el Pokémon no vuelva a su Poké Ball.\n$Tus Pokémon vuelven a sus Poké Balls antes de combates contra entrenadores y de entrar a un nuevo bioma.\n$También puedes ver los cambios de estadísticas del Pokémon en campo manteniendo pulsado C o Shift.\n$También puedes ver los movimientos de un Pokémon enemigo manteniendo presionada la V.\n$Esto solo revela los movimientos que has visto usar al Pokémon en esta combate.",
"selectItem": "Tras cada combate, tendrás la opción de elegir entre tres objetos aleatorios. Solo podrás escoger uno.\n$Estos objetos pueden ser consumibles, objetos equipables u objetos pasivos permanentes (hasta acabar la partida).\n$La mayoría de los efectos de objetos no consumibles se acumularán de varias maneras.\n$Algunos objetos solo aparecerán si pueden ser utilizados, como las piedras evolutivas.\n$También puedes transferir objetos equipados entre Pokémon, utilizando la opción de transferir.\n$La opción de transferir aparecerá en la parte inferior derecha una vez hayas obtenido un objeto equipable.\n$También puedes comprar objetos consumibles con dinero y su variedad irá aumentando según tu avance.\n$Asegúrate de comprar antes de escoger una recompensa, ya que se avanzará automáticamente al siguiente combate.",
"eggGacha": "En esta pantalla podrás canjear tus vales por huevos\nde Pokémon.\n$Los huevos deben eclosionar y estarán más cerca de\nhacerlo tras cada combate.\n$Los huevos más raros tardarán más en eclosionar.\n$Los Pokémon que hayan salido del huevo no se\nañadirán a tu equipo, pero sí a tus iniciales.\n$Los Pokémon salidos de un huevo suelen tener mejores\nIVs que los Pokémon salvajes.\n$Algunos Pokémon solo pueden ser obtenidos de huevos.\n$Hay 3 máquinas diferentes entre las que elegir, cada\nuna con diferentes bonificaciones.\n$¡Así que escoge la que más te interese!"
}
}

View File

@ -141,7 +141,7 @@ export class EncounterPhase extends BattlePhase {
loadEnemyAssets.push(enemyPokemon.loadAssets());
console.log(getPokemonNameWithAffix(enemyPokemon), enemyPokemon.species.speciesId, enemyPokemon.stats);
console.log(`Pokemon: ${getPokemonNameWithAffix(enemyPokemon)}`, `Species ID: ${enemyPokemon.species.speciesId}`, `Stats: ${enemyPokemon.stats}`, `Ability: ${enemyPokemon.getAbility().name}`, `Passive Ability: ${enemyPokemon.getPassiveAbility().name}`);
return true;
});

View File

@ -9,6 +9,7 @@ import PartyUiHandler from "../ui/party-ui-handler";
import { getPokemonNameWithAffix } from "../messages";
import { EndEvolutionPhase } from "./end-evolution-phase";
import { EvolutionPhase } from "./evolution-phase";
import { BattlerTagType } from "#app/enums/battler-tag-type";
export class FormChangePhase extends EvolutionPhase {
private formChange: SpeciesFormChange;
@ -157,6 +158,7 @@ export class FormChangePhase extends EvolutionPhase {
}
end(): void {
this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED);
if (this.modal) {
this.scene.ui.revertMode().then(() => {
if (this.scene.ui.getMode() === Mode.PARTY) {

View File

@ -3,6 +3,7 @@ import { SemiInvulnerableTag } from "#app/data/battler-tags";
import { SpeciesFormChange, getSpeciesFormChangeMessage } from "#app/data/pokemon-forms";
import { getTypeRgb } from "#app/data/type";
import { BattleSpec } from "#app/enums/battle-spec";
import { BattlerTagType } from "#app/enums/battler-tag-type";
import Pokemon, { EnemyPokemon } from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { BattlePhase } from "./battle-phase";
@ -113,6 +114,7 @@ export class QuietFormChangePhase extends BattlePhase {
}
end(): void {
this.pokemon.findAndRemoveTags(t => t.tagType === BattlerTagType.AUTOTOMIZED);
if (this.pokemon.scene?.currentBattle.battleSpec === BattleSpec.FINAL_BOSS && this.pokemon instanceof EnemyPokemon) {
this.scene.playBgm();
this.scene.unshiftPhase(new PokemonHealPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getMaxHp(), null, false, false, false, true));

View File

@ -0,0 +1,98 @@
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest";
describe("Moves - Autotomize", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const TIMEOUT = 20 * 1000;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.AUTOTOMIZE, Moves.KINGS_SHIELD, Moves.FALSE_SWIPE])
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH);
});
it("Autotomize should reduce weight", async () => {
const baseDracozoltWeight = 190;
const oneAutotomizeDracozoltWeight = 90;
const twoAutotomizeDracozoltWeight = 0.1;
const threeAutotomizeDracozoltWeight = 0.1;
await game.classicMode.startBattle([Species.DRACOZOLT]);
const playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getWeight()).toBe(baseDracozoltWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(oneAutotomizeDracozoltWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(twoAutotomizeDracozoltWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(threeAutotomizeDracozoltWeight);
}, TIMEOUT);
it("Changing forms should revert weight", async () => {
const baseAegislashWeight = 53;
const autotomizeAegislashWeight = 0.1;
await game.classicMode.startBattle([Species.AEGISLASH]);
const playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getWeight()).toBe(baseAegislashWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(autotomizeAegislashWeight);
// Transform to sword form
game.move.select(Moves.FALSE_SWIPE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(baseAegislashWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(autotomizeAegislashWeight);
// Transform to shield form
game.move.select(Moves.KINGS_SHIELD);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(baseAegislashWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(autotomizeAegislashWeight);
}, TIMEOUT);
it("Autotomize should interact with light metal correctly", async () => {
const baseLightGroudonWeight = 475;
const autotomizeLightGroudonWeight = 425;
game.override.ability(Abilities.LIGHT_METAL);
await game.classicMode.startBattle([Species.GROUDON]);
const playerPokemon = game.scene.getPlayerPokemon()!;
expect(playerPokemon.getWeight()).toBe(baseLightGroudonWeight);
game.move.select(Moves.AUTOTOMIZE);
await game.toNextTurn();
expect(playerPokemon.getWeight()).toBe(autotomizeLightGroudonWeight);
}, TIMEOUT);
});

View File

@ -1,16 +1,11 @@
import { BattlerIndex } from "#app/battle";
import { allMoves } from "#app/data/move";
import { BattleEndPhase } from "#app/phases/battle-end-phase";
import { BerryPhase } from "#app/phases/berry-phase";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
import GameManager from "../utils/gameManager";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Moves - Dragon Tail", () => {
let phaserGame: Phaser.Game;
@ -29,7 +24,7 @@ describe("Moves - Dragon Tail", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleType("single")
.moveset([Moves.DRAGON_TAIL, Moves.SPLASH])
.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER])
.enemySpecies(Species.WAILORD)
.enemyMoveset(Moves.SPLASH)
.startingLevel(5)
@ -38,109 +33,110 @@ describe("Moves - Dragon Tail", () => {
vi.spyOn(allMoves[Moves.DRAGON_TAIL], "accuracy", "get").mockReturnValue(100);
});
test(
"Single battle should cause opponent to flee, and not crash",
async () => {
await game.startBattle([Species.DRATINI]);
it("should cause opponent to flee, and not crash", async () => {
await game.classicMode.startBattle([Species.DRATINI]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.DRAGON_TAIL);
game.move.select(Moves.DRAGON_TAIL);
await game.phaseInterceptor.to(BerryPhase);
await game.phaseInterceptor.to("BerryPhase");
const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true);
const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true);
// simply want to test that the game makes it this far without crashing
await game.phaseInterceptor.to(BattleEndPhase);
}
);
// simply want to test that the game makes it this far without crashing
await game.phaseInterceptor.to("BattleEndPhase");
});
test(
"Single battle should cause opponent to flee, display ability, and not crash",
async () => {
game.override.enemyAbility(Abilities.ROUGH_SKIN);
await game.startBattle([Species.DRATINI]);
it("should cause opponent to flee, display ability, and not crash", async () => {
game.override.enemyAbility(Abilities.ROUGH_SKIN);
await game.classicMode.startBattle([Species.DRATINI]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.DRAGON_TAIL);
game.move.select(Moves.DRAGON_TAIL);
await game.phaseInterceptor.to(BerryPhase);
await game.phaseInterceptor.to("BerryPhase");
const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
}
);
const isVisible = enemyPokemon.visible;
const hasFled = enemyPokemon.switchOutStatus;
expect(!isVisible && hasFled).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
});
test(
"Double battles should proceed without crashing",
async () => {
game.override.battleType("double").enemyMoveset(Moves.SPLASH);
game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER])
.enemyAbility(Abilities.ROUGH_SKIN);
await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]);
it("should proceed without crashing in a double battle", async () => {
game.override
.battleType("double").enemyMoveset(Moves.SPLASH)
.enemyAbility(Abilities.ROUGH_SKIN);
await game.classicMode.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]);
const leadPokemon = game.scene.getParty()[0]!;
const leadPokemon = game.scene.getParty()[0]!;
const enemyLeadPokemon = game.scene.getEnemyParty()[0]!;
const enemySecPokemon = game.scene.getEnemyParty()[1]!;
const enemyLeadPokemon = game.scene.getEnemyParty()[0]!;
const enemySecPokemon = game.scene.getEnemyParty()[1]!;
game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY);
game.move.select(Moves.SPLASH, 1);
game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY);
game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to(TurnEndPhase);
await game.phaseInterceptor.to("TurnEndPhase");
const isVisibleLead = enemyLeadPokemon.visible;
const hasFledLead = enemyLeadPokemon.switchOutStatus;
const isVisibleSec = enemySecPokemon.visible;
const hasFledSec = enemySecPokemon.switchOutStatus;
expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
const isVisibleLead = enemyLeadPokemon.visible;
const hasFledLead = enemyLeadPokemon.switchOutStatus;
const isVisibleSec = enemySecPokemon.visible;
const hasFledSec = enemySecPokemon.switchOutStatus;
expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
// second turn
game.move.select(Moves.FLAMETHROWER, 0, BattlerIndex.ENEMY_2);
game.move.select(Moves.SPLASH, 1);
// second turn
game.move.select(Moves.FLAMETHROWER, 0, BattlerIndex.ENEMY_2);
game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to(BerryPhase);
expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp());
}
);
await game.phaseInterceptor.to("BerryPhase");
expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp());
});
test(
"Flee move redirection works",
async () => {
game.override.battleType("double").enemyMoveset(Moves.SPLASH);
game.override.moveset([Moves.DRAGON_TAIL, Moves.SPLASH, Moves.FLAMETHROWER]);
game.override.enemyAbility(Abilities.ROUGH_SKIN);
await game.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]);
it("should redirect targets upon opponent flee", async () => {
game.override
.battleType("double")
.enemyMoveset(Moves.SPLASH)
.enemyAbility(Abilities.ROUGH_SKIN);
await game.classicMode.startBattle([Species.DRATINI, Species.DRATINI, Species.WAILORD, Species.WAILORD]);
const leadPokemon = game.scene.getParty()[0]!;
const secPokemon = game.scene.getParty()[1]!;
const leadPokemon = game.scene.getParty()[0]!;
const secPokemon = game.scene.getParty()[1]!;
const enemyLeadPokemon = game.scene.getEnemyParty()[0]!;
const enemySecPokemon = game.scene.getEnemyParty()[1]!;
const enemyLeadPokemon = game.scene.getEnemyParty()[0]!;
const enemySecPokemon = game.scene.getEnemyParty()[1]!;
game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY);
// target the same pokemon, second move should be redirected after first flees
game.move.select(Moves.DRAGON_TAIL, 1, BattlerIndex.ENEMY);
game.move.select(Moves.DRAGON_TAIL, 0, BattlerIndex.ENEMY);
// target the same pokemon, second move should be redirected after first flees
game.move.select(Moves.DRAGON_TAIL, 1, BattlerIndex.ENEMY);
await game.phaseInterceptor.to(BerryPhase);
await game.phaseInterceptor.to("BerryPhase");
const isVisibleLead = enemyLeadPokemon.visible;
const hasFledLead = enemyLeadPokemon.switchOutStatus;
const isVisibleSec = enemySecPokemon.visible;
const hasFledSec = enemySecPokemon.switchOutStatus;
expect(!isVisibleLead && hasFledLead && !isVisibleSec && hasFledSec).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
expect(secPokemon.hp).toBeLessThan(secPokemon.getMaxHp());
expect(enemyLeadPokemon.hp).toBeLessThan(enemyLeadPokemon.getMaxHp());
expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp());
}
);
const isVisibleLead = enemyLeadPokemon.visible;
const hasFledLead = enemyLeadPokemon.switchOutStatus;
const isVisibleSec = enemySecPokemon.visible;
const hasFledSec = enemySecPokemon.switchOutStatus;
expect(!isVisibleLead && hasFledLead && !isVisibleSec && hasFledSec).toBe(true);
expect(leadPokemon.hp).toBeLessThan(leadPokemon.getMaxHp());
expect(secPokemon.hp).toBeLessThan(secPokemon.getMaxHp());
expect(enemyLeadPokemon.hp).toBeLessThan(enemyLeadPokemon.getMaxHp());
expect(enemySecPokemon.hp).toBeLessThan(enemySecPokemon.getMaxHp());
});
it("doesn't switch out if the target has suction cups", async () => {
game.override.enemyAbility(Abilities.SUCTION_CUPS);
await game.classicMode.startBattle([Species.REGIELEKI]);
const enemy = game.scene.getEnemyPokemon()!;
game.move.select(Moves.DRAGON_TAIL);
await game.phaseInterceptor.to("TurnEndPhase");
expect(enemy.isFullHp()).toBe(false);
});
});

View File

@ -5,11 +5,14 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Moves - Shell Side Arm", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
@ -34,14 +37,11 @@ describe("Moves - Shell Side Arm", () => {
it("becomes a physical attack if forecasted to deal more damage as physical", async () => {
game.override.enemySpecies(Species.SNORLAX);
await game.classicMode.startBattle([Species.MANAPHY]);
await game.classicMode.startBattle([Species.RAMPARDOS]);
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
vi.spyOn(shellSideArmAttr, "apply");
game.move.select(Moves.SHELL_SIDE_ARM);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(shellSideArmAttr.apply).toHaveLastReturnedWith(true);
@ -50,14 +50,11 @@ describe("Moves - Shell Side Arm", () => {
it("remains a special attack if forecasted to deal more damage as special", async () => {
game.override.enemySpecies(Species.SLOWBRO);
await game.classicMode.startBattle([Species.MANAPHY]);
await game.classicMode.startBattle([Species.XURKITREE]);
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
vi.spyOn(shellSideArmAttr, "apply");
game.move.select(Moves.SHELL_SIDE_ARM);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(shellSideArmAttr.apply).toHaveLastReturnedWith(false);
@ -70,14 +67,10 @@ describe("Moves - Shell Side Arm", () => {
await game.classicMode.startBattle([Species.MANAPHY]);
const shellSideArm = allMoves[Moves.SHELL_SIDE_ARM];
const shellSideArmAttr = shellSideArm.getAttrs(ShellSideArmCategoryAttr)[0];
vi.spyOn(shellSideArmAttr, "apply");
game.move.select(Moves.SHELL_SIDE_ARM);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(shellSideArmAttr.apply).toHaveLastReturnedWith(false);

View File

@ -231,7 +231,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => {
});
describe("Option 3 - Leave", () => {
it.each(Array.from({length: 30}))("should leave encounter without battle", async () => {
it("should leave encounter without battle", async () => {
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, defaultParty);

View File

@ -42,7 +42,8 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
.startingWave(defaultWave)
.startingBiome(defaultBiome)
.disableTrainerWaves()
.enemyPassiveAbility(Abilities.BALL_FETCH);
.enemyPassiveAbility(Abilities.BALL_FETCH)
.enemyAbility(Abilities.BALL_FETCH);
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
new Map<Biome, MysteryEncounterType[]>([

View File

@ -25,6 +25,7 @@ import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/myster
import { CommandPhase } from "#app/phases/command-phase";
import { MovePhase } from "#app/phases/move-phase";
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
import { Abilities } from "#app/enums/abilities";
const namespace = "mysteryEncounter:theStrongStuff";
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
@ -43,10 +44,13 @@ describe("The Strong Stuff - Mystery Encounter", () => {
beforeEach(async () => {
game = new GameManager(phaserGame);
scene = game.scene;
game.override.mysteryEncounterChance(100);
game.override.startingWave(defaultWave);
game.override.startingBiome(defaultBiome);
game.override.disableTrainerWaves();
game.override
.mysteryEncounterChance(100)
.startingWave(defaultWave)
.startingBiome(defaultBiome)
.disableTrainerWaves()
.enemyAbility(Abilities.BALL_FETCH)
.enemyPassiveAbility(Abilities.BALL_FETCH);
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(
new Map<Biome, MysteryEncounterType[]>([

View File

@ -35,7 +35,7 @@ describe("Reload", () => {
expect(preReloadRngState).toBe(postReloadRngState);
}, 20000);
it.each(Array.from({length: 100}))("should not have RNG inconsistencies after a biome switch", async () => {
it("should not have RNG inconsistencies after a biome switch", async () => {
game.override
.startingWave(10)
.battleType("single")

View File

@ -28,7 +28,7 @@ export default class GameChallengesUiHandler extends UiHandler {
private descriptionText: BBCodeText;
private challengeLabels: Array<{ label: Phaser.GameObjects.Text, value: Phaser.GameObjects.Text }>;
private challengeLabels: Array<{ label: Phaser.GameObjects.Text, value: Phaser.GameObjects.Text, leftArrow: Phaser.GameObjects.Image, rightArrow: Phaser.GameObjects.Image }>;
private monoTypeValue: Phaser.GameObjects.Sprite;
private cursorObj: Phaser.GameObjects.NineSlice | null;
@ -40,6 +40,11 @@ export default class GameChallengesUiHandler extends UiHandler {
private optionsWidth: number;
private widestTextBox: number;
private readonly leftArrowGap: number = 90; // distance from the label to the left arrow
private readonly arrowSpacing: number = 3; // distance between the arrows and the value area
constructor(scene: BattleScene, mode: Mode | null = null) {
super(scene, mode);
}
@ -47,6 +52,8 @@ export default class GameChallengesUiHandler extends UiHandler {
setup() {
const ui = this.getUi();
this.widestTextBox = 0;
this.challengesContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1);
this.challengesContainer.setName("challenges");
@ -135,6 +142,20 @@ export default class GameChallengesUiHandler extends UiHandler {
this.valuesContainer.add(label);
const leftArrow = this.scene.add.image(0, 0, "cursor_reverse");
leftArrow.setName(`challenge-left-arrow-${i}`);
leftArrow.setOrigin(0, 0);
leftArrow.setVisible(false);
leftArrow.setScale(0.75);
this.valuesContainer.add(leftArrow);
const rightArrow = this.scene.add.image(0, 0, "cursor");
rightArrow.setName(`challenge-right-arrow-${i}`);
rightArrow.setOrigin(0, 0);
rightArrow.setScale(0.75);
rightArrow.setVisible(false);
this.valuesContainer.add(rightArrow);
const value = addTextObject(this.scene, 0, 28 + i * 16, "", TextStyle.SETTINGS_LABEL);
value.setName(`challenge-value-text-${i}`);
value.setPositionRelative(label, 100, 0);
@ -142,7 +163,9 @@ export default class GameChallengesUiHandler extends UiHandler {
this.challengeLabels[i] = {
label: label,
value: value
value: value,
leftArrow: leftArrow,
rightArrow: rightArrow
};
}
@ -187,10 +210,26 @@ export default class GameChallengesUiHandler extends UiHandler {
*/
initLabels(): void {
this.setDescription(this.scene.gameMode.challenges[0].getDescription());
this.widestTextBox = 0;
for (let i = 0; i < 9; i++) {
if (i < this.scene.gameMode.challenges.length) {
this.challengeLabels[i].label.setVisible(true);
this.challengeLabels[i].value.setVisible(true);
this.challengeLabels[i].leftArrow.setVisible(true);
this.challengeLabels[i].rightArrow.setVisible(true);
const tempText = addTextObject(this.scene, 0, 0, "", TextStyle.SETTINGS_LABEL); // this is added here to get the widest text object for this language, which will be used for the arrow placement
for (let j = 0; j <= this.scene.gameMode.challenges[i].maxValue; j++) { // this goes through each challenge's value to find out what the max width will be
if (this.scene.gameMode.challenges[i].id !== Challenges.SINGLE_TYPE) {
tempText.setText(this.scene.gameMode.challenges[i].getValue(j));
if (tempText.displayWidth > this.widestTextBox) {
this.widestTextBox = tempText.displayWidth;
}
}
}
tempText.destroy();
}
}
}
@ -203,16 +242,33 @@ export default class GameChallengesUiHandler extends UiHandler {
let monoTypeVisible = false;
for (let i = 0; i < Math.min(9, this.scene.gameMode.challenges.length); i++) {
const challenge = this.scene.gameMode.challenges[this.scrollCursor + i];
this.challengeLabels[i].label.setText(challenge.getName());
const challengeLabel = this.challengeLabels[i];
challengeLabel.label.setText(challenge.getName());
challengeLabel.leftArrow.setPositionRelative(challengeLabel.label, this.leftArrowGap, 4.5);
challengeLabel.leftArrow.setVisible(challenge.value !== 0);
challengeLabel.rightArrow.setPositionRelative(challengeLabel.leftArrow, Math.max(this.monoTypeValue.width, this.widestTextBox) + challengeLabel.leftArrow.displayWidth + 2 * this.arrowSpacing, 0);
challengeLabel.rightArrow.setVisible(challenge.value !== challenge.maxValue);
// this check looks to make sure that the arrows and value textbox don't take up too much space that they'll clip the right edge of the options background
if (challengeLabel.rightArrow.x + challengeLabel.rightArrow.width + this.optionsBg.rightWidth + this.arrowSpacing > this.optionsWidth) {
// if we go out of bounds of the box, set the x position as far right as we can without going past the box, with this.arrowSpacing to allow a small gap between the arrow and border
challengeLabel.rightArrow.setX(this.optionsWidth - this.arrowSpacing - this.optionsBg.rightWidth);
}
// this line of code gets the center point between the left and right arrows from their left side (Arrow.x gives middle point), taking into account the width of the arrows
const xLocation = Math.round((challengeLabel.leftArrow.x + challengeLabel.rightArrow.x + challengeLabel.leftArrow.displayWidth) / 2);
if (challenge.id === Challenges.SINGLE_TYPE) {
this.monoTypeValue.setPositionRelative(this.challengeLabels[i].label, 113, 8);
this.monoTypeValue.setX(xLocation);
this.monoTypeValue.setY(challengeLabel.label.y + 8);
this.monoTypeValue.setFrame(challenge.getValue());
this.monoTypeValue.setVisible(true);
this.challengeLabels[i].value.setVisible(false);
challengeLabel.value.setVisible(false);
monoTypeVisible = true;
} else {
this.challengeLabels[i].value.setText(challenge.getValue());
this.challengeLabels[i].value.setVisible(true);
challengeLabel.value.setText(challenge.getValue());
challengeLabel.value.setX(xLocation);
challengeLabel.value.setOrigin(0.5, 0);
challengeLabel.value.setVisible(true);
}
}
if (!monoTypeVisible) {
@ -244,6 +300,7 @@ export default class GameChallengesUiHandler extends UiHandler {
super.show(args);
this.startCursor.setVisible(false);
this.updateChallengeArrows(false);
this.challengesContainer.setVisible(true);
// Should always be false at the start
this.hasSelectedChallenge = this.scene.gameMode.challenges.some(c => c.value !== 0);
@ -259,6 +316,21 @@ export default class GameChallengesUiHandler extends UiHandler {
return true;
}
/* This code updates the challenge starter arrows to be tinted/not tinted when the start button is selected to show they can't be changed
*/
updateChallengeArrows(tinted: boolean) {
for (let i = 0; i < Math.min(9, this.scene.gameMode.challenges.length); i++) {
const challengeLabel = this.challengeLabels[i];
if (tinted) {
challengeLabel.leftArrow.setTint(0x808080);
challengeLabel.rightArrow.setTint(0x808080);
} else {
challengeLabel.leftArrow.clearTint();
challengeLabel.rightArrow.clearTint();
}
}
}
/**
* Processes input from a specified button.
* This method handles navigation through a UI menu, including movement through menu items
@ -280,6 +352,7 @@ export default class GameChallengesUiHandler extends UiHandler {
// If the user presses cancel when the start cursor has been activated, the game deactivates the start cursor and allows typical challenge selection behavior
this.startCursor.setVisible(false);
this.cursorObj?.setVisible(true);
this.updateChallengeArrows(this.startCursor.visible);
} else {
this.scene.clearPhaseQueue();
this.scene.pushPhase(new TitlePhase(this.scene));
@ -294,6 +367,7 @@ export default class GameChallengesUiHandler extends UiHandler {
} else {
this.startCursor.setVisible(true);
this.cursorObj?.setVisible(false);
this.updateChallengeArrows(this.startCursor.visible);
}
success = true;
} else {