mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-08 09:22:17 +02:00
Merge branch 'beta' into danish-workspace
This commit is contained in:
commit
4ccc66ed51
68
src/data/ability.ts
Executable file → Normal file
68
src/data/ability.ts
Executable file → Normal file
@ -1798,6 +1798,61 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for defining all {@linkcode Ability} Attributes after a status effect has been set.
|
||||
* @see {@linkcode applyPostSetStatus()}.
|
||||
*/
|
||||
export class PostSetStatusAbAttr extends AbAttr {
|
||||
/**
|
||||
* Does nothing after a status condition is set.
|
||||
* @param pokemon {@linkcode Pokemon} that status condition was set on.
|
||||
* @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is `null` if status was not set by a Pokemon.
|
||||
* @param passive Whether this ability is a passive.
|
||||
* @param effect {@linkcode StatusEffect} that was set.
|
||||
* @param args Set of unique arguments needed by this attribute.
|
||||
* @returns `true` if application of the ability succeeds.
|
||||
*/
|
||||
applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]) : boolean | Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If another Pokemon burns, paralyzes, poisons, or badly poisons this Pokemon,
|
||||
* that Pokemon receives the same non-volatile status condition as part of this
|
||||
* ability attribute. For Synchronize ability.
|
||||
*/
|
||||
export class SynchronizeStatusAbAttr extends PostSetStatusAbAttr {
|
||||
/**
|
||||
* If the `StatusEffect` that was set is Burn, Paralysis, Poison, or Toxic, and the status
|
||||
* was set by a source Pokemon, set the source Pokemon's status to the same `StatusEffect`.
|
||||
* @param pokemon {@linkcode Pokemon} that status condition was set on.
|
||||
* @param sourcePokemon {@linkcode Pokemon} that that set the status condition. Is null if status was not set by a Pokemon.
|
||||
* @param passive Whether this ability is a passive.
|
||||
* @param effect {@linkcode StatusEffect} that was set.
|
||||
* @param args Set of unique arguments needed by this attribute.
|
||||
* @returns `true` if application of the ability succeeds.
|
||||
*/
|
||||
override applyPostSetStatus(pokemon: Pokemon, sourcePokemon: Pokemon | null = null, passive: boolean, effect: StatusEffect, simulated: boolean, args: any[]): boolean {
|
||||
/** Synchronizable statuses */
|
||||
const syncStatuses = new Set<StatusEffect>([
|
||||
StatusEffect.BURN,
|
||||
StatusEffect.PARALYSIS,
|
||||
StatusEffect.POISON,
|
||||
StatusEffect.TOXIC
|
||||
]);
|
||||
|
||||
if (sourcePokemon && syncStatuses.has(effect)) {
|
||||
if (!simulated) {
|
||||
sourcePokemon.trySetStatus(effect, true, pokemon);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class PostVictoryAbAttr extends AbAttr {
|
||||
applyPostVictory(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||
return false;
|
||||
@ -4241,6 +4296,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;
|
||||
|
||||
@ -4677,6 +4736,10 @@ export function applyStatMultiplierAbAttrs(attrType: Constructor<StatMultiplierA
|
||||
pokemon: Pokemon, stat: BattleStat, statValue: Utils.NumberHolder, simulated: boolean = false, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<StatMultiplierAbAttr>(attrType, pokemon, (attr, passive) => attr.applyStatStage(pokemon, passive, simulated, stat, statValue, args), args);
|
||||
}
|
||||
export function applyPostSetStatusAbAttrs(attrType: Constructor<PostSetStatusAbAttr>,
|
||||
pokemon: Pokemon, effect: StatusEffect, sourcePokemon?: Pokemon | null, simulated: boolean = false, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostSetStatusAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), args, false, simulated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a field Stat multiplier attribute
|
||||
@ -4907,7 +4970,8 @@ export function initAbilities() {
|
||||
.attr(EffectSporeAbAttr),
|
||||
new Ability(Abilities.SYNCHRONIZE, 3)
|
||||
.attr(SyncEncounterNatureAbAttr)
|
||||
.unimplemented(),
|
||||
.attr(SynchronizeStatusAbAttr)
|
||||
.partial(), // interaction with psycho shift needs work, keeping to old Gen interaction for now
|
||||
new Ability(Abilities.CLEAR_BODY, 3)
|
||||
.attr(ProtectStatAbAttr)
|
||||
.ignorable(),
|
||||
@ -5879,6 +5943,6 @@ export function initAbilities() {
|
||||
new Ability(Abilities.POISON_PUPPETEER, 9)
|
||||
.attr(UncopiableAbilityAbAttr)
|
||||
.attr(UnswappableAbilityAbAttr)
|
||||
.conditionalAttr(pokemon => pokemon.species.speciesId===Species.PECHARUNT, ConfusionOnStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
.attr(ConfusionOnStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
|
||||
);
|
||||
}
|
||||
|
@ -915,12 +915,12 @@ export abstract class BattleAnim {
|
||||
this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ];
|
||||
this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ];
|
||||
|
||||
let r = anim!.frames.length; // TODO: is this bang correct?
|
||||
let r = anim?.frames.length ?? 0;
|
||||
let f = 0;
|
||||
|
||||
scene.tweens.addCounter({
|
||||
duration: Utils.getFrameMs(3),
|
||||
repeat: anim!.frames.length, // TODO: is this bang correct?
|
||||
repeat: anim?.frames.length ?? 0,
|
||||
onRepeat: () => {
|
||||
if (!f) {
|
||||
userSprite.setVisible(false);
|
||||
@ -1264,7 +1264,7 @@ export class CommonBattleAnim extends BattleAnim {
|
||||
}
|
||||
|
||||
getAnim(): AnimConfig | null {
|
||||
return this.commonAnim ? commonAnims.get(this.commonAnim)! : null; // TODO: is this bang correct?
|
||||
return this.commonAnim ? commonAnims.get(this.commonAnim) ?? null : null;
|
||||
}
|
||||
|
||||
isOppAnim(): boolean {
|
||||
@ -1284,7 +1284,7 @@ export class MoveAnim extends BattleAnim {
|
||||
getAnim(): AnimConfig {
|
||||
return moveAnims.get(this.move) instanceof AnimConfig
|
||||
? moveAnims.get(this.move) as AnimConfig
|
||||
: moveAnims.get(this.move)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct?
|
||||
: moveAnims.get(this.move)?.[this.user?.isPlayer() ? 0 : 1] as AnimConfig;
|
||||
}
|
||||
|
||||
isOppAnim(): boolean {
|
||||
@ -1316,7 +1316,7 @@ export class MoveChargeAnim extends MoveAnim {
|
||||
getAnim(): AnimConfig {
|
||||
return chargeAnims.get(this.chargeAnim) instanceof AnimConfig
|
||||
? chargeAnims.get(this.chargeAnim) as AnimConfig
|
||||
: chargeAnims.get(this.chargeAnim)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct?
|
||||
: chargeAnims.get(this.chargeAnim)?.[this.user?.isPlayer() ? 0 : 1] as AnimConfig;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2281,6 +2281,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;
|
||||
@ -2568,6 +2598,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:
|
||||
|
@ -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}`) });
|
||||
}
|
||||
|
||||
|
||||
|
138
src/data/move.ts
138
src/data/move.ts
@ -650,7 +650,7 @@ export default class Move implements Localizable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies each {@linkcode MoveCondition} of this move to the params
|
||||
* Applies each {@linkcode MoveCondition} function of this move to the params, determines if the move can be used prior to calling each attribute's apply()
|
||||
* @param user {@linkcode Pokemon} to apply conditions to
|
||||
* @param target {@linkcode Pokemon} to apply conditions to
|
||||
* @param move {@linkcode Move} to apply conditions to
|
||||
@ -2091,21 +2091,20 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr {
|
||||
|
||||
if (target.status) {
|
||||
return false;
|
||||
}
|
||||
//@ts-ignore - how can target.status.effect be checked when we return `false` before when it's defined?
|
||||
if (!target.status || (target.status.effect === statusToApply && move.chance < 0)) { // TODO: resolve ts-ignore
|
||||
const statusAfflictResult = target.trySetStatus(statusToApply, true, user);
|
||||
if (statusAfflictResult) {
|
||||
} else {
|
||||
const canSetStatus = target.canSetStatus(statusToApply, true, false, user);
|
||||
|
||||
if (canSetStatus) {
|
||||
if (user.status) {
|
||||
user.scene.queueMessage(getStatusEffectHealText(user.status.effect, getPokemonNameWithAffix(user)));
|
||||
}
|
||||
user.resetStatus();
|
||||
user.updateInfo();
|
||||
target.trySetStatus(statusToApply, true, user);
|
||||
}
|
||||
return statusAfflictResult;
|
||||
}
|
||||
|
||||
return false;
|
||||
return canSetStatus;
|
||||
}
|
||||
}
|
||||
|
||||
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
||||
@ -5175,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) {
|
||||
@ -5208,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 {
|
||||
@ -5257,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();
|
||||
};
|
||||
}
|
||||
|
||||
@ -5287,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));
|
||||
}
|
||||
@ -5296,6 +5299,21 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ChillyReceptionAttr extends ForceSwitchOutAttr {
|
||||
|
||||
// using inherited constructor
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
user.scene.arena.trySetWeather(WeatherType.SNOW, true);
|
||||
return super.apply(user, target, move, args);
|
||||
}
|
||||
|
||||
getCondition(): MoveConditionFunc {
|
||||
// chilly reception move will go through if the weather is change-able to snow, or the user can switch out, else move will fail
|
||||
return (user, target, move) => user.scene.arena.trySetWeather(WeatherType.SNOW, true) || super.getSwitchOutCondition()(user, target, move);
|
||||
}
|
||||
}
|
||||
export class RemoveTypeAttr extends MoveEffectAttr {
|
||||
|
||||
private removedType: Type;
|
||||
@ -8091,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),
|
||||
@ -9073,8 +9091,7 @@ export function initMoves() {
|
||||
new AttackMove(Moves.AURA_WHEEL, Type.ELECTRIC, MoveCategory.PHYSICAL, 110, 100, 10, 100, 0, 8)
|
||||
.attr(StatStageChangeAttr, [ Stat.SPD ], 1, true)
|
||||
.makesContact(false)
|
||||
.attr(AuraWheelTypeAttr)
|
||||
.condition((user, target, move) => [user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)), // Missing custom fail message
|
||||
.attr(AuraWheelTypeAttr),
|
||||
new AttackMove(Moves.BREAKING_SWIPE, Type.DRAGON, MoveCategory.PHYSICAL, 60, 100, 15, 100, 0, 8)
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK ], -1),
|
||||
@ -9486,10 +9503,9 @@ export function initMoves() {
|
||||
.makesContact(),
|
||||
new SelfStatusMove(Moves.SHED_TAIL, Type.NORMAL, -1, 10, -1, 0, 9)
|
||||
.unimplemented(),
|
||||
new StatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9)
|
||||
.attr(WeatherChangeAttr, WeatherType.SNOW)
|
||||
.attr(ForceSwitchOutAttr, true, false)
|
||||
.target(MoveTarget.BOTH_SIDES),
|
||||
new SelfStatusMove(Moves.CHILLY_RECEPTION, Type.ICE, -1, 10, -1, 0, 9)
|
||||
.attr(PreMoveMessageAttr, (user, move) => i18next.t("moveTriggers:chillyReception", {pokemonName: getPokemonNameWithAffix(user)}))
|
||||
.attr(ChillyReceptionAttr, true, false),
|
||||
new SelfStatusMove(Moves.TIDY_UP, Type.NORMAL, -1, 10, -1, 0, 9)
|
||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPD ], 1, true, null, true, true)
|
||||
.attr(RemoveArenaTrapAttr, true)
|
||||
|
@ -597,7 +597,7 @@ export class TrainerConfig {
|
||||
case "flare": {
|
||||
return {
|
||||
[TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKORUPI, Species.PURRLOIN, Species.CLAWITZER, Species.PANCHAM, Species.ESPURR, Species.BUNNELBY],
|
||||
[TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.BERGMITE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO],
|
||||
[TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO],
|
||||
[TrainerPoolTier.RARE]: [Species.NOIVERN, Species.HISUI_AVALUGG, Species.HISUI_SLIGGOO]
|
||||
};
|
||||
}
|
||||
@ -640,14 +640,14 @@ export class TrainerConfig {
|
||||
return {
|
||||
[TrainerPoolTier.COMMON]: [ Species.ZUBAT, Species.GRIMER, Species.STUNKY, Species.FOONGUS, Species.MAREANIE, Species.TOXEL, Species.SHROODLE, Species.PALDEA_WOOPER ],
|
||||
[TrainerPoolTier.UNCOMMON]: [ Species.GASTLY, Species.SEVIPER, Species.SKRELP, Species.ALOLA_GRIMER, Species.GALAR_SLOWPOKE, Species.HISUI_QWILFISH ],
|
||||
[TrainerPoolTier.RARE]: [ Species.BULBASAUR, Species.GLIMMET ]
|
||||
[TrainerPoolTier.RARE]: [ Species.GLIMMET, Species.BULBASAUR ]
|
||||
};
|
||||
}
|
||||
case "star_4": {
|
||||
return {
|
||||
[TrainerPoolTier.COMMON]: [ Species.CLEFFA, Species.IGGLYBUFF, Species.AZURILL, Species.COTTONEE, Species.FLABEBE, Species.HATENNA, Species.IMPIDIMP, Species.TINKATINK ],
|
||||
[TrainerPoolTier.UNCOMMON]: [ Species.TOGEPI, Species.GARDEVOIR, Species.SYLVEON, Species.KLEFKI, Species.MIMIKYU, Species.ALOLA_VULPIX ],
|
||||
[TrainerPoolTier.RARE]: [ Species.POPPLIO, Species.GALAR_PONYTA ]
|
||||
[TrainerPoolTier.RARE]: [ Species.GALAR_PONYTA, Species.POPPLIO ]
|
||||
};
|
||||
}
|
||||
case "star_5": {
|
||||
@ -1509,7 +1509,7 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setSpeciesPools({
|
||||
[TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL, Species.REMORAID, Species.QWILFISH, Species.BARBOACH],
|
||||
[TrainerPoolTier.UNCOMMON]: [Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.CLOBBOPUS, Species.HORSEA],
|
||||
[TrainerPoolTier.RARE]: [Species.MANTINE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP],
|
||||
[TrainerPoolTier.RARE]: [Species.MANTYKE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP],
|
||||
[TrainerPoolTier.SUPER_RARE]: [Species.DONDOZO, Species.BASCULEGION]
|
||||
}),
|
||||
[TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [Species.SHARPEDO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
|
||||
@ -1527,8 +1527,8 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
[TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene))
|
||||
.setSpeciesPools({
|
||||
[TrainerPoolTier.COMMON]: [Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH, Species.TYMPOLE],
|
||||
[TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK],
|
||||
[TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK, Species.CUBCHOO, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT],
|
||||
[TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK, Species.CUBCHOO, Species.KLINK],
|
||||
[TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT],
|
||||
[TrainerPoolTier.SUPER_RARE]: [Species.DRUDDIGON, Species.HISUI_ZORUA, Species.AXEW, Species.DEINO]
|
||||
}),
|
||||
[TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [Species.CRYOGONAL]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
|
||||
@ -1537,7 +1537,7 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
.setSpeciesPools({
|
||||
[TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR],
|
||||
[TrainerPoolTier.UNCOMMON]: [Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP],
|
||||
[TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.BERGMITE, Species.SLIGGOO],
|
||||
[TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.SLIGGOO],
|
||||
[TrainerPoolTier.SUPER_RARE]: [Species.NOIVERN, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG]
|
||||
}),
|
||||
[TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [Species.LIEPARD]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
|
||||
@ -1545,15 +1545,15 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
[TrainerType.AETHER_GRUNT]: new TrainerConfig(++t).setHasGenders("Aether Grunt Female").setHasDouble("Aether Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene))
|
||||
.setSpeciesPools({
|
||||
[TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.ALOLA_EXEGGUTOR, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.ALOLA_RAICHU, Species.BOUNSWEET, Species.LILLIPUP, Species.KOMALA, Species.MORELULL, Species.COMFEY, Species.TOGEDEMARU],
|
||||
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK],
|
||||
[TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.TURTONATOR, Species.DRAMPA],
|
||||
[TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK],
|
||||
[TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.TURTONATOR, Species.MIMIKYU, Species.MAGNEMITE, Species.DRAMPA],
|
||||
[TrainerPoolTier.SUPER_RARE]: [Species.JANGMO_O, Species.PORYGON]
|
||||
}),
|
||||
[TrainerType.FABA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aether_admin", "aether", [Species.HYPNO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)),
|
||||
[TrainerType.SKULL_GRUNT]: new TrainerConfig(++t).setHasGenders("Skull Grunt Female").setHasDouble("Skull Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene))
|
||||
.setSpeciesPools({
|
||||
[TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH],
|
||||
[TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.DROWZEE, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY],
|
||||
[TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH, Species.DROWZEE],
|
||||
[TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY],
|
||||
[TrainerPoolTier.RARE]: [Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.WISHIWASHI, Species.NYMBLE],
|
||||
[TrainerPoolTier.SUPER_RARE]: [Species.GRUBBIN, Species.DEWPIDER]
|
||||
}),
|
||||
@ -1916,7 +1916,14 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
p.formIndex = 1; // Mega Kangaskhan
|
||||
p.generateName();
|
||||
}))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON, Species.SEISMITOAD]))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON, Species.SEISMITOAD], TrainerSlot.TRAINER, true, p => {
|
||||
//Storm Drain Gastrodon, Water Absorb Seismitoad
|
||||
if (p.species.speciesId === Species.GASTRODON) {
|
||||
p.abilityIndex = 0;
|
||||
} else if (p.species.speciesId === Species.SEISMITOAD) {
|
||||
p.abilityIndex = 2;
|
||||
}
|
||||
}))
|
||||
.setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.MEWTWO], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
@ -2060,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();
|
||||
@ -2153,9 +2160,23 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
p.pokeball = PokeballType.MASTER_BALL;
|
||||
})),
|
||||
[TrainerType.GUZMA]: new TrainerConfig(++t).setName("Guzma").initForEvilTeamLeader("Skull Boss", []).setMixedBattleBgm("battle_skull_boss").setVictoryBgm("victory_team_plasma")
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LOKIX, Species.YANMEGA ]))
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.LOKIX, Species.YANMEGA ], TrainerSlot.TRAINER, true, p => {
|
||||
//Tinted Lens Lokix, Tinted Lens Yanmega
|
||||
if (p.species.speciesId === Species.LOKIX) {
|
||||
p.abilityIndex = 2;
|
||||
} else if (p.species.speciesId === Species.YANMEGA) {
|
||||
p.abilityIndex = 1;
|
||||
}
|
||||
}))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HERACROSS ]))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ]))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ], TrainerSlot.TRAINER, true, p => {
|
||||
//Technician Scizor, Sharpness Kleavor
|
||||
if (p.species.speciesId === Species.SCIZOR) {
|
||||
p.abilityIndex = 1;
|
||||
} else if (p.species.speciesId === Species.KLEAVOR) {
|
||||
p.abilityIndex = 2;
|
||||
}
|
||||
}))
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GALVANTULA, Species.VIKAVOLT]))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.PINSIR ], TrainerSlot.TRAINER, true, p => {
|
||||
p.generateAndPopulateMoveset();
|
||||
@ -2175,25 +2196,32 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
p.abilityIndex = 2; //Anticipation
|
||||
p.pokeball = PokeballType.ULTRA_BALL;
|
||||
}))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.HISUI_SAMUROTT, Species.CRAWDAUNT ], TrainerSlot.TRAINER, true, p => {
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ], TrainerSlot.TRAINER, true, p => {
|
||||
//Technician Scizor, Sharpness Kleavor
|
||||
if (p.species.speciesId === Species.SCIZOR) {
|
||||
p.abilityIndex = 1;
|
||||
} else if (p.species.speciesId === Species.KLEAVOR) {
|
||||
p.abilityIndex = 2;
|
||||
}
|
||||
}))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.HISUI_SAMUROTT, Species.CRAWDAUNT ], TrainerSlot.TRAINER, true, p => {
|
||||
p.abilityIndex = 2; //Sharpness Hisui Samurott, Adaptability Crawdaunt
|
||||
}))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ]))
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.PINSIR ], TrainerSlot.TRAINER, true, p => {
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.BUZZWOLE ], TrainerSlot.TRAINER, true, p => {
|
||||
p.generateAndPopulateMoveset();
|
||||
p.pokeball = PokeballType.ROGUE_BALL;
|
||||
}))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.XURKITREE ], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
p.pokeball = PokeballType.ROGUE_BALL;
|
||||
}))
|
||||
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.PINSIR ], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.formIndex = 1;
|
||||
p.generateAndPopulateMoveset();
|
||||
p.generateName();
|
||||
p.pokeball = PokeballType.ULTRA_BALL;
|
||||
}))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.BUZZWOLE ], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
p.pokeball = PokeballType.ROGUE_BALL;
|
||||
}))
|
||||
.setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.XURKITREE ], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
p.pokeball = PokeballType.ROGUE_BALL;
|
||||
})),
|
||||
[TrainerType.ROSE]: new TrainerConfig(++t).setName("Rose").initForEvilTeamLeader("Macro Boss", []).setMixedBattleBgm("battle_macro_boss").setVictoryBgm("victory_team_plasma")
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ]))
|
||||
@ -2209,17 +2237,16 @@ export const trainerConfigs: TrainerConfigs = {
|
||||
p.pokeball = PokeballType.ULTRA_BALL;
|
||||
})),
|
||||
[TrainerType.ROSE_2]: new TrainerConfig(++t).setName("Rose").initForEvilTeamLeader("Macro Boss", [], true).setMixedBattleBgm("battle_macro_boss").setVictoryBgm("victory_team_plasma")
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.MELMETAL ], TrainerSlot.TRAINER, true, p => {
|
||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
p.pokeball = PokeballType.ULTRA_BALL;
|
||||
}))
|
||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.AEGISLASH, Species.GHOLDENGO ]))
|
||||
.setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.DRACOVISH, Species.DRACOZOLT ], TrainerSlot.TRAINER, true, p => {
|
||||
p.generateAndPopulateMoveset();
|
||||
p.abilityIndex = 1; //Strong Jaw Dracovish, Hustle Dracozolt
|
||||
}))
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.ARCHALUDON ]))
|
||||
.setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.MELMETAL ]))
|
||||
.setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.GALAR_ARTICUNO, Species.GALAR_ZAPDOS, Species.GALAR_MOLTRES ], TrainerSlot.TRAINER, true, p => {
|
||||
p.setBoss(true, 2);
|
||||
p.generateAndPopulateMoveset();
|
||||
|
@ -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",
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ export class Arena {
|
||||
public tags: ArenaTag[];
|
||||
public bgm: string;
|
||||
public ignoreAbilities: boolean;
|
||||
public ignoringEffectSource: BattlerIndex | null;
|
||||
|
||||
private lastTimeOfDay: TimeOfDay;
|
||||
|
||||
@ -569,8 +570,9 @@ export class Arena {
|
||||
}
|
||||
}
|
||||
|
||||
setIgnoreAbilities(ignoreAbilities: boolean = true): void {
|
||||
setIgnoreAbilities(ignoreAbilities: boolean, ignoringEffectSource: BattlerIndex | null = null): void {
|
||||
this.ignoreAbilities = ignoreAbilities;
|
||||
this.ignoringEffectSource = ignoreAbilities ? ignoringEffectSource : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,10 +17,10 @@ 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 } from "../data/ability";
|
||||
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";
|
||||
import PokemonData from "../system/pokemon-data";
|
||||
import { BattlerIndex } from "../battle";
|
||||
import { Mode } from "../ui/ui";
|
||||
@ -1364,7 +1364,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (this.isFusion() && ability.hasAttr(NoFusionAbilityAbAttr)) {
|
||||
return false;
|
||||
}
|
||||
if (this.scene?.arena.ignoreAbilities && ability.isIgnorable) {
|
||||
const arena = this.scene?.arena;
|
||||
if (arena.ignoreAbilities && arena.ignoringEffectSource !== this.getBattlerIndex() && ability.isIgnorable) {
|
||||
return false;
|
||||
}
|
||||
if (this.summonData?.abilitySuppressed && !ability.hasAttr(UnsuppressableAbilityAbAttr)) {
|
||||
@ -1426,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3365,7 +3378,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
}
|
||||
|
||||
if (asPhase) {
|
||||
this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText!, sourcePokemon!)); // TODO: are these bangs correct?
|
||||
this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText, sourcePokemon));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3399,6 +3412,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
||||
if (effect !== StatusEffect.FAINT) {
|
||||
this.scene.triggerPokemonFormChange(this, SpeciesFormChangeStatusEffectTrigger, true);
|
||||
applyPostSetStatusAbAttrs(PostSetStatusAbAttr, this, effect, sourcePokemon);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "Giftpuppenspiel",
|
||||
"description": "Wenn Infamomo ein Ziel mit einer Attacke vergiftet, so wird dieses auch verwirrt."
|
||||
"description": "Wenn das Pokémon ein Ziel mit einer Attacke vergiftet, so wird dieses auch verwirrt."
|
||||
}
|
||||
}
|
@ -111,17 +111,17 @@
|
||||
"forest": "PMD Erkundungsteam Himmel Düsterwald",
|
||||
"grass": "PMD Erkundungsteam Himmel Apfelwald",
|
||||
"graveyard": "PMD Erkundungsteam Himmel Verwirrwald",
|
||||
"ice_cave": "PMD Erkundungsteam Himmel Rieseneisberg",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "PMD Erkundungsteam Himmel Schroffküste",
|
||||
"jungle": "Lmz - Jungle",
|
||||
"laboratory": "Firel - Laboratory",
|
||||
"lake": "PMD Erkundungsteam Himmel Kristallhöhle",
|
||||
"lake": "Lmz - Lake",
|
||||
"meadow": "PMD Erkundungsteam Himmel Himmelsgipfel-Wald",
|
||||
"metropolis": "Firel - Metropolis",
|
||||
"mountain": "PMD Erkundungsteam Himmel Hornberg",
|
||||
"plains": "PMD Erkundungsteam Himmel Himmelsgipfel-Prärie",
|
||||
"power_plant": "PMD Erkundungsteam Himmel Weite Ampere-Ebene",
|
||||
"ruins": "PMD Erkundungsteam Himmel Tiefes Ruinenverlies",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - Marine Mystique",
|
||||
"seabed": "Firel - Seabed",
|
||||
"slum": "Andr06 - Sneaky Snom",
|
||||
@ -131,7 +131,7 @@
|
||||
"tall_grass": "PMD Erkundungsteam Himmel Nebelwald",
|
||||
"temple": "PMD Erkundungsteam Himmel Ägishöhle",
|
||||
"town": "PMD Erkundungsteam Himmel Zufälliges Dungeon-Theme 3",
|
||||
"volcano": "PMD Erkundungsteam Himmel Dunsthöhle",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "PMD Erkundungsteam Himmel Verborgenes Hochland",
|
||||
"encounter_ace_trainer": "SW Trainerblicke treffen sich (Ass-Trainer)",
|
||||
"encounter_backpacker": "SW Trainerblicke treffen sich (Backpacker)",
|
||||
|
@ -65,6 +65,7 @@
|
||||
"suppressAbilities": "Die Fähigkeit von {{pokemonName}} wirkt nicht mehr!",
|
||||
"revivalBlessing": "{{pokemonName}} ist wieder fit und kampfbereit!",
|
||||
"swapArenaTags": "{{pokemonName}} hat die Effekte, die auf den beiden Seiten des Kampffeldes wirken, miteinander getauscht!",
|
||||
"chillyReception": "{{pokemonName}} erzählt einen schlechten Witz, der nicht besonders gut ankommt...",
|
||||
"exposedMove": "{{pokemonName}} erkennt {{targetPokemonName}}!",
|
||||
"safeguard": "{{targetName}} wird durch Bodyguard geschützt!",
|
||||
"afterYou": "{{targetName}} lässt sich auf Galanterie ein!"
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "Aura-Rad",
|
||||
"effect": "Mithilfe der in den Backentaschen gespeicherten Energie greift der Anwender an und erhöht seine Initiative. Der Typ der Attacke hängt von Morpekos Form ab."
|
||||
"effect": "Mithilfe der in den Backentaschen gespeicherten Energie greift der Anwender an und erhöht seine Initiative. Wenn dies von Morpeko verwendet wird hängt der Typ der Attacke von dessen Form ab."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "Breitseite",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "Poison Puppeteer",
|
||||
"description": "Pokémon poisoned by Pecharunt's moves will also become confused."
|
||||
"description": "Pokémon poisoned by this Pokémon's moves will also become confused."
|
||||
}
|
||||
}
|
@ -73,5 +73,6 @@
|
||||
"tarShotOnAdd": "{{pokemonNameWithAffix}} became weaker to fire!",
|
||||
"substituteOnAdd": "{{pokemonNameWithAffix}} put in a substitute!",
|
||||
"substituteOnHit": "The substitute took damage for {{pokemonNameWithAffix}}!",
|
||||
"substituteOnRemove": "{{pokemonNameWithAffix}}'s substitute faded!"
|
||||
"substituteOnRemove": "{{pokemonNameWithAffix}}'s substitute faded!",
|
||||
"autotomizeOnAdd": "{{pokemonNameWIthAffix}} became nimble!"
|
||||
}
|
||||
|
@ -111,7 +111,7 @@
|
||||
"forest": "PMD EoS Dusk Forest",
|
||||
"grass": "PMD EoS Apple Woods",
|
||||
"graveyard": "PMD EoS Mystifying Forest",
|
||||
"ice_cave": "Firel - -60F",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "PMD EoS Craggy Coast",
|
||||
"jungle": "Lmz - Jungle",
|
||||
"laboratory": "Firel - Laboratory",
|
||||
|
@ -66,6 +66,7 @@
|
||||
"suppressAbilities": "{{pokemonName}}'s ability\nwas suppressed!",
|
||||
"revivalBlessing": "{{pokemonName}} was revived!",
|
||||
"swapArenaTags": "{{pokemonName}} swapped the battle effects affecting each side of the field!",
|
||||
"chillyReception": "{{pokemonName}} is preparing to tell a chillingly bad joke!",
|
||||
"exposedMove": "{{pokemonName}} identified\n{{targetPokemonName}}!",
|
||||
"safeguard": "{{targetName}} is protected by Safeguard!",
|
||||
"substituteOnOverlap": "{{pokemonName}} already\nhas a substitute!",
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "Aura Wheel",
|
||||
"effect": "Morpeko attacks and raises its Speed with the energy stored in its cheeks. This move's type changes depending on the user's form."
|
||||
"effect": "The user attacks and raises its Speed with the energy stored in its cheeks. If used by Morpeko, this move's type changes depending on the user's form."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "Breaking Swipe",
|
||||
|
@ -4,7 +4,7 @@
|
||||
"costar": "¡{{pokemonName}} copió los cambios de características de {{allyName}}!",
|
||||
"iceFaceAvoidedDamage": "¡{{pokemonNameWithAffix}} evitó\ndaño con {{abilityName}}!",
|
||||
"perishBody": "¡{{abilityName}} de {{pokemonName}} debilitará a ambos Pokémon en 3 turnos!",
|
||||
"poisonHeal": "¡{{pokemonNameWithAffix}} restauró algunos de sus PS gracias a {{abilityName}}!",
|
||||
"poisonHeal": "¡{{pokemonName}} restauró algunos de sus PS gracias a {{abilityName}}!",
|
||||
"trace": "¡{{pokemonName}} ha copiado la habilidad {{abilityName}} \nde {{targetName}}!",
|
||||
"windPowerCharged": "¡{{pokemonName}} se ha cargado de electricidad gracias a {{moveName}}!",
|
||||
"quickDraw": "¡{{pokemonName}} ataca primero gracias a la habilidad Mano Rápida!",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "Títere Tóxico",
|
||||
"description": "Los rivales que Pecharunt envenene con sus movimientos también sufrirán confusión."
|
||||
"description": "Los rivales que el usuario envenene con sus movimientos también sufrirán confusión."
|
||||
}
|
||||
}
|
||||
|
@ -107,17 +107,17 @@
|
||||
"forest": "PMD EoS - Bosque Sombrío",
|
||||
"grass": "PMD EoS - Manzanar",
|
||||
"graveyard": "PMD EoS - Bosque Misterio",
|
||||
"ice_cave": "PMD EoS - Gran Iceberg",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "PMD EoS - Costa Escarpada",
|
||||
"jungle": "Lmz - Jungla",
|
||||
"laboratory": "Firel - Laboratorio",
|
||||
"lake": "PMD EoS - Cueva Cristal",
|
||||
"lake": "Lmz - Lake",
|
||||
"meadow": "PMD EoS - Bosque de la Cumbre del Cielo",
|
||||
"metropolis": "Firel - Metrópolis",
|
||||
"mountain": "PMD EoS - Monte Cuerno",
|
||||
"plains": "PMD EoS - Pradera de la Cumbre del Cielo",
|
||||
"power_plant": "PMD EoS - Pradera Destello",
|
||||
"ruins": "PMD EoS - Sima Hermética",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - Misticismo marino",
|
||||
"seabed": "Firel - Lecho del mar",
|
||||
"slum": "Andr06 - Snom sigiloso",
|
||||
@ -127,7 +127,7 @@
|
||||
"tall_grass": "PMD EoS - Bosque Niebla",
|
||||
"temple": "PMD EoS - Cueva Regia",
|
||||
"town": "PMD EoS - Tema del territorio aleatorio 3",
|
||||
"volcano": "PMD EoS - Cueva Vapor",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "PMD EoS - Corazón Tierra Oculta",
|
||||
"encounter_ace_trainer": "BW - ¡Vs. entrenador guay!",
|
||||
"encounter_backpacker": "BW - ¡Vs. mochilero!",
|
||||
|
@ -2913,7 +2913,7 @@
|
||||
},
|
||||
"zippyZap": {
|
||||
"name": "Pikaturbo",
|
||||
"effect": "The user attacks the target with bursts of electricity at high speed. This move always goes first and raises the user's evasiveness."
|
||||
"effect": "Ataque eléctrico a la velocidad del rayo. Este movimiento tiene prioridad alta y aumenta la Evasión del usuario."
|
||||
},
|
||||
"splishySplash": {
|
||||
"name": "Salpikasurf",
|
||||
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "Rueda Aural",
|
||||
"effect": "La energía que acumula en las mejillas le sirve para atacar y aumentar su Velocidad. Este movimiento cambia de tipo según la forma que adopte Morpeko."
|
||||
"effect": "La energía que acumula en las mejillas le sirve para atacar y aumentar su Velocidad. Si es utilizado por Morpeko, este movimiento cambia de tipo según la forma que adopte."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "Vasto Impacto",
|
||||
|
@ -1077,7 +1077,7 @@
|
||||
},
|
||||
"thermalExchange": {
|
||||
"name": "Thermodynamique",
|
||||
"description": "Lorsque le Pokémon est touché par une capacité de type Feu, il ne subit aucun dégât et son Attaque augmente."
|
||||
"description": "Lorsque le Pokémon est touché par une capacité de type Feu, son Attaque augmente. Il ne peut pas être brulé."
|
||||
},
|
||||
"angerShell": {
|
||||
"name": "Courroupace",
|
||||
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "Emprise Toxique",
|
||||
"description": "Lorsque Pêchaminus empoisonne un Pokémon grâce à l’une de ses capacités, ce dernier devient également confus."
|
||||
"description": "Lorsque le Pokémon en empoisonne un autre grâce à l’une de ses capacités, ce dernier devient également confus."
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,10 @@
|
||||
"moneyWon": "Vous remportez\n{{moneyAmount}} ₽ !",
|
||||
"moneyPickedUp": "Vous obtenez {{moneyAmount}} ₽ !",
|
||||
"pokemonCaught": "Vous avez attrapé\n{{pokemonName}} !",
|
||||
"pokemonObtained": "Vous obtenez\nun {{pokemonName}} !",
|
||||
"pokemonBrokeFree": "Oh non !\nLe Pokémon s’est libéré !",
|
||||
"pokemonFled": "Le {{pokemonName}} sauvage\nprend la fuite !",
|
||||
"playerFled": "Vous fuyez le {{pokemonName}} !",
|
||||
"addedAsAStarter": "{{pokemonName}} est ajouté\ncomme starter !",
|
||||
"partyFull": "Votre équipe est pleine.\nRelâcher un Pokémon pour {{pokemonName}} ?",
|
||||
"pokemon": "de Pokémon",
|
||||
@ -65,6 +69,7 @@
|
||||
"skipItemQuestion": "Êtes-vous sûr·e de ne pas vouloir prendre d’objet ?",
|
||||
"itemStackFull": "Quantité maximale de {{fullItemName}} atteinte.\nVous recevez {{itemName}} à la place.",
|
||||
"eggHatching": "Hein ?",
|
||||
"eggSkipPrompt": "Aller directement au résumé des Œufs éclos ?",
|
||||
"ivScannerUseQuestion": "Utiliser le Scanner d’IV\nsur {{pokemonName}} ?",
|
||||
"wildPokemonWithAffix": "{{pokemonName}} sauvage",
|
||||
"foePokemonWithAffix": "{{pokemonName}} ennemi",
|
||||
@ -99,7 +104,7 @@
|
||||
"unlockedSomething": "{{unlockedThing}}\na été débloqué.",
|
||||
"congratulations": "Félicitations !",
|
||||
"beatModeFirstTime": "{{speciesName}} a battu le mode {{gameMode}} pour la première fois !\nVous avez reçu {{newModifier}} !",
|
||||
"eggSkipPrompt": "Aller directement au résumé des Œufs éclos ?",
|
||||
"ppReduced": "Les PP de la capacité {{moveName}}\nde{{targetName}} baissent de {{reduction}} !",
|
||||
"battlerTagsHealBlock": "{{pokemonNameWithAffix}} ne peut pas guérir !",
|
||||
"battlerTagsHealBlockOnRemove": "Le blocage de soins qui affectait\n{{pokemonNameWithAffix}} s’est dissipé !"
|
||||
}
|
||||
|
@ -111,17 +111,17 @@
|
||||
"forest": "PDM EdC - Forêt Crépuscule",
|
||||
"grass": "PDM EdC - Bois aux Pommes",
|
||||
"graveyard": "PDM EdC - Forêt Trompeuse",
|
||||
"ice_cave": "PDM EdC - Montagne Glacier",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "PDM EdC - Côte Escarpée",
|
||||
"jungle": "Lmz - Jungle",
|
||||
"laboratory": "Firel - Laboratory",
|
||||
"lake": "PDM EdC - Caverne Cristal",
|
||||
"lake": "Lmz - Lake",
|
||||
"meadow": "PDM EdC - Pic Céleste (forêt)",
|
||||
"metropolis": "Firel - Metropolis",
|
||||
"mountain": "PDM EdC - Mont Corne",
|
||||
"plains": "PDM EdC - Pic Céleste (prairie)",
|
||||
"power_plant": "PDM EdC - Plaines Élek",
|
||||
"ruins": "PDM EdC - Ruine Scellée",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - Marine Mystique",
|
||||
"seabed": "Firel - Seabed",
|
||||
"slum": "Andr06 - Sneaky Snom",
|
||||
@ -131,7 +131,7 @@
|
||||
"tall_grass": "PDM EdC - Forêt Brumeuse",
|
||||
"temple": "PDM EdC - Grotte Égide",
|
||||
"town": "PDM EdC - Donjon aléatoire - Thème 3",
|
||||
"volcano": "PDM EdC - Grotte Étuve",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "PDM EdC - Terres Illusoires",
|
||||
"encounter_ace_trainer": "NB - Regards croisés (Topdresseur·euse)",
|
||||
"encounter_backpacker": "NB - Regards croisés (Randonneur·euse)",
|
||||
|
@ -66,6 +66,7 @@
|
||||
"suppressAbilities": "Le talent de {{pokemonName}}\na été rendu inactif !",
|
||||
"revivalBlessing": "{{pokemonName}} a repris connaissance\net est prêt à se battre de nouveau !",
|
||||
"swapArenaTags": "Les effets affectant chaque côté du terrain\nont été échangés par {{pokemonName}} !",
|
||||
"chillyReception": "{{pokemonName}} s’apprête\nà faire un mauvais jeu de mots…",
|
||||
"exposedMove": "{{targetPokemonName}} est identifié\npar {{pokemonName}} !",
|
||||
"safeguard": "{{targetName}} est protégé\npar la capacité Rune Protect !",
|
||||
"substituteOnOverlap": "{{pokemonName}} a déjà\nun clone !",
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "Roue Libre",
|
||||
"effect": "Inflige et change en type Ténèbres"
|
||||
"effect": "Le Pokémon libère l’énergie stockée dans ses joues pour attaquer et augmenter sa Vitesse. Le type de cette capacité change en fonction de la forme du lanceur."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "Abattage",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "\tMalia Tossica",
|
||||
"description": "I Pokémon avvelenati dalle mosse di Pecharunt entreranno anche in stato di confusione."
|
||||
"description": "I Pokémon avvelenati dalle mosse di questo Pokémon entreranno anche in stato di confusione."
|
||||
}
|
||||
}
|
@ -66,6 +66,7 @@
|
||||
"revivalBlessing": "{{pokemonName}} torna in forze!",
|
||||
"swapArenaTags": "{{pokemonName}} ha invertito gli effetti attivi\nnelle due metà del campo!",
|
||||
"exposedMove": "{{pokemonName}} ha identificato\n{{targetPokemonName}}!",
|
||||
"chillyReception": "{{pokemonName}} sta per fare una battuta!",
|
||||
"safeguard": "Salvaguardia protegge {{targetName}}!",
|
||||
"afterYou": "{{pokemonName}} approfitta della cortesia!"
|
||||
}
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "Ruota d'Aura",
|
||||
"effect": "Il Pokémon emette l'energia accumulata nelle guance per attaccare e aumentare la Velocità. Il tipo della mossa cambia in base alla forma assunta da Morpeko."
|
||||
"effect": "Il Pokémon emette l'energia accumulata nelle guance per attaccare e aumentare la Velocità. Se usata da Morpeko, il tipo della mossa cambia in base alla forma assunta."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "Vastoimpatto",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "どくくぐつ",
|
||||
"description": "モモワロウの 技によって どく状態に なった 相手は こんらん状態にも なってしまう。"
|
||||
"description": "このポケモンの 技によって どく状態に なった 相手は こんらん状態にも なってしまう。"
|
||||
}
|
||||
}
|
||||
|
@ -111,17 +111,17 @@
|
||||
"forest": "ポケダン空 くろのもり",
|
||||
"grass": "ポケダン空 リンゴのもり",
|
||||
"graveyard": "ポケダン空 しんぴのもり",
|
||||
"ice_cave": "ポケダン空 だいひょうざん",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "ポケダン空 えんがんのいわば",
|
||||
"jungle": "Lmz - Jungle(ジャングル)",
|
||||
"laboratory": "Firel - Laboratory(ラボラトリー)",
|
||||
"lake": "ポケダン空 すいしょうのどうくつ",
|
||||
"lake": "Lmz - Lake(湖)",
|
||||
"meadow": "ポケダン空 そらのいただき(もり)",
|
||||
"metropolis": "Firel - Metropolis(大都市)",
|
||||
"mountain": "ポケダン空 ツノやま",
|
||||
"plains": "ポケダン空 そらのいただき(そうげん)",
|
||||
"power_plant": "ポケダン空 エレキへいげん",
|
||||
"ruins": "ポケダン空 ふういんのいわば",
|
||||
"plains": "Firel - Route 888(888ばんどうろ)",
|
||||
"power_plant": "Firel - The Klink(ザ・ギアル)",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - Marine Mystique(海の神秘性)",
|
||||
"seabed": "Firel - Seabed(海底)",
|
||||
"slum": "Andr06 - Sneaky Snom(ずるいユキハミ)",
|
||||
@ -131,7 +131,7 @@
|
||||
"tall_grass": "ポケダン空 のうむのもり",
|
||||
"temple": "ポケダン空 ばんにんのどうくつ",
|
||||
"town": "ポケダン空 ランダムダンジョン3",
|
||||
"volcano": "ポケダン空 ねっすいのどうくつ",
|
||||
"volcano": "Firel - Twisturn Volcano(曲がる折れる火山)",
|
||||
"wasteland": "ポケダン空 まぼろしのだいち",
|
||||
"encounter_ace_trainer": "BW 視線!エリートトレーナー",
|
||||
"encounter_backpacker": "BW 視線!バックパッカー",
|
||||
|
@ -64,6 +64,8 @@
|
||||
"copyType": "{{pokemonName}}は {{targetPokemonName}}と\n同じタイプに なった!",
|
||||
"suppressAbilities": "{{pokemonName}}の 特性が 効かなくなった!",
|
||||
"revivalBlessing": "{{pokemonName}}は\n復活して 戦えるようになった!",
|
||||
"swapArenaTags": "{{pokemonName}}は\nお互いの 場の効果を 入れ替えた!",
|
||||
"chillyReception": "{{pokemonName}}は\n寒い ギャグを かました!",
|
||||
"swapArenaTags": "{{pokemonName}}は\nお互いの 場の 効果を 入れ替えた!",
|
||||
"exposedMove": "{{pokemonName}}は {{targetPokemonName}}の\n正体を 見破った!",
|
||||
"afterYou": "{{pokemonName}}は\nお言葉に 甘えることにした!"
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "オーラぐるま",
|
||||
"effect": "ほほぶくろに 溜めた エネルギーで 攻撃し 自分の 素早さを あげる。 モルペコの 姿で タイプが 変わる。"
|
||||
"effect": "ほほぶくろに 溜めた エネルギーで 攻撃し 自分の 素早さを あげる。 モルペコが この技を 使う場合 姿で 技の タイプが 変わる。"
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "ワイドブレイカー",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "독조종",
|
||||
"description": "복숭악동의 기술에 의해 독 상태가 된 상대는 혼란 상태도 되어 버린다."
|
||||
"description": "이 기술에 의해 독 상태가 된 상대는 혼란 상태도 되어 버린다."
|
||||
}
|
||||
}
|
@ -110,17 +110,17 @@
|
||||
"forest": "불가사의 던전 하늘의 탐험대 검은 숲",
|
||||
"grass": "불가사의 던전 하늘의 탐험대 사과의 숲",
|
||||
"graveyard": "불가사의 던전 하늘의 탐험대 신비의 숲",
|
||||
"ice_cave": "불가사의 던전 하늘의 탐험대 광대한 얼음산",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "불가사의 던전 하늘의 탐험대 연안의 암반",
|
||||
"jungle": "Lmz - Jungle",
|
||||
"laboratory": "Firel - Laboratory",
|
||||
"lake": "불가사의 던전 하늘의 탐험대 수정 동굴",
|
||||
"jungle": "Lmz - 정글",
|
||||
"laboratory": "Firel - 연구소",
|
||||
"lake": "Lmz - 호수",
|
||||
"meadow": "불가사의 던전 하늘의 탐험대 하늘 꼭대기 숲",
|
||||
"metropolis": "Firel - Metropolis",
|
||||
"mountain": "불가사의 던전 하늘의 탐험대 뿔산",
|
||||
"plains": "불가사의 던전 하늘의 탐험대 하늘 꼭대기 초원",
|
||||
"power_plant": "불가사의 던전 하늘의 탐험대 일렉트릭 평원",
|
||||
"ruins": "불가사의 던전 하늘의 탐험대 봉인의 암반",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - 기어르",
|
||||
"ruins": "Lmz - 고대 유적",
|
||||
"sea": "Andr06 - Marine Mystique",
|
||||
"seabed": "Firel - Seabed",
|
||||
"slum": "Andr06 - Sneaky Snom",
|
||||
@ -130,7 +130,7 @@
|
||||
"tall_grass": "불가사의 던전 하늘의 탐험대 짙은 안개의 숲",
|
||||
"temple": "불가사의 던전 하늘의 탐험대 파수꾼의 동굴",
|
||||
"town": "불가사의 던전 하늘의 탐험대 랜덤 던전 테마 3",
|
||||
"volcano": "불가사의 던전 하늘의 탐험대 열수의 동굴",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "불가사의 던전 하늘의 탐험대 환상의 대지",
|
||||
"encounter_ace_trainer": "BW 눈이 마주치면 승부! (엘리트 트레이너)",
|
||||
"encounter_backpacker": "BW 눈이 마주치면 승부! (등산가)",
|
||||
|
@ -66,6 +66,7 @@
|
||||
"suppressAbilities": "{{pokemonName}}의\n특성이 효과를 발휘하지 못하게 되었다!",
|
||||
"revivalBlessing": "{{pokemonName}}[[는]]\n정신을 차려 싸울 수 있게 되었다!",
|
||||
"swapArenaTags": "{{pokemonName}}[[는]]\n서로의 필드 효과를 교체했다!",
|
||||
"chillyReception": "{{pokemonName}}[[는]] 썰렁한 개그를 선보였다!",
|
||||
"exposedMove": "{{pokemonName}}[[는]]\n{{targetPokemonName}}의 정체를 꿰뚫어 보았다!",
|
||||
"safeguard": "{{targetName}}[[는]] 신비의 베일이 지켜 주고 있다!",
|
||||
"afterYou": "{{pokemonName}}[[는]]\n배려를 받아들이기로 했다!"
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "오라휠",
|
||||
"effect": "볼주머니에 저장해둔 에너지로 공격하고 자신의 스피드를 올린다. 모르페코의 모습에 따라 타입이 바뀐다."
|
||||
"effect": "볼주머니에 저장해둔 에너지로 공격하고 자신의 스피드를 올린다. 모르페코가 사용할 경우 모습에 따라 타입이 바뀐다."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "와이드브레이커",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "Poison Puppeteer",
|
||||
"description": "Pokémon envenenados pelos movimentos de Pecharunt também ficarão confusos."
|
||||
"description": "Pokémon envenenados pelos movimentos deste Pokémon também ficarão confusos."
|
||||
}
|
||||
}
|
||||
|
@ -111,17 +111,17 @@
|
||||
"forest": "PMD EoS Dusk Forest",
|
||||
"grass": "PMD EoS Apple Woods",
|
||||
"graveyard": "PMD EoS Mystifying Forest",
|
||||
"ice_cave": "PMD EoS Vast Ice Mountain",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "PMD EoS Craggy Coast",
|
||||
"jungle": "Lmz - Jungle",
|
||||
"laboratory": "Firel - Laboratory",
|
||||
"lake": "PMD EoS Crystal Cave",
|
||||
"lake": "Lmz - Lake",
|
||||
"meadow": "PMD EoS Sky Peak Forest",
|
||||
"metropolis": "Firel - Metropolis",
|
||||
"mountain": "PMD EoS Mt. Horn",
|
||||
"plains": "PMD EoS Sky Peak Prairie",
|
||||
"power_plant": "PMD EoS Far Amp Plains",
|
||||
"ruins": "PMD EoS Deep Sealed Ruin",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - Marine Mystique",
|
||||
"seabed": "Firel - Seabed",
|
||||
"slum": "Andr06 - Sneaky Snom",
|
||||
@ -131,7 +131,7 @@
|
||||
"tall_grass": "PMD EoS Foggy Forest",
|
||||
"temple": "PMD EoS Aegis Cave",
|
||||
"town": "PMD EoS Random Dungeon Theme 3",
|
||||
"volcano": "PMD EoS Steam Cave",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "PMD EoS Hidden Highland",
|
||||
"encounter_ace_trainer": "BW Encontro com Treinador (Treinador Ás)",
|
||||
"encounter_backpacker": "BW Encontro com Treinador (Mochileiro)",
|
||||
|
@ -61,6 +61,7 @@
|
||||
"suppressAbilities": "A habilidade de {{pokemonName}}\nfoi suprimida!",
|
||||
"revivalBlessing": "{{pokemonName}} foi reanimado!",
|
||||
"swapArenaTags": "{{pokemonName}} trocou os efeitos de batalha que afetam cada lado do campo!",
|
||||
"chillyReception": "{{pokemonName}} está prestes a contar uma piada gelada!",
|
||||
"exposedMove": "{{pokemonName}} identificou\n{{targetPokemonName}}!",
|
||||
"safeguard": "{{targetName}} está protegido por Safeguard!",
|
||||
"afterYou": "{{pokemonName}} aceitou a gentil oferta!"
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "Aura Wheel",
|
||||
"effect": "Morpeko ataca e aumenta sua Velocidade com a energia armazenada em suas bochechas. O tipo deste movimento muda dependendo da forma do usuário."
|
||||
"effect": "O usuário ataca e aumenta sua Velocidade com a energia armazenada em suas bochechas. Se usado por Morpeko, o tipo deste movimento muda dependendo da forma do usuário."
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "Breaking Swipe",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "毒傀儡",
|
||||
"description": "因桃歹郎的招式而陷入中毒状态的\n对手同时也会陷入混乱状态。"
|
||||
"description": "因此宝可梦的招式而陷入中毒状态的对手\n同时也会陷入混乱状态。"
|
||||
}
|
||||
}
|
||||
|
@ -109,17 +109,17 @@
|
||||
"forest": "空之探险队「黑暗森林」",
|
||||
"grass": "空之探险队「苹果森林」",
|
||||
"graveyard": "空之探险队「神秘森林」",
|
||||
"ice_cave": "空之探险队「大冰山」",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "空之探险队「沿岸岩地」",
|
||||
"jungle": "Lmz - 丛林",
|
||||
"laboratory": "Firel - 研究所",
|
||||
"lake": "空之探险队「水晶洞窟」",
|
||||
"lake": "Lmz - Lake",
|
||||
"meadow": "空之探险队「天空顶端(森林)」",
|
||||
"metropolis": "Firel - 城市",
|
||||
"mountain": "空之探险队「角山」",
|
||||
"plains": "空之探险队「天空顶端(草原)」",
|
||||
"power_plant": "空之探险队「电气平原 深处」",
|
||||
"ruins": "空之探险队「封印岩地 深处」",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - 海洋之秘",
|
||||
"seabed": "Firel - 海底",
|
||||
"slum": "Andr06 - 狡猾的雪吞虫",
|
||||
@ -129,7 +129,7 @@
|
||||
"tall_grass": "空之探险队「浓雾森林」",
|
||||
"temple": "空之探险队「守护洞穴」",
|
||||
"town": "空之探险队「随机迷宫3」",
|
||||
"volcano": "空之探险队「热水洞窟」",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "空之探险队「梦幻高原」",
|
||||
"encounter_ace_trainer": "黑白 「视线!精英训练师」",
|
||||
"encounter_backpacker": "黑白 「视线!背包客」",
|
||||
|
@ -65,6 +65,7 @@
|
||||
"suppressAbilities": "{{pokemonName}}的特性\n变得无效了!",
|
||||
"revivalBlessing": "{{pokemonName}}复活了!",
|
||||
"swapArenaTags": "{{pokemonName}}\n交换了双方的场地效果!",
|
||||
"chillyReception": "{{pokemonName}}\n说出了冷笑话!",
|
||||
"exposedMove": "{{pokemonName}}识破了\n{{targetPokemonName}}的原型!",
|
||||
"safeguard": "{{targetName}}\n正受到神秘之幕的保护!",
|
||||
"afterYou": "{{pokemonName}}\n接受了对手的好意!"
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "气场轮",
|
||||
"effect": "用储存在颊囊里的能量进行攻击,\n并提高自己的速度。其属性会随着\n莫鲁贝可的样子而改变"
|
||||
"effect": "用储存在颊囊里的能量进行攻击,\n并提高自己的速度。如果由莫鲁贝可使用,\n其属性会随着它的样子而改变"
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "广域破坏",
|
||||
|
@ -1237,6 +1237,6 @@
|
||||
},
|
||||
"poisonPuppeteer": {
|
||||
"name": "毒傀儡",
|
||||
"description": "因為桃歹郎的招式而陷入中\n毒狀態的對手同時也會陷入\n混亂狀態。"
|
||||
"description": "因為此寶可夢的招式而陷入中毒狀態的對手\n同時也會陷入混亂狀態。"
|
||||
}
|
||||
}
|
||||
|
@ -100,17 +100,17 @@
|
||||
"forest": "空之探險隊「黑暗森林」",
|
||||
"grass": "空之探險隊「蘋果森林」",
|
||||
"graveyard": "空之探險隊「神秘森林」",
|
||||
"ice_cave": "空之探險隊「大冰山」",
|
||||
"ice_cave": "Firel - -50°C",
|
||||
"island": "空之探險隊「沿岸岩地」",
|
||||
"jungle": "Lmz - 叢林",
|
||||
"laboratory": "Firel - 研究所",
|
||||
"lake": "空之探險隊「水晶洞窟」",
|
||||
"lake": "Lmz - Lake",
|
||||
"meadow": "空之探險隊「天空頂端(森林)」",
|
||||
"metropolis": "Firel - 城市",
|
||||
"mountain": "空之探險隊「角山」",
|
||||
"plains": "空之探險隊「天空頂端(草原)」",
|
||||
"power_plant": "空之探險隊「電氣平原 深處」",
|
||||
"ruins": "空之探險隊「封印岩地 深處」",
|
||||
"plains": "Firel - Route 888",
|
||||
"power_plant": "Firel - The Klink",
|
||||
"ruins": "Lmz - Ancient Ruins",
|
||||
"sea": "Andr06 - 海洋之秘",
|
||||
"seabed": "Firel - 海底",
|
||||
"slum": "Andr06 - 狡猾的雪吞蟲",
|
||||
@ -120,7 +120,7 @@
|
||||
"tall_grass": "空之探險隊「濃霧森林」",
|
||||
"temple": "空之探險隊「守護洞穴」",
|
||||
"town": "空之探險隊「隨機迷宮3」",
|
||||
"volcano": "空之探險隊「熱水洞窟」",
|
||||
"volcano": "Firel - Twisturn Volcano",
|
||||
"wasteland": "空之探險隊「夢幻高原」",
|
||||
|
||||
"encounter_ace_trainer": "黑白 「視線!精英訓練師」",
|
||||
|
@ -65,6 +65,7 @@
|
||||
"suppressAbilities": "{{pokemonName}}的特性\n變得無效了!",
|
||||
"revivalBlessing": "{{pokemonName}}復活了!",
|
||||
"swapArenaTags": "{{pokemonName}}\n交換了雙方的場地效果!",
|
||||
"chillyReception": "{{pokemonName}}\n說了冷笑話!",
|
||||
"exposedMove": "{{pokemonName}}識破了\n{{targetPokemonName}}的原形!",
|
||||
"safeguard": "{{targetName}}\n正受到神秘之幕的保護!",
|
||||
"afterYou": "{{pokemonName}}\n接受了對手的好意!"
|
||||
|
@ -3129,7 +3129,7 @@
|
||||
},
|
||||
"auraWheel": {
|
||||
"name": "氣場輪",
|
||||
"effect": "用儲存在頰囊裏的能量進行\n攻擊,並提高自己的速度。\n其屬性會隨着莫魯貝可的樣\n子而改變"
|
||||
"effect": "用儲存在頰囊裏的能量進行\n攻擊,並提高自己的速度。\n如果由莫魯貝可使用,\n其屬性會隨着它的樣子而改變"
|
||||
},
|
||||
"breakingSwipe": {
|
||||
"name": "廣域破壞",
|
||||
|
@ -1288,6 +1288,21 @@ function skipInClassicAfterWave(wave: integer, defaultWeight: integer): Weighted
|
||||
function skipInLastClassicWaveOrDefault(defaultWeight: integer) : WeightedModifierTypeWeightFunc {
|
||||
return skipInClassicAfterWave(199, defaultWeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* High order function that returns a WeightedModifierTypeWeightFunc to ensure Lures don't spawn on Classic 199
|
||||
* or if the lure still has over 60% of its duration left
|
||||
* @param maxBattles The max battles the lure type in question lasts. 10 for green, 15 for Super, 30 for Max
|
||||
* @param weight The desired weight for the lure when it does spawn
|
||||
* @returns A WeightedModifierTypeWeightFunc
|
||||
*/
|
||||
function lureWeightFunc(maxBattles: number, weight: number): WeightedModifierTypeWeightFunc {
|
||||
return (party: Pokemon[]) => {
|
||||
const lures = party[0].scene.getModifiers(Modifiers.DoubleBattleChanceBoosterModifier);
|
||||
return !(party[0].scene.gameMode.isClassic && party[0].scene.currentBattle.waveIndex === 199) && (lures.length === 0 || lures.filter(m => m.getMaxBattles() === maxBattles && m.getBattleCount() >= maxBattles * 0.6).length === 0) ? weight : 0;
|
||||
};
|
||||
}
|
||||
|
||||
class WeightedModifierType {
|
||||
public modifierType: ModifierType;
|
||||
public weight: integer | WeightedModifierTypeWeightFunc;
|
||||
@ -1611,7 +1626,7 @@ const modifierPool: ModifierPool = {
|
||||
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5 && m.ppUsed >= Math.floor(m.getMovePp() / 2)).length).length, 3);
|
||||
return thresholdPartyMemberCount;
|
||||
}, 3),
|
||||
new WeightedModifierType(modifierTypes.LURE, skipInLastClassicWaveOrDefault(2)),
|
||||
new WeightedModifierType(modifierTypes.LURE, lureWeightFunc(10, 2)),
|
||||
new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4),
|
||||
new WeightedModifierType(modifierTypes.BERRY, 2),
|
||||
new WeightedModifierType(modifierTypes.TM_COMMON, 2),
|
||||
@ -1668,7 +1683,7 @@ const modifierPool: ModifierPool = {
|
||||
return thresholdPartyMemberCount;
|
||||
}, 3),
|
||||
new WeightedModifierType(modifierTypes.DIRE_HIT, 4),
|
||||
new WeightedModifierType(modifierTypes.SUPER_LURE, skipInLastClassicWaveOrDefault(4)),
|
||||
new WeightedModifierType(modifierTypes.SUPER_LURE, lureWeightFunc(15, 4)),
|
||||
new WeightedModifierType(modifierTypes.NUGGET, skipInLastClassicWaveOrDefault(5)),
|
||||
new WeightedModifierType(modifierTypes.EVOLUTION_ITEM, (party: Pokemon[]) => {
|
||||
return Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 15), 8);
|
||||
@ -1691,7 +1706,7 @@ const modifierPool: ModifierPool = {
|
||||
}),
|
||||
[ModifierTier.ULTRA]: [
|
||||
new WeightedModifierType(modifierTypes.ULTRA_BALL, (party: Pokemon[]) => (hasMaximumBalls(party, PokeballType.ULTRA_BALL)) ? 0 : 15, 15),
|
||||
new WeightedModifierType(modifierTypes.MAX_LURE, skipInLastClassicWaveOrDefault(4)),
|
||||
new WeightedModifierType(modifierTypes.MAX_LURE, lureWeightFunc(30, 4)),
|
||||
new WeightedModifierType(modifierTypes.BIG_NUGGET, skipInLastClassicWaveOrDefault(12)),
|
||||
new WeightedModifierType(modifierTypes.PP_MAX, 3),
|
||||
new WeightedModifierType(modifierTypes.MINT, 4),
|
||||
|
@ -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) {
|
||||
|
@ -74,7 +74,7 @@ export class MovePhase extends BattlePhase {
|
||||
|
||||
if (!this.followUp) {
|
||||
if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) {
|
||||
this.scene.arena.setIgnoreAbilities();
|
||||
this.scene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex());
|
||||
}
|
||||
} else {
|
||||
this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct?
|
||||
|
@ -9,26 +9,26 @@ import { PokemonPhase } from "./pokemon-phase";
|
||||
import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase";
|
||||
|
||||
export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||
private statusEffect: StatusEffect | undefined;
|
||||
private cureTurn: integer | null;
|
||||
private sourceText: string | null;
|
||||
private sourcePokemon: Pokemon | null;
|
||||
private statusEffect?: StatusEffect | undefined;
|
||||
private cureTurn?: integer | null;
|
||||
private sourceText?: string | null;
|
||||
private sourcePokemon?: Pokemon | null;
|
||||
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect?: StatusEffect, cureTurn?: integer | null, sourceText?: string, sourcePokemon?: Pokemon) {
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect?: StatusEffect, cureTurn?: integer | null, sourceText?: string | null, sourcePokemon?: Pokemon | null) {
|
||||
super(scene, battlerIndex);
|
||||
|
||||
this.statusEffect = statusEffect;
|
||||
this.cureTurn = cureTurn!; // TODO: is this bang correct?
|
||||
this.sourceText = sourceText!; // TODO: is this bang correct?
|
||||
this.sourcePokemon = sourcePokemon!; // For tracking which Pokemon caused the status effect // TODO: is this bang correct?
|
||||
this.cureTurn = cureTurn;
|
||||
this.sourceText = sourceText;
|
||||
this.sourcePokemon = sourcePokemon; // For tracking which Pokemon caused the status effect
|
||||
}
|
||||
|
||||
start() {
|
||||
const pokemon = this.getPokemon();
|
||||
if (!pokemon?.status) {
|
||||
if (pokemon?.trySetStatus(this.statusEffect, false, this.sourcePokemon)) {
|
||||
if (pokemon && !pokemon.status) {
|
||||
if (pokemon.trySetStatus(this.statusEffect, false, this.sourcePokemon)) {
|
||||
if (this.cureTurn) {
|
||||
pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct?
|
||||
pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct?
|
||||
}
|
||||
pokemon.updateInfo(true);
|
||||
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, false, () => {
|
||||
@ -40,8 +40,8 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (pokemon.status.effect === this.statusEffect) {
|
||||
this.scene.queueMessage(getStatusEffectOverlapText(this.statusEffect, getPokemonNameWithAffix(pokemon)));
|
||||
} else if (pokemon.status?.effect === this.statusEffect) {
|
||||
this.scene.queueMessage(getStatusEffectOverlapText(this.statusEffect ?? StatusEffect.NONE, getPokemonNameWithAffix(pokemon)));
|
||||
}
|
||||
this.end();
|
||||
}
|
||||
|
@ -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));
|
||||
|
109
src/test/abilities/synchronize.test.ts
Normal file
109
src/test/abilities/synchronize.test.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { StatusEffect } from "#app/data/status-effect";
|
||||
import GameManager from "#app/test/utils/gameManager";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Abilities - Synchronize", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
|
||||
game.override
|
||||
.battleType("single")
|
||||
.startingLevel(100)
|
||||
.enemySpecies(Species.MAGIKARP)
|
||||
.enemyAbility(Abilities.SYNCHRONIZE)
|
||||
.moveset([Moves.SPLASH, Moves.THUNDER_WAVE, Moves.SPORE, Moves.PSYCHO_SHIFT])
|
||||
.ability(Abilities.NO_GUARD);
|
||||
}, 20000);
|
||||
|
||||
it("does not trigger when no status is applied by opponent Pokemon", async () => {
|
||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getParty()[0].status).toBeUndefined();
|
||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||
}, 20000);
|
||||
|
||||
it("sets the status of the source pokemon to Paralysis when paralyzed by it", async () => {
|
||||
await game.classicMode.startBattle([Species.FEEBAS]);
|
||||
|
||||
game.move.select(Moves.THUNDER_WAVE);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
||||
}, 20000);
|
||||
|
||||
it("does not trigger on Sleep", async () => {
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
game.move.select(Moves.SPORE);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getParty()[0].status?.effect).toBeUndefined();
|
||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.SLEEP);
|
||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||
}, 20000);
|
||||
|
||||
it("does not trigger when Pokemon is statused by Toxic Spikes", async () => {
|
||||
game.override
|
||||
.ability(Abilities.SYNCHRONIZE)
|
||||
.enemyAbility(Abilities.BALL_FETCH)
|
||||
.enemyMoveset(Array(4).fill(Moves.TOXIC_SPIKES));
|
||||
|
||||
await game.classicMode.startBattle([Species.FEEBAS, Species.MILOTIC]);
|
||||
|
||||
game.move.select(Moves.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.doSwitchPokemon(1);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getParty()[0].status?.effect).toBe(StatusEffect.POISON);
|
||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBeUndefined();
|
||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||
}, 20000);
|
||||
|
||||
it("shows ability even if it fails to set the status of the opponent Pokemon", async () => {
|
||||
await game.classicMode.startBattle([Species.PIKACHU]);
|
||||
|
||||
game.move.select(Moves.THUNDER_WAVE);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getParty()[0].status?.effect).toBeUndefined();
|
||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
||||
}, 20000);
|
||||
|
||||
it("should activate with Psycho Shift after the move clears the status", async () => {
|
||||
game.override.statusEffect(StatusEffect.PARALYSIS);
|
||||
await game.classicMode.startBattle();
|
||||
|
||||
game.move.select(Moves.PSYCHO_SHIFT);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.getParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS); // keeping old gen < V impl for now since it's buggy otherwise
|
||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
||||
}, 20000);
|
||||
});
|
98
src/test/moves/autotomize.test.ts
Normal file
98
src/test/moves/autotomize.test.ts
Normal 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);
|
||||
});
|
71
src/test/moves/chilly_reception.test.ts
Normal file
71
src/test/moves/chilly_reception.test.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { Abilities } from "#app/enums/abilities";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { Species } from "#enums/species";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import GameManager from "#test/utils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
const TIMEOUT = 20 * 1000;
|
||||
|
||||
describe("Moves - Chilly Reception", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override.battleType("single")
|
||||
.moveset([Moves.CHILLY_RECEPTION, Moves.SNOWSCAPE])
|
||||
.enemyMoveset(Array(4).fill(Moves.SPLASH))
|
||||
.enemyAbility(Abilities.NONE)
|
||||
.ability(Abilities.NONE);
|
||||
|
||||
});
|
||||
|
||||
it("should still change the weather if user can't switch out", async () => {
|
||||
await game.classicMode.startBattle([Species.SLOWKING]);
|
||||
|
||||
game.move.select(Moves.CHILLY_RECEPTION);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("should switch out even if it's snowing", async () => {
|
||||
await game.classicMode.startBattle([Species.SLOWKING, Species.MEOWTH]);
|
||||
// first turn set up snow with snowscape, try chilly reception on second turn
|
||||
game.move.select(Moves.SNOWSCAPE);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
|
||||
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||
game.move.select(Moves.CHILLY_RECEPTION);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MEOWTH);
|
||||
}, TIMEOUT);
|
||||
|
||||
it("happy case - switch out and weather changes", async () => {
|
||||
|
||||
await game.classicMode.startBattle([Species.SLOWKING, Species.MEOWTH]);
|
||||
|
||||
game.move.select(Moves.CHILLY_RECEPTION);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SNOW);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(Species.MEOWTH);
|
||||
}, TIMEOUT);
|
||||
});
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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[]>([
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user