Compare commits

...

16 Commits

Author SHA1 Message Date
YounesM
4277439a2d
Implemented Dancer Ability (#590)
* implemented Dancer Ability

* corrected target selection for double battles, improved ability detection

* Added TSDoc

* Enhanced TSDoc

* Corrected dancing StatusMove triggering

* Linter corrections
2024-05-24 23:00:58 -05:00
SnowCharm
5f7c593365
Update zh_CN ability.ts and move.ts (#1350)
* fix translation error in zh_CN ability.ts

* add translation for Zippy Zap

* fix translation to keep up with the current impl
2024-05-24 23:46:16 -04:00
Matthew Olker
1e283afc84 fix anmation duplicate warnings 2024-05-24 22:57:47 -04:00
Benjamin Odom
32fadf8cb6
Update menu.ts (#1349) 2024-05-24 21:46:21 -04:00
Matthew Olker
840ac9f53f fix single pokemon run away in double battle 2024-05-24 21:16:27 -04:00
Matthew
623c05a3df
update running away in double battles for real this time (#1345) 2024-05-24 19:39:58 -05:00
zaccie
564add66d2
Changing Biome Text Colour (#1346)
On light coloured biomes this change should be similar readability
In darker biomes it's has much easier readability
+ now becomes more consistent colour with the rest of the game text which just looks better imo
2024-05-24 20:31:33 -04:00
Tempoanon
dc828c6801
Revert "Update running away in Double Battles (#1336)" (#1342)
This reverts commit 5e7a9b0872.
2024-05-25 09:54:46 +10:00
Matthew
5e7a9b0872
Update running away in Double Battles (#1336)
* Add check for active pokemon and double battle with 1 active

* remove unecessary case
2024-05-24 19:46:52 -04:00
Benjamin Odom
416d666b30
Update Biome Text Labels and Variable Name (#1340)
* Added ability to get hex colour from type, added biome text, added functionality for querying biomeType with object.

* Revert "Added ability to get hex colour from type, added biome text, added functionality for querying biomeType with object."

This reverts commit 0f87000aa4.

* Reverted changes, added biome text to line one and renamed wavecounttext to a more standard name.

* Update battle-scene.ts

* Update Formatting

* Update Enums

* Revert

* Update overrides.ts

* Update battle-scene.ts

---------

Co-authored-by: Jon Studders <jonstudders1@gmail.com>
2024-05-24 18:43:38 -05:00
Nicholas Galauxy
815b37d23c
Convert reconnect method to exponential backoff timeout (#1293)
* Convert reconnect method to exponential backoff timeout

* Reset to the starting duration after getting a response

* Fix broken scope enclosures
2024-05-24 17:57:49 -05:00
Jon Studders
c8b77cffc1
Added current biome text (#915)
* Added ability to get hex colour from type, added biome text, added functionality for querying biomeType with object.

* Revert "Added ability to get hex colour from type, added biome text, added functionality for querying biomeType with object."

This reverts commit 0f87000aa4.

* Reverted changes, added biome text to line one and renamed wavecounttext to a more standard name.

* Update battle-scene.ts

---------

Co-authored-by: Benjamin Odom <bennybroseph@gmail.com>
2024-05-24 18:36:02 -04:00
Jon Studders
919760e2e1
Added if statement to deploy.yml stopping local builds failing. (#1335) 2024-05-24 18:26:23 -04:00
Greenlamp2
410b33a44f
Merge pull request #1338 from bennybroseph/MoveUsedEvent-Add
Add MoveUsedEvent to Listen For When a Move Gets Used
2024-05-25 00:20:12 +02:00
Benjamin Odom
3985c63dad Update battle-scene.ts 2024-05-24 17:03:49 -05:00
Benjamin Odom
74bf42b4b7 Let's try this again 2024-05-24 17:01:47 -05:00
16 changed files with 240 additions and 101 deletions

View File

@ -6,6 +6,7 @@ on:
jobs:
deploy:
if: github.repository == 'pagefaultgames/pokerogue'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

View File

@ -0,0 +1,25 @@
import Move from "./data/move";
export enum BattleSceneEventType {
MOVE_USED = "onMoveUsed"
}
/**
* Container class for `onMoveUsed` events
* @extends Event
*/
export class MoveUsedEvent extends Event {
/** The ID of the {@linkcode Pokemon} that used the {@linkcode Move} */
public userId: number;
/** The {@linkcode Move} used */
public move: Move;
/** The amount of PP used on the {@linkcode Move} this turn */
public ppUsed: number;
constructor(userId: number, move: Move, ppUsed: number) {
super(BattleSceneEventType.MOVE_USED);
this.userId = userId;
this.move = move;
this.ppUsed = ppUsed;
}
}

View File

@ -151,7 +151,8 @@ export default class BattleScene extends SceneBase {
public money: integer;
public pokemonInfoContainer: PokemonInfoContainer;
private party: PlayerPokemon[];
private waveCountText: Phaser.GameObjects.Text;
/** Combined Biome and Wave count text */
private biomeWaveText: Phaser.GameObjects.Text;
private moneyText: Phaser.GameObjects.Text;
private scoreText: Phaser.GameObjects.Text;
private luckLabelText: Phaser.GameObjects.Text;
@ -184,6 +185,14 @@ export default class BattleScene extends SceneBase {
public rngSeedOverride: string = "";
public rngOffset: integer = 0;
/**
* Allows subscribers to listen for events
*
* Current Events:
* - {@linkcode BattleSceneEventType.MOVE_USED} {@linkcode MoveUsedEvent}
*/
public readonly eventTarget: EventTarget = new EventTarget();
constructor() {
super("battle");
this.phaseQueue = [];
@ -342,9 +351,9 @@ export default class BattleScene extends SceneBase {
this.candyBar.setup();
this.fieldUI.add(this.candyBar);
this.waveCountText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
this.waveCountText.setOrigin(1, 0);
this.fieldUI.add(this.waveCountText);
this.biomeWaveText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, startingWave.toString(), TextStyle.BATTLE_INFO);
this.biomeWaveText.setOrigin(1, 0);
this.fieldUI.add(this.biomeWaveText);
this.moneyText = addTextObject(this, (this.game.canvas.width / 6) - 2, 0, "", TextStyle.MONEY);
this.moneyText.setOrigin(1, 0);
@ -472,7 +481,7 @@ export default class BattleScene extends SceneBase {
}
});
this.updateWaveCountText();
this.updateBiomeWaveText();
this.updateMoneyText();
this.updateScoreText();
}
@ -786,8 +795,8 @@ export default class BattleScene extends SceneBase {
this.currentBattle = null;
this.waveCountText.setText(startingWave.toString());
this.waveCountText.setVisible(false);
this.biomeWaveText.setText(startingWave.toString());
this.biomeWaveText.setVisible(false);
this.updateMoneyText();
this.moneyText.setVisible(false);
@ -1236,12 +1245,13 @@ export default class BattleScene extends SceneBase {
});
}
updateWaveCountText(): void {
updateBiomeWaveText(): void {
const isBoss = !(this.currentBattle.waveIndex % 10);
this.waveCountText.setText(this.currentBattle.waveIndex.toString());
this.waveCountText.setColor(!isBoss ? "#404040" : "#f89890");
this.waveCountText.setShadowColor(!isBoss ? "#ded6b5" : "#984038");
this.waveCountText.setVisible(true);
const biomeString: string = getBiomeName(this.arena.biomeType);
this.biomeWaveText.setText( biomeString + " - " + this.currentBattle.waveIndex.toString());
this.biomeWaveText.setColor(!isBoss ? "#ffffff" : "#f89890");
this.biomeWaveText.setShadowColor(!isBoss ? "#636363" : "#984038");
this.biomeWaveText.setVisible(true);
}
updateMoneyText(): void {
@ -1289,8 +1299,8 @@ export default class BattleScene extends SceneBase {
updateUIPositions(): void {
const enemyModifierCount = this.enemyModifiers.filter(m => m.isIconVisible(this)).length;
this.waveCountText.setY(-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0));
this.moneyText.setY(this.waveCountText.y + 10);
this.biomeWaveText.setY(-(this.game.canvas.height / 6) + (enemyModifierCount ? enemyModifierCount <= 12 ? 15 : 24 : 0));
this.moneyText.setY(this.biomeWaveText.y + 10);
this.scoreText.setY(this.moneyText.y + 10);
[ this.luckLabelText, this.luckText ].map(l => l.setY((this.scoreText.visible ? this.scoreText : this.moneyText).y + 10));
const offsetY = (this.scoreText.visible ? this.scoreText : this.moneyText).y + 15;

View File

@ -2,14 +2,14 @@ import Pokemon, { HitResult, PokemonMove } from "../field/pokemon";
import { Type } from "./type";
import * as Utils from "../utils";
import { BattleStat, getBattleStatName } from "./battle-stat";
import { PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases";
import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases";
import { getPokemonMessage, getPokemonPrefix } from "../messages";
import { Weather, WeatherType } from "./weather";
import { BattlerTag } from "./battler-tags";
import { BattlerTagType } from "./enums/battler-tag-type";
import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves, StatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { Stat } from "./pokemon-stat";
@ -22,6 +22,7 @@ import i18next, { Localizable } from "#app/plugins/i18n.js";
import { Command } from "../ui/command-ui-handler";
import { getPokeballName } from "./pokeball";
import { BerryModifierType } from "#app/modifier/modifier-type";
import {BattlerIndex} from "#app/battle";
export class Ability implements Localizable {
public id: Abilities;
@ -2484,6 +2485,62 @@ export class PostBiomeChangeTerrainChangeAbAttr extends PostBiomeChangeAbAttr {
}
}
/**
* Triggers just after a move is used either by the opponent or the player
* @extends AbAttr
*/
export class PostMoveUsedAbAttr extends AbAttr {
applyPostMoveUsed(pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise<boolean> {
return false;
}
}
/**
* Triggers after a dance move is used either by the opponent or the player
* @extends PostMoveUsedAbAttr
*/
export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
/**
* Resolves the Dancer ability by replicating the move used by the source of the dance
* either on the source itself or on the target of the dance
* @param dancer {@linkcode Pokemon} with Dancer ability
* @param move {@linkcode PokemonMove} Dancing move used by the source
* @param source {@linkcode Pokemon} that used the dancing move
* @param targets {@linkcode BattlerIndex}Targets of the dancing move
* @param args N/A
*
* @return true if the Dancer ability was resolved
*/
applyPostMoveUsed(dancer: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], args: any[]): boolean | Promise<boolean> {
// The move to replicate cannot come from the Dancer
if (source.getBattlerIndex() !== dancer.getBattlerIndex()) {
// If the move is an AttackMove or a StatusMove the Dancer must replicate the move on the source of the Dance
if (move.getMove() instanceof AttackMove || move.getMove() instanceof StatusMove) {
const target = this.getTarget(dancer, source, targets);
dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, target, move, true));
} else if (move.getMove() instanceof SelfStatusMove) {
// If the move is a SelfStatusMove (ie. Swords Dance) the Dancer should replicate it on itself
dancer.scene.unshiftPhase(new MovePhase(dancer.scene, dancer, [dancer.getBattlerIndex()], move, true));
}
}
return true;
}
/**
* Get the correct targets of Dancer ability
*
* @param dancer {@linkcode Pokemon} Pokemon with Dancer ability
* @param source {@linkcode Pokemon} Source of the dancing move
* @param targets {@linkcode BattlerIndex} Targets of the dancing move
*/
getTarget(dancer: Pokemon, source: Pokemon, targets: BattlerIndex[]) : BattlerIndex[] {
if (dancer.isPlayer()) {
return source.isPlayer() ? targets : [source.getBattlerIndex()];
}
return source.isPlayer() ? [source.getBattlerIndex()] : targets;
}
}
export class StatChangeMultiplierAbAttr extends AbAttr {
private multiplier: integer;
@ -3013,6 +3070,11 @@ export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefe
return applyAbAttrsInternal<PostDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args);
}
export function applyPostMoveUsedAbAttrs(attrType: { new(...args: any[]): PostMoveUsedAbAttr },
pokemon: Pokemon, move: PokemonMove, source: Pokemon, targets: BattlerIndex[], ...args: any[]): Promise<void> {
return applyAbAttrsInternal<PostMoveUsedAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostMoveUsed(pokemon, move, source, targets, args), args);
}
export function applyBattleStatMultiplierAbAttrs(attrType: { new(...args: any[]): BattleStatMultiplierAbAttr },
pokemon: Pokemon, battleStat: BattleStat, statValue: Utils.NumberHolder, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<BattleStatMultiplierAbAttr>(attrType, pokemon, (attr, passive) => attr.applyBattleStat(pokemon, passive, battleStat, statValue, args), args);
@ -3770,7 +3832,7 @@ export function initAbilities() {
.attr(PostFaintHPDamageAbAttr)
.bypassFaint(),
new Ability(Abilities.DANCER, 7)
.unimplemented(),
.attr(PostDancingMoveAbAttr),
new Ability(Abilities.BATTERY, 7)
.unimplemented(),
new Ability(Abilities.FLUFFY, 7)

View File

@ -18,10 +18,8 @@ export function getBiomeName(biome: Biome | -1) {
return "Ancient Ruins";
case Biome.ABYSS:
return "The Abyss";
case Biome.SPACE:
return "Stratosphere";
case Biome.END:
return "Final Destination";
return "???";
default:
return Utils.toReadableString(Biome[biome]);
}

View File

@ -630,13 +630,15 @@ export class TrainerConfig {
? scene.anims.generateFrameNames(partnerTrainerKey, {zeroPad: 4,suffix: ".png",start: 1,end: 128})
: null;
console.warn = originalWarn;
if (!(scene.anims.exists(trainerKey))) {
scene.anims.create({
key: trainerKey,
frames: frameNames,
frameRate: 24,
repeat: -1
});
if (isDouble) {
}
if (isDouble && !(scene.anims.exists(partnerTrainerKey))) {
scene.anims.create({
key: partnerTrainerKey,
frames: partnerFrameNames,

View File

@ -24,12 +24,14 @@ export function addPokeballOpenParticles(scene: BattleScene, x: number, y: numbe
function doDefaultPbOpenParticles(scene: BattleScene, x: number, y: number, radius: number) {
const pbOpenParticlesFrameNames = scene.anims.generateFrameNames("pb_particles", { start: 0, end: 3, suffix: ".png" });
if (!(scene.anims.exists("pb_open_particle"))) {
scene.anims.create({
key: "pb_open_particle",
frames: pbOpenParticlesFrameNames,
frameRate: 16,
repeat: -1
});
}
const addParticle = (index: integer) => {
const particle = scene.add.sprite(x, y, "pb_open_particle");

View File

@ -726,12 +726,14 @@ export class ArenaBase extends Phaser.GameObjects.Container {
if (this.base.texture.frameTotal > 1) {
const baseFrameNames = this.scene.anims.generateFrameNames(baseKey, { zeroPad: 4, suffix: ".png", start: 1, end: this.base.texture.frameTotal - 1 });
if (!(this.scene.anims.exists(baseKey))) {
this.scene.anims.create({
key: baseKey,
frames: baseFrameNames,
frameRate: 12,
repeat: -1
});
}
this.base.play(baseKey);
} else {
this.base.stop();
@ -751,12 +753,14 @@ export class ArenaBase extends Phaser.GameObjects.Container {
if (hasProps && prop.texture.frameTotal > 1) {
const propFrameNames = this.scene.anims.generateFrameNames(propKey, { zeroPad: 4, suffix: ".png", start: 1, end: prop.texture.frameTotal - 1 });
if (!(this.scene.anims.exists(propKey))) {
this.scene.anims.create({
key: propKey,
frames: propFrameNames,
frameRate: 12,
repeat: -1
});
}
prop.play(propKey);
} else {
prop.stop();

View File

@ -322,6 +322,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
console.warn = () => {};
const battleFrameNames = this.scene.anims.generateFrameNames(this.getBattleSpriteKey(), { zeroPad: 4, suffix: ".png", start: 1, end: 400 });
console.warn = originalWarn;
if (!(this.scene.anims.exists(this.getBattleSpriteKey()))) {
this.scene.anims.create({
key: this.getBattleSpriteKey(),
frames: battleFrameNames,
@ -329,6 +330,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
repeat: -1
});
}
}
this.playAnim();
const updateFusionPaletteAndResolve = () => {
this.updateFusionPalette();
@ -524,6 +526,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
shinySparkle.setVisible(false);
shinySparkle.setOrigin(0.5, 1);
const frameNames = this.scene.anims.generateFrameNames(key, { suffix: ".png", end: 34 });
if (!(this.scene.anims.exists(`sparkle${keySuffix}`))) {
this.scene.anims.create({
key: `sparkle${keySuffix}`,
frames: frameNames,
@ -531,6 +534,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
showOnStart: true,
hideOnComplete: true,
});
}
this.add(shinySparkle);
this.shinySparkle = shinySparkle;

View File

@ -29,7 +29,7 @@ export const menu: SimpleTranslationEntries = {
"confirmPassword": "Confirm Password",
"registrationAgeWarning": "By registering, you confirm you are of 13 years of age or older.",
"backToLogin": "Back to Login",
"failedToLoadSaveData": "Failed to load save data. Please reload the page.\nIf this continues, please contact the administrator.",
"failedToLoadSaveData": "Failed to load save data. Please reload the page.\nIf this persists, please check #announcements in Discord.",
"sessionSuccess": "Session loaded successfully.",
"failedToLoadSession": "Your session data could not be loaded.\nIt may be corrupted.",
"boyOrGirl": "Are you a boy or a girl?",

View File

@ -475,7 +475,7 @@ export const ability: AbilityTranslationEntries = {
},
frisk: {
name: "察觉",
description: "进入战斗时,神奇宝贝可以检查对方神奇宝贝的能力。",
description: "出场时,可以察觉对手的特性。",
},
reckless: {
name: "舍身",
@ -1063,7 +1063,7 @@ export const ability: AbilityTranslationEntries = {
},
asOneGlastrier: {
name: "人马一体",
description: "兼备蕾冠王的紧张感和灵幽\n马的漆黑嘶鸣这两种特性。",
description: "兼备蕾冠王的紧张感和雪暴\n马的苍白嘶鸣这两种特性。",
},
asOneSpectrier: {
name: "人马一体",
@ -1211,11 +1211,11 @@ export const ability: AbilityTranslationEntries = {
},
embodyAspectTeal: {
name: "面影辉映",
description: "将回忆映于心中,让水井面\n具发出光辉提高自己的特\n防。",
description: "将回忆映于心中,让碧草面\n具发出光辉提高自己的速\n度。",
},
embodyAspectWellspring: {
name: "面影辉映",
description: "将回忆映于心中,让碧草面\n具发出光辉提高自己的速\n度。",
description: "将回忆映于心中,让水井面\n具发出光辉提高自己的特\n防。",
},
embodyAspectHearthflame: {
name: "面影辉映",

View File

@ -2915,7 +2915,7 @@ export const move: MoveTranslationEntries = {
},
"zippyZap": {
name: "电电加速",
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: "迅猛无比的电击。必定能够\n先制攻击并且提高自己的\n闪避率。",
},
"splishySplash": {
name: "滔滔冲浪",

View File

@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type";
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr } from "./data/ability";
import { CheckTrappedAbAttr, IgnoreOpponentStatChangesAbAttr, IgnoreOpponentEvasionAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -61,6 +61,7 @@ import { Abilities } from "./data/enums/abilities";
import * as Overrides from "./overrides";
import { TextStyle, addTextObject } from "./ui/text";
import { Type } from "./data/type";
import { MoveUsedEvent } from "./battle-scene-events";
export class LoginPhase extends Phase {
@ -2155,13 +2156,28 @@ export class TurnStartPhase extends FieldPhase {
this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets[0] % 2, turnCommand.cursor));
break;
case Command.POKEMON:
case Command.RUN:
const isSwitch = turnCommand.command === Command.POKEMON;
if (isSwitch) {
this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor, true, turnCommand.args[0] as boolean, pokemon.isPlayer()));
break;
case Command.RUN:
let runningPokemon = pokemon;
if (this.scene.currentBattle.double) {
const playerActivePokemon = field.filter(pokemon => {
if (!!pokemon) {
return pokemon.isPlayer() && pokemon.isActive();
} else {
this.scene.unshiftPhase(new AttemptRunPhase(this.scene, pokemon.getFieldIndex()));
return;
}
});
// if only one pokemon is alive, use that one
if (playerActivePokemon.length > 1) {
// find which active pokemon has faster speed
const fasterPokemon = playerActivePokemon[0].getStat(Stat.SPD) > playerActivePokemon[1].getStat(Stat.SPD) ? playerActivePokemon[0] : playerActivePokemon[1];
// check if either active pokemon has the ability "Run Away"
const hasRunAway = playerActivePokemon.find(p => p.hasAbility(Abilities.RUN_AWAY));
runningPokemon = hasRunAway !== undefined ? hasRunAway : fasterPokemon;
}
}
this.scene.unshiftPhase(new AttemptRunPhase(this.scene, runningPokemon.getFieldIndex()));
break;
}
}
@ -2388,7 +2404,7 @@ export class MovePhase extends BattlePhase {
console.log(Moves[this.move.moveId]);
if (!this.canMove()) {
if (this.move.moveId && this.pokemon.summonData.disabledMove === this.move.moveId) {
if (this.move.moveId && this.pokemon.summonData?.disabledMove === this.move.moveId) {
this.scene.queueMessage(`${this.move.getName()} is disabled!`);
}
return this.end();
@ -2466,8 +2482,9 @@ export class MovePhase extends BattlePhase {
const moveQueue = this.pokemon.getMoveQueue();
if (this.cancelled || this.failed) {
if (this.failed) {
this.move.usePp(ppUsed);
} // Only use PP if the move failed
this.move.usePp(ppUsed); // Only use PP if the move failed
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed));
}
// Record a failed move so Abilities like Truant don't trigger next turn and soft-lock
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
@ -2499,6 +2516,7 @@ export class MovePhase extends BattlePhase {
if (!moveQueue.length || !moveQueue.shift().ignorePP) { // using .shift here clears out two turn moves once they've been used
this.move.usePp(ppUsed);
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed));
}
if (!allMoves[this.move.moveId].getAttrs(CopyMoveAttr).length) {
@ -2525,7 +2543,16 @@ export class MovePhase extends BattlePhase {
this.showFailedText(failedText);
}
}
// Checks if Dancer ability is triggered
if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) {
// Pokemon with Dancer can be on either side of the battle so we check in both cases
this.scene.getPlayerField().forEach(pokemon => {
applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets);
});
this.scene.getEnemyParty().forEach(pokemon => {
applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets);
});
}
this.end();
};
@ -4393,7 +4420,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
}
const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
if (healAmount.value < 0) {
pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL);
pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL as DamageResult);
healAmount.value = 0;
}
// Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock)

View File

@ -61,16 +61,20 @@ export default class EggGachaUiHandler extends MessageUiHandler {
this.eggGachaContainer.add(bg);
const hatchFrameNames = this.scene.anims.generateFrameNames("gacha_hatch", { suffix: ".png", start: 1, end: 4 });
if (!(this.scene.anims.exists("open"))) {
this.scene.anims.create({
key: "open",
frames: hatchFrameNames,
frameRate: 12
});
}
if (!(this.scene.anims.exists("close"))) {
this.scene.anims.create({
key: "close",
frames: hatchFrameNames.reverse(),
frameRate: 12
});
}
Utils.getEnumValues(GachaType).forEach((gachaType, g) => {
const gachaTypeKey = GachaType[gachaType].toString().toLowerCase();

View File

@ -16,12 +16,14 @@ export default class EggHatchSceneHandler extends UiHandler {
this.scene.fieldUI.add(this.eggHatchContainer);
const eggLightraysAnimFrames = this.scene.anims.generateFrameNames("egg_lightrays", { start: 0, end: 3 });
if (!(this.scene.anims.exists("egg_lightrays"))) {
this.scene.anims.create({
key: "egg_lightrays",
frames: eggLightraysAnimFrames,
frameRate: 32
});
}
}
show(_args: any[]): boolean {
super.show(_args);

View File

@ -6,7 +6,7 @@ import { updateUserInfo } from "#app/account";
export default class UnavailableModalUiHandler extends ModalUiHandler {
private reconnectTimer: number;
private reconnectInterval: number;
private reconnectDuration: number;
private reconnectCallback: () => void;
private readonly minTime = 1000 * 5;
@ -16,7 +16,7 @@ export default class UnavailableModalUiHandler extends ModalUiHandler {
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
this.reconnectInterval = this.minTime;
this.reconnectDuration = this.minTime;
}
getModalTitle(): string {
@ -51,19 +51,17 @@ export default class UnavailableModalUiHandler extends ModalUiHandler {
tryReconnect(): void {
updateUserInfo().then(response => {
if (response[0] || [200, 400].includes(response[1])) {
clearInterval(this.reconnectTimer);
this.reconnectTimer = null;
this.reconnectInterval = this.minTime;
this.reconnectDuration = this.minTime;
this.scene.playSound("pb_bounce_1");
this.reconnectCallback();
} else {
clearInterval(this.reconnectTimer);
this.reconnectInterval = Math.min(this.reconnectInterval * 2, this.maxTime); // Set a max delay so it isn't infinite
this.reconnectDuration = Math.min(this.reconnectDuration * 2, this.maxTime); // Set a max delay so it isn't infinite
this.reconnectTimer =
setTimeout(
() => this.tryReconnect(),
// Adds a random factor to avoid pendulum effect during long total breakdown
this.reconnectInterval + (Math.random() * this.randVarianceTime));
this.reconnectDuration + (Math.random() * this.randVarianceTime));
}
});
}
@ -75,8 +73,8 @@ export default class UnavailableModalUiHandler extends ModalUiHandler {
};
this.reconnectCallback = args[0];
this.reconnectTimer = setInterval(() => this.tryReconnect(), this.reconnectInterval);
this.reconnectDuration = this.minTime;
this.reconnectTimer = setTimeout(() => this.tryReconnect(), this.reconnectDuration);
return super.show([ config ]);
}