Merge branch 'main' into trainer-team-bar

This commit is contained in:
RedstonewolfX 2024-07-18 15:21:22 -04:00 committed by GitHub
commit efb3b3a87d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
107 changed files with 6630 additions and 385 deletions

154
README.md
View File

@ -1,116 +1,46 @@
<picture><img src="./public/images/logo.png" width="300" alt="PokéRogue"></picture> *PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, fighting trainers, bosses, and more!*
PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, fighting trainers, bosses, and more! This is a mod for PokéRogue, for use with the offline version.
It's used to help with our routing project.
# Contributing This program is for Windows - it does not have installers for Mac or Linux right now.
## 🛠️ Development (You can still do run validation without this mod, of course)
If you have the motivation and experience with Typescript/Javascript (or are willing to learn) please feel free to fork the repository and make pull requests with contributions. If you don't know what to work on but want to help, reference the below **To-Do** section or the **#feature-vote** channel in the discord.
### 💻 Environment Setup ## Feature progress
#### Prerequisites - [ ] Logs all the steps you take while playing
- node: 20.13.1 - [x] Logs the wild Pokémon you encounter and their stats
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) - [x] Logs the category of trainers you encounter
- [x] In-Game GUI to export logs
- [ ] Show damage values for attacks (present, but incomplete)
- [x] Show catch rates
- [x] Show attributes of wild Pokémon (max IVs, nature, abilities)
#### Running Locally # Instructions
1. Clone the repo and in the root directory run `npm install` ### Installation
- *if you run into any errors, reach out in the **#dev-corner** channel in discord* - Make sure you have the app (download v1.3.1 [here](https://github.com/Admiral-Billy/Pokerogue-App/releases) - v2.0.0 and up will not work!)
2. Run `npm run start:dev` to locally run the project in `localhost:8000` - Look on the `record-path` channel for the modified installer that allows downloading different versions
- Replace `resources/update-game.js` in the offline version's files with the modified installer
#### Linting - Run the installer, typing `y` and pressing enter to confirm you want to install offline mode
We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script. - Select Pokerogue-Projects/Pathing-Tool (option 2 by default) and press enter again
- Wait (it will take a few minutes to install no matter which version you selected)
### ❔ FAQ - Choose whether you want the offline version of the `pkmn.help` type calculator, then press enter one final time when prompted to close the terminal
### Setting up a run
**How do I test a new _______?** - Open PokéRogue online (you can use [PokeRogue](https://pokerogue.net/) or the online mode of the app)
- In the `src/overrides.ts` file there are overrides for most values you'll need to change for testing - Start a new Daily Run
- Save & Quit
- Open the menu
## 🪧 To Do - Go to Manage Data, select Export Session, and select the slot you saved the Daily Run to - you will download a `.prsv` file
Check out [Github Issues](https://github.com/pagefaultgames/pokerogue/issues) to see how can you help us! - Open the app in offline mode by running `Pokerogue Offine.bat`
- Open the menu, go to Manage Data, and instead *import* a session
# 📝 Credits - Select the `.prsv` you downloaded, and select a slot to save it to. When the game reloads, you'll see that the newly imported run has appeared as an option on the title screen.
> If this project contains assets you have produced and you do not see your name here, **please** reach out. - Open Manage Logs on the title screen.
- If you played a run already, be sure to export your files first.
### 🎵 BGM - Select `Clear All (3)` to delete any previous run data.
- Pokémon Mystery Dungeon: Explorers of Sky ### Playing the Daily Run
- Arata Iiyoshi - All Daily Run saves will appear as buttons on the title screen. Selecting them will load the file as if you had opened the Load Game menu. (Selecting them in that menu still works, of course.)
- Hideki Sakamoto - Play! The game will automatically log your run as you go.
- Keisuke Ito - **Warning**: The logs do not discriminate between saves, and if you open another save file, it will **overwrite** any data in Steps (`instructions.txt`) or Encounters (`encounters.csv`).
- Ken-ichi Saito - When you're done, go to the title screen and open Manage Logs.
- Yoshihiro Maeda - Select a log to save it to your device (the number in parenthases indicates the file size)
- Pokémon Black/White - Select "Export All" to save all logs to your device at once (the number in parenthases indicates how many logs will be exported)
- Go Ichinose - Select "Reset All" to delete all existing run data
- Hitomi Sato
- Shota Kageyama
- Pokémon Mystery Dungeon: Rescue Team DX
- Keisuke Ito
- Arata Iiyoshi
- Atsuhiro Ishizuna
- Pokémon HeartGold/SoulSilver
- Pokémon Black/White 2
- Pokémon X/Y
- Pokémon Omega Ruby/Alpha Sapphire
- Pokémon Sun/Moon
- Pokémon Ultra Sun/Ultra Moon
- Pokémon Sword/Shield
- Pokémon Scarlet/Violet
- Firel (Custom Laboratory, Metropolis, Seabed, and Space biome music)
- Lmz (Custom Jungle biome music)
### 🎵 Sound Effects
- Pokémon Emerald
- Pokémon Black/White
### 🎨 Backgrounds
- Squip (Paid Commissions)
- Contributions by Someonealive-QN
### 🎨 UI
- GAMEFREAK
- LJ Birdman
### 🎨 Pagefault Games Intro
- Spectremint
### 🎨 Game Logo
- Gonstar (Paid Commission)
### 🎨 Trainer Sprites
- GAMEFREAK (Pokémon Black/White 2, Pokémon Diamond/Pearl)
- kyledove
- Brumirage
- pkmn_realidea (Paid Commissions)
### 🎨 Trainer Portraits
- pkmn_realidea (Paid Commissions)
### 🎨 Pokemon Sprites and Animation
- GAMEFREAK (Pokémon Black/White 2)
- Smogon Sprite Project (Various Artists)
- Skyflyer
- Nolo33
- Ebaru
- EricLostie
- KingOfThe-X-Roads
- kiriaura
- Caruban
- Sopita_Yorita
- Azrita
- AshnixsLaw
- Hellfire0raptor
- RetroNC
- Franark122k
- OldSoulja
- PKMarioG
- ItsYugen
- lucasomi
- Pkm Sinfonia
- Poki Papillon
- Fleimer_
- bizcoeindoloro
- mangalos810
- Involuntary-Twitch
- selstar
### 🎨 Move Animations
- Pokémon Reborn

View File

@ -3300,6 +3300,11 @@
1, 1,
1 1
], ],
"307": [
0,
1,
1
],
"308": [ "308": [
0, 0,
1, 1,
@ -6678,6 +6683,11 @@
1, 1,
1 1
], ],
"307": [
0,
1,
1
],
"308": [ "308": [
0, 0,
1, 1,

View File

@ -1,15 +1,5 @@
{ {
"1": { "1": {
"7b6b6b": "314b76",
"b5adad": "677d98",
"e6dede": "c2cfdb",
"000000": "000000",
"3a84b5": "51876e",
"3a4a5a": "113926",
"6bcee6": "7edfb7",
"5aa5ce": "66c3a3"
},
"2": {
"7b6b6b": "7a5f5f", "7b6b6b": "7a5f5f",
"b5adad": "9f8383", "b5adad": "9f8383",
"e6dede": "deccc3", "e6dede": "deccc3",
@ -18,5 +8,15 @@
"3a4a5a": "5a2859", "3a4a5a": "5a2859",
"6bcee6": "f4a8c8", "6bcee6": "f4a8c8",
"5aa5ce": "ce7bb0" "5aa5ce": "ce7bb0"
},
"2": {
"7b6b6b": "314b76",
"b5adad": "677d98",
"e6dede": "c2cfdb",
"000000": "000000",
"3a84b5": "51876e",
"3a4a5a": "113926",
"6bcee6": "7edfb7",
"5aa5ce": "66c3a3"
} }
} }

View File

@ -0,0 +1,22 @@
{
"1": {
"7b6b6b": "7a5f5f",
"b5adad": "9f8383",
"e6dede": "deccc3",
"000000": "000000",
"3a84b5": "7e4377",
"3a4a5a": "5a2859",
"6bcee6": "f4a8c8",
"5aa5ce": "ce7bb0"
},
"2": {
"7b6b6b": "314b76",
"b5adad": "677d98",
"e6dede": "c2cfdb",
"000000": "000000",
"3a84b5": "51876e",
"3a4a5a": "113926",
"6bcee6": "7edfb7",
"5aa5ce": "66c3a3"
}
}

View File

@ -0,0 +1,34 @@
{
"1": {
"7b6b6b": "7a5f5f",
"000000": "000000",
"e6dede": "deccc3",
"b5adad": "9f8383",
"4a4242": "4a4242",
"ffffff": "ffffff",
"3a4a5a": "5a2859",
"b5d6ff": "f4a8c8",
"6bcee6": "ce7bb0",
"d65252": "d65287",
"84424a": "84424a",
"3a84b5": "7e4377",
"5aa5ce": "b95ba1",
"d65273": "d65273"
},
"2": {
"7b6b6b": "314b76",
"000000": "000000",
"e6dede": "c2cfdb",
"b5adad": "6f89aa",
"4a4242": "1e2f52",
"ffffff": "ffffff",
"3a4a5a": "113926",
"b5d6ff": "7edfb7",
"6bcee6": "66c3a3",
"d65252": "c067c7",
"84424a": "84424a",
"3a84b5": "375a47",
"5aa5ce": "579578",
"d65273": "d65273"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -67,6 +67,7 @@ import { Species } from "#enums/species";
import { UiTheme } from "#enums/ui-theme"; import { UiTheme } from "#enums/ui-theme";
import { TimedEventManager } from "#app/timed-event-manager.js"; import { TimedEventManager } from "#app/timed-event-manager.js";
import i18next from "i18next"; import i18next from "i18next";
import * as LoggerTools from "./logger"
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -121,6 +122,13 @@ export default class BattleScene extends SceneBase {
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1"; public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
public enableMoveInfo: boolean = true; public enableMoveInfo: boolean = true;
public enableRetries: boolean = false; public enableRetries: boolean = false;
public damageDisplay: string = "Off";
public lazyReloads: boolean = false;
public menuChangesBiome: boolean = false;
public showAutosaves: boolean = false;
public doBiomePanels: boolean = false;
public disableDailyShinies: boolean = true; // Disables shiny luck in Daily Runs to prevent affecting RNG
public quickloadDisplayMode: string = "Dailies";
/** /**
* Determines the condition for a notification should be shown for Candy Upgrades * Determines the condition for a notification should be shown for Candy Upgrades
* - 0 = 'Off' * - 0 = 'Off'
@ -226,7 +234,7 @@ export default class BattleScene extends SceneBase {
private fieldOverlay: Phaser.GameObjects.Rectangle; private fieldOverlay: Phaser.GameObjects.Rectangle;
private shopOverlay: Phaser.GameObjects.Rectangle; private shopOverlay: Phaser.GameObjects.Rectangle;
public modifiers: PersistentModifier[]; public modifiers: PersistentModifier[];
private enemyModifiers: PersistentModifier[]; public enemyModifiers: PersistentModifier[];
public uiContainer: Phaser.GameObjects.Container; public uiContainer: Phaser.GameObjects.Container;
public ui: UI; public ui: UI;
@ -255,6 +263,8 @@ export default class BattleScene extends SceneBase {
public eventManager: TimedEventManager; public eventManager: TimedEventManager;
public biomeChangeMode: boolean = false;
/** /**
* Allows subscribers to listen for events * Allows subscribers to listen for events
* *
@ -297,6 +307,7 @@ export default class BattleScene extends SceneBase {
Phaser.Math.RND.realInRange = function (min: number, max: number): number { Phaser.Math.RND.realInRange = function (min: number, max: number): number {
const ret = originalRealInRange.apply(this, [ min, max ]); const ret = originalRealInRange.apply(this, [ min, max ]);
const args = [ "RNG", ++scene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ]; const args = [ "RNG", ++scene.rngCounter, ret / (max - min), `min: ${min} / max: ${max}` ];
scene.setScoreText("RNG: " + this.rngCounter + ")")
args.push(`seed: ${scene.rngSeedOverride || scene.waveSeed || scene.seed}`); args.push(`seed: ${scene.rngSeedOverride || scene.waveSeed || scene.seed}`);
if (scene.rngOffset) { if (scene.rngOffset) {
args.push(`offset: ${scene.rngOffset}`); args.push(`offset: ${scene.rngOffset}`);
@ -896,10 +907,37 @@ export default class BattleScene extends SceneBase {
return container; return container;
} }
addPkIcon(pokemon: PokemonSpecies, form: integer = 0, x: number, y: number, originX: number = 0.5, originY: number = 0.5, ignoreOverride: boolean = false): Phaser.GameObjects.Container {
const container = this.add.container(x, y);
container.setName(`${pokemon.name}-icon`);
const icon = this.add.sprite(0, 0, pokemon.getIconAtlasKey(form));
icon.setName(`sprite-${pokemon.name}-icon`);
icon.setFrame(pokemon.getIconId(true));
// Temporary fix to show pokemon's default icon if variant icon doesn't exist
if (icon.frame.name !== pokemon.getIconId(true)) {
console.log(`${pokemon.name}'s variant icon does not exist. Replacing with default.`);
icon.setTexture(pokemon.getIconAtlasKey(0));
icon.setFrame(pokemon.getIconId(true));
}
icon.setOrigin(0.5, 0);
container.add(icon);
if (originX !== 0.5) {
container.x -= icon.width * (originX - 0.5);
}
if (originY !== 0) {
container.y -= icon.height * originY;
}
return container;
}
setSeed(seed: string): void { setSeed(seed: string): void {
this.seed = seed; this.seed = seed;
this.rngCounter = 0; this.rngCounter = 0;
//this.setScoreText("RNG: 0")
this.waveCycleOffset = this.getGeneratedWaveCycleOffset(); this.waveCycleOffset = this.getGeneratedWaveCycleOffset();
this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym(); this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym();
} }
@ -1351,6 +1389,7 @@ export default class BattleScene extends SceneBase {
Phaser.Math.RND.sow([ this.waveSeed ]); Phaser.Math.RND.sow([ this.waveSeed ]);
console.log("Wave Seed:", this.waveSeed, wave); console.log("Wave Seed:", this.waveSeed, wave);
this.rngCounter = 0; this.rngCounter = 0;
//this.setScoreText("RNG: 0")
} }
executeWithSeedOffset(func: Function, offset: integer, seedOverride?: string): void { executeWithSeedOffset(func: Function, offset: integer, seedOverride?: string): void {
@ -1367,6 +1406,7 @@ export default class BattleScene extends SceneBase {
this.rngSeedOverride = seedOverride || ""; this.rngSeedOverride = seedOverride || "";
func(); func();
Phaser.Math.RND.state(state); Phaser.Math.RND.state(state);
//this.setScoreText("RNG: " + tempRngCounter + " (Last sim: " + this.rngCounter + ")")
this.rngCounter = tempRngCounter; this.rngCounter = tempRngCounter;
this.rngOffset = tempRngOffset; this.rngOffset = tempRngOffset;
this.rngSeedOverride = tempRngSeedOverride; this.rngSeedOverride = tempRngSeedOverride;
@ -1496,8 +1536,18 @@ export default class BattleScene extends SceneBase {
} }
updateScoreText(): void { updateScoreText(): void {
this.scoreText.setText(`Score: ${this.score.toString()}`); //this.scoreText.setText(`Score: ${this.score.toString()}`);
this.scoreText.setVisible(this.gameMode.isDaily); //this.scoreText.setVisible(this.gameMode.isDaily);
}
setScoreText(text: string): void {
if (this.scoreText == undefined)
return;
if (this.scoreText.setText == undefined)
return;
if (this.scoreText.setVisible == undefined)
return;
this.scoreText.setText(text);
this.scoreText.setVisible(true);
} }
updateAndShowText(duration: integer): void { updateAndShowText(duration: integer): void {
@ -1583,6 +1633,7 @@ export default class BattleScene extends SceneBase {
if (fromArenaPool) { if (fromArenaPool) {
return this.arena.randomSpecies(waveIndex, level,null , getPartyLuckValue(this.party)); return this.arena.randomSpecies(waveIndex, level,null , getPartyLuckValue(this.party));
} }
LoggerTools.rarities[LoggerTools.rarityslot[0]] = ""
const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => { const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => {
if (!filterAllEvolutions) { if (!filterAllEvolutions) {
while (pokemonPrevolutions.hasOwnProperty(s.speciesId)) { while (pokemonPrevolutions.hasOwnProperty(s.speciesId)) {
@ -2321,7 +2372,7 @@ export default class BattleScene extends SceneBase {
if (isBoss) { if (isBoss) {
count = Math.max(count, Math.floor(chances / 2)); count = Math.max(count, Math.floor(chances / 2));
} }
getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance) getEnemyModifierTypesForWave(difficultyWaveIndex, count, [ enemyPokemon ], this.currentBattle.battleType === BattleType.TRAINER ? ModifierPoolType.TRAINER : ModifierPoolType.WILD, upgradeChance, this)
.map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this)); .map(mt => mt.newModifier(enemyPokemon).add(this.enemyModifiers, false, this));
}); });

View File

@ -13,6 +13,7 @@ import { Moves } from "#enums/moves";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import i18next from "#app/plugins/i18n";
export enum BattleType { export enum BattleType {
WILD, WILD,
@ -173,7 +174,10 @@ export default class Battle {
scene.addMoney(moneyAmount.value); scene.addMoney(moneyAmount.value);
scene.queueMessage(`You picked up ₽${moneyAmount.value.toLocaleString("en-US")}!`, null, true); const userLocale = navigator.language || "en-US";
const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale);
const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount });
scene.queueMessage(message, null, true);
scene.currentBattle.moneyScattered = 0; scene.currentBattle.moneyScattered = 0;
} }
@ -359,6 +363,7 @@ export default class Battle {
const ret = Utils.randSeedInt(range, min); const ret = Utils.randSeedInt(range, min);
this.battleSeedState = Phaser.Math.RND.state(); this.battleSeedState = Phaser.Math.RND.state();
Phaser.Math.RND.state(state); Phaser.Math.RND.state(state);
//scene.setScoreText("RNG: " + tempRngCounter + " (Last sim: " + this.rngCounter + ")")
scene.rngCounter = tempRngCounter; scene.rngCounter = tempRngCounter;
scene.rngSeedOverride = tempSeedOverride; scene.rngSeedOverride = tempSeedOverride;
return ret; return ret;

View File

@ -2645,6 +2645,8 @@ function getAnticipationCondition(): AbAttrCondition {
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
for (const opponent of pokemon.getOpponents()) { for (const opponent of pokemon.getOpponents()) {
for (const move of opponent.moveset) { for (const move of opponent.moveset) {
if (move == undefined)
continue;
// move is super effective // move is super effective
if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true) >= 2) { if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true) >= 2) {
return true; return true;
@ -3999,6 +4001,88 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: Constructor<TAttr>
applyNextAbAttr(); applyNextAbAttr();
}); });
} }
function applyAbAttrsInternalNoApply<TAttr extends AbAttr>(attrType: Constructor<TAttr>,
pokemon: Pokemon, applyFunc: AbAttrApplyFunc<TAttr>, args: any[], isAsync: boolean = false, showAbilityInstant: boolean = false, quiet: boolean = false, passive: boolean = false): Promise<void> {
return new Promise(resolve => {
if (!pokemon.canApplyAbility(passive)) {
if (!passive) {
args[0].value = 0
return resolve();
return applyAbAttrsInternal(attrType, pokemon, applyFunc, args, isAsync, showAbilityInstant, quiet, true).then(() => resolve());
} else {
return resolve();
}
}
const ability = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility());
const attrs = ability.getAttrs(attrType);
const clearSpliceQueueAndResolve = () => {
pokemon.scene?.clearPhaseQueueSplice();
if (!passive) {
args[0].value = 0
return resolve()
return applyAbAttrsInternal(attrType, pokemon, applyFunc, args, isAsync, showAbilityInstant, quiet, true).then(() => resolve());
} else {
return resolve();
}
};
return resolve();
const applyNextAbAttr = () => {
if (attrs.length) {
applyAbAttr(attrs.shift());
} else {
clearSpliceQueueAndResolve();
}
};
const applyAbAttr = (attr: TAttr) => {
if (!canApplyAttr(pokemon, attr)) {
return applyNextAbAttr();
}
pokemon.scene.setPhaseQueueSplice();
const onApplySuccess = () => {
if (pokemon.summonData && !pokemon.summonData.abilitiesApplied.includes(ability.id)) {
pokemon.summonData.abilitiesApplied.push(ability.id);
}
if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(ability.id)) {
pokemon.battleData.abilitiesApplied.push(ability.id);
}
if (attr.showAbility && !quiet) {
if (showAbilityInstant) {
pokemon.scene.abilityBar.showAbility(pokemon, passive);
} else {
queueShowAbility(pokemon, passive);
}
}
if (!quiet) {
const message = attr.getTriggerMessage(pokemon, (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name, args);
if (message) {
if (isAsync) {
pokemon.scene.ui.showText(message, null, () => pokemon.scene.ui.showText(null, 0), null, true);
} else {
pokemon.scene.queueMessage(message);
}
}
}
};
const result = applyFunc(attr, passive);
if (result instanceof Promise) {
result.then(success => {
if (success) {
onApplySuccess();
}
applyNextAbAttr();
});
} else {
if (result) {
onApplySuccess();
}
applyNextAbAttr();
}
};
applyNextAbAttr();
});
}
export function applyAbAttrs(attrType: Constructor<AbAttr>, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> { export function applyAbAttrs(attrType: Constructor<AbAttr>, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
return applyAbAttrsInternal<AbAttr>(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args); return applyAbAttrsInternal<AbAttr>(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args);
@ -4014,6 +4098,11 @@ export function applyPreDefendAbAttrs(attrType: Constructor<PreDefendAbAttr>,
const simulated = args.length > 1 && args[1]; const simulated = args.length > 1 && args[1];
return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, false, simulated); return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, false, simulated);
} }
export function applyPreDefendAbAttrsNoApply(attrType: Constructor<PreDefendAbAttr>,
pokemon: Pokemon, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
const simulated = args.length > 1 && args[1];
return applyAbAttrsInternalNoApply<PreDefendAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, false, simulated);
}
export function applyPostDefendAbAttrs(attrType: Constructor<PostDefendAbAttr>, export function applyPostDefendAbAttrs(attrType: Constructor<PostDefendAbAttr>,
pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> { pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise<void> {

View File

@ -1590,7 +1590,7 @@ export class IncrementMovePriorityAttr extends MoveAttr {
* @see {@linkcode apply} * @see {@linkcode apply}
*/ */
export class MultiHitAttr extends MoveAttr { export class MultiHitAttr extends MoveAttr {
private multiHitType: MultiHitType; public multiHitType: MultiHitType;
constructor(multiHitType?: MultiHitType) { constructor(multiHitType?: MultiHitType) {
super(); super();
@ -4218,7 +4218,7 @@ export class AddArenaTagAttr extends MoveEffectAttr {
public selfSideTarget: boolean; public selfSideTarget: boolean;
constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) { constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) {
super(true, MoveEffectTrigger.POST_APPLY, true); super(true, MoveEffectTrigger.POST_APPLY);
this.tagType = tagType; this.tagType = tagType;
this.turnCount = turnCount; this.turnCount = turnCount;
@ -4717,7 +4717,7 @@ export class AddTypeAttr extends MoveEffectAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const types = target.getTypes().slice(0, 2).filter(t => t !== Type.UNKNOWN); // TODO: Figure out some way to actually check if another version of this effect is already applied const types = target.getTypes().slice(0, 2).filter(t => t !== Type.UNKNOWN).map(t => t as Type); // TODO: Figure out some way to actually check if another version of this effect is already applied
types.push(this.type); types.push(this.type);
target.summonData.types = types; target.summonData.types = types;
target.updateInfo(); target.updateInfo();

View File

@ -61,6 +61,89 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals
return ret; return ret;
} }
export function getNatureIncrease(nature: Nature) {
switch (nature) {
case Nature.LONELY:
case Nature.BRAVE:
case Nature.ADAMANT:
case Nature.NAUGHTY:
return "atk";
case Nature.BOLD:
case Nature.RELAXED:
case Nature.IMPISH:
case Nature.LAX:
return "def";
case Nature.MODEST:
case Nature.MILD:
case Nature.QUIET:
case Nature.RASH:
return "spatk";
case Nature.CALM:
case Nature.GENTLE:
case Nature.SASSY:
case Nature.CAREFUL:
return "spdef";
case Nature.TIMID:
case Nature.HASTY:
case Nature.JOLLY:
case Nature.NAIVE:
return "spe"
case Nature.HARDY:
//return "atk"
case Nature.DOCILE:
//return "def"
case Nature.SERIOUS:
//return "spatk"
case Nature.BASHFUL:
//return "spdef"
case Nature.QUIRKY:
//return "spe"
default:
return ""
}
}
export function getNatureDecrease(nature: Nature) {
switch (nature) {
case Nature.BOLD:
case Nature.TIMID:
case Nature.MODEST:
case Nature.CALM:
return "atk";
case Nature.LONELY:
case Nature.HASTY:
case Nature.MILD:
case Nature.GENTLE:
return "def"
case Nature.ADAMANT:
case Nature.IMPISH:
case Nature.JOLLY:
case Nature.CAREFUL:
return "spatk"
case Nature.NAUGHTY:
case Nature.LAX:
case Nature.NAIVE:
case Nature.RASH:
return "spdef"
case Nature.BRAVE:
case Nature.RELAXED:
case Nature.QUIET:
case Nature.SASSY:
return "spe"
case Nature.HARDY:
//return "atk"
case Nature.DOCILE:
//return "def"
case Nature.SERIOUS:
//return "spatk"
case Nature.BASHFUL:
//return "spdef"
case Nature.QUIRKY:
//return "spe"
default:
return ""
}
}
export function getNatureStatMultiplier(nature: Nature, stat: Stat): number { export function getNatureStatMultiplier(nature: Nature, stat: Stat): number {
switch (stat) { switch (stat) {
case Stat.ATK: case Stat.ATK:

View File

@ -53,6 +53,30 @@ export function getPokeballName(type: PokeballType): string {
} }
return ret; return ret;
} }
export function getPokeballShortName(type: PokeballType): string {
let ret: string;
switch (type) {
case PokeballType.POKEBALL:
ret = "Poké";
break;
case PokeballType.GREAT_BALL:
ret = "Great";
break;
case PokeballType.ULTRA_BALL:
ret = "Ultra";
break;
case PokeballType.ROGUE_BALL:
ret = "Rogue";
break;
case PokeballType.MASTER_BALL:
ret = "Master";
break;
case PokeballType.LUXURY_BALL:
ret = "Luxury";
break;
}
return ret;
}
export function getPokeballCatchMultiplier(type: PokeballType): number { export function getPokeballCatchMultiplier(type: PokeballType): number {
switch (type) { switch (type) {

View File

@ -5,6 +5,7 @@ import * as Utils from "../utils";
import { IncrementMovePriorityAbAttr, applyAbAttrs } from "./ability"; import { IncrementMovePriorityAbAttr, applyAbAttrs } from "./ability";
import { ProtectAttr } from "./move"; import { ProtectAttr } from "./move";
import { BattlerIndex } from "#app/battle.js"; import { BattlerIndex } from "#app/battle.js";
import i18next from "i18next";
export enum TerrainType { export enum TerrainType {
NONE, NONE,
@ -67,6 +68,22 @@ export class Terrain {
} }
} }
export function getTerrainName(terrainType: TerrainType): string {
switch (terrainType) {
case TerrainType.MISTY:
return i18next.t("terrain:misty");
case TerrainType.ELECTRIC:
return i18next.t("terrain:electric");
case TerrainType.GRASSY:
return i18next.t("terrain:grassy");
case TerrainType.PSYCHIC:
return i18next.t("terrain:psychic");
}
return "";
}
export function getTerrainColor(terrainType: TerrainType): [ integer, integer, integer ] { export function getTerrainColor(terrainType: TerrainType): [ integer, integer, integer ] {
switch (terrainType) { switch (terrainType) {
case TerrainType.MISTY: case TerrainType.MISTY:

View File

@ -59855,16 +59855,11 @@ export const tmSpecies: TmSpecies = {
Species.ZUBAT, Species.ZUBAT,
Species.GOLBAT, Species.GOLBAT,
Species.TENTACRUEL, Species.TENTACRUEL,
Species.MUK,
Species.KOFFING, Species.KOFFING,
Species.WEEZING, Species.WEEZING,
Species.MEW, Species.MEW,
Species.ARIADOS,
Species.CROBAT, Species.CROBAT,
Species.QWILFISH, Species.QWILFISH,
Species.GULPIN,
Species.SWALOT,
Species.SEVIPER,
Species.ROSERADE, Species.ROSERADE,
Species.STUNKY, Species.STUNKY,
Species.SKUNTANK, Species.SKUNTANK,
@ -59896,8 +59891,6 @@ export const tmSpecies: TmSpecies = {
Species.NAGANADEL, Species.NAGANADEL,
Species.PINCURCHIN, Species.PINCURCHIN,
Species.ETERNATUS, Species.ETERNATUS,
Species.PIKACHU,
Species.ALOLA_MUK,
Species.GALAR_WEEZING, Species.GALAR_WEEZING,
Species.GALAR_SLOWKING, Species.GALAR_SLOWKING,
[ [

View File

@ -1,12 +1,12 @@
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { getPokemonMessage, getPokemonNameWithAffix } from "../messages"; import { getPokemonNameWithAffix } from "../messages";
import Pokemon from "../field/pokemon"; import Pokemon from "../field/pokemon";
import { Type } from "./type"; import { Type } from "./type";
import Move, { AttackMove } from "./move"; import Move, { AttackMove } from "./move";
import * as Utils from "../utils"; import * as Utils from "../utils";
import BattleScene from "../battle-scene"; import BattleScene from "../battle-scene";
import { SuppressWeatherEffectAbAttr } from "./ability"; import { SuppressWeatherEffectAbAttr } from "./ability";
import { TerrainType } from "./terrain"; import { TerrainType, getTerrainName } from "./terrain";
import i18next from "i18next"; import i18next from "i18next";
export enum WeatherType { export enum WeatherType {
@ -216,34 +216,34 @@ export function getWeatherClearMessage(weatherType: WeatherType): string {
export function getTerrainStartMessage(terrainType: TerrainType): string { export function getTerrainStartMessage(terrainType: TerrainType): string {
switch (terrainType) { switch (terrainType) {
case TerrainType.MISTY: case TerrainType.MISTY:
return "Mist swirled around the battlefield!"; return i18next.t("terrain:mistyStartMessage");
case TerrainType.ELECTRIC: case TerrainType.ELECTRIC:
return "An electric current ran across the battlefield!"; return i18next.t("terrain:electricStartMessage");
case TerrainType.GRASSY: case TerrainType.GRASSY:
return "Grass grew to cover the battlefield!"; return i18next.t("terrain:grassyStartMessage");
case TerrainType.PSYCHIC: case TerrainType.PSYCHIC:
return "The battlefield got weird!"; return i18next.t("terrain:psychicStartMessage");
} }
} }
export function getTerrainClearMessage(terrainType: TerrainType): string { export function getTerrainClearMessage(terrainType: TerrainType): string {
switch (terrainType) { switch (terrainType) {
case TerrainType.MISTY: case TerrainType.MISTY:
return "The mist disappeared from the battlefield."; return i18next.t("terrain:mistyClearMessage");
case TerrainType.ELECTRIC: case TerrainType.ELECTRIC:
return "The electricity disappeared from the battlefield."; return i18next.t("terrain:electricClearMessage");
case TerrainType.GRASSY: case TerrainType.GRASSY:
return "The grass disappeared from the battlefield."; return i18next.t("terrain:grassyClearMessage");
case TerrainType.PSYCHIC: case TerrainType.PSYCHIC:
return "The weirdness disappeared from the battlefield!"; return i18next.t("terrain:psychicClearMessage");
} }
} }
export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainType): string { export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainType): string {
if (terrainType === TerrainType.MISTY) { if (terrainType === TerrainType.MISTY) {
return getPokemonMessage(pokemon, " surrounds itself with a protective mist!"); return i18next.t("terrain:mistyBlockMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)});
} }
return getPokemonMessage(pokemon, ` is protected by the ${Utils.toReadableString(TerrainType[terrainType])} Terrain!`); return i18next.t("terrain:defaultBlockMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), terrainName: getTerrainName(terrainType)});
} }
interface WeatherPoolEntry { interface WeatherPoolEntry {

View File

@ -10,6 +10,7 @@ import { cos, sin } from "./field/anims";
import { PlayerPokemon } from "./field/pokemon"; import { PlayerPokemon } from "./field/pokemon";
import { getTypeRgb } from "./data/type"; import { getTypeRgb } from "./data/type";
import i18next from "i18next"; import i18next from "i18next";
import * as LoggerTools from "./logger";
export class EvolutionPhase extends Phase { export class EvolutionPhase extends Phase {
protected pokemon: PlayerPokemon; protected pokemon: PlayerPokemon;
@ -200,10 +201,12 @@ export class EvolutionPhase extends Phase {
this.end(); this.end();
}; };
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => { this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Cancel " + preName + "'s evolution and pause evolutions")
this.scene.ui.revertMode(); this.scene.ui.revertMode();
this.pokemon.pauseEvolutions = true; this.pokemon.pauseEvolutions = true;
this.scene.ui.showText(i18next.t("menu:evolutionsPaused", { pokemonName: preName }), null, end, 3000); this.scene.ui.showText(i18next.t("menu:evolutionsPaused", { pokemonName: preName }), null, end, 3000);
}, () => { }, () => {
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Cancel " + preName + "'s evolution")
this.scene.ui.revertMode(); this.scene.ui.revertMode();
this.scene.time.delayedCall(3000, end); this.scene.time.delayedCall(3000, end);
}); });
@ -219,6 +222,7 @@ export class EvolutionPhase extends Phase {
evolutionHandler.canCancel = false; evolutionHandler.canCancel = false;
this.pokemon.evolve(this.evolution).then(() => { this.pokemon.evolve(this.evolution).then(() => {
LoggerTools.logActions(this.scene, this.scene.currentBattle.waveIndex, "Evolve " + preName)
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true); const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
for (const lm of levelMoves) { for (const lm of levelMoves) {
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getParty().indexOf(this.pokemon), lm[1])); this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getParty().indexOf(this.pokemon), lm[1]));

View File

@ -21,6 +21,7 @@ import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day"; import { TimeOfDay } from "#enums/time-of-day";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
import * as LoggerTools from "../logger"
export class Arena { export class Arena {
public scene: BattleScene; public scene: BattleScene;
@ -33,7 +34,7 @@ export class Arena {
private lastTimeOfDay: TimeOfDay; private lastTimeOfDay: TimeOfDay;
private pokemonPool: PokemonPools; public pokemonPool: PokemonPools;
private trainerPool: BiomeTierTrainerPools; private trainerPool: BiomeTierTrainerPools;
public readonly eventTarget: EventTarget = new EventTarget(); public readonly eventTarget: EventTarget = new EventTarget();
@ -91,6 +92,19 @@ export class Arena {
? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE ? tierValue >= 156 ? BiomePoolTier.COMMON : tierValue >= 32 ? BiomePoolTier.UNCOMMON : tierValue >= 6 ? BiomePoolTier.RARE : tierValue >= 1 ? BiomePoolTier.SUPER_RARE : BiomePoolTier.ULTRA_RARE
: tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE; : tierValue >= 20 ? BiomePoolTier.BOSS : tierValue >= 6 ? BiomePoolTier.BOSS_RARE : tierValue >= 1 ? BiomePoolTier.BOSS_SUPER_RARE : BiomePoolTier.BOSS_ULTRA_RARE;
console.log(BiomePoolTier[tier]); console.log(BiomePoolTier[tier]);
var tiernames = [
"Common",
"Uncommon",
"Rare",
"Super Rare",
"Ultra Rare",
"Common",
"Rare",
"Super Rare",
"Ultra Rare",
]
LoggerTools.rarities[LoggerTools.rarityslot[0]] = tiernames[tier]
console.log(tiernames[tier])
while (!this.pokemonPool[tier].length) { while (!this.pokemonPool[tier].length) {
console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`); console.log(`Downgraded rarity tier from ${BiomePoolTier[tier]} to ${BiomePoolTier[tier - 1]}`);
tier--; tier--;
@ -384,6 +398,10 @@ export class Arena {
return weatherMultiplier * terrainMultiplier; return weatherMultiplier * terrainMultiplier;
} }
/**
* Gets the denominator for the chance for a trainer spawn
* @returns n where 1/n is the chance of a trainer battle
*/
getTrainerChance(): integer { getTrainerChance(): integer {
switch (this.biomeType) { switch (this.biomeType) {
case Biome.METROPOLIS: case Biome.METROPOLIS:

View File

@ -109,6 +109,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
private shinySparkle: Phaser.GameObjects.Sprite; private shinySparkle: Phaser.GameObjects.Sprite;
public usedInBattle: boolean = false; public usedInBattle: boolean = false;
public partyslot: integer;
constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) { constructor(scene: BattleScene, x: number, y: number, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) {
super(scene, x, y); super(scene, x, y);

View File

@ -168,6 +168,113 @@ export default class Trainer extends Phaser.GameObjects.Container {
// Return the formatted name, including the title if it is set. // Return the formatted name, including the title if it is set.
return title ? `${title} ${name}` : name; return title ? `${title} ${name}` : name;
} }
getNameOnly(trainerSlot: TrainerSlot = TrainerSlot.NONE): string {
// Get the base title based on the trainer slot and variant.
let name = this.config.getTitle(trainerSlot, this.variant);
// Determine the title to include based on the configuration and includeTitle flag.
let title = true && this.config.title ? this.config.title : null;
if (this.name === "" && name.toLowerCase().includes("grunt")) {
// This is a evil team grunt so we localize it by only using the "name" as the title
title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, "_")}`);
console.log("Localized grunt name: " + title);
// Since grunts are not named we can just return the title
return title;
}
// If the trainer has a name (not null or undefined).
if (this.name) {
// If the title should be included.
if (true) {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) {
// Initialize the i18n system if it is not already initialized.
initI18n();
}
// Get the localized trainer class name from the i18n file and set it as the title.
// This is used for trainer class names, not titles like "Elite Four, Champion, etc."
title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, "_")}`);
}
// If no specific trainer slot is set.
if (!trainerSlot) {
// Use the trainer's name.
name = this.name;
// If there is a partner name, concatenate it with the trainer's name using "&".
if (this.partnerName) {
name = `${name} & ${this.partnerName}`;
}
} else {
// Assign the name based on the trainer slot:
// Use 'this.name' if 'trainerSlot' is TRAINER.
// Otherwise, use 'this.partnerName' if it exists, or 'this.name' if it doesn't.
name = trainerSlot === TrainerSlot.TRAINER ? this.name : this.partnerName || this.name;
}
}
if (this.config.titleDouble && this.variant === TrainerVariant.DOUBLE && !this.config.doubleOnly) {
title = this.config.titleDouble;
name = i18next.t(`trainerNames:${this.config.nameDouble.toLowerCase().replace(/\s/g, "_")}`);
}
// Return the formatted name, including the title if it is set.
return name || "";
}
getTitleOnly(trainerSlot: TrainerSlot = TrainerSlot.NONE): string {
// Get the base title based on the trainer slot and variant.
let name = this.config.getTitle(trainerSlot, this.variant);
// Determine the title to include based on the configuration and includeTitle flag.
let title = true && this.config.title ? this.config.title : null;
if (this.name === "" && name.toLowerCase().includes("grunt")) {
return "Grunt";
// This is a evil team grunt so we localize it by only using the "name" as the title
title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, "_")}`);
console.log("Localized grunt name: " + title);
// Since grunts are not named we can just return the title
return title;
}
// If the trainer has a name (not null or undefined).
if (this.name) {
// If the title should be included.
if (true) {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) {
// Initialize the i18n system if it is not already initialized.
initI18n();
}
// Get the localized trainer class name from the i18n file and set it as the title.
// This is used for trainer class names, not titles like "Elite Four, Champion, etc."
title = i18next.t(`trainerClasses:${name.toLowerCase().replace(/\s/g, "_")}`);
}
// If no specific trainer slot is set.
if (!trainerSlot) {
// Use the trainer's name.
name = this.name;
// If there is a partner name, concatenate it with the trainer's name using "&".
if (this.partnerName) {
name = `${name} & ${this.partnerName}`;
}
} else {
// Assign the name based on the trainer slot:
// Use 'this.name' if 'trainerSlot' is TRAINER.
// Otherwise, use 'this.partnerName' if it exists, or 'this.name' if it doesn't.
name = trainerSlot === TrainerSlot.TRAINER ? this.name : this.partnerName || this.name;
}
}
if (this.config.titleDouble && this.variant === TrainerVariant.DOUBLE && !this.config.doubleOnly) {
title = this.config.titleDouble;
name = i18next.t(`trainerNames:${this.config.nameDouble.toLowerCase().replace(/\s/g, "_")}`);
}
// Return the formatted name, including the title if it is set.
return title || "";
}
isDouble(): boolean { isDouble(): boolean {

View File

@ -107,22 +107,37 @@ export class GameMode implements GameModeConfig {
} }
} }
/**
* Determines whether or not to generate a trainer
* @param waveIndex the current floor the player is on (trainer sprites fail to generate on X1 floors)
* @param arena the arena that contains the scene and functions
* @returns true if a trainer should be generated, false otherwise
*/
isWaveTrainer(waveIndex: integer, arena: Arena): boolean { isWaveTrainer(waveIndex: integer, arena: Arena): boolean {
/**
* Daily spawns trainers on floors 5, 15, 20, 25, 30, 35, 40, and 45
*/
if (this.isDaily) { if (this.isDaily) {
return waveIndex % 10 === 5 || (!(waveIndex % 10) && waveIndex > 10 && !this.isWaveFinal(waveIndex)); return waveIndex % 10 === 5 || (!(waveIndex % 10) && waveIndex > 10 && !this.isWaveFinal(waveIndex));
} }
if ((waveIndex % 30) === (arena.scene.offsetGym ? 0 : 20) && !this.isWaveFinal(waveIndex)) { if ((waveIndex % 30) === (arena.scene.offsetGym ? 0 : 20) && !this.isWaveFinal(waveIndex)) {
return true; return true;
} else if (waveIndex % 10 !== 1 && waveIndex % 10) { } else if (waveIndex % 10 !== 1 && waveIndex % 10) {
/**
* Do not check X1 floors since there's a bug that stops trainer sprites from appearing
* after a X0 full party heal
*/
const trainerChance = arena.getTrainerChance(); const trainerChance = arena.getTrainerChance();
let allowTrainerBattle = true; let allowTrainerBattle = true;
if (trainerChance) { if (trainerChance) {
const waveBase = Math.floor(waveIndex / 10) * 10; const waveBase = Math.floor(waveIndex / 10) * 10;
// Stop generic trainers from spawning in within 3 waves of a trainer battle
for (let w = Math.max(waveIndex - 3, waveBase + 2); w <= Math.min(waveIndex + 3, waveBase + 9); w++) { for (let w = Math.max(waveIndex - 3, waveBase + 2); w <= Math.min(waveIndex + 3, waveBase + 9); w++) {
if (w === waveIndex) { if (w === waveIndex) {
continue; continue;
} }
if ((w % 30) === (arena.scene.offsetGym ? 0 : 20) || this.isFixedBattle(waveIndex)) { if ((w % 30) === (arena.scene.offsetGym ? 0 : 20) || this.isFixedBattle(w)) {
allowTrainerBattle = false; allowTrainerBattle = false;
break; break;
} else if (w < waveIndex) { } else if (w < waveIndex) {
@ -138,7 +153,7 @@ export class GameMode implements GameModeConfig {
} }
} }
} }
return allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance); return Boolean(allowTrainerBattle && trainerChance && !Utils.randSeedInt(trainerChance));
} }
return false; return false;
} }

View File

@ -23,6 +23,97 @@ import { initVouchers } from "./system/voucher";
import { Biome } from "#enums/biome"; import { Biome } from "#enums/biome";
import { TrainerType } from "#enums/trainer-type"; import { TrainerType } from "#enums/trainer-type";
export const biomePanelIDs: string[] = [
"town",
"plains",
"grass",
"tall_grass",
"metropolis",
"forest",
"sea",
"swamp",
"beach",
"lake",
"seabed",
"mountain",
"badlands",
"cave",
"desert",
"ice_cave",
"meadow",
"power_plant",
"volcano",
"graveyard",
"dojo",
"factory",
"ruins",
"wasteland",
"abyss",
"space",
"construction_site",
"jungle",
"fairy_cave",
"temple",
"slum",
"snowy_forest",
"",
"",
"",
"",
"",
"",
"",
"",
"island",
"laboratory",
"",
"",
"",
"",
"",
"",
"",
"",
"end"
]
export const allpanels: string[] = [
"abyss",
"badlands",
"beach",
"cave",
"construction_site",
//"desert",
//"dojo",
"end",
//"factory",
//"fairy_cave",
//"forest",
//"grass",
//"graveyard",
//"ice_cave",
//"island",
//"jungle",
//"laboratory",
//"lake",
//"meadow",
//"metropolis",
//"mountain",
//"plains",
//"power_plant",
//"ruins",
"sea",
//"seabed",
"slum",
//"snowy_forest",
"space",
//"swamp",
//"tall_grass",
//"temple",
"town",
"volcano",
"wasteland"
]
export class LoadingScene extends SceneBase { export class LoadingScene extends SceneBase {
readonly LOAD_EVENTS = Phaser.Loader.Events; readonly LOAD_EVENTS = Phaser.Loader.Events;
@ -53,6 +144,44 @@ export class LoadingScene extends SceneBase {
this.loadImage(`window_${w}${getWindowVariantSuffix(wv)}`, "ui/windows"); this.loadImage(`window_${w}${getWindowVariantSuffix(wv)}`, "ui/windows");
} }
} }
//this.loadImage(`abyss_panel`, "ui/windows");
// this.loadImage(`badlands_panel`, "ui/windows");
//this.loadImage(`beach_panel`, "ui/windows");
//this.loadImage(`cave_panel`, "ui/windows");
//this.loadImage(`construction_site_panel`, "ui/windows");
//this.loadImage(`desert_panel`, "ui/windows");
//this.loadImage(`dojo_panel`, "ui/windows");
// this.loadImage(`end_panel`, "ui/windows");
//this.loadImage(`factory_panel`, "ui/windows");
//this.loadImage(`fairy_cave_panel`, "ui/windows");
//this.loadImage(`forest_panel`, "ui/windows");
//this.loadImage(`grass_panel`, "ui/windows");
//this.loadImage(`graveyard_panel`, "ui/windows");
//this.loadImage(`ice_cave_panel`, "ui/windows");
//this.loadImage(`island_panel`, "ui/windows");
//this.loadImage(`jungle_panel`, "ui/windows");
//this.loadImage(`laboratory_panel`, "ui/windows");
//this.loadImage(`lake_panel`, "ui/windows");
//this.loadImage(`meadow_panel`, "ui/windows");
//this.loadImage(`metropolis_panel`, "ui/windows");
//this.loadImage(`mountain_panel`, "ui/windows");
//this.loadImage(`plains_panel`, "ui/windows");
//this.loadImage(`power_plant_panel`, "ui/windows");
//this.loadImage(`ruins_panel`, "ui/windows");
// this.loadImage(`sea_panel`, "ui/windows");
//this.loadImage(`seabed_panel`, "ui/windows");
//this.loadImage(`slum_panel`, "ui/windows");
//this.loadImage(`snowy_forest_panel`, "ui/windows");
// this.loadImage(`space_panel`, "ui/windows");
//this.loadImage(`swamp_panel`, "ui/windows");
//this.loadImage(`tall_grass_panel`, "ui/windows");
//this.loadImage(`temple_panel`, "ui/windows");
//this.loadImage(`town_panel`, "ui/windows");
// this.loadImage(`volcano_panel`, "ui/windows");
// this.loadImage(`wasteland_panel`, "ui/windows");
for (var i = 0; i < allpanels.length; i++) {
this.loadImageNoLegacy(`${allpanels[i]}_panel`, "ui/windows");
}
this.loadAtlas("namebox", "ui"); this.loadAtlas("namebox", "ui");
this.loadImage("pbinfo_player", "ui"); this.loadImage("pbinfo_player", "ui");
this.loadImage("pbinfo_player_stats", "ui"); this.loadImage("pbinfo_player_stats", "ui");

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "Möchtest du\n{{pokemonName}} auswechseln?", "switchQuestion": "Möchtest du\n{{pokemonName}} auswechseln?",
"trainerDefeated": "{{trainerName}}\nwurde besiegt!", "trainerDefeated": "{{trainerName}}\nwurde besiegt!",
"moneyWon": "Du gewinnst\n{{moneyAmount}} ₽!", "moneyWon": "Du gewinnst\n{{moneyAmount}} ₽!",
"moneyPickedUp": "Du hebst {{moneyAmount}} ₽ auf!",
"pokemonCaught": "{{pokemonName}} wurde gefangen!", "pokemonCaught": "{{pokemonName}} wurde gefangen!",
"addedAsAStarter": "{{pokemonName}} wurde als Starterpokémon hinzugefügt!", "addedAsAStarter": "{{pokemonName}} wurde als Starterpokémon hinzugefügt!",
"partyFull": "Dein Team ist voll.\nMöchtest du ein Pokémon durch {{pokemonName}} ersetzen?", "partyFull": "Dein Team ist voll.\nMöchtest du ein Pokémon durch {{pokemonName}} ersetzen?",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const deConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const deConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}} hält mithilfe des Items {{typeName}} durch!",
"turnHealApply": "{{typeName}} von {{pokemonNameWithAffix}} füllt einige KP auf!",
"hitHealApply": "{{typeName}} von {{pokemonNameWithAffix}} füllt einige KP auf!",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}} wurde durch {{typeName}} wiederbelebt!",
"moneyInterestApply": "Du erhählst {{moneyAmount}} ₽ durch das Item {{typeName}}!",
"turnHeldItemTransferApply": "{{itemName}} von {{pokemonNameWithAffix}} wurde durch {{typeName}} von {{pokemonName}} absorbiert!",
"contactHeldItemTransferApply": "{{itemName}} von {{pokemonNameWithAffix}} wurde durch {{typeName}} von {{pokemonName}} geklaut!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}} stellt einige KP wieder her!",
} as const;

View File

@ -82,7 +82,7 @@ export const settings: SimpleTranslationEntries = {
"buttonMenu": "Menü", "buttonMenu": "Menü",
"buttonSubmit": "Bestätigen", "buttonSubmit": "Bestätigen",
"buttonCancel": "Abbrechen", "buttonCancel": "Abbrechen",
"buttonStats": "Statistiken", "buttonStats": "Statuswerte",
"buttonCycleForm": "Form wechseln", "buttonCycleForm": "Form wechseln",
"buttonCycleShiny": "Schillernd wechseln", "buttonCycleShiny": "Schillernd wechseln",
"buttonCycleGender": "Geschlecht wechseln", "buttonCycleGender": "Geschlecht wechseln",

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "Rätselhafte Luftströmungen haben den Angriff abgeschwächt!", "strongWindsEffectMessage": "Rätselhafte Luftströmungen haben den Angriff abgeschwächt!",
"strongWindsClearMessage": "Die rätselhafte Luftströmung hat sich wieder geleget.", "strongWindsClearMessage": "Die rätselhafte Luftströmung hat sich wieder geleget.",
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "Nebelfeld",
"mistyStartMessage": "Am Boden breitet sich dichter Nebel aus!",
"mistyClearMessage": "Das Nebelfeld ist wieder verschwunden!",
"mistyBlockMessage": "{{pokemonNameWithAffix}} wird vom Nebelfeld geschützt!",
"electric": "Elektrofeld",
"electricStartMessage": "Elektrische Energie fließt durch den Boden!",
"electricClearMessage": "Das Elektrofeld ist wieder verschwunden!",
"grassy": "Grasfeld",
"grassyStartMessage": "Dichtes Gras schießt aus dem Boden!",
"grassyClearMessage": "Das Grasfeld ist wieder verschwunden!",
"psychic": "Psychofeld",
"psychicStartMessage": "Der Boden fühlt sich seltsam an!",
"psychicClearMessage": "Das Psychofeld ist wieder verschwunden!",
"defaultBlockMessage": "{{pokemonNameWithAffix}} wird vom {{terrainName}} geschützt!"
};

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "Will you switch\n{{pokemonName}}?", "switchQuestion": "Will you switch\n{{pokemonName}}?",
"trainerDefeated": "You defeated\n{{trainerName}}!", "trainerDefeated": "You defeated\n{{trainerName}}!",
"moneyWon": "You got\n₽{{moneyAmount}} for winning!", "moneyWon": "You got\n₽{{moneyAmount}} for winning!",
"moneyPickedUp": "You picked up ₽{{moneyAmount}}!",
"pokemonCaught": "{{pokemonName}} was caught!", "pokemonCaught": "{{pokemonName}} was caught!",
"addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!", "addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!",
"partyFull": "Your party is full.\nRelease a Pokémon to make room for {{pokemonName}}?", "partyFull": "Your party is full.\nRelease a Pokémon to make room for {{pokemonName}}?",

View File

@ -27,6 +27,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -42,7 +43,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { modifierSelectUiHandler } from "./modifier-select-ui-handler"; import { modifierSelectUiHandler } from "./modifier-select-ui-handler";
export const enConfig = { export const enConfig = {
@ -73,10 +74,10 @@ export const enConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
partyUiHandler: partyUiHandler,
pokeball: pokeball, pokeball: pokeball,
pokemon: pokemon, pokemon: pokemon,
pokemonInfo: pokemonInfo, pokemonInfo: pokemonInfo,
@ -86,11 +87,13 @@ export const enConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,
tutorial: tutorial, tutorial: tutorial,
voucher: voucher, voucher: voucher,
weather: weather, weather: weather,
partyUiHandler: partyUiHandler,
modifierSelectUiHandler: modifierSelectUiHandler modifierSelectUiHandler: modifierSelectUiHandler
}; };

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!",
"turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!",
"hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!",
"moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!",
"turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!",
"contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!",
} as const;

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "The heavy wind stopped." "strongWindsClearMessage": "The heavy wind stopped."
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "Misty",
"mistyStartMessage": "Mist swirled around the battlefield!",
"mistyClearMessage": "The mist disappeared from the battlefield.",
"mistyBlockMessage": "{{pokemonNameWithAffix}} surrounds itself with a protective mist!",
"electric": "Electric",
"electricStartMessage": "An electric current ran across the battlefield!",
"electricClearMessage": "The electricity disappeared from the battlefield.",
"grassy": "Grassy",
"grassyStartMessage": "Grass grew to cover the battlefield!",
"grassyClearMessage": "The grass disappeared from the battlefield.",
"psychic": "Psychic",
"psychicStartMessage": "The battlefield got weird!",
"psychicClearMessage": "The weirdness disappeared from the battlefield!",
"defaultBlockMessage": "{{pokemonNameWithAffix}} is protected by the {{terrainName}} Terrain!"
};

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "¿Quieres cambiar a\n{{pokemonName}}?", "switchQuestion": "¿Quieres cambiar a\n{{pokemonName}}?",
"trainerDefeated": "¡Has derrotado a\n{{trainerName}}!", "trainerDefeated": "¡Has derrotado a\n{{trainerName}}!",
"moneyWon": "¡Has ganado\n₽{{moneyAmount}} por vencer!", "moneyWon": "¡Has ganado\n₽{{moneyAmount}} por vencer!",
"moneyPickedUp": "You picked up ₽{{moneyAmount}}!",
"pokemonCaught": "¡{{pokemonName}} atrapado!", "pokemonCaught": "¡{{pokemonName}} atrapado!",
"addedAsAStarter": "{{pokemonName}} ha sido añadido\na tus iniciales!", "addedAsAStarter": "{{pokemonName}} ha sido añadido\na tus iniciales!",
"partyFull": "Tu equipo esta completo.\n¿Quieres liberar un Pokémon para meter a {{pokemonName}}?", "partyFull": "Tu equipo esta completo.\n¿Quieres liberar un Pokémon para meter a {{pokemonName}}?",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const esConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const esConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!",
"turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!",
"hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!",
"moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!",
"turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!",
"contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!",
} as const;

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "¡Las misteriosas turbulencias atenúan el ataque!", "strongWindsEffectMessage": "¡Las misteriosas turbulencias atenúan el ataque!",
"strongWindsClearMessage": "El fuerte viento cesó." "strongWindsClearMessage": "El fuerte viento cesó."
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "Misty",
"mistyStartMessage": "Mist swirled around the battlefield!",
"mistyClearMessage": "The mist disappeared from the battlefield.",
"mistyBlockMessage": "{{pokemonNameWithAffix}} surrounds itself with a protective mist!",
"electric": "Electric",
"electricStartMessage": "An electric current ran across the battlefield!",
"electricClearMessage": "The electricity disappeared from the battlefield.",
"grassy": "Grassy",
"grassyStartMessage": "Grass grew to cover the battlefield!",
"grassyClearMessage": "The grass disappeared from the battlefield.",
"psychic": "Psychic",
"psychicStartMessage": "The battlefield got weird!",
"psychicClearMessage": "The weirdness disappeared from the battlefield!",
"defaultBlockMessage": "{{pokemonNameWithAffix}} is protected by the {{terrainName}} Terrain!"
};

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "Voulez-vous changer\nvotre {{pokemonName}} ?", "switchQuestion": "Voulez-vous changer\nvotre {{pokemonName}} ?",
"trainerDefeated": "Vous avez battu\n{{trainerName}} !", "trainerDefeated": "Vous avez battu\n{{trainerName}} !",
"moneyWon": "Vous remportez\n{{moneyAmount}} ₽ !", "moneyWon": "Vous remportez\n{{moneyAmount}} ₽ !",
"moneyPickedUp": "Vous obtenez {{moneyAmount}} ₽ !",
"pokemonCaught": "Vous avez attrapé {{pokemonName}} !", "pokemonCaught": "Vous avez attrapé {{pokemonName}} !",
"addedAsAStarter": "{{pokemonName}} est ajouté\ncomme starter !", "addedAsAStarter": "{{pokemonName}} est ajouté\ncomme starter !",
"partyFull": "Votre équipe est pleine.\nRelâcher un Pokémon pour {{pokemonName}} ?", "partyFull": "Votre équipe est pleine.\nRelâcher un Pokémon pour {{pokemonName}} ?",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const frConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const frConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}} tient bon\ngrâce à son {{typeName}} !",
"turnHealApply": "Les PV de {{pokemonNameWithAffix}}\nsont un peu restaurés par les {{typeName}} !",
"hitHealApply": "Les PV de {{pokemonNameWithAffix}}\nsont un peu restaurés par le {{typeName}} !",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}} a repris connaissance\navec sa {{typeName}} et est prêt à se battre de nouveau !",
"moneyInterestApply": "La {{typeName}} vous rapporte\n{{moneyAmount}}  dintérêts !",
"turnHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} est absorbé·e\npar le {{typeName}} de {{pokemonName}} !",
"contactHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} est volé·e\npar l{{typeName}} de {{pokemonName}} !",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestaure un peu ses PV !",
} as const;

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "Le courant aérien mystérieux affaiblit lattaque!", "strongWindsEffectMessage": "Le courant aérien mystérieux affaiblit lattaque!",
"strongWindsClearMessage": "Le vent mystérieux sest dissipé…" "strongWindsClearMessage": "Le vent mystérieux sest dissipé…"
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "Brumeux",
"mistyStartMessage": "La brume recouvre le terrain !",
"mistyClearMessage": "La brume qui recouvrait le terrain se dissipe…",
"mistyBlockMessage": "La brume enveloppe {{pokemonNameWithAffix}} !",
"electric": "Électrifié",
"electricStartMessage": "De lélectricité parcourt le terrain !",
"electricClearMessage": "Lélectricité parcourant le terrain sest dissipée…",
"grassy": "Herbu",
"grassyStartMessage": "Un beau gazon pousse sur le terrain !",
"grassyClearMessage": "Le gazon disparait…",
"psychic": "Psychique",
"psychicStartMessage": "Le sol se met à réagir de façon bizarre…",
"psychicClearMessage": "Le sol redevient normal !",
"defaultBlockMessage": "{{pokemonNameWithAffix}} est protégé\npar le Champ {{terrainName}} !"
};

View File

@ -3,9 +3,9 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const abilityTriggers: SimpleTranslationEntries = { export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{abilityName}} di {{pokemonName}}\nl'ha protetto dal contraccolpo!", "blockRecoilDamage" : "{{abilityName}} di {{pokemonName}}\nl'ha protetto dal contraccolpo!",
"badDreams": "{{pokemonName}} è tormentato dagli incubi!", "badDreams": "{{pokemonName}} è tormentato dagli incubi!",
"costar": "{{pokemonName}} copied {{allyName}}'s stat changes!", "costar": "{{pokemonName}} ha copiato le modifiche alle statistiche\ndel suo alleato {{allyName}}!",
"iceFaceAvoidedDamage": "{{pokemonName}} ha evitato\ni danni grazie a {{abilityName}}!", "iceFaceAvoidedDamage": "{{pokemonName}} ha evitato\ni danni grazie a {{abilityName}}!",
"trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", "trace": "L'abilità {{abilityName}} di {{targetName}}\nviene copiata da {{pokemonName}} con Traccia!",
"windPowerCharged": "Venire colpito da {{moveName}} ha caricato {{pokemonName}}!", "windPowerCharged": "Venire colpito da {{moveName}} ha caricato {{pokemonName}}!",
"quickDraw":"{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", "quickDraw":"{{pokemonName}} agisce più rapidamente del normale grazie a Colpolesto!",
} as const; } as const;

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "Vuoi cambiare\n{{pokemonName}}?", "switchQuestion": "Vuoi cambiare\n{{pokemonName}}?",
"trainerDefeated": "Hai sconfitto\n{{trainerName}}!", "trainerDefeated": "Hai sconfitto\n{{trainerName}}!",
"moneyWon": "Hai vinto {{moneyAmount}}₽", "moneyWon": "Hai vinto {{moneyAmount}}₽",
"moneyPickedUp": "You picked up ₽{{moneyAmount}}!",
"pokemonCaught": "Preso! {{pokemonName}} è stato catturato!", "pokemonCaught": "Preso! {{pokemonName}} è stato catturato!",
"addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!", "addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!",
"partyFull": "La tua squadra è al completo.\nVuoi liberare un Pokémon per far spazio a {{pokemonName}}?", "partyFull": "La tua squadra è al completo.\nVuoi liberare un Pokémon per far spazio a {{pokemonName}}?",

View File

@ -2,7 +2,7 @@ import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const common: SimpleTranslationEntries = { export const common: SimpleTranslationEntries = {
"start": "Inizia", "start": "Inizia",
"luckIndicator": "Luck:", "luckIndicator": "Fortuna:",
"shinyOnHover": "Shiny", "shinyOnHover": "Shiny",
"commonShiny": "Comune", "commonShiny": "Comune",
"rareShiny": "Raro", "rareShiny": "Raro",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const itConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const itConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}} hung on\nusing its {{typeName}}!",
"turnHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!",
"hitHealApply": "{{pokemonNameWithAffix}} restored a little HP using\nits {{typeName}}!",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}} was revived\nby its {{typeName}}!",
"moneyInterestApply": "You received interest of ₽{{moneyAmount}}\nfrom the {{typeName}}!",
"turnHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was absorbed\nby {{pokemonName}}'s {{typeName}}!",
"contactHeldItemTransferApply": "{{pokemonNameWithAffix}}'s {{itemName}} was snatched\nby {{pokemonName}}'s {{typeName}}!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestored some HP!",
} as const;

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "La corrente misteriosa indebolisce lattacco!", "strongWindsEffectMessage": "La corrente misteriosa indebolisce lattacco!",
"strongWindsClearMessage": "La corrente d'aria è cessata." "strongWindsClearMessage": "La corrente d'aria è cessata."
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "Misty",
"mistyStartMessage": "Mist swirled around the battlefield!",
"mistyClearMessage": "The mist disappeared from the battlefield.",
"mistyBlockMessage": "{{pokemonNameWithAffix}} surrounds itself with a protective mist!",
"electric": "Electric",
"electricStartMessage": "An electric current ran across the battlefield!",
"electricClearMessage": "The electricity disappeared from the battlefield.",
"grassy": "Grassy",
"grassyStartMessage": "Grass grew to cover the battlefield!",
"grassyClearMessage": "The grass disappeared from the battlefield.",
"psychic": "Psychic",
"psychicStartMessage": "The battlefield got weird!",
"psychicClearMessage": "The weirdness disappeared from the battlefield!",
"defaultBlockMessage": "{{pokemonNameWithAffix}} is protected by the {{terrainName}} Terrain!"
};

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "{{pokemonName}}[[를]]\n교체하시겠습니까?", "switchQuestion": "{{pokemonName}}[[를]]\n교체하시겠습니까?",
"trainerDefeated": "{{trainerName}}[[와]]의\n승부에서 이겼다!", "trainerDefeated": "{{trainerName}}[[와]]의\n승부에서 이겼다!",
"moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!", "moneyWon": "상금으로\n₽{{moneyAmount}}을 손에 넣었다!",
"moneyPickedUp": "₽{{moneyAmount}}을 주웠다!",
"pokemonCaught": "신난다-!\n{{pokemonName}}[[를]] 잡았다!", "pokemonCaught": "신난다-!\n{{pokemonName}}[[를]] 잡았다!",
"addedAsAStarter": "{{pokemonName}}[[가]]\n스타팅 포켓몬에 추가되었다!", "addedAsAStarter": "{{pokemonName}}[[가]]\n스타팅 포켓몬에 추가되었다!",
"partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}[[를]]\n대신해 포켓몬을 놓아주시겠습니까?", "partyFull": "지닌 포켓몬이 가득 찼습니다. {{pokemonName}}[[를]]\n대신해 포켓몬을 놓아주시겠습니까?",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const koConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const koConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -22,7 +22,7 @@ export const egg: SimpleTranslationEntries = {
"hatchFromTheEgg": "알이 부화해서\n{{pokemonName}}[[가]] 태어났다!", "hatchFromTheEgg": "알이 부화해서\n{{pokemonName}}[[가]] 태어났다!",
"eggMoveUnlock": "알 기술 {{moveName}}[[를]]\n사용할 수 있게 되었다!", "eggMoveUnlock": "알 기술 {{moveName}}[[를]]\n사용할 수 있게 되었다!",
"rareEggMoveUnlock": "레어 알 기술 {{moveName}}[[를]]\n사용할 수 있게 되었다!", "rareEggMoveUnlock": "레어 알 기술 {{moveName}}[[를]]\n사용할 수 있게 되었다!",
"moveUPGacha": "기술 UP!", "moveUPGacha": "기술 UP!",
"shinyUPGacha": "특별색 UP!", "shinyUPGacha": "이 다른 포켓몬\nUP!",
"legendaryUPGacha": "UP!", "legendaryUPGacha": "UP!",
} as const; } as const;

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}}[[는]]\n{{typeName}}[[로]] 버텼다!!",
"turnHealApply": "{{pokemonNameWithAffix}}[[는]]\n{{typeName}}[[로]] 인해 조금 회복했다.",
"hitHealApply": "{{pokemonNameWithAffix}}[[는]]\n{{typeName}}[[로]] 인해 조금 회복했다.",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}}[[는]] {{typeName}}[[로]]\n정신을 차려 싸울 수 있게 되었다!",
"moneyInterestApply": "{{typeName}}[[로]]부터\n₽{{moneyAmount}}[[를]] 받았다!",
"turnHeldItemTransferApply": "{{pokemonName}}의 {{typeName}}[[는]]\n{{pokemonNameWithAffix}}의 {{itemName}}[[를]] 흡수했다!",
"contactHeldItemTransferApply": "{{pokemonName}}의 {{typeName}}[[는]]\n{{pokemonNameWithAffix}}의 {{itemName}}[[를]] 가로챘다!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}의\n체력이 약간 회복되었다!",
} as const;

View File

@ -84,7 +84,7 @@ export const settings: SimpleTranslationEntries = {
"buttonCancel": "취소", "buttonCancel": "취소",
"buttonStats": "스탯", "buttonStats": "스탯",
"buttonCycleForm": "폼 변환", "buttonCycleForm": "폼 변환",
"buttonCycleShiny": "특별한 색 변환", "buttonCycleShiny": "이 다른 변환",
"buttonCycleGender": "성별 변환", "buttonCycleGender": "성별 변환",
"buttonCycleAbility": "특성 변환", "buttonCycleAbility": "특성 변환",
"buttonCycleNature": "성격 변환", "buttonCycleNature": "성격 변환",

View File

@ -32,7 +32,7 @@ export const starterSelectUiHandler: SimpleTranslationEntries = {
"unlockPassive": "패시브 해금", "unlockPassive": "패시브 해금",
"reduceCost": "코스트 줄이기", "reduceCost": "코스트 줄이기",
"sameSpeciesEgg": "알 구매하기", "sameSpeciesEgg": "알 구매하기",
"cycleShiny": ": 특별한 색", "cycleShiny": ": 이 다른",
"cycleForm": ": 폼", "cycleForm": ": 폼",
"cycleGender": ": 암수", "cycleGender": ": 암수",
"cycleAbility": ": 특성", "cycleAbility": ": 특성",

View File

@ -44,3 +44,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "수수께끼의 난기류가 공격을 약하게 만들었다!", "strongWindsEffectMessage": "수수께끼의 난기류가 공격을 약하게 만들었다!",
"strongWindsClearMessage": "수수께끼의 난기류가 멈췄다!" // 임의번역 "strongWindsClearMessage": "수수께끼의 난기류가 멈췄다!" // 임의번역
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "미스트필드",
"mistyStartMessage": "발밑이 안개로 자욱해졌다!",
"mistyClearMessage": "발밑의 안개가 사라졌다!",
"mistyBlockMessage": "{{pokemonNameWithAffix}}[[를]]\n미스트필드가 지켜주고 있다!",
"electric": "일렉트릭필드",
"electricStartMessage": "발밑에 전기가 흐르기 시작했다!",
"electricClearMessage": "발밑의 전기가 사라졌다!",
"grassy": "그래스필드",
"grassyStartMessage": "발밑에 풀이 무성해졌다!",
"grassyClearMessage": "발밑의 풀이 사라졌다!",
"psychic": "사이코필드",
"psychicStartMessage": "발밑에서 이상한 느낌이 든다!",
"psychicClearMessage": "발밑의 이상한 느낌이 사라졌다!",
"defaultBlockMessage": "{{pokemonNameWithAffix}}[[를]]\n{{terrainName}}[[가]] 지켜주고 있다!"
};

View File

@ -1,7 +1,7 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales"; import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const abilityTriggers: SimpleTranslationEntries = { export const abilityTriggers: SimpleTranslationEntries = {
"blockRecoilDamage" : "{{abilityName}} de {{pokemonName}}\nprotegeu-o do dano de recuo!", "blockRecoilDamage": "{{abilityName}} de {{pokemonName}}\nprotegeu-o do dano reverso!",
"badDreams": "{{pokemonName}} está tendo pesadelos!", "badDreams": "{{pokemonName}} está tendo pesadelos!",
"costar": "{{pokemonName}} copiou as mudanças\nde atributo de {{allyName}}!", "costar": "{{pokemonName}} copiou as mudanças\nde atributo de {{allyName}}!",
"iceFaceAvoidedDamage": "{{pokemonName}} evitou\ndanos com sua {{abilityName}}!", "iceFaceAvoidedDamage": "{{pokemonName}} evitou\ndanos com sua {{abilityName}}!",
@ -9,5 +9,5 @@ export const abilityTriggers: SimpleTranslationEntries = {
"poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaurou seus PS um pouco!", "poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaurou seus PS um pouco!",
"trace": "{{pokemonName}} copiou {{abilityName}}\nde {{targetName}}!", "trace": "{{pokemonName}} copiou {{abilityName}}\nde {{targetName}}!",
"windPowerCharged": "Ser atingido por {{moveName}} carregou {{pokemonName}} com poder!", "windPowerCharged": "Ser atingido por {{moveName}} carregou {{pokemonName}} com poder!",
"quickDraw":"{{pokemonName}} pode agir mais rápido que o normal\ngraças ao seu Quick Draw!", "quickDraw": "{{pokemonName}} pode agir mais rápido que o normal\ngraças ao seu Quick Draw!",
} as const; } as const;

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "Quer trocar\nde {{pokemonName}}?", "switchQuestion": "Quer trocar\nde {{pokemonName}}?",
"trainerDefeated": "Você derrotou\n{{trainerName}}!", "trainerDefeated": "Você derrotou\n{{trainerName}}!",
"moneyWon": "Você ganhou\n₽{{moneyAmount}} por ganhar!", "moneyWon": "Você ganhou\n₽{{moneyAmount}} por ganhar!",
"moneyPickedUp": "Você pegou ₽{{moneyAmount}} do chão!",
"pokemonCaught": "{{pokemonName}} foi capturado!", "pokemonCaught": "{{pokemonName}} foi capturado!",
"addedAsAStarter": "{{pokemonName}} foi adicionado\naos seus iniciais!", "addedAsAStarter": "{{pokemonName}} foi adicionado\naos seus iniciais!",
"partyFull": "Sua equipe está cheia.\nSolte um Pokémon para ter espaço para {{pokemonName}}?", "partyFull": "Sua equipe está cheia.\nSolte um Pokémon para ter espaço para {{pokemonName}}?",

View File

@ -1,7 +1,7 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales"; import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const biome: SimpleTranslationEntries = { export const biome: SimpleTranslationEntries = {
"unknownLocation": "Em algum lugar do qual você não se lembra", "unknownLocation": "em algum lugar do qual você não se lembra",
"TOWN": "Cidade", "TOWN": "Cidade",
"PLAINS": "Planície", "PLAINS": "Planície",
"GRASS": "Grama", "GRASS": "Grama",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,16 +74,17 @@ export const ptBrConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
partyUiHandler: partyUiHandler,
pokeball: pokeball, pokeball: pokeball,
pokemon: pokemon, pokemon: pokemon,
pokemonInfo: pokemonInfo, pokemonInfo: pokemonInfo,
pokemonInfoContainer: pokemonInfoContainer, pokemonInfoContainer: pokemonInfoContainer,
saveSlotSelectUiHandler: saveSlotSelectUiHandler, saveSlotSelectUiHandler: saveSlotSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
settings: settings, settings: settings,
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
@ -92,5 +94,6 @@ export const ptBrConfig = {
tutorial: tutorial, tutorial: tutorial,
voucher: voucher, voucher: voucher,
weather: weather, weather: weather,
partyUiHandler: partyUiHandler,
modifierSelectUiHandler: modifierSelectUiHandler modifierSelectUiHandler: modifierSelectUiHandler
}; };

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}} aguentou o tranco\nusando sua {{typeName}}!",
"turnHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsuas {{typeName}}!",
"hitHealApply": "{{pokemonNameWithAffix}} restaurou um pouco de PS usando\nsua {{typeName}}!",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}} foi revivido\npor sua {{typeName}}!",
"moneyInterestApply": "Você recebeu um juros de ₽{{moneyAmount}}\nde sua {{typeName}}!",
"turnHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} foi absorvido(a)\npelo {{typeName}} de {{pokemonName}}!",
"contactHeldItemTransferApply": "{{itemName}} de {{pokemonNameWithAffix}} foi pego(a)\npela {{typeName}} de {{pokemonName}}!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\nrestaurou um pouco de seus PS!",
} as const;

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "Os ventos fortes diminuíram.", "strongWindsClearMessage": "Os ventos fortes diminuíram.",
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "Enevoado",
"mistyStartMessage": "Uma névoa se espalhou pelo campo de batalha!",
"mistyClearMessage": "A névou sumiu do campo de batalha.",
"mistyBlockMessage": "{{pokemonNameWithAffix}} se envolveu com uma névoa protetora!",
"electric": "Elétrico",
"electricStartMessage": "Uma corrente elétrica se espalhou pelo campo de batalha!",
"electricClearMessage": "A eletricidade sumiu do campo de batalha.",
"grassy": "de Plantas",
"grassyStartMessage": "Grama cresceu para cobrir o campo de batalha!",
"grassyClearMessage": "A grama sumiu do campo de batalha.",
"psychic": "Psíquico",
"psychicStartMessage": "O campo de batalha ficou esquisito!",
"psychicClearMessage": "A esquisitice sumiu do campo de batalha",
"defaultBlockMessage": "{{pokemonNameWithAffix}} está protegido pelo Terreno {{terrainName}}!"
};

View File

@ -14,6 +14,7 @@ export const battle: SimpleTranslationEntries = {
"switchQuestion": "要更换\n{{pokemonName}}吗?", "switchQuestion": "要更换\n{{pokemonName}}吗?",
"trainerDefeated": "你击败了\n{{trainerName}}", "trainerDefeated": "你击败了\n{{trainerName}}",
"moneyWon": "你赢得了\n₽{{moneyAmount}}", "moneyWon": "你赢得了\n₽{{moneyAmount}}",
"moneyPickedUp": "捡到了 ₽{{moneyAmount}}",
"pokemonCaught": "{{pokemonName}}被抓住了!", "pokemonCaught": "{{pokemonName}}被抓住了!",
"addedAsAStarter": "增加了{{pokemonName}}作为\n一个新的基础宝可梦", "addedAsAStarter": "增加了{{pokemonName}}作为\n一个新的基础宝可梦",
"partyFull": "你的队伍已满员。是否放生其他宝可梦\n为{{pokemonName}}腾出空间?", "partyFull": "你的队伍已满员。是否放生其他宝可梦\n为{{pokemonName}}腾出空间?",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const zhCnConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const zhCnConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}}用{{typeName}}\n撑住了",
"turnHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回复了体力",
"hitHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回复了体力",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}}用{{typeName}}\n恢复了活力",
"moneyInterestApply": "用{{typeName}}\n获得了 ₽{{moneyAmount}} 利息!",
"turnHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}吸收了!",
"contactHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}夺取了!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\n回复了一些体力",
} as const;

View File

@ -107,7 +107,7 @@ export const move: MoveTranslationEntries = {
}, },
"rollingKick": { "rollingKick": {
name: "回旋踢", name: "回旋踢",
effect: "一边使身体快速旋转,\n一边踢飞对手进行攻击。有时会使对手畏缩", effect: "一边使身体快速旋转,\n一边踢飞对手进行攻击。\n有时会使对手畏缩",
}, },
"sandAttack": { "sandAttack": {
name: "泼沙", name: "泼沙",
@ -179,7 +179,7 @@ export const move: MoveTranslationEntries = {
}, },
"growl": { "growl": {
name: "叫声", name: "叫声",
effect: "让对手听可爱的叫声,\n引开注意力使其疏忽从而降低对手的攻击", effect: "让对手听可爱的叫声,\n引开注意力使其疏忽\n从而降低对手的攻击",
}, },
"roar": { "roar": {
name: "吼叫", name: "吼叫",
@ -399,7 +399,7 @@ export const move: MoveTranslationEntries = {
}, },
"teleport": { "teleport": {
name: "瞬间移动", name: "瞬间移动",
effect: "当有后备宝可梦时使用,\n就可以进行替换。野生的宝可梦使用则会逃走", effect: "当有后备宝可梦时使用,\n就可以进行替换。\n野生的宝可梦使用则会逃走",
}, },
"nightShade": { "nightShade": {
name: "黑夜魔影", name: "黑夜魔影",
@ -611,7 +611,7 @@ export const move: MoveTranslationEntries = {
}, },
"explosion": { "explosion": {
name: "大爆炸", name: "大爆炸",
effect: "引发大爆炸,攻击自己周围所有的宝可梦。\n使用后自己会陷入昏厥", effect: "引发大爆炸,\n攻击自己周围所有的宝可梦。\n使用后自己会陷入昏厥",
}, },
"furySwipes": { "furySwipes": {
name: "乱抓", name: "乱抓",
@ -643,7 +643,7 @@ export const move: MoveTranslationEntries = {
}, },
"triAttack": { "triAttack": {
name: "三重攻击", name: "三重攻击",
effect: "用3种光线进行攻击。\n有时会让对手陷入麻痹、灼伤或冰冻的状态", effect: "用3种光线进行攻击。\n有时会让对手陷入麻痹、\n灼伤或冰冻的状态",
}, },
"superFang": { "superFang": {
name: "愤怒门牙", name: "愤怒门牙",
@ -659,7 +659,7 @@ export const move: MoveTranslationEntries = {
}, },
"struggle": { "struggle": {
name: "挣扎", name: "挣扎",
effect: "当自己的PP耗尽时,\n努力挣扎攻击对手。自己也会受到少许伤害", effect: "当自己的PP耗尽时,\n努力挣扎攻击对手。\n自己也会受到少许伤害",
}, },
"sketch": { "sketch": {
name: "写生", name: "写生",
@ -775,7 +775,7 @@ export const move: MoveTranslationEntries = {
}, },
"destinyBond": { "destinyBond": {
name: "同命", name: "同命",
effect: "使出招式后,当受到对手攻击陷入昏厥时,\n对手也会一同昏厥。\n连续使出则会失败", effect: "使出招式后,当受到对手攻击\n陷入昏厥时,对手也会一同昏厥。\n连续使出则会失败",
}, },
"perishSong": { "perishSong": {
name: "终焉之歌", name: "终焉之歌",
@ -911,7 +911,7 @@ export const move: MoveTranslationEntries = {
}, },
"pursuit": { "pursuit": {
name: "追打", name: "追打",
effect: "当对手替换宝可梦上场时使出此招式的话,\n能够以倍的威力进行攻击", effect: "当对手替换宝可梦上场时\n使出此招式的话,\n能够以倍的威力进行攻击",
}, },
"rapidSpin": { "rapidSpin": {
name: "高速旋转", name: "高速旋转",
@ -959,7 +959,7 @@ export const move: MoveTranslationEntries = {
}, },
"rainDance": { "rainDance": {
name: "求雨", name: "求雨",
effect: "在5回合内一直降雨,\n从而提高水属性的招式威力。火属性的招式威\n力则降低", effect: "在5回合内一直降雨,\n从而提高水属性的招式威力。\n火属性的招式威力则降低",
}, },
"sunnyDay": { "sunnyDay": {
name: "大晴天", name: "大晴天",
@ -979,7 +979,7 @@ export const move: MoveTranslationEntries = {
}, },
"extremeSpeed": { "extremeSpeed": {
name: "神速", name: "神速",
effect: "以迅雷不及掩耳之势猛撞向对手进行攻击。\n必定能够先制攻击", effect: "以迅雷不及掩耳之势猛\n撞向对手进行攻击。\n必定能够先制攻击",
}, },
"ancientPower": { "ancientPower": {
name: "原始之力", name: "原始之力",
@ -1023,7 +1023,7 @@ export const move: MoveTranslationEntries = {
}, },
"swallow": { "swallow": {
name: "吞下", name: "吞下",
effect: "将积蓄的力量吞下,从而回复自己的HP。\n积蓄得越多回复越大", effect: "将积蓄的力量吞下,\n从而回复自己的HP。\n积蓄得越多回复越大",
}, },
"heatWave": { "heatWave": {
name: "热风", name: "热风",
@ -1031,7 +1031,7 @@ export const move: MoveTranslationEntries = {
}, },
"hail": { "hail": {
name: "冰雹", name: "冰雹",
effect: "在5回合内一直降冰雹,\n除冰属性的宝可梦以外给予全体宝可梦伤害", effect: "在5回合内一直降冰雹,\n除冰属性的宝可梦以外\n给予全体宝可梦伤害",
}, },
"torment": { "torment": {
name: "无理取闹", name: "无理取闹",
@ -1059,7 +1059,7 @@ export const move: MoveTranslationEntries = {
}, },
"smellingSalts": { "smellingSalts": {
name: "清醒", name: "清醒",
effect: "对于麻痹状态下的对手,\n威力会变成倍。但相反对手的麻痹也会被治愈", effect: "对于麻痹状态下的对手,\n威力会变成倍。\n但相反对手的麻痹也会被治愈",
}, },
"followMe": { "followMe": {
name: "看我嘛", name: "看我嘛",
@ -1067,7 +1067,7 @@ export const move: MoveTranslationEntries = {
}, },
"naturePower": { "naturePower": {
name: "自然之力", name: "自然之力",
effect: "用自然之力进行攻击。\n根据所使用场所的不同使出的招式也会有所变化", effect: "用自然之力进行攻击。\n根据所使用场所的不同\n使出的招式也会有所变化",
}, },
"charge": { "charge": {
name: "充电", name: "充电",
@ -1111,7 +1111,7 @@ export const move: MoveTranslationEntries = {
}, },
"recycle": { "recycle": {
name: "回收利用", name: "回收利用",
effect: "使战斗中已经消耗掉的自己的持有物再生,\n并可以再次使用", effect: "使战斗中已经消耗掉的\n自己的持有物再生,\n并可以再次使用",
}, },
"revenge": { "revenge": {
name: "报复", name: "报复",
@ -1199,7 +1199,7 @@ export const move: MoveTranslationEntries = {
}, },
"mudSport": { "mudSport": {
name: "玩泥巴", name: "玩泥巴",
effect: "一旦使用此招式,周围就会弄得到处是泥。\n在回合内减弱电属性的招式", effect: "一旦使用此招式,\n周围就会弄得到处是泥。\n在回合内减弱电属性的招式",
}, },
"iceBall": { "iceBall": {
name: "冰球", name: "冰球",
@ -1259,7 +1259,7 @@ export const move: MoveTranslationEntries = {
}, },
"overheat": { "overheat": {
name: "过热", name: "过热",
effect: "使出全部力量攻击对手。\n使用之后会因为反作用力自己的特攻大幅降低", effect: "使出全部力量攻击对手。\n使用之后会因为反作用力\n自己的特攻大幅降低",
}, },
"odorSleuth": { "odorSleuth": {
name: "气味侦测", name: "气味侦测",
@ -1367,11 +1367,11 @@ export const move: MoveTranslationEntries = {
}, },
"poisonTail": { "poisonTail": {
name: "毒尾", name: "毒尾",
effect: "用尾巴拍打。有时会让对手陷入中毒状态,\n也容易击中要害", effect: "用尾巴拍打。\n有时会让对手陷入中毒状态,\n也容易击中要害",
}, },
"covet": { "covet": {
name: "渴望", name: "渴望",
effect: "一边可爱地撒娇,一边靠近对手进行攻击,\n还能夺取对手携带的道具", effect: "一边可爱地撒娇,\n一边靠近对手进行攻击,\n还能夺取对手携带的道具",
}, },
"voltTackle": { "voltTackle": {
name: "伏特攻击", name: "伏特攻击",
@ -1415,7 +1415,7 @@ export const move: MoveTranslationEntries = {
}, },
"psychoBoost": { "psychoBoost": {
name: "精神突进", name: "精神突进",
effect: "使出全部力量攻击对手。\n使用之后会因为反作用力自己的特攻大幅降低", effect: "使出全部力量攻击对手。\n使用之后会因为反作用力\n自己的特攻大幅降低",
}, },
"roost": { "roost": {
name: "羽栖", name: "羽栖",
@ -1527,11 +1527,11 @@ export const move: MoveTranslationEntries = {
}, },
"meFirst": { "meFirst": {
name: "抢先一步", name: "抢先一步",
effect: "提高威力,抢先使出对手想要使出的招式。\n如果不先使出则会失败", effect: "提高威力,\n抢先使出对手想要使出的招式。\n如果不先使出则会失败",
}, },
"copycat": { "copycat": {
name: "仿效", name: "仿效",
effect: "模仿对手刚才使出的招式,\n并使出相同招式。如果对手还没出招则会失败", effect: "模仿对手刚才使出的招式,\n并使出相同招式。\n如果对手还没出招则会失败",
}, },
"powerSwap": { "powerSwap": {
name: "力量互换", name: "力量互换",
@ -1715,7 +1715,7 @@ export const move: MoveTranslationEntries = {
}, },
"mirrorShot": { "mirrorShot": {
name: "镜光射击", name: "镜光射击",
effect: "抛光自己的身体,向对手释放出闪光之力。\n有时会降低对手的命中率", effect: "抛光自己的身体,\n向对手释放出闪光之力。\n有时会降低对手的命中率",
}, },
"flashCannon": { "flashCannon": {
name: "加农光炮", name: "加农光炮",
@ -1735,11 +1735,11 @@ export const move: MoveTranslationEntries = {
}, },
"dracoMeteor": { "dracoMeteor": {
name: "流星群", name: "流星群",
effect: "从天空中向对手落下陨石。\n使用之后因为反作用力自己的特攻会大幅降低", effect: "从天空中向对手落下陨石。\n使用之后因为反作用力\n自己的特攻会大幅降低",
}, },
"discharge": { "discharge": {
name: "放电", name: "放电",
effect: "用耀眼的电击攻击自己周围所有的宝可梦。\n有时会陷入麻痹状态", effect: "用耀眼的电击攻击\n自己周围所有的宝可梦。\n有时会陷入麻痹状态",
}, },
"lavaPlume": { "lavaPlume": {
name: "喷烟", name: "喷烟",
@ -1791,7 +1791,7 @@ export const move: MoveTranslationEntries = {
}, },
"chatter": { "chatter": {
name: "喋喋不休", name: "喋喋不休",
effect: "用非常烦人的,喋喋不休的音波攻击对手。\n使对手混乱", effect: "用非常烦人的,\n喋喋不休的音波攻击对手。\n使对手混乱",
}, },
"judgment": { "judgment": {
name: "制裁光砾", name: "制裁光砾",
@ -1827,7 +1827,7 @@ export const move: MoveTranslationEntries = {
}, },
"headSmash": { "headSmash": {
name: "双刃头锤", name: "双刃头锤",
effect: "拼命使出浑身力气,向对手进行头锤攻击。\n自己也会受到非常大的伤害", effect: "拼命使出浑身力气,\n向对手进行头锤攻击。\n自己也会受到非常大的伤害",
}, },
"doubleHit": { "doubleHit": {
name: "二连击", name: "二连击",
@ -1943,7 +1943,7 @@ export const move: MoveTranslationEntries = {
}, },
"electroBall": { "electroBall": {
name: "电球", name: "电球",
effect: "用电气团撞向对手。自己比对手速度越快,\n威力越大", effect: "用电气团撞向对手。\n自己比对手速度越快,\n威力越大",
}, },
"soak": { "soak": {
name: "浸水", name: "浸水",
@ -1991,7 +1991,7 @@ export const move: MoveTranslationEntries = {
}, },
"chipAway": { "chipAway": {
name: "逐步击破", name: "逐步击破",
effect: "看准机会稳步攻击。无视对手的能力变化,\n直接给予伤害", effect: "看准机会稳步攻击。\n无视对手的能力变化,\n直接给予伤害",
}, },
"clearSmog": { "clearSmog": {
name: "清除之烟", name: "清除之烟",
@ -2007,7 +2007,7 @@ export const move: MoveTranslationEntries = {
}, },
"allySwitch": { "allySwitch": {
name: "交换场地", name: "交换场地",
effect: "用神奇的力量瞬间移动,\n互换自己和同伴所在的位置。连续使出则容易失败", effect: "用神奇的力量瞬间移动,\n互换自己和同伴所在的位置。\n连续使出则容易失败",
}, },
"scald": { "scald": {
name: "热水", name: "热水",
@ -2115,7 +2115,7 @@ export const move: MoveTranslationEntries = {
}, },
"drillRun": { "drillRun": {
name: "直冲钻", name: "直冲钻",
effect: "像钢钻一样,一边旋转身体一边撞击对手。\n容易击中要害", effect: "像钢钻一样,\n一边旋转身体一边撞击对手。\n容易击中要害",
}, },
"dualChop": { "dualChop": {
name: "二连劈", name: "二连劈",
@ -2199,11 +2199,11 @@ export const move: MoveTranslationEntries = {
}, },
"boltStrike": { "boltStrike": {
name: "雷击", name: "雷击",
effect: "让强大的电流覆盖全身,\n猛撞向对手进行攻击。有时会让对手陷入麻痹状态", effect: "让强大的电流覆盖全身,\n猛撞向对手进行攻击。\n有时会让对手陷入麻痹状态",
}, },
"blueFlare": { "blueFlare": {
name: "青焰", name: "青焰",
effect: "用美丽而激烈的青焰包裹住对手进行攻击。\n有时会让对手陷入灼伤状态", effect: "用美丽而激烈的青焰\n包裹住对手进行攻击。\n有时会让对手陷入灼伤状态",
}, },
"fieryDance": { "fieryDance": {
name: "火之舞", name: "火之舞",
@ -2211,7 +2211,7 @@ export const move: MoveTranslationEntries = {
}, },
"freezeShock": { "freezeShock": {
name: "冰冻伏特", name: "冰冻伏特",
effect: "用覆盖着电流的冰块,\n在第回合撞向对手。有时会让对手陷入麻痹状态", effect: "用覆盖着电流的冰块,\n在第回合撞向对手。\n有时会让对手陷入麻痹状态",
}, },
"iceBurn": { "iceBurn": {
name: "极寒冷焰", name: "极寒冷焰",
@ -2263,7 +2263,7 @@ export const move: MoveTranslationEntries = {
}, },
"phantomForce": { "phantomForce": {
name: "潜灵奇袭", name: "潜灵奇袭",
effect: "第1回合消失在某处,\n第回合攻击对手。可以无视守护进行攻击", effect: "第1回合消失在某处,\n第回合攻击对手。\n可以无视守护进行攻击",
}, },
"trickOrTreat": { "trickOrTreat": {
name: "万圣夜", name: "万圣夜",
@ -2295,7 +2295,7 @@ export const move: MoveTranslationEntries = {
}, },
"disarmingVoice": { "disarmingVoice": {
name: "魅惑之声", name: "魅惑之声",
effect: "发出魅惑的叫声,给予对手精神上的伤害。\n攻击必定会命中", effect: "发出魅惑的叫声,\n给予对手精神上的伤害。\n攻击必定会命中",
}, },
"partingShot": { "partingShot": {
name: "抛下狠话", name: "抛下狠话",
@ -2311,7 +2311,7 @@ export const move: MoveTranslationEntries = {
}, },
"craftyShield": { "craftyShield": {
name: "戏法防守", name: "戏法防守",
effect: "使用神奇的力量防住攻击我方的变化招式。\n但无法防住伤害招式的攻击", effect: "使用神奇的力量防住\n攻击我方的变化招式。\n但无法防住伤害招式的攻击",
}, },
"flowerShield": { "flowerShield": {
name: "鲜花防守", name: "鲜花防守",
@ -2351,7 +2351,7 @@ export const move: MoveTranslationEntries = {
}, },
"kingsShield": { "kingsShield": {
name: "王者盾牌", name: "王者盾牌",
effect: "防住对手攻击的同时,\n自己变为防御姿态。能够降低所接触到的对手的攻击", effect: "防住对手攻击的同时,\n自己变为防御姿态。\n能够降低所接触到的对手的攻击",
}, },
"playNice": { "playNice": {
name: "和睦相处", name: "和睦相处",
@ -2395,7 +2395,7 @@ export const move: MoveTranslationEntries = {
}, },
"venomDrench": { "venomDrench": {
name: "毒液陷阱", name: "毒液陷阱",
effect: "将特殊的毒液泼向对手。\n对处于中毒状态的对手其攻击、特攻和速\n度都会降低", effect: "将特殊的毒液泼向对手。\n对处于中毒状态的对手其攻击、\n特攻和速度都会降低",
}, },
"powder": { "powder": {
name: "粉尘", name: "粉尘",
@ -2459,15 +2459,15 @@ export const move: MoveTranslationEntries = {
}, },
"thousandWaves": { "thousandWaves": {
name: "千波激荡", name: "千波激荡",
effect: "从地面掀起波浪进行攻击。\n被掀入波浪中的对手将无法从战斗中逃走", effect: "从地面掀起波浪进行攻击。\n被掀入波浪中的对手\n将无法从战斗中逃走",
}, },
"landsWrath": { "landsWrath": {
name: "大地神力", name: "大地神力",
effect: "聚集大地的力量,将此力量集中攻击对手,\n并给予伤害", effect: "聚集大地的力量,\n将此力量集中攻击对手,\n并给予伤害",
}, },
"lightOfRuin": { "lightOfRuin": {
name: "破灭之光", name: "破灭之光",
effect: "借用永恒之花的力量,\n发射出强力光线。自己也会受到非常大的伤害", effect: "借用永恒之花的力量,\n发射出强力光线。\n自己也会受到非常大的伤害",
}, },
"originPulse": { "originPulse": {
name: "根源波动", name: "根源波动",
@ -2495,43 +2495,43 @@ export const move: MoveTranslationEntries = {
}, },
"allOutPummelingPhysical": { "allOutPummelingPhysical": {
name: "格斗Z全力无双激烈拳", name: "格斗Z全力无双激烈拳",
effect: "通过Z力量制造出能量弹,\n全力撞向对手。威力会根据原来的招式而改变", effect: "通过Z力量制造出能量弹,\n全力撞向对手。\n威力会根据原来的招式而改变",
}, },
"allOutPummelingSpecial": { "allOutPummelingSpecial": {
name: "格斗Z全力无双激烈拳", name: "格斗Z全力无双激烈拳",
effect: "通过Z力量制造出能量弹,\n全力撞向对手。威力会根据原来的招式而改变", effect: "通过Z力量制造出能量弹,\n全力撞向对手。\n威力会根据原来的招式而改变",
}, },
"supersonicSkystrikePhysical": { "supersonicSkystrikePhysical": {
name: "飞行Z极速俯冲轰烈撞", name: "飞行Z极速俯冲轰烈撞",
effect: "通过Z力量猛烈地飞向天空,\n朝对手全力落下。威力会根据原来的招\n式而改变", effect: "通过Z力量猛烈地飞向天空,\n朝对手全力落下。\n威力会根据原来的招式而改变",
}, },
"supersonicSkystrikeSpecial": { "supersonicSkystrikeSpecial": {
name: "飞行Z极速俯冲轰烈撞", name: "飞行Z极速俯冲轰烈撞",
effect: "通过Z力量猛烈地飞向天空,\n朝对手全力落下。威力会根据原来的招\n式而改变", effect: "通过Z力量猛烈地飞向天空,\n朝对手全力落下。\n威力会根据原来的招式而改变",
}, },
"acidDownpourPhysical": { "acidDownpourPhysical": {
name: "毒Z强酸剧毒灭绝雨", name: "毒Z强酸剧毒灭绝雨",
effect: "通过Z力量使毒沼涌起,\n全力让对手沉下去。威力会根据原来的招式而改变", effect: "通过Z力量使毒沼涌起,\n全力让对手沉下去。\n威力会根据原来的招式而改变",
}, },
"acidDownpourSpecial": { "acidDownpourSpecial": {
name: "毒Z强酸剧毒灭绝雨", name: "毒Z强酸剧毒灭绝雨",
effect: "通过Z力量使毒沼涌起,\n全力让对手沉下去。威力会根据原来的招式而改变", effect: "通过Z力量使毒沼涌起,\n全力让对手沉下去。\n威力会根据原来的招式而改变",
}, },
"tectonicRagePhysical": { "tectonicRagePhysical": {
name: "地面Z地隆啸天大终结", name: "地面Z地隆啸天大终结",
effect: "通过Z力量潜入地里最深处,\n全力撞上对手。威力会根据原来的招式而改变", effect: "通过Z力量潜入地里最深处,\n全力撞上对手。\n威力会根据原来的招式而改变",
}, },
"tectonicRageSpecial": { "tectonicRageSpecial": {
name: "地面Z地隆啸天大终结", name: "地面Z地隆啸天大终结",
effect: "通过Z力量潜入地里最深处,\n全力撞上对手。威力会根据原来的招式而改变", effect: "通过Z力量潜入地里最深处,\n全力撞上对手。\n威力会根据原来的招式而改变",
}, },
"continentalCrushPhysical": { "continentalCrushPhysical": {
name: "岩石Z毁天灭地巨岩坠", name: "岩石Z毁天灭地巨岩坠",
effect: "通过Z力量召唤大大的岩山,\n全力撞向对手。威力会根据原来的招式而改变", effect: "通过Z力量召唤大大的岩山,\n全力撞向对手。\n威力会根据原来的招式而改变",
}, },
"continentalCrushSpecial": { "continentalCrushSpecial": {
name: "岩石Z毁天灭地巨岩坠", name: "岩石Z毁天灭地巨岩坠",
effect: "通过Z力量召唤大大的岩山,\n全力撞向对手。威力会根据原来的招式而改变", effect: "通过Z力量召唤大大的岩山,\n全力撞向对手。\n威力会根据原来的招式而改变",
}, },
"savageSpinOutPhysical": { "savageSpinOutPhysical": {
name: "虫Z绝对捕食回旋斩", name: "虫Z绝对捕食回旋斩",
@ -2543,43 +2543,43 @@ export const move: MoveTranslationEntries = {
}, },
"neverEndingNightmarePhysical": { "neverEndingNightmarePhysical": {
name: "幽灵Z无尽暗夜之诱惑", name: "幽灵Z无尽暗夜之诱惑",
effect: "通过Z力量召唤强烈的怨念,\n全力降临到对手身上。威力会根据原来\n的招式而改变", effect: "通过Z力量召唤强烈的怨念,\n全力降临到对手身上。\n威力会根据原来的招式而改变",
}, },
"neverEndingNightmareSpecial": { "neverEndingNightmareSpecial": {
name: "幽灵Z无尽暗夜之诱惑", name: "幽灵Z无尽暗夜之诱惑",
effect: "通过Z力量召唤强烈的怨念,\n全力降临到对手身上。威力会根据原来\n的招式而改变", effect: "通过Z力量召唤强烈的怨念,\n全力降临到对手身上。\n威力会根据原来的招式而改变",
}, },
"corkscrewCrashPhysical": { "corkscrewCrashPhysical": {
name: "钢Z超绝螺旋连击", name: "钢Z超绝螺旋连击",
effect: "通过Z力量进行高速旋转,\n全力撞上对手。威力会根据原来的招式而改变", effect: "通过Z力量进行高速旋转,\n全力撞上对手。\n威力会根据原来的招式而改变",
}, },
"corkscrewCrashSpecial": { "corkscrewCrashSpecial": {
name: "钢Z超绝螺旋连击", name: "钢Z超绝螺旋连击",
effect: "通过Z力量进行高速旋转,\n全力撞上对手。威力会根据原来的招式而改变", effect: "通过Z力量进行高速旋转,\n全力撞上对手。\n威力会根据原来的招式而改变",
}, },
"infernoOverdrivePhysical": { "infernoOverdrivePhysical": {
name: "火Z超强极限爆焰弹", name: "火Z超强极限爆焰弹",
effect: "通过Z力量喷出熊熊烈火,\n全力撞向对手。威力会根据原来的招式而改变", effect: "通过Z力量喷出熊熊烈火,\n全力撞向对手。\n威力会根据原来的招式而改变",
}, },
"infernoOverdriveSpecial": { "infernoOverdriveSpecial": {
name: "火Z超强极限爆焰弹", name: "火Z超强极限爆焰弹",
effect: "通过Z力量喷出熊熊烈火,\n全力撞向对手。威力会根据原来的招式而改变", effect: "通过Z力量喷出熊熊烈火,\n全力撞向对手。\n威力会根据原来的招式而改变",
}, },
"hydroVortexPhysical": { "hydroVortexPhysical": {
name: "水Z超级水流大漩涡", name: "水Z超级水流大漩涡",
effect: "通过Z力量制造大大的潮旋,\n全力吞没对手。威力会根据原来的招式而改变", effect: "通过Z力量制造大大的潮旋,\n全力吞没对手。\n威力会根据原来的招式而改变",
}, },
"hydroVortexSpecial": { "hydroVortexSpecial": {
name: "水Z超级水流大漩涡", name: "水Z超级水流大漩涡",
effect: "通过Z力量制造大大的潮旋,\n全力吞没对手。威力会根据原来的招式而改变", effect: "通过Z力量制造大大的潮旋,\n全力吞没对手。\n威力会根据原来的招式而改变",
}, },
"bloomDoomPhysical": { "bloomDoomPhysical": {
name: "草Z绚烂缤纷花怒放", name: "草Z绚烂缤纷花怒放",
effect: "通过Z力量借助花草的能量,\n全力攻击对手。威力会根据原来的招式而改变", effect: "通过Z力量借助花草的能量,\n全力攻击对手。\n威力会根据原来的招式而改变",
}, },
"bloomDoomSpecial": { "bloomDoomSpecial": {
name: "草Z绚烂缤纷花怒放", name: "草Z绚烂缤纷花怒放",
effect: "通过Z力量借助花草的能量,\n全力攻击对手。威力会根据原来的招式而改变", effect: "通过Z力量借助花草的能量,\n全力攻击对手。\n威力会根据原来的招式而改变",
}, },
"gigavoltHavocPhysical": { "gigavoltHavocPhysical": {
name: "电Z终极伏特狂雷闪", name: "电Z终极伏特狂雷闪",
@ -2591,43 +2591,43 @@ export const move: MoveTranslationEntries = {
}, },
"shatteredPsychePhysical": { "shatteredPsychePhysical": {
name: "超能力Z至高精神破坏波", name: "超能力Z至高精神破坏波",
effect: "通过Z力量操纵对手,\n全力使其感受到痛苦。威力会根据原来的招式而改变", effect: "通过Z力量操纵对手,\n全力使其感受到痛苦。\n威力会根据原来的招式而改变",
}, },
"shatteredPsycheSpecial": { "shatteredPsycheSpecial": {
name: "超能力Z至高精神破坏波", name: "超能力Z至高精神破坏波",
effect: "通过Z力量操纵对手,\n全力使其感受到痛苦。威力会根据原来的招式而改变", effect: "通过Z力量操纵对手,\n全力使其感受到痛苦。\n威力会根据原来的招式而改变",
}, },
"subzeroSlammerPhysical": { "subzeroSlammerPhysical": {
name: "冰Z激狂大地万里冰", name: "冰Z激狂大地万里冰",
effect: "通过Z力量急剧降低气温,\n全力冰冻对手。威力会根据原来的招式而改变", effect: "通过Z力量急剧降低气温,\n全力冰冻对手。\n威力会根据原来的招式而改变",
}, },
"subzeroSlammerSpecial": { "subzeroSlammerSpecial": {
name: "冰Z激狂大地万里冰", name: "冰Z激狂大地万里冰",
effect: "通过Z力量急剧降低气温,\n全力冰冻对手。威力会根据原来的招式而改变", effect: "通过Z力量急剧降低气温,\n全力冰冻对手。\n威力会根据原来的招式而改变",
}, },
"devastatingDrakePhysical": { "devastatingDrakePhysical": {
name: "龙Z究极巨龙震天地", name: "龙Z究极巨龙震天地",
effect: "通过Z力量将气场实体化,\n向对手全力发动袭击。威力会根据原来的\n招式而改变", effect: "通过Z力量将气场实体化,\n向对手全力发动袭击。\n威力会根据原来的招式而改变",
}, },
"devastatingDrakeSpecial": { "devastatingDrakeSpecial": {
name: "龙Z究极巨龙震天地", name: "龙Z究极巨龙震天地",
effect: "通过Z力量将气场实体化,\n向对手全力发动袭击。威力会根据原来的\n招式而改变", effect: "通过Z力量将气场实体化,\n向对手全力发动袭击。\n威力会根据原来的招式而改变",
}, },
"blackHoleEclipsePhysical": { "blackHoleEclipsePhysical": {
name: "恶Z黑洞吞噬万物灭", name: "恶Z黑洞吞噬万物灭",
effect: "通过Z力量收集恶能量,\n全力将对手吸入。威力会根据原来的招式而改变", effect: "通过Z力量收集恶能量,\n全力将对手吸入。\n威力会根据原来的招式而改变",
}, },
"blackHoleEclipseSpecial": { "blackHoleEclipseSpecial": {
name: "恶Z黑洞吞噬万物灭", name: "恶Z黑洞吞噬万物灭",
effect: "通过Z力量收集恶能量,\n全力将对手吸入。威力会根据原来的招式而改变", effect: "通过Z力量收集恶能量,\n全力将对手吸入。\n威力会根据原来的招式而改变",
}, },
"twinkleTacklePhysical": { "twinkleTacklePhysical": {
name: "妖精Z可爱星星飞天撞", name: "妖精Z可爱星星飞天撞",
effect: "通过Z力量制造魅惑空间,\n全力捉弄对手。威力会根据原来的招式而改变", effect: "通过Z力量制造魅惑空间,\n全力捉弄对手。\n威力会根据原来的招式而改变",
}, },
"twinkleTackleSpecial": { "twinkleTackleSpecial": {
name: "妖精Z可爱星星飞天撞", name: "妖精Z可爱星星飞天撞",
effect: "通过Z力量制造魅惑空间,\n全力捉弄对手。威力会根据原来的招式而改变", effect: "通过Z力量制造魅惑空间,\n全力捉弄对手。\n威力会根据原来的招式而改变",
}, },
"catastropika": { "catastropika": {
name: "皮卡丘Z皮卡皮卡必杀击", name: "皮卡丘Z皮卡皮卡必杀击",
@ -2651,7 +2651,7 @@ export const move: MoveTranslationEntries = {
}, },
"darkestLariat": { "darkestLariat": {
name: "DD金勾臂", name: "DD金勾臂",
effect: "旋转双臂打向对手。无视对手的能力变化,\n直接给予伤害", effect: "旋转双臂打向对手。\n无视对手的能力变化,\n直接给予伤害",
}, },
"sparklingAria": { "sparklingAria": {
name: "泡影的咏叹调", name: "泡影的咏叹调",
@ -2671,7 +2671,7 @@ export const move: MoveTranslationEntries = {
}, },
"strengthSap": { "strengthSap": {
name: "吸取力量", name: "吸取力量",
effect: "给自己回复和对手攻击力相同数值的HP,\n然后降低对手的攻击", effect: "给自己回复和对手攻击力\n相同数值的HP,\n然后降低对手的攻击",
}, },
"solarBlade": { "solarBlade": {
name: "日光刃", name: "日光刃",
@ -2727,7 +2727,7 @@ export const move: MoveTranslationEntries = {
}, },
"burnUp": { "burnUp": {
name: "燃尽", name: "燃尽",
effect: "将自己全身燃烧起火焰来,\n给予对手大大的伤害。自己的火属性将会消失", effect: "将自己全身燃烧起火焰来,\n给予对手大大的伤害。\n自己的火属性将会消失",
}, },
"speedSwap": { "speedSwap": {
name: "速度互换", name: "速度互换",
@ -2763,7 +2763,7 @@ export const move: MoveTranslationEntries = {
}, },
"clangingScales": { "clangingScales": {
name: "鳞片噪音", name: "鳞片噪音",
effect: "摩擦全身鳞片,发出响亮的声音进行攻击。\n攻击后自己的防御会降低", effect: "摩擦全身鳞片,\n发出响亮的声音进行攻击。\n攻击后自己的防御会降低",
}, },
"dragonHammer": { "dragonHammer": {
name: "龙锤", name: "龙锤",
@ -2827,7 +2827,7 @@ export const move: MoveTranslationEntries = {
}, },
"stompingTantrum": { "stompingTantrum": {
name: "跺脚", name: "跺脚",
effect: "化悔恨为力量进行攻击。\n如果上一回合招式没有打中威力就会翻倍", effect: "化悔恨为力量进行攻击。\n如果上一回合招式没有打中\n威力就会翻倍",
}, },
"shadowBone": { "shadowBone": {
name: "暗影之骨", name: "暗影之骨",
@ -2871,7 +2871,7 @@ export const move: MoveTranslationEntries = {
}, },
"multiAttack": { "multiAttack": {
name: "多属性攻击", name: "多属性攻击",
effect: "一边覆盖高能量,一边撞向对手进行攻击。\n根据存储碟不同\n属性会改变", effect: "一边覆盖高能量,\n一边撞向对手进行攻击。\n根据存储碟不同\n属性会改变",
}, },
"tenMillionVoltThunderbolt": { "tenMillionVoltThunderbolt": {
name: "智皮卡Z千万伏特", name: "智皮卡Z千万伏特",
@ -2895,7 +2895,7 @@ export const move: MoveTranslationEntries = {
}, },
"searingSunrazeSmash": { "searingSunrazeSmash": {
name: "索尔迦雷欧Z日光回旋下苍穹", name: "索尔迦雷欧Z日光回旋下苍穹",
effect: "得到Z力量的索尔迦雷欧将全力进行攻击。\n可以无视对手的特性效果", effect: "得到Z力量的索尔迦雷欧\n将全力进行攻击。\n可以无视对手的特性效果",
}, },
"menacingMoonrazeMaelstrom": { "menacingMoonrazeMaelstrom": {
name: "露奈雅拉Z月华飞溅落灵霄", name: "露奈雅拉Z月华飞溅落灵霄",
@ -2911,7 +2911,7 @@ export const move: MoveTranslationEntries = {
}, },
"clangorousSoulblaze": { "clangorousSoulblaze": {
name: "杖尾鳞甲龙Z炽魂热舞烈音爆", name: "杖尾鳞甲龙Z炽魂热舞烈音爆",
effect: "得到Z力量的杖尾鳞甲龙将全力攻击对手。\n并且自己的能力会提高", effect: "得到Z力量的杖尾鳞甲龙\n将全力攻击对手。\n并且自己的能力会提高",
}, },
"zippyZap": { "zippyZap": {
name: "电电加速", name: "电电加速",
@ -2983,7 +2983,7 @@ export const move: MoveTranslationEntries = {
}, },
"jawLock": { "jawLock": {
name: "紧咬不放", name: "紧咬不放",
effect: "使双方直到一方昏厥为止无法替换宝可梦。\n其中一方退场则可以解除效果", effect: "使双方直到一方昏厥为止\n无法替换宝可梦。\n其中一方退场则可以解除效果",
}, },
"stuffCheeks": { "stuffCheeks": {
name: "大快朵颐", name: "大快朵颐",
@ -3015,11 +3015,11 @@ export const move: MoveTranslationEntries = {
}, },
"boltBeak": { "boltBeak": {
name: "电喙", name: "电喙",
effect: "用带电的喙啄刺对手。\n如果比对手先出手攻击招式的威力会变成2倍", effect: "用带电的喙啄刺对手。\n如果比对手先出手攻击\n招式的威力会变成2倍",
}, },
"fishiousRend": { "fishiousRend": {
name: "鳃咬", name: "鳃咬",
effect: "用坚硬的腮咬住对手。\n如果比对手先出手攻击招式的威力会变成2倍", effect: "用坚硬的腮咬住对手。\n如果比对手先出手攻击\n招式的威力会变成2倍",
}, },
"courtChange": { "courtChange": {
name: "换场", name: "换场",
@ -3179,7 +3179,7 @@ export const move: MoveTranslationEntries = {
}, },
"eternabeam": { "eternabeam": {
name: "无极光束", name: "无极光束",
effect: "无极汰那变回原来的样子后,\n发动的最强攻击。下一回合自己将无法动弹", effect: "无极汰那变回原来的样子后,\n发动的最强攻击。\n下一回合自己将无法动弹",
}, },
"steelBeam": { "steelBeam": {
name: "铁蹄光线", name: "铁蹄光线",
@ -3227,7 +3227,7 @@ export const move: MoveTranslationEntries = {
}, },
"burningJealousy": { "burningJealousy": {
name: "妒火", name: "妒火",
effect: "用嫉妒的能量攻击对手。\n会让在该回合内能力有所提高的宝可梦陷入\n灼伤状态", effect: "用嫉妒的能量攻击对手。\n会让在该回合内能力有所提高\n的宝可梦陷入灼伤状态",
}, },
"lashOut": { "lashOut": {
name: "泄愤", name: "泄愤",
@ -3291,7 +3291,7 @@ export const move: MoveTranslationEntries = {
}, },
"thunderousKick": { "thunderousKick": {
name: "雷鸣蹴击", name: "雷鸣蹴击",
effect: "以雷电般的动作戏耍对手的同时使出脚踢。\n可降低对手的防御", effect: "以雷电般的动作\n戏耍对手的同时使出脚踢。\n可降低对手的防御",
}, },
"glacialLance": { "glacialLance": {
name: "雪矛", name: "雪矛",
@ -3307,7 +3307,7 @@ export const move: MoveTranslationEntries = {
}, },
"direClaw": { "direClaw": {
name: "克命爪", name: "克命爪",
effect: "以破灭之爪进行攻击。\n有时还会让对手陷入中毒、麻痹、睡眠之中的\n一种状态", effect: "以破灭之爪进行攻击。\n有时还会让对手陷入中毒、麻痹、\n睡眠之中的一种状态",
}, },
"psyshieldBash": { "psyshieldBash": {
name: "屏障猛攻", name: "屏障猛攻",
@ -3323,7 +3323,7 @@ export const move: MoveTranslationEntries = {
}, },
"springtideStorm": { "springtideStorm": {
name: "阳春风暴", name: "阳春风暴",
effect: "用交织着爱与恨的烈风席卷对手进行攻击。\n有时会降低对手的攻击", effect: "用交织着爱与恨的烈风席卷对手\n进行攻击。有时会降低对手的攻击",
}, },
"mysticalPower": { "mysticalPower": {
name: "神秘之力", name: "神秘之力",
@ -3355,7 +3355,7 @@ export const move: MoveTranslationEntries = {
}, },
"barbBarrage": { "barbBarrage": {
name: "毒千针", name: "毒千针",
effect: "用无数的毒针进行攻击。\n有时还会让对手陷入中毒状态。\n攻击处于中毒状态的对手时威力会变成2倍", effect: "用无数的毒针进行攻击。\n有时还会让对手陷入中毒状态。\n攻击处于中毒状态的对手时\n威力会变成2倍",
}, },
"esperWing": { "esperWing": {
name: "气场之翼", name: "气场之翼",
@ -3375,11 +3375,11 @@ export const move: MoveTranslationEntries = {
}, },
"infernalParade": { "infernalParade": {
name: "群魔乱舞", name: "群魔乱舞",
effect: "用无数的火球进行攻击。\n有时会让对手陷入灼伤状态。攻击处于异常\n状态的对手时,威力会变成2倍", effect: "用无数的火球进行攻击。有时会让对手陷\n入灼伤状态。攻击处于异常状态\n的对手时威力会变成倍",
}, },
"ceaselessEdge": { "ceaselessEdge": {
name: "秘剑・千重涛", name: "秘剑・千重涛",
effect: "用贝壳之剑进行攻击。\n散落的贝壳碎片会散落在对手脚下成为撒菱", effect: "用贝壳之剑进行攻击。\n散落的贝壳碎片会散落\n在对手脚下成为撒菱",
}, },
"bleakwindStorm": { "bleakwindStorm": {
name: "枯叶风暴", name: "枯叶风暴",
@ -3487,7 +3487,7 @@ export const move: MoveTranslationEntries = {
}, },
"gMaxSmite": { "gMaxSmite": {
name: "超极巨天谴雷诛", name: "超极巨天谴雷诛",
effect: "超极巨化的布莉姆温使出的妖精属性攻击。\n会让对手陷入混乱状态", effect: "超极巨化的布莉姆温使出的\n妖精属性攻击。\n会让对手陷入混乱状态",
}, },
"gMaxSteelsurge": { "gMaxSteelsurge": {
name: "超极巨钢铁阵法", name: "超极巨钢铁阵法",
@ -3515,7 +3515,7 @@ export const move: MoveTranslationEntries = {
}, },
"gMaxDrumSolo": { "gMaxDrumSolo": {
name: "超极巨狂擂乱打", name: "超极巨狂擂乱打",
effect: "超极巨化的轰擂金刚猩使出的草属性攻击。\n不会受到对手特性的干扰", effect: "超极巨化的轰擂金刚猩使出的\n草属性攻击。\n不会受到对手特性的干扰",
}, },
"gMaxFireball": { "gMaxFireball": {
name: "超极巨破阵火球", name: "超极巨破阵火球",
@ -3567,11 +3567,11 @@ export const move: MoveTranslationEntries = {
}, },
"spinOut": { "spinOut": {
name: "疾速转轮", name: "疾速转轮",
effect: "通过往腿上增加负荷,\n以激烈的旋转给予对手伤害。自己的速度会大幅降低", effect: "通过往腿上增加负荷,\n以激烈的旋转给予对手伤害。\n自己的速度会大幅降低",
}, },
"populationBomb": { "populationBomb": {
name: "鼠数儿", name: "鼠数儿",
effect: "伙伴们会纷纷赶来集合,\n以群体行动给予对手攻击。连续命中1~10次", effect: "伙伴们会纷纷赶来集合,\n以群体行动给予对手攻击。\n连续命中1~10次",
}, },
"iceSpinner": { "iceSpinner": {
name: "冰旋", name: "冰旋",
@ -3583,11 +3583,11 @@ export const move: MoveTranslationEntries = {
}, },
"revivalBlessing": { "revivalBlessing": {
name: "复生祈祷", name: "复生祈祷",
effect: "通过以慈爱之心祈祷,\n让陷入昏厥的后备宝可梦以回复一半HP的状态复活", effect: "通过以慈爱之心祈祷,\n让陷入昏厥的后备宝可梦\n以回复一半HP的状态复活",
}, },
"saltCure": { "saltCure": {
name: "盐腌", name: "盐腌",
effect: "使对手陷入盐腌状态,\n每回合给予对手伤害。对手为钢或水属性时会更痛苦", effect: "使对手陷入盐腌状态,\n每回合给予对手伤害。\n对手为钢或水属性时会更痛苦",
}, },
"tripleDive": { "tripleDive": {
name: "三连钻", name: "三连钻",
@ -3599,7 +3599,7 @@ export const move: MoveTranslationEntries = {
}, },
"doodle": { "doodle": {
name: "描绘", name: "描绘",
effect: "把握并映射出对手的本质,\n让自己和同伴宝可梦的特性变得和对手相同", effect: "把握并映射出对手的本质,\n让自己和同伴宝可梦的特性\n变得和对手相同",
}, },
"filletAway": { "filletAway": {
name: "甩肉", name: "甩肉",
@ -3615,7 +3615,7 @@ export const move: MoveTranslationEntries = {
}, },
"torchSong": { "torchSong": {
name: "闪焰高歌", name: "闪焰高歌",
effect: "如唱歌一样喷出熊熊燃烧的火焰烧焦对手。\n会提高自己的特攻", effect: "如唱歌一样喷出熊熊燃烧的火焰\n烧焦对手。会提高自己的特攻",
}, },
"aquaStep": { "aquaStep": {
name: "流水旋舞", name: "流水旋舞",
@ -3623,7 +3623,7 @@ export const move: MoveTranslationEntries = {
}, },
"ragingBull": { "ragingBull": {
name: "怒牛", name: "怒牛",
effect: "狂怒暴牛的猛烈冲撞。\n招式的属性随形态改变光墙和反射壁等招式\n也能破坏", effect: "狂怒暴牛的猛烈冲撞。\n招式的属性随形态改变\n光墙和反射壁等招式也能破坏",
}, },
"makeItRain": { "makeItRain": {
name: "淘金潮", name: "淘金潮",
@ -3631,7 +3631,7 @@ export const move: MoveTranslationEntries = {
}, },
"psyblade": { "psyblade": {
name: "精神剑", name: "精神剑",
effect: "用无形的利刃劈开对手。\n处于电气场地时招式威力会变成1.5倍", effect: "用无形的利刃劈开对手。\n处于电气场地时\n招式威力会变成1.5倍",
}, },
"hydroSteam": { "hydroSteam": {
name: "水蒸气", name: "水蒸气",
@ -3655,7 +3655,7 @@ export const move: MoveTranslationEntries = {
}, },
"chillyReception": { "chillyReception": {
name: "冷笑话", name: "冷笑话",
effect: "留下冷场的冷笑话后,\n和后备宝可梦进行替换。在5回合内会下雪", effect: "留下冷场的冷笑话后,\n和后备宝可梦进行替换。\n在5回合内会下雪",
}, },
"tidyUp": { "tidyUp": {
name: "大扫除", name: "大扫除",
@ -3691,7 +3691,7 @@ export const move: MoveTranslationEntries = {
}, },
"armorCannon": { "armorCannon": {
name: "铠农炮", name: "铠农炮",
effect: "熊熊燃烧自己的铠甲,\n将其做成炮弹射出攻击。自己的防御和特防会降低", effect: "熊熊燃烧自己的铠甲,\n将其做成炮弹射出攻击。\n自己的防御和特防会降低",
}, },
"bitterBlade": { "bitterBlade": {
name: "悔念剑", name: "悔念剑",
@ -3699,7 +3699,7 @@ export const move: MoveTranslationEntries = {
}, },
"doubleShock": { "doubleShock": {
name: "电光双击", name: "电光双击",
effect: "将全身所有的电力放出,\n给予对手大大的伤害。自己的电属性将会消失", effect: "将全身所有的电力放出,\n给予对手大大的伤害。\n自己的电属性将会消失",
}, },
"gigatonHammer": { "gigatonHammer": {
name: "巨力锤", name: "巨力锤",
@ -3743,11 +3743,11 @@ export const move: MoveTranslationEntries = {
}, },
"syrupBomb": { "syrupBomb": {
name: "糖浆炸弹", name: "糖浆炸弹",
effect: "使粘稠的麦芽糖浆爆炸,\n让对手陷入满身糖状态在3回合内持续降\n低其速度", effect: "使粘稠的麦芽糖浆爆炸,\n让对手陷入满身糖状态\n在3回合内持续降\n低其速度",
}, },
"ivyCudgel": { "ivyCudgel": {
name: "棘藤棒", name: "棘藤棒",
effect: "用缠有藤蔓的棍棒殴打。\n属性会随所戴的面具而改变。容易击中要害", effect: "用缠有藤蔓的棍棒殴打。\n属性会随所戴的面具而改变。\n容易击中要害",
}, },
"electroShot": { "electroShot": {
name: "电光束", name: "电光束",
@ -3787,7 +3787,7 @@ export const move: MoveTranslationEntries = {
}, },
"alluringVoice": { "alluringVoice": {
name: "魅诱之声", name: "魅诱之声",
effect: "用天使般的歌声攻击对手。\n会让此回合内能力有提高的宝可梦陷入混乱状态", effect: "用天使般的歌声攻击对手。\n会让此回合内能力有提高的\n宝可梦陷入混乱状态",
}, },
"temperFlare": { "temperFlare": {
name: "豁出去", name: "豁出去",

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "神秘的乱流停止了。" "strongWindsClearMessage": "神秘的乱流停止了。"
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "薄雾",
"mistyStartMessage": "脚下雾气缭绕!",
"mistyClearMessage": "脚下的雾气消失不见了!",
"mistyBlockMessage": "{{pokemonNameWithAffix}}正受到薄雾场地的保护!",
"electric": "电气",
"electricStartMessage": "脚下电光飞闪!",
"electricClearMessage": "脚下的电光消失不见了!",
"grassy": "青草",
"grassyStartMessage": "脚下青草如茵!",
"grassyClearMessage": "脚下的青草消失不见了!",
"psychic": "精神",
"psychicStartMessage": "脚下传来了奇妙的感觉!",
"psychicClearMessage": "脚下的奇妙感觉消失了!",
"defaultBlockMessage": "{{pokemonNameWithAffix}}正受到{{terrainName}}的的保护!"
};

View File

@ -12,6 +12,7 @@ export const battle: SimpleTranslationEntries = {
"trainerGo": "{{trainerName}} 派出了 {{pokemonName}}", "trainerGo": "{{trainerName}} 派出了 {{pokemonName}}",
"switchQuestion": "要更換\n{{pokemonName}}嗎?", "switchQuestion": "要更換\n{{pokemonName}}嗎?",
"trainerDefeated": "你擊敗了\n{{trainerName}}", "trainerDefeated": "你擊敗了\n{{trainerName}}",
"moneyPickedUp": "撿到了 ₽{{moneyAmount}}",
"pokemonCaught": "{{pokemonName}} 被抓住了!", "pokemonCaught": "{{pokemonName}} 被抓住了!",
"addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!", "addedAsAStarter": "{{pokemonName}} has been\nadded as a starter!",
"pokemon": "寶可夢", "pokemon": "寶可夢",

View File

@ -25,6 +25,7 @@ import { gameStatsUiHandler } from "./game-stats-ui-handler";
import { growth } from "./growth"; import { growth } from "./growth";
import { menu } from "./menu"; import { menu } from "./menu";
import { menuUiHandler } from "./menu-ui-handler"; import { menuUiHandler } from "./menu-ui-handler";
import { modifier } from "./modifier";
import { modifierType } from "./modifier-type"; import { modifierType } from "./modifier-type";
import { move } from "./move"; import { move } from "./move";
import { nature } from "./nature"; import { nature } from "./nature";
@ -39,7 +40,7 @@ import { statusEffect } from "./status-effect";
import { titles, trainerClasses, trainerNames } from "./trainers"; import { titles, trainerClasses, trainerNames } from "./trainers";
import { tutorial } from "./tutorial"; import { tutorial } from "./tutorial";
import { voucher } from "./voucher"; import { voucher } from "./voucher";
import { weather } from "./weather"; import { terrain, weather } from "./weather";
import { partyUiHandler } from "./party-ui-handler"; import { partyUiHandler } from "./party-ui-handler";
import { settings } from "./settings.js"; import { settings } from "./settings.js";
import { common } from "./common.js"; import { common } from "./common.js";
@ -73,6 +74,7 @@ export const zhTwConfig = {
growth: growth, growth: growth,
menu: menu, menu: menu,
menuUiHandler: menuUiHandler, menuUiHandler: menuUiHandler,
modifier: modifier,
modifierType: modifierType, modifierType: modifierType,
move: move, move: move,
nature: nature, nature: nature,
@ -85,6 +87,7 @@ export const zhTwConfig = {
splashMessages: splashMessages, splashMessages: splashMessages,
starterSelectUiHandler: starterSelectUiHandler, starterSelectUiHandler: starterSelectUiHandler,
statusEffect: statusEffect, statusEffect: statusEffect,
terrain: terrain,
titles: titles, titles: titles,
trainerClasses: trainerClasses, trainerClasses: trainerClasses,
trainerNames: trainerNames, trainerNames: trainerNames,

View File

@ -0,0 +1,12 @@
import { SimpleTranslationEntries } from "#app/interfaces/locales";
export const modifier: SimpleTranslationEntries = {
"surviveDamageApply": "{{pokemonNameWithAffix}}用{{typeName}}\n撐住了",
"turnHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回復了體力",
"hitHealApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回復了體力",
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}}用{{typeName}}\n回復了活力",
"moneyInterestApply": "用{{typeName}}\n獲得了 ₽{{moneyAmount}} 利息!",
"turnHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}吸收了!",
"contactHeldItemTransferApply": "{{pokemonNameWithAffix}}的{{itemName}}被\n{{pokemonName}}的{{typeName}}奪取了!",
"enemyTurnHealApply": "{{pokemonNameWithAffix}}\n回復了一些體力",
} as const;

View File

@ -43,3 +43,24 @@ export const weather: SimpleTranslationEntries = {
"strongWindsEffectMessage": "The mysterious air current weakened the attack!", "strongWindsEffectMessage": "The mysterious air current weakened the attack!",
"strongWindsClearMessage": "神秘的亂流停止了。" "strongWindsClearMessage": "神秘的亂流停止了。"
}; };
export const terrain: SimpleTranslationEntries = {
"misty": "薄霧",
"mistyStartMessage": "腳下霧氣繚繞!",
"mistyClearMessage": "腳下的霧氣消失不見了!",
"mistyBlockMessage": "{{pokemonNameWithAffix}}正受到薄霧場地的保護!",
"electric": "電氣",
"electricStartMessage": "腳下電流飛閃!",
"electricClearMessage": "腳下的電流消失了!",
"grassy": "青草",
"grassyStartMessage": "腳下青草如茵!",
"grassyClearMessage": "腳下的青草消失不見了!",
"psychic": "精神",
"psychicStartMessage": "腳下傳來了奇妙的感覺!",
"psychicClearMessage": "腳下的奇妙感覺消失了!",
"defaultBlockMessage": "{{pokemonNameWithAffix}}正受到{{terrainName}}的保護!"
};

1823
src/logger.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,7 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type"; import { BerryType } from "#enums/berry-type";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import { GameModes } from "#app/game-mode.js";
const outputModifierData = false; const outputModifierData = false;
const useMaxWeightForOutput = false; const useMaxWeightForOutput = false;
@ -64,6 +65,10 @@ export class ModifierType {
return i18next.t(`${this.localeKey}.name` as any); return i18next.t(`${this.localeKey}.name` as any);
} }
get identifier(): string {
return "Modifier:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t(`${this.localeKey}.description` as any); return i18next.t(`${this.localeKey}.description` as any);
} }
@ -159,6 +164,9 @@ class AddPokeballModifierType extends ModifierType {
"pokeballName": getPokeballName(this.pokeballType), "pokeballName": getPokeballName(this.pokeballType),
}); });
} }
get identifier(): string {
return "PokeballModifier:" + Utils.getEnumKeys(PokeballType)[this.pokeballType];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", { return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", {
@ -198,6 +206,10 @@ class AddVoucherModifierType extends ModifierType {
export class PokemonModifierType extends ModifierType { export class PokemonModifierType extends ModifierType {
public selectFilter: PokemonSelectFilter; public selectFilter: PokemonSelectFilter;
get identifier(): string {
return "PokemonModifier:undefined";
}
constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, group?: string, soundName?: string) { constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, group?: string, soundName?: string) {
super(localeKey, iconImage, newModifierFunc, group, soundName); super(localeKey, iconImage, newModifierFunc, group, soundName);
@ -221,6 +233,10 @@ export class PokemonHeldItemModifierType extends PokemonModifierType {
}, group, soundName); }, group, soundName);
} }
get identifier(): string {
return "HeldItem:" + this.localeKey.split(".")[1];
}
newModifier(...args: any[]): Modifiers.PokemonHeldItemModifier { newModifier(...args: any[]): Modifiers.PokemonHeldItemModifier {
return super.newModifier(...args) as Modifiers.PokemonHeldItemModifier; return super.newModifier(...args) as Modifiers.PokemonHeldItemModifier;
} }
@ -245,6 +261,10 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType {
this.healStatus = healStatus; this.healStatus = healStatus;
} }
get identifier(): string {
return "HpRestore:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return this.restorePoints return this.restorePoints
? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", { ? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", {
@ -274,6 +294,9 @@ export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
return null; return null;
}; };
} }
get identifier(): string {
return "Revive:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", { restorePercent: this.restorePercent }); return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", { restorePercent: this.restorePercent });
@ -291,6 +314,10 @@ export class PokemonStatusHealModifierType extends PokemonModifierType {
})); }));
} }
get identifier(): string {
return "StatusCure:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description");
} }
@ -323,6 +350,10 @@ export class PokemonPpRestoreModifierType extends PokemonMoveModifierType {
this.restorePoints = restorePoints; this.restorePoints = restorePoints;
} }
get identifier(): string {
return "PpRestore:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return this.restorePoints > -1 return this.restorePoints > -1
? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", { restorePoints: this.restorePoints }) ? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", { restorePoints: this.restorePoints })
@ -346,6 +377,10 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
this.restorePoints = restorePoints; this.restorePoints = restorePoints;
} }
get identifier(): string {
return "PpAllRestore:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return this.restorePoints > -1 return this.restorePoints > -1
? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", { restorePoints: this.restorePoints }) ? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", { restorePoints: this.restorePoints })
@ -371,6 +406,10 @@ export class PokemonPpUpModifierType extends PokemonMoveModifierType {
this.upPoints = upPoints; this.upPoints = upPoints;
} }
get identifier(): string {
return "PpBooster:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints }); return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints });
} }
@ -395,6 +434,10 @@ export class PokemonNatureChangeModifierType extends PokemonModifierType {
return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", { natureName: getNatureName(this.nature) }); return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", { natureName: getNatureName(this.nature) });
} }
get identifier(): string {
return "Mint:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", { natureName: getNatureName(this.nature, true, true, true) }); return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", { natureName: getNatureName(this.nature, true, true, true) });
} }
@ -410,6 +453,10 @@ export class RememberMoveModifierType extends PokemonModifierType {
return null; return null;
}, group); }, group);
} }
get identifier(): string {
return "MemoryMushroom:" + this.localeKey.split(".")[1];
}
} }
export class DoubleBattleChanceBoosterModifierType extends ModifierType { export class DoubleBattleChanceBoosterModifierType extends ModifierType {
@ -421,6 +468,10 @@ export class DoubleBattleChanceBoosterModifierType extends ModifierType {
this.battleCount = battleCount; this.battleCount = battleCount;
} }
get identifier(): string {
return "DoubleModifier:" + this.localeKey.split(".")[1];
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", { battleCount: this.battleCount }); return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", { battleCount: this.battleCount });
} }
@ -440,6 +491,10 @@ export class TempBattleStatBoosterModifierType extends ModifierType implements G
return i18next.t(`modifierType:TempBattleStatBoosterItem.${getTempBattleStatBoosterItemName(this.tempBattleStat).replace(/\./g, "").replace(/[ ]/g, "_").toLowerCase()}`); return i18next.t(`modifierType:TempBattleStatBoosterItem.${getTempBattleStatBoosterItemName(this.tempBattleStat).replace(/\./g, "").replace(/[ ]/g, "_").toLowerCase()}`);
} }
get identifier(): string {
return "TempStatBooster:" + Utils.getEnumKeys(TempBattleStat)[this.tempBattleStat]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.TempBattleStatBoosterModifierType.description", { tempBattleStatName: getTempBattleStatName(this.tempBattleStat) }); return i18next.t("modifierType:ModifierType.TempBattleStatBoosterModifierType.description", { tempBattleStatName: getTempBattleStatName(this.tempBattleStat) });
} }
@ -462,6 +517,10 @@ export class BerryModifierType extends PokemonHeldItemModifierType implements Ge
return getBerryName(this.berryType); return getBerryName(this.berryType);
} }
get identifier(): string {
return "Berry:" + Utils.getEnumKeys(BerryType)[this.berryType]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return getBerryEffectDescription(this.berryType); return getBerryEffectDescription(this.berryType);
} }
@ -528,6 +587,10 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i
return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType).replace(/[ \-]/g, "_").toLowerCase()}`); return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType).replace(/[ \-]/g, "_").toLowerCase()}`);
} }
get identifier(): string {
return "MoveBooster:" + Utils.getEnumKeys(Type)[this.moveType]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
// TODO: Need getTypeName? // TODO: Need getTypeName?
return i18next.t("modifierType:ModifierType.AttackTypeBoosterModifierType.description", { moveType: i18next.t(`pokemonInfo:Type.${Type[this.moveType]}`) }); return i18next.t("modifierType:ModifierType.AttackTypeBoosterModifierType.description", { moveType: i18next.t(`pokemonInfo:Type.${Type[this.moveType]}`) });
@ -554,6 +617,10 @@ export class SpeciesStatBoosterModifierType extends PokemonHeldItemModifierType
this.key = key; this.key = key;
} }
get identifier(): string {
return "SpeciesBooster:" + this.key
}
getPregenArgs(): any[] { getPregenArgs(): any[] {
return [ this.key ]; return [ this.key ];
@ -565,6 +632,10 @@ export class PokemonLevelIncrementModifierType extends PokemonModifierType {
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), (_pokemon: PlayerPokemon) => null); super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonLevelIncrementModifier(this, (args[0] as PlayerPokemon).id), (_pokemon: PlayerPokemon) => null);
} }
get identifier(): string {
return "RareCandy:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonLevelIncrementModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonLevelIncrementModifierType.description");
} }
@ -575,6 +646,10 @@ export class AllPokemonLevelIncrementModifierType extends ModifierType {
super(localeKey, iconImage, (_type, _args) => new Modifiers.PokemonLevelIncrementModifier(this, -1)); super(localeKey, iconImage, (_type, _args) => new Modifiers.PokemonLevelIncrementModifier(this, -1));
} }
get identifier(): string {
return "RareCandy:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.AllPokemonLevelIncrementModifierType.description"); return i18next.t("modifierType:ModifierType.AllPokemonLevelIncrementModifierType.description");
} }
@ -612,6 +687,10 @@ export class PokemonBaseStatBoosterModifierType extends PokemonHeldItemModifierT
return i18next.t(`modifierType:BaseStatBoosterItem.${this.localeName.replace(/[ \-]/g, "_").toLowerCase()}`); return i18next.t(`modifierType:BaseStatBoosterItem.${this.localeName.replace(/[ \-]/g, "_").toLowerCase()}`);
} }
get identifier(): string {
return "StatBooster:" + Utils.getEnumKeys(Stat)[this.stat]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonBaseStatBoosterModifierType.description", { statName: getStatName(this.stat) }); return i18next.t("modifierType:ModifierType.PokemonBaseStatBoosterModifierType.description", { statName: getStatName(this.stat) });
} }
@ -630,6 +709,10 @@ class AllPokemonFullHpRestoreModifierType extends ModifierType {
this.descriptionKey = descriptionKey; this.descriptionKey = descriptionKey;
} }
get identifier(): string {
return "HealAll:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t(`${this.descriptionKey || "modifierType:ModifierType.AllPokemonFullHpRestoreModifierType"}.description` as any); return i18next.t(`${this.descriptionKey || "modifierType:ModifierType.AllPokemonFullHpRestoreModifierType"}.description` as any);
} }
@ -639,6 +722,10 @@ class AllPokemonFullReviveModifierType extends AllPokemonFullHpRestoreModifierTy
constructor(localeKey: string, iconImage: string) { constructor(localeKey: string, iconImage: string) {
super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType", (_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 0, 100, false, true)); super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType", (_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 0, 100, false, true));
} }
get identifier(): string {
return "ReviveAll:" + this.localeKey.split(".")[1]
}
} }
export class MoneyRewardModifierType extends ModifierType { export class MoneyRewardModifierType extends ModifierType {
@ -652,6 +739,10 @@ export class MoneyRewardModifierType extends ModifierType {
this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey; this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey;
} }
get identifier(): string {
return "Money:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
const moneyAmount = new Utils.IntegerHolder(scene.getWaveMoneyAmount(this.moneyMultiplier)); const moneyAmount = new Utils.IntegerHolder(scene.getWaveMoneyAmount(this.moneyMultiplier));
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount); scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
@ -673,6 +764,10 @@ export class ExpBoosterModifierType extends ModifierType {
this.boostPercent = boostPercent; this.boostPercent = boostPercent;
} }
get identifier(): string {
return "ExpBooster:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.ExpBoosterModifierType.description", { boostPercent: this.boostPercent }); return i18next.t("modifierType:ModifierType.ExpBoosterModifierType.description", { boostPercent: this.boostPercent });
} }
@ -687,6 +782,10 @@ export class PokemonExpBoosterModifierType extends PokemonHeldItemModifierType {
this.boostPercent = boostPercent; this.boostPercent = boostPercent;
} }
get identifier(): string {
return "PokemonExpBooster:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonExpBoosterModifierType.description", { boostPercent: this.boostPercent }); return i18next.t("modifierType:ModifierType.PokemonExpBoosterModifierType.description", { boostPercent: this.boostPercent });
} }
@ -697,6 +796,10 @@ export class PokemonFriendshipBoosterModifierType extends PokemonHeldItemModifie
super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonFriendshipBoosterModifier(this, (args[0] as Pokemon).id)); super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonFriendshipBoosterModifier(this, (args[0] as Pokemon).id));
} }
get identifier(): string {
return "FriendshipBooster:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonFriendshipBoosterModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonFriendshipBoosterModifierType.description");
} }
@ -711,6 +814,10 @@ export class PokemonMoveAccuracyBoosterModifierType extends PokemonHeldItemModif
this.amount = amount; this.amount = amount;
} }
get identifier(): string {
return "AccuracyBooster:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonMoveAccuracyBoosterModifierType.description", { accuracyAmount: this.amount }); return i18next.t("modifierType:ModifierType.PokemonMoveAccuracyBoosterModifierType.description", { accuracyAmount: this.amount });
} }
@ -721,6 +828,10 @@ export class PokemonMultiHitModifierType extends PokemonHeldItemModifierType {
super(localeKey, iconImage, (type, args) => new Modifiers.PokemonMultiHitModifier(type as PokemonMultiHitModifierType, (args[0] as Pokemon).id)); super(localeKey, iconImage, (type, args) => new Modifiers.PokemonMultiHitModifier(type as PokemonMultiHitModifierType, (args[0] as Pokemon).id));
} }
get identifier(): string {
return "MultiHit:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.PokemonMultiHitModifierType.description"); return i18next.t("modifierType:ModifierType.PokemonMultiHitModifierType.description");
} }
@ -728,8 +839,9 @@ export class PokemonMultiHitModifierType extends PokemonHeldItemModifierType {
export class TmModifierType extends PokemonModifierType { export class TmModifierType extends PokemonModifierType {
public moveId: Moves; public moveId: Moves;
public rarity: string;
constructor(moveId: Moves) { constructor(moveId: Moves, rarity: ModifierTier) {
super("", `tm_${Type[allMoves[moveId].type].toLowerCase()}`, (_type, args) => new Modifiers.TmModifier(this, (args[0] as PlayerPokemon).id), super("", `tm_${Type[allMoves[moveId].type].toLowerCase()}`, (_type, args) => new Modifiers.TmModifier(this, (args[0] as PlayerPokemon).id),
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.getMoveset().filter(m => m?.moveId === moveId).length) { if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.getMoveset().filter(m => m?.moveId === moveId).length) {
@ -739,6 +851,26 @@ export class TmModifierType extends PokemonModifierType {
}, "tm"); }, "tm");
this.moveId = moveId; this.moveId = moveId;
switch (rarity) {
case ModifierTier.COMMON:
this.rarity = "Common"
break;
case ModifierTier.GREAT:
this.rarity = "Great"
break;
case ModifierTier.ULTRA:
this.rarity = "Ultra"
break;
case ModifierTier.ROGUE:
this.rarity = "Rogue"
break;
case ModifierTier.MASTER:
this.rarity = "Master"
break;
case ModifierTier.LUXURY:
this.rarity = "Luxury"
break;
}
} }
get name(): string { get name(): string {
@ -748,6 +880,10 @@ export class TmModifierType extends PokemonModifierType {
}); });
} }
get identifier(): string {
return "Tm" + this.rarity + ":" + Utils.getEnumKeys(Moves)[this.moveId]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t(scene.enableMoveInfo ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" : "modifierType:ModifierType.TmModifierType.description", { moveName: allMoves[this.moveId].name }); return i18next.t(scene.enableMoveInfo ? "modifierType:ModifierType.TmModifierTypeWithInfo.description" : "modifierType:ModifierType.TmModifierType.description", { moveName: allMoves[this.moveId].name });
} }
@ -777,6 +913,10 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`); return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`);
} }
get identifier(): string {
return "Evolution" + (this.evolutionItem > 50 ? "Rare" : "") + ":" + Utils.getEnumKeys(EvolutionItem)[this.evolutionItem]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description"); return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description");
} }
@ -815,6 +955,9 @@ export class FormChangeItemModifierType extends PokemonModifierType implements G
get name(): string { get name(): string {
return i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.formChangeItem]}`); return i18next.t(`modifierType:FormChangeItem.${FormChangeItem[this.formChangeItem]}`);
} }
get identifier(): string {
return "FormChange:" + Utils.getEnumKeys(FormChangeItem)[this.formChangeItem]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.FormChangeItemModifierType.description"); return i18next.t("modifierType:ModifierType.FormChangeItemModifierType.description");
@ -836,6 +979,10 @@ export class FusePokemonModifierType extends PokemonModifierType {
}); });
} }
get identifier(): string {
return "Fusion:" + this.localeKey.split(".")[1]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description"); return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description");
} }
@ -975,7 +1122,7 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator {
return null; return null;
} }
const randTmIndex = Utils.randSeedInt(tierUniqueCompatibleTms.length); const randTmIndex = Utils.randSeedInt(tierUniqueCompatibleTms.length);
return new TmModifierType(tierUniqueCompatibleTms[randTmIndex]); return new TmModifierType(tierUniqueCompatibleTms[randTmIndex], tier);
}); });
} }
} }
@ -1044,6 +1191,10 @@ export class TerastallizeModifierType extends PokemonHeldItemModifierType implem
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) }); return i18next.t("modifierType:ModifierType.TerastallizeModifierType.name", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
} }
get identifier(): string {
return "TeraShard:" + Utils.getEnumKeys(Type)[this.teraType]
}
getDescription(scene: BattleScene): string { getDescription(scene: BattleScene): string {
return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) }); return i18next.t("modifierType:ModifierType.TerastallizeModifierType.description", { teraType: i18next.t(`pokemonInfo:Type.${Type[this.teraType]}`) });
} }
@ -1805,14 +1956,14 @@ export function getModifierTypeFuncById(id: string): ModifierTypeFunc {
return modifierTypes[id]; return modifierTypes[id];
} }
export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[], modifierTiers?: ModifierTier[]): ModifierTypeOption[] { export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemon[], modifierTiers?: ModifierTier[], scene?: BattleScene): ModifierTypeOption[] {
const options: ModifierTypeOption[] = []; const options: ModifierTypeOption[] = [];
const retryCount = Math.min(count * 5, 50); const retryCount = Math.min(count * 5, 50);
new Array(count).fill(0).map((_, i) => { new Array(count).fill(0).map((_, i) => {
let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers?.length > i ? modifierTiers[i] : undefined); let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers?.length > i ? modifierTiers[i] : undefined, undefined, undefined, scene);
let r = 0; let r = 0;
while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) { while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) {
candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount); candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount, undefined, scene);
} }
options.push(candidate); options.push(candidate);
}); });
@ -1867,11 +2018,11 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base
export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: Modifiers.PersistentModifier[], scene: BattleScene): Modifiers.EnemyPersistentModifier { export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: Modifiers.PersistentModifier[], scene: BattleScene): Modifiers.EnemyPersistentModifier {
const tierStackCount = tier === ModifierTier.ULTRA ? 5 : tier === ModifierTier.GREAT ? 3 : 1; const tierStackCount = tier === ModifierTier.ULTRA ? 5 : tier === ModifierTier.GREAT ? 3 : 1;
const retryCount = 50; const retryCount = 50;
let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier, undefined, undefined, scene);
let r = 0; let r = 0;
let matchingModifier: Modifiers.PersistentModifier; let matchingModifier: Modifiers.PersistentModifier;
while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate.type.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate.type.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) {
candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier, undefined, undefined, scene);
} }
const modifier = candidate.type.newModifier() as Modifiers.EnemyPersistentModifier; const modifier = candidate.type.newModifier() as Modifiers.EnemyPersistentModifier;
@ -1880,21 +2031,21 @@ export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers:
return modifier; return modifier;
} }
export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, upgradeChance: integer = 0): PokemonHeldItemModifierType[] { export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, upgradeChance: integer = 0, scene?: BattleScene): PokemonHeldItemModifierType[] {
const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0).type as PokemonHeldItemModifierType); const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0, undefined, scene).type as PokemonHeldItemModifierType);
if (!(waveIndex % 1000)) { if (!(waveIndex % 1000)) {
ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType); ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType);
} }
return ret; return ret;
} }
export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.PokemonHeldItemModifier[] { export function getDailyRunStarterModifiers(party: PlayerPokemon[], scene?: BattleScene): Modifiers.PokemonHeldItemModifier[] {
const ret: Modifiers.PokemonHeldItemModifier[] = []; const ret: Modifiers.PokemonHeldItemModifier[] = [];
for (const p of party) { for (const p of party) {
for (let m = 0; m < 3; m++) { for (let m = 0; m < 3; m++) {
const tierValue = Utils.randSeedInt(64); const tierValue = Utils.randSeedInt(64);
const tier = tierValue > 25 ? ModifierTier.COMMON : tierValue > 12 ? ModifierTier.GREAT : tierValue > 4 ? ModifierTier.ULTRA : tierValue ? ModifierTier.ROGUE : ModifierTier.MASTER; const tier = tierValue > 25 ? ModifierTier.COMMON : tierValue > 12 ? ModifierTier.GREAT : tierValue > 4 ? ModifierTier.ULTRA : tierValue ? ModifierTier.ROGUE : ModifierTier.MASTER;
const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier).type.newModifier(p) as Modifiers.PokemonHeldItemModifier; const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier, undefined, undefined, scene).type.newModifier(p) as Modifiers.PokemonHeldItemModifier;
ret.push(modifier); ret.push(modifier);
} }
} }
@ -1902,7 +2053,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.P
return ret; return ret;
} }
function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0): ModifierTypeOption { function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0, scene?: BattleScene): ModifierTypeOption {
const player = !poolType; const player = !poolType;
const pool = getModifierPoolForType(poolType); const pool = getModifierPoolForType(poolType);
let thresholds: object; let thresholds: object;
@ -1929,7 +2080,12 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
upgradeCount = 0; upgradeCount = 0;
} }
if (player && tierValue) { if (player && tierValue) {
const partyLuckValue = getPartyLuckValue(party); var partyLuckValue = getPartyLuckValue(party);
if (scene) {
if (scene.gameMode.modeId == GameModes.DAILY && scene.disableDailyShinies) {
partyLuckValue = 0
}
}
const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4)); const upgradeOdds = Math.floor(128 / ((partyLuckValue + 4) / 4));
let upgraded = false; let upgraded = false;
do { do {
@ -1954,7 +2110,12 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
} else if (upgradeCount === undefined && player) { } else if (upgradeCount === undefined && player) {
upgradeCount = 0; upgradeCount = 0;
if (tier < ModifierTier.MASTER) { if (tier < ModifierTier.MASTER) {
const partyShinyCount = party.filter(p => p.isShiny() && !p.isFainted()).length; var partyShinyCount = party.filter(p => p.isShiny() && !p.isFainted()).length;
if (scene) {
if (scene.gameMode.modeId == GameModes.DAILY && scene.disableDailyShinies) {
partyShinyCount = 0
}
}
const upgradeOdds = Math.floor(32 / ((partyShinyCount + 2) / 2)); const upgradeOdds = Math.floor(32 / ((partyShinyCount + 2) / 2));
while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) { while (modifierPool.hasOwnProperty(tier + upgradeCount + 1) && modifierPool[tier + upgradeCount + 1].length) {
if (!Utils.randSeedInt(upgradeOdds)) { if (!Utils.randSeedInt(upgradeOdds)) {
@ -1996,7 +2157,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
if (player) { if (player) {
console.log(ModifierTier[tier], upgradeCount); console.log(ModifierTier[tier], upgradeCount);
} }
return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount); return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount, scene);
} }
} }

View File

@ -24,6 +24,7 @@ import * as Overrides from "../overrides";
import { ModifierType, modifierTypes } from "./modifier-type"; import { ModifierType, modifierTypes } from "./modifier-type";
import { Command } from "#app/ui/command-ui-handler.js"; import { Command } from "#app/ui/command-ui-handler.js";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import i18next from "i18next";
import { allMoves } from "#app/data/move.js"; import { allMoves } from "#app/data/move.js";
import { Abilities } from "#app/enums/abilities.js"; import { Abilities } from "#app/enums/abilities.js";
@ -142,6 +143,10 @@ export abstract class Modifier {
return true; return true;
} }
get identifier(): string {
return this.type.identifier;
}
abstract apply(args: any[]): boolean | Promise<boolean>; abstract apply(args: any[]): boolean | Promise<boolean>;
} }
@ -965,7 +970,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
if (!surviveDamage.value && pokemon.randSeedInt(10) < this.getStackCount()) { if (!surviveDamage.value && pokemon.randSeedInt(10) < this.getStackCount()) {
surviveDamage.value = true; surviveDamage.value = true;
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`)); pokemon.scene.queueMessage(i18next.t("modifier:surviveDamageApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }));
return true; return true;
} }
@ -1070,7 +1075,7 @@ export class TurnHealModifier extends PokemonHeldItemModifier {
if (pokemon.getHpRatio() < 1) { if (pokemon.getHpRatio() < 1) {
const scene = pokemon.scene; const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true)); Math.max(Math.floor(pokemon.getMaxHp() / 16) * this.stackCount, 1), i18next.t("modifier:turnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
return true; return true;
} }
@ -1161,7 +1166,7 @@ export class HitHealModifier extends PokemonHeldItemModifier {
if (pokemon.turnData.damageDealt && pokemon.getHpRatio() < 1) { if (pokemon.turnData.damageDealt && pokemon.getHpRatio() < 1) {
const scene = pokemon.scene; const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), getPokemonMessage(pokemon, `'s ${this.type.name}\nrestored its HP a little!`), true)); Math.max(Math.floor(pokemon.turnData.damageDealt / 8) * this.stackCount, 1), i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
} }
return true; return true;
@ -1296,7 +1301,7 @@ export class PokemonInstantReviveModifier extends PokemonHeldItemModifier {
const pokemon = args[0] as Pokemon; const pokemon = args[0] as Pokemon;
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), getPokemonMessage(pokemon, ` was revived\nby its ${this.type.name}!`), false, false, true)); Math.max(Math.floor(pokemon.getMaxHp() / 2), 1), i18next.t("modifier:pokemonInstantReviveApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), false, false, true));
pokemon.resetStatus(true, false, true); pokemon.resetStatus(true, false, true);
return true; return true;
@ -2010,7 +2015,10 @@ export class MoneyInterestModifier extends PersistentModifier {
const interestAmount = Math.floor(scene.money * 0.1 * this.getStackCount()); const interestAmount = Math.floor(scene.money * 0.1 * this.getStackCount());
scene.addMoney(interestAmount); scene.addMoney(interestAmount);
scene.queueMessage(`You received interest of ₽${interestAmount.toLocaleString("en-US")}\nfrom the ${this.type.name}!`, null, true); const userLocale = navigator.language || "en-US";
const formattedMoneyAmount = interestAmount.toLocaleString(userLocale);
const message = i18next.t("modifier:moneyInterestApply", { moneyAmount: formattedMoneyAmount, typeName: this.type.name });
scene.queueMessage(message, null, true);
return true; return true;
} }
@ -2231,7 +2239,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier {
} }
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string { getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string {
return getPokemonMessage(targetPokemon, `'s ${item.name} was absorbed\nby ${pokemon.name}'s ${this.type.name}!`); return i18next.t("modifier:turnHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.name, typeName: this.type.name });
} }
getMaxHeldItemCount(pokemon: Pokemon): integer { getMaxHeldItemCount(pokemon: Pokemon): integer {
@ -2285,7 +2293,7 @@ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModif
} }
getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string { getTransferMessage(pokemon: Pokemon, targetPokemon: Pokemon, item: ModifierTypes.ModifierType): string {
return getPokemonMessage(targetPokemon, `'s ${item.name} was snatched\nby ${pokemon.name}'s ${this.type.name}!`); return i18next.t("modifier:contactHeldItemTransferApply", { pokemonNameWithAffix: getPokemonNameWithAffix(targetPokemon), itemName: item.name, pokemonName: pokemon.name, typeName: this.type.name });
} }
getMaxHeldItemCount(pokemon: Pokemon): integer { getMaxHeldItemCount(pokemon: Pokemon): integer {
@ -2443,7 +2451,7 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifier {
if (pokemon.getHpRatio() < 1) { if (pokemon.getHpRatio() < 1) {
const scene = pokemon.scene; const scene = pokemon.scene;
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(), scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
Math.max(Math.floor(pokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), getPokemonMessage(pokemon, "\nrestored some HP!"), true, false, false, false, true)); Math.max(Math.floor(pokemon.getMaxHp() / (100 / this.healPercent)) * this.stackCount, 1), i18next.t("modifier:enemyTurnHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), true, false, false, false, true));
return true; return true;
} }

File diff suppressed because it is too large Load Diff

View File

@ -40,6 +40,12 @@ export class SceneBase extends Phaser.Scene {
this.load.image(`${key}_legacy`, this.getCachedUrl(`images/${folder}/${filename}`)); this.load.image(`${key}_legacy`, this.getCachedUrl(`images/${folder}/${filename}`));
} }
} }
loadImageNoLegacy(key: string, folder: string, filename?: string) {
if (!filename) {
filename = `${key}.png`;
}
this.load.image(key, this.getCachedUrl(`images/${folder}/${filename}`));
}
loadSpritesheet(key: string, folder: string, size: integer, filename?: string) { loadSpritesheet(key: string, folder: string, size: integer, filename?: string) {
if (!filename) { if (!filename) {

View File

@ -40,6 +40,7 @@ import { GameDataType } from "#enums/game-data-type";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { PlayerGender } from "#enums/player-gender"; import { PlayerGender } from "#enums/player-gender";
import { Species } from "#enums/species"; import { Species } from "#enums/species";
import * as LoggerTools from "../logger"
export const defaultStarterSpecies: Species[] = [ export const defaultStarterSpecies: Species[] = [
Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE,
@ -122,6 +123,9 @@ export interface SessionSaveData {
gameVersion: string; gameVersion: string;
timestamp: integer; timestamp: integer;
challenges: ChallengeData[]; challenges: ChallengeData[];
slot: integer;
description: string;
autoSlot: integer;
} }
interface Unlocks { interface Unlocks {
@ -840,7 +844,7 @@ export class GameData {
} as SessionSaveData; } as SessionSaveData;
} }
getSession(slotId: integer): Promise<SessionSaveData> { getSession(slotId: integer, autoSlot?: integer): Promise<SessionSaveData> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
if (slotId < 0) { if (slotId < 0) {
return resolve(null); return resolve(null);
@ -848,14 +852,18 @@ export class GameData {
const handleSessionData = async (sessionDataStr: string) => { const handleSessionData = async (sessionDataStr: string) => {
try { try {
const sessionData = this.parseSessionData(sessionDataStr); const sessionData = this.parseSessionData(sessionDataStr);
sessionData.autoSlot = autoSlot;
resolve(sessionData); resolve(sessionData);
} catch (err) { } catch (err) {
reject(err); reject(err);
return; return;
} }
}; };
var autokey = ""
if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`)) { if (autoSlot != undefined) {
autokey = "_auto" + autoSlot
}
if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}${autokey}`)) {
Utils.apiFetch(`savedata/session/get?slot=${slotId}&clientSessionId=${clientSessionId}`, true) Utils.apiFetch(`savedata/session/get?slot=${slotId}&clientSessionId=${clientSessionId}`, true)
.then(response => response.text()) .then(response => response.text())
.then(async response => { .then(async response => {
@ -869,7 +877,8 @@ export class GameData {
await handleSessionData(response); await handleSessionData(response);
}); });
} else { } else {
const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`); const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}${autokey}`);
//console.log(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}${autokey}`, sessionData)
if (sessionData) { if (sessionData) {
await handleSessionData(decrypt(sessionData, bypassLogin)); await handleSessionData(decrypt(sessionData, bypassLogin));
} else { } else {
@ -879,7 +888,7 @@ export class GameData {
}); });
} }
loadSession(scene: BattleScene, slotId: integer, sessionData?: SessionSaveData): Promise<boolean> { loadSession(scene: BattleScene, slotId: integer, sessionData?: SessionSaveData, autoSlot?: integer): Promise<boolean> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const initSessionFromData = async (sessionData: SessionSaveData) => { const initSessionFromData = async (sessionData: SessionSaveData) => {
@ -979,7 +988,7 @@ export class GameData {
if (sessionData) { if (sessionData) {
initSessionFromData(sessionData); initSessionFromData(sessionData);
} else { } else {
this.getSession(slotId) this.getSession(slotId, autoSlot)
.then(data => initSessionFromData(data)) .then(data => initSessionFromData(data))
.catch(err => { .catch(err => {
reject(err); reject(err);
@ -1149,6 +1158,13 @@ export class GameData {
}) as SessionSaveData; }) as SessionSaveData;
} }
saveGameToAuto(scene: BattleScene) {
var autoSlot = LoggerTools.autoCheckpoints.indexOf(scene.currentBattle.waveIndex)
var dat = this.getSessionSaveData(scene)
console.log(`Stored autosave as sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}_auto${autoSlot}`)
localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}_auto${autoSlot}`, encrypt(JSON.stringify(dat), bypassLogin));
}
saveAll(scene: BattleScene, skipVerification: boolean = false, sync: boolean = false, useCachedSession: boolean = false, useCachedSystem: boolean = false): Promise<boolean> { saveAll(scene: BattleScene, skipVerification: boolean = false, sync: boolean = false, useCachedSession: boolean = false, useCachedSystem: boolean = false): Promise<boolean> {
return new Promise<boolean>(resolve => { return new Promise<boolean>(resolve => {
Utils.executeIf(!skipVerification, updateUserInfo).then(success => { Utils.executeIf(!skipVerification, updateUserInfo).then(success => {

View File

@ -43,7 +43,8 @@ const AUTO_DISABLED: SettingOption[] = [
export enum SettingType { export enum SettingType {
GENERAL, GENERAL,
DISPLAY, DISPLAY,
AUDIO AUDIO,
MOD
} }
type SettingOption = { type SettingOption = {
@ -98,7 +99,14 @@ export const SettingKeys = {
SE_Volume: "SE_VOLUME", SE_Volume: "SE_VOLUME",
Music_Preference: "MUSIC_PREFERENCE", Music_Preference: "MUSIC_PREFERENCE",
Show_BGM_Bar: "SHOW_BGM_BAR", Show_BGM_Bar: "SHOW_BGM_BAR",
Show_Pokemon_Teams: "SHOW_POKEMON_TEAMS" Show_Pokemon_Teams: "SHOW_POKEMON_TEAMS",
Damage_Display: "DAMAGE_DISPLAY",
LazyReloads: "FLAG_EVERY_RESET_AS_RELOAD",
FancyBiome: "FANCY_BIOMES",
ShowAutosaves: "SHOW_AUTOSAVES",
TitleScreenContinueMode: "TITLE_SCREEN_QUICKLOAD",
BiomePanels: "BIOME_PANELS",
DailyShinyLuck: "DAILY_LUCK"
}; };
/** /**
@ -145,6 +153,77 @@ export const Setting: Array<Setting> = [
default: 3, default: 3,
type: SettingType.GENERAL type: SettingType.GENERAL
}, },
{
key: SettingKeys.Damage_Display,
label: "Damage Display",
options: [{
label: "Off",
value: "Off"
}, {
label: "Value",
value: "Value"
}, {
label: "Percent",
value: "Percent"
}],
default: 0,
type: SettingType.GENERAL,
},
{
key: SettingKeys.LazyReloads,
label: "Lazy Reloads",
options: [{
label: "Off",
value: "Off"
}, {
label: "On",
value: "On"
}],
default: 0,
type: SettingType.GENERAL,
},
{
key: SettingKeys.FancyBiome,
label: "Fancy Title Screen",
options: [{
label: "Off",
value: "Off"
}, {
label: "On",
value: "On"
}],
default: 0,
type: SettingType.GENERAL,
},
{
key: SettingKeys.TitleScreenContinueMode,
label: "Quick Load",
options: [{
label: "Off",
value: "Off" // Shows "Continue" button on the home screen
}, {
label: "Daily",
value: "Daily" // Shows the last played Daily Run, or the last run if there are no Daily Runs
}, {
label: "Dailies",
value: "Dailies" // Shows all Daily Runs, or the last run if there are no Daily Runs
}, {
label: "Latest",
value: "Latest" // Shows the last run
}, {
label: "Both",
value: "Both" // Shows the last run and the last Daily Run, or only the last played game if it is a Daily Run
}],
default: 1,
type: SettingType.GENERAL,
},
{
key: SettingKeys.DailyShinyLuck,
label: "Daily Shiny Luck",
options: OFF_ON,
default: 0,
type: SettingType.GENERAL,
},
{ {
key: SettingKeys.HP_Bar_Speed, key: SettingKeys.HP_Bar_Speed,
label: i18next.t("settings:hpBarSpeed"), label: i18next.t("settings:hpBarSpeed"),
@ -518,6 +597,32 @@ export const Setting: Array<Setting> = [
type: SettingType.DISPLAY, type: SettingType.DISPLAY,
requireReload: true requireReload: true
}, },
{
key: SettingKeys.BiomePanels,
label: "Biome Panels",
options: [{
label: "Off",
value: "Off"
}, {
label: "On",
value: "On"
}],
default: 0,
type: SettingType.DISPLAY,
},
{
key: SettingKeys.ShowAutosaves,
label: "Show Autosaves",
options: [{
label: "Off",
value: "Off"
}, {
label: "On",
value: "On"
}],
default: 0,
type: SettingType.DISPLAY,
},
{ {
key: SettingKeys.Master_Volume, key: SettingKeys.Master_Volume,
label: i18next.t("settings:masterVolume"), label: i18next.t("settings:masterVolume"),
@ -625,6 +730,20 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
case SettingKeys.Enable_Retries: case SettingKeys.Enable_Retries:
scene.enableRetries = Setting[index].options[value].value === "On"; scene.enableRetries = Setting[index].options[value].value === "On";
break; break;
case SettingKeys.Damage_Display:
scene.damageDisplay = Setting[index].options[value].value
case SettingKeys.LazyReloads:
scene.lazyReloads = Setting[index].options[value].value == "On"
case SettingKeys.FancyBiome:
scene.menuChangesBiome = Setting[index].options[value].value == "On"
case SettingKeys.ShowAutosaves:
scene.showAutosaves = Setting[index].options[value].value == "On"
case SettingKeys.BiomePanels:
scene.doBiomePanels = Setting[index].options[value].value == "On"
case SettingKeys.DailyShinyLuck:
scene.disableDailyShinies = Setting[index].options[value].value == "Off"
case SettingKeys.TitleScreenContinueMode:
scene.quickloadDisplayMode = Setting[index].options[value].value;
case SettingKeys.Skip_Seen_Dialogues: case SettingKeys.Skip_Seen_Dialogues:
scene.skipSeenDialogues = Setting[index].options[value].value === "On"; scene.skipSeenDialogues = Setting[index].options[value].value === "On";
break; break;

View File

@ -29,7 +29,7 @@ describe("Abilities - COSTAR", () => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
vi.spyOn(Overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); vi.spyOn(Overrides, "DOUBLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
vi.spyOn(Overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.COSTAR); vi.spyOn(Overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.COSTAR);
vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.NASTY_PLOT, Moves.CURSE]); vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.NASTY_PLOT]);
vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); vi.spyOn(Overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]);
}); });
@ -37,15 +37,17 @@ describe("Abilities - COSTAR", () => {
test( test(
"ability copies positive stat changes", "ability copies positive stat changes",
async () => { async () => {
vi.spyOn(Overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.BALL_FETCH);
await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]); await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]);
let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); let [leftPokemon, rightPokemon] = game.scene.getPlayerField();
expect(leftPokemon).not.toBe(undefined); expect(leftPokemon).toBeDefined();
expect(rightPokemon).not.toBe(undefined); expect(rightPokemon).toBeDefined();
game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT));
await game.phaseInterceptor.to(CommandPhase); await game.phaseInterceptor.to(CommandPhase);
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); game.doAttack(getMovePosition(game.scene, 1, Moves.SPLASH));
await game.toNextTurn(); await game.toNextTurn();
expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2); expect(leftPokemon.summonData.battleStats[BattleStat.SPATK]).toBe(+2);
@ -71,8 +73,8 @@ describe("Abilities - COSTAR", () => {
await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]); await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.FLAMIGO]);
let [leftPokemon, rightPokemon] = game.scene.getPlayerField(); let [leftPokemon, rightPokemon] = game.scene.getPlayerField();
expect(leftPokemon).not.toBe(undefined); expect(leftPokemon).toBeDefined();
expect(rightPokemon).not.toBe(undefined); expect(rightPokemon).toBeDefined();
expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2);
expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2); expect(leftPokemon.summonData.battleStats[BattleStat.ATK]).toBe(-2);

View File

@ -0,0 +1,52 @@
import { GameMode, GameModes, getGameMode } from "#app/game-mode.js";
import {
afterEach,
beforeAll,
beforeEach,
describe,
expect,
it,
vi,
} from "vitest";
import GameManager from "./utils/gameManager";
import * as Utils from "../utils";
describe("game-mode", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
vi.resetAllMocks();
});
beforeEach(() => {
game = new GameManager(phaserGame);
});
describe("classic", () => {
let classicGameMode: GameMode;
beforeEach(() => {
classicGameMode = getGameMode(GameModes.CLASSIC);
});
it("does NOT spawn trainers within 3 waves of fixed battle", () => {
const { arena } = game.scene;
/** set wave 16 to be a fixed trainer fight meaning wave 13-19 don't allow trainer spawns */
vi.spyOn(classicGameMode, "isFixedBattle").mockImplementation(
(n: number) => (n === 16 ? true : false)
);
vi.spyOn(arena, "getTrainerChance").mockReturnValue(1);
vi.spyOn(Utils, "randSeedInt").mockReturnValue(0);
expect(classicGameMode.isWaveTrainer(11, arena)).toBeFalsy();
expect(classicGameMode.isWaveTrainer(12, arena)).toBeTruthy();
expect(classicGameMode.isWaveTrainer(13, arena)).toBeFalsy();
expect(classicGameMode.isWaveTrainer(14, arena)).toBeFalsy();
expect(classicGameMode.isWaveTrainer(15, arena)).toBeFalsy();
// Wave 16 is a fixed trainer battle
expect(classicGameMode.isWaveTrainer(17, arena)).toBeFalsy();
expect(classicGameMode.isWaveTrainer(18, arena)).toBeFalsy();
expect(classicGameMode.isWaveTrainer(19, arena)).toBeFalsy();
});
});
});

View File

@ -0,0 +1,192 @@
import { beforeAll, describe, beforeEach, afterEach, expect, it, vi } from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import * as overrides from "#app/overrides";
import { Species } from "#enums/species";
import { TerrainType, getTerrainName } from "#app/data/terrain";
import { getTerrainStartMessage, getTerrainClearMessage, getTerrainBlockMessage } from "#app/data/weather";
import i18next from "i18next";
import { mockI18next } from "../utils/testUtils";
describe("terrain", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
i18next.init();
});
beforeEach(() => {
game = new GameManager(phaserGame);
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
});
describe("NONE", () => {
const terrainType = TerrainType.NONE;
it("should return the obtain text", () => {
mockI18next();
const text = getTerrainName(terrainType);
expect(text).toBe("");
});
it("should return the start text", () => {
mockI18next();
const text = getTerrainStartMessage(terrainType);
expect(text).toBe(undefined);
});
it("should return the clear text", () => {
mockI18next();
const text = getTerrainClearMessage(terrainType);
expect(text).toBe(undefined);
});
it("should return the block text", async () => {
await game.startBattle([Species.MAGIKARP]);
const pokemon = game.scene.getPlayerPokemon();
mockI18next();
const text = getTerrainBlockMessage(pokemon, terrainType);
expect(text).toBe("terrain:defaultBlockMessage");
});
});
describe("MISTY", () => {
const terrainType = TerrainType.MISTY;
it("should return the obtain text", () => {
mockI18next();
const text = getTerrainName(terrainType);
expect(text).toBe("terrain:misty");
});
it("should return the start text", () => {
mockI18next();
const text = getTerrainStartMessage(terrainType);
expect(text).toBe("terrain:mistyStartMessage");
});
it("should return the clear text", () => {
mockI18next();
const text = getTerrainClearMessage(terrainType);
expect(text).toBe("terrain:mistyClearMessage");
});
it("should return the block text", async () => {
await game.startBattle([Species.MAGIKARP]);
const pokemon = game.scene.getPlayerPokemon();
mockI18next();
const text = getTerrainBlockMessage(pokemon, terrainType);
expect(text).toBe("terrain:mistyBlockMessage");
});
});
describe("ELECTRIC", () => {
const terrainType = TerrainType.ELECTRIC;
it("should return the obtain text", () => {
mockI18next();
const text = getTerrainName(terrainType);
expect(text).toBe("terrain:electric");
});
it("should return the start text", () => {
mockI18next();
const text = getTerrainStartMessage(terrainType);
expect(text).toBe("terrain:electricStartMessage");
});
it("should return the clear text", () => {
mockI18next();
const text = getTerrainClearMessage(terrainType);
expect(text).toBe("terrain:electricClearMessage");
});
it("should return the block text", async () => {
await game.startBattle([Species.MAGIKARP]);
const pokemon = game.scene.getPlayerPokemon();
mockI18next();
const text = getTerrainBlockMessage(pokemon, terrainType);
expect(text).toBe("terrain:defaultBlockMessage");
});
});
describe("GRASSY", () => {
const terrainType = TerrainType.GRASSY;
it("should return the obtain text", () => {
mockI18next();
const text = getTerrainName(terrainType);
expect(text).toBe("terrain:grassy");
});
it("should return the start text", () => {
mockI18next();
const text = getTerrainStartMessage(terrainType);
expect(text).toBe("terrain:grassyStartMessage");
});
it("should return the clear text", () => {
mockI18next();
const text = getTerrainClearMessage(terrainType);
expect(text).toBe("terrain:grassyClearMessage");
});
it("should return the block text", async () => {
await game.startBattle([Species.MAGIKARP]);
const pokemon = game.scene.getPlayerPokemon();
mockI18next();
const text = getTerrainBlockMessage(pokemon, terrainType);
expect(text).toBe("terrain:defaultBlockMessage");
});
});
describe("PSYCHIC", () => {
const terrainType = TerrainType.PSYCHIC;
it("should return the obtain text", () => {
mockI18next();
const text = getTerrainName(terrainType);
expect(text).toBe("terrain:psychic");
});
it("should return the start text", () => {
mockI18next();
const text = getTerrainStartMessage(terrainType);
expect(text).toBe("terrain:psychicStartMessage");
});
it("should return the clear text", () => {
mockI18next();
const text = getTerrainClearMessage(terrainType);
expect(text).toBe("terrain:psychicClearMessage");
});
it("should return the block text", async () => {
await game.startBattle([Species.MAGIKARP]);
const pokemon = game.scene.getPlayerPokemon();
mockI18next();
const text = getTerrainBlockMessage(pokemon, terrainType);
expect(text).toBe("terrain:defaultBlockMessage");
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
vi.resetAllMocks();
});
});

View File

@ -0,0 +1,137 @@
import {afterEach, beforeAll, beforeEach, describe, expect, test, vi} from "vitest";
import Phaser from "phaser";
import GameManager from "#app/test/utils/gameManager";
import * as overrides from "#app/overrides";
import {
MoveEffectPhase,
TurnEndPhase
} from "#app/phases";
import {getMovePosition} from "#app/test/utils/gameManagerUtils";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import { ArenaTagType } from "#app/enums/arena-tag-type";
import { allMoves } from "#app/data/move";
import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag";
import { Abilities } from "#app/enums/abilities";
const TIMEOUT = 20 * 1000;
describe("Moves - Ceaseless Edge", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true);
vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.RATTATA);
vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.RUN_AWAY);
vi.spyOn(overrides, "OPP_PASSIVE_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.RUN_AWAY);
vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100);
vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(100);
vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.CEASELESS_EDGE, Moves.SPLASH, Moves.ROAR ]);
vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH,Moves.SPLASH,Moves.SPLASH,Moves.SPLASH]);
vi.spyOn(allMoves[Moves.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100);
});
test(
"move should hit and apply spikes",
async () => {
await game.startBattle([ Species.ILLUMISE ]);
const leadPokemon = game.scene.getPlayerPokemon();
expect(leadPokemon).toBeDefined();
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon).toBeDefined();
const enemyStartingHp = enemyPokemon.hp;
game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE));
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Spikes should not have any layers before move effect is applied
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagBefore instanceof ArenaTrapTag).toBeFalsy();
await game.phaseInterceptor.to(TurnEndPhase);
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagAfter instanceof ArenaTrapTag).toBeTruthy();
expect(tagAfter.layers).toBe(1);
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
}, TIMEOUT
);
test(
"move should hit twice with multi lens and apply two layers of spikes",
async () => {
vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "MULTI_LENS"}]);
await game.startBattle([ Species.ILLUMISE ]);
const leadPokemon = game.scene.getPlayerPokemon();
expect(leadPokemon).toBeDefined();
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon).toBeDefined();
const enemyStartingHp = enemyPokemon.hp;
game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE));
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Spikes should not have any layers before move effect is applied
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagBefore instanceof ArenaTrapTag).toBeFalsy();
await game.phaseInterceptor.to(TurnEndPhase);
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagAfter instanceof ArenaTrapTag).toBeTruthy();
expect(tagAfter.layers).toBe(2);
expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp);
}, TIMEOUT
);
test(
"trainer - move should hit twice, apply two layers of spikes, force switch opponent - opponent takes damage",
async () => {
vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "MULTI_LENS"}]);
vi.spyOn(overrides, "STARTING_WAVE_OVERRIDE", "get").mockReturnValue(5);
await game.startBattle([ Species.ILLUMISE ]);
const leadPokemon = game.scene.getPlayerPokemon();
expect(leadPokemon).toBeDefined();
const enemyPokemon = game.scene.getEnemyPokemon();
expect(enemyPokemon).toBeDefined();
game.doAttack(getMovePosition(game.scene, 0, Moves.CEASELESS_EDGE));
await game.phaseInterceptor.to(MoveEffectPhase, false);
// Spikes should not have any layers before move effect is applied
const tagBefore = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagBefore instanceof ArenaTrapTag).toBeFalsy();
await game.phaseInterceptor.to(TurnEndPhase, false);
const tagAfter = game.scene.arena.getTagOnSide(ArenaTagType.SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag;
expect(tagAfter instanceof ArenaTrapTag).toBeTruthy();
expect(tagAfter.layers).toBe(2);
const hpBeforeSpikes = game.scene.currentBattle.enemyParty[1].hp;
// Check HP of pokemon that WILL BE switched in (index 1)
game.forceOpponentToSwitch();
game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH));
await game.phaseInterceptor.to(TurnEndPhase, false);
expect(game.scene.currentBattle.enemyParty[0].hp).toBeLessThan(hpBeforeSpikes);
}, TIMEOUT
);
});

View File

@ -6,6 +6,8 @@ import { addWindow } from "./ui-theme";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { argbFromRgba } from "@material/material-color-utilities"; import { argbFromRgba } from "@material/material-color-utilities";
import {Button} from "#enums/buttons"; import {Button} from "#enums/buttons";
import { Biome } from "#app/enums/biome.js";
import { getBiomeKey } from "#app/field/arena.js";
export interface OptionSelectConfig { export interface OptionSelectConfig {
xOffset?: number; xOffset?: number;

View File

@ -9,6 +9,10 @@ import { BattleSceneEventType, TurnEndEvent } from "../events/battle-scene";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
import TimeOfDayWidget from "./time-of-day-widget"; import TimeOfDayWidget from "./time-of-day-widget";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { getNatureDecrease, getNatureIncrease, getNatureName } from "#app/data/nature.js";
import * as LoggerTools from "../logger"
import { BattleEndPhase } from "#app/phases.js";
import { Gender } from "#app/data/gender.js";
/** Enum used to differentiate {@linkcode Arena} effects */ /** Enum used to differentiate {@linkcode Arena} effects */
enum ArenaEffectType { enum ArenaEffectType {
@ -191,14 +195,45 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container {
this.flyoutTextPlayer.text = ""; this.flyoutTextPlayer.text = "";
this.flyoutTextField.text = ""; this.flyoutTextField.text = "";
this.flyoutTextEnemy.text = ""; this.flyoutTextEnemy.text = "";
this.flyoutTextPlayer.setPosition(6, 13)
this.flyoutTextPlayer.setFontSize(48);
}
public printIVs() {
this.clearText()
var poke = (this.scene as BattleScene).getEnemyField()
this.flyoutTextPlayer.text = ""
this.flyoutTextField.text = ""
this.flyoutTextEnemy.text = ""
this.flyoutTextHeaderField.text = "Stats"
this.flyoutTextHeaderPlayer.text = ""
this.flyoutTextHeaderEnemy.text = ""
this.flyoutTextHeader.text = "IVs"
for (var i = 0; i < poke.length; i++) {
if (i == 1 || true) {
this.flyoutTextPlayer.text += poke[i].name + " " + (poke[i].gender == Gender.MALE ? "♂" : (poke[i].gender == Gender.FEMALE ? "♀" : "-")) + " " + poke[i].level + "\n"
this.flyoutTextEnemy.text += poke[i].getAbility().name + " / " + (poke[i].isBoss() ? poke[i].getPassiveAbility().name + " / " : "") + getNatureName(poke[i].nature) + (getNatureIncrease(poke[i].nature) != "" ? " (+" + getNatureIncrease(poke[i].nature) + " -" + getNatureDecrease(poke[i].nature) + ")" : "") + "\n\n\n"
}
this.flyoutTextPlayer.text += "HP: " + poke[i].ivs[0]
this.flyoutTextPlayer.text += ", Atk: " + poke[i].ivs[1]
this.flyoutTextPlayer.text += ", Def: " + poke[i].ivs[2]
this.flyoutTextPlayer.text += ", Sp.A: " + poke[i].ivs[3]
this.flyoutTextPlayer.text += ", Sp.D: " + poke[i].ivs[4]
this.flyoutTextPlayer.text += ", Speed: " + poke[i].ivs[5] + "\n\n"
}
} }
/** Parses through all set Arena Effects and puts them into the proper {@linkcode Phaser.GameObjects.Text} object */ /** Parses through all set Arena Effects and puts them into the proper {@linkcode Phaser.GameObjects.Text} object */
private updateFieldText() { public updateFieldText() {
this.clearText(); this.clearText();
this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration); this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration);
this.flyoutTextHeaderPlayer.text = "Player"
this.flyoutTextHeaderField.text = "Neutral"
this.flyoutTextHeaderEnemy.text = "Enemy"
this.flyoutTextHeader.text = "Active Battle Effects"
for (let i = 0; i < this.fieldEffectInfo.length; i++) { for (let i = 0; i < this.fieldEffectInfo.length; i++) {
const fieldEffectInfo = this.fieldEffectInfo[i]; const fieldEffectInfo = this.fieldEffectInfo[i];
@ -232,6 +267,37 @@ export default class ArenaFlyout extends Phaser.GameObjects.Container {
textObject.text += "\n"; textObject.text += "\n";
} }
this.flyoutTextPlayer.text = ""
this.flyoutTextField.text = ""
this.flyoutTextEnemy.text = ""
this.flyoutTextHeaderField.text = ""
this.flyoutTextHeaderPlayer.text = ""
this.flyoutTextHeaderEnemy.text = ""
this.flyoutTextHeader.text = "Game Logs"
this.flyoutTextPlayer.setPosition(6, 4)
this.flyoutTextPlayer.setFontSize(30);
var instructions = []
var drpd = LoggerTools.getDRPD(this.scene as BattleScene);
var doWaveInstructions = true;
for (var i = 0; i < drpd.waves.length && drpd.waves[i] != undefined && doWaveInstructions; i++) {
if (drpd.waves[i].id > (this.scene as BattleScene).currentBattle.waveIndex) {
doWaveInstructions = false;
} else {
instructions.push("")
instructions.push("Wave " + drpd.waves[i].id)
for (var j = 0; j < drpd.waves[i].actions.length; j++) {
instructions.push("- " + drpd.waves[i].actions[j])
}
if (drpd.waves[i].shop != "")
instructions.push("Reward: " + drpd.waves[i].shop)
}
}
for (var i = instructions.length - 10; i < instructions.length; i++) {
if (i >= 0) {
this.flyoutTextPlayer.text += instructions[i]
}
this.flyoutTextPlayer.text += "\n"
}
} }
/** /**

View File

@ -51,7 +51,7 @@ export default class BattleFlyout extends Phaser.GameObjects.Container {
private flyoutContainer: Phaser.GameObjects.Container; private flyoutContainer: Phaser.GameObjects.Container;
/** The array of {@linkcode Phaser.GameObjects.Text} objects which are drawn on the flyout */ /** The array of {@linkcode Phaser.GameObjects.Text} objects which are drawn on the flyout */
private flyoutText: Phaser.GameObjects.Text[] = new Array(4); public flyoutText: Phaser.GameObjects.Text[] = new Array(4);
/** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */ /** The array of {@linkcode MoveInfo} used to track moves for the {@linkcode Pokemon} linked to the flyout */
private moveInfo: MoveInfo[] = new Array(); private moveInfo: MoveInfo[] = new Array();

View File

@ -11,6 +11,7 @@ import { BattleStat } from "#app/data/battle-stat";
import BattleFlyout from "./battle-flyout"; import BattleFlyout from "./battle-flyout";
import { WindowVariant, addWindow } from "./ui-theme"; import { WindowVariant, addWindow } from "./ui-theme";
import i18next from "i18next"; import i18next from "i18next";
import { calcDamage } from "./fight-ui-handler";
const battleStatOrder = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD ]; const battleStatOrder = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.ACC, BattleStat.EVA, BattleStat.SPD ];
@ -472,6 +473,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
ease: "Sine.easeInOut", ease: "Sine.easeInOut",
alpha: visible ? 1 : 0 alpha: visible ? 1 : 0
}); });
if (visible) {
(this.scene as BattleScene).arenaFlyout.printIVs()
} else {
(this.scene as BattleScene).arenaFlyout.updateFieldText()
}
} }
updateBossSegments(pokemon: EnemyPokemon): void { updateBossSegments(pokemon: EnemyPokemon): void {
@ -921,7 +927,8 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
if (visible) { if (visible) {
this.effectivenessContainer?.setVisible(false); this.effectivenessContainer?.setVisible(false);
} else { } else {
this.updateEffectiveness(this.currentEffectiveness); //this.updateEffectiveness(this.currentEffectiveness);
this.effectivenessContainer?.setVisible(true);
} }
if (!this.override) this.switchIconVisibility(visible); if (!this.override) this.switchIconVisibility(visible);
// this.teamIconOver[ballindex].setAlpha(0.4, 0.4, 0.7, 0.7) // this.teamIconOver[ballindex].setAlpha(0.4, 0.4, 0.7, 0.7)

View File

@ -103,7 +103,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
let pokemonIconX = -20; let pokemonIconX = -20;
let pokemonIconY = 6; let pokemonIconY = 6;
if (["de", "es", "fr", "pt-BR"].includes(currentLanguage)) { if (["de", "es", "fr", "ko", "pt-BR"].includes(currentLanguage)) {
gachaTextStyle = TextStyle.SMALLER_WINDOW_ALT; gachaTextStyle = TextStyle.SMALLER_WINDOW_ALT;
gachaX = 2; gachaX = 2;
gachaY = 2; gachaY = 2;
@ -155,7 +155,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
gachaUpLabel.setOrigin(0.5, 0); gachaUpLabel.setOrigin(0.5, 0);
break; break;
case GachaType.SHINY: case GachaType.SHINY:
if (["de", "fr"].includes(currentLanguage)) { if (["de", "fr", "ko"].includes(currentLanguage)) {
gachaUpLabel.setAlign("center"); gachaUpLabel.setAlign("center");
gachaUpLabel.setY(0); gachaUpLabel.setY(0);
} }

View File

@ -5,11 +5,27 @@ import { Command } from "./command-ui-handler";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { CommandPhase } from "../phases"; import { CommandPhase, MoveEffectPhase } from "../phases";
import { MoveCategory } from "#app/data/move.js"; import Move, * as MoveData from "../data/move";
import i18next from "i18next"; import i18next from "i18next";
import {Button} from "#enums/buttons"; import {Button} from "#enums/buttons";
import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; import Pokemon, { DamageResult, EnemyPokemon, HitResult, PlayerPokemon, PokemonMove } from "#app/field/pokemon.js";
import Battle from "#app/battle.js";
import { Stat } from "#app/data/pokemon-stat.js";
import { Abilities } from "#app/enums/abilities.js";
import { WeatherType } from "#app/data/weather.js";
import { Moves } from "#app/enums/moves.js";
import { AddSecondStrikeAbAttr, AllyMoveCategoryPowerBoostAbAttr, AlwaysHitAbAttr, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreDefendAbAttrsNoApply, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, ConditionalCritAbAttr, DamageBoostAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentEvasionAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, MultCritAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, TypeImmunityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "#app/data/ability.js";
import { ArenaTagType } from "#app/enums/arena-tag-type.js";
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from "#app/data/arena-tag.js";
import { BattlerTagLapseType, HelpingHandTag, SemiInvulnerableTag, TypeBoostTag } from "#app/data/battler-tags.js";
import { TerrainType } from "#app/data/terrain.js";
import { AttackTypeBoosterModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, TempBattleStatBoosterModifier } from "#app/modifier/modifier.js";
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
import { TempBattleStat } from "#app/data/temp-battle-stat.js";
import { StatusEffect } from "#app/data/status-effect.js";
import { BattleStat } from "#app/data/battle-stat.js";
import { PokemonMultiHitModifierType } from "#app/modifier/modifier-type.js";
export default class FightUiHandler extends UiHandler { export default class FightUiHandler extends UiHandler {
private movesContainer: Phaser.GameObjects.Container; private movesContainer: Phaser.GameObjects.Container;
@ -153,6 +169,535 @@ export default class FightUiHandler extends UiHandler {
return !this.fieldIndex ? this.cursor : this.cursor2; return !this.fieldIndex ? this.cursor : this.cursor2;
} }
simulateAttack(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move) {
let result: HitResult;
const damage1 = new Utils.NumberHolder(0);
const damage2 = new Utils.NumberHolder(0);
const defendingSidePlayField = target.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField();
const variableCategory = new Utils.IntegerHolder(move.category);
MoveData.applyMoveAttrs(MoveData.VariableMoveCategoryAttr, user, target, move, variableCategory);
const moveCategory = variableCategory.value as MoveData.MoveCategory;
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
MoveData.applyMoveAttrs(MoveData.VariableMoveTypeAttr, user, target, move);
applyPreAttackAbAttrs(MoveTypeChangeAttr, user, target, move, typeChangeMovePowerMultiplier);
const types = target.getTypes(true, true);
const cancelled = new Utils.BooleanHolder(false);
const typeless = move.hasAttr(MoveData.TypelessAttr);
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveData.MoveCategory.STATUS || move.getAttrs(MoveData.StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
? target.getAttackTypeEffectiveness(move.type, user, false, false)
: 1);
MoveData.applyMoveAttrs(MoveData.VariableMoveTypeMultiplierAttr, user, target, move, typeMultiplier);
if (typeless) {
typeMultiplier.value = 1;
}
if (types.find(t => move.isTypeImmune(user, target, t))) {
typeMultiplier.value = 0;
}
// Apply arena tags for conditional protection
if (!move.checkFlag(MoveData.MoveFlags.IGNORE_PROTECT, user, target) && !move.isAllyTarget()) {
const defendingSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
this.scene.arena.applyTagsForSide(ArenaTagType.QUICK_GUARD, defendingSide, cancelled, this, move.priority);
this.scene.arena.applyTagsForSide(ArenaTagType.WIDE_GUARD, defendingSide, cancelled, this, move.moveTarget);
this.scene.arena.applyTagsForSide(ArenaTagType.MAT_BLOCK, defendingSide, cancelled, this, move.category);
this.scene.arena.applyTagsForSide(ArenaTagType.CRAFTY_SHIELD, defendingSide, cancelled, this, move.category, move.moveTarget);
}
switch (moveCategory) {
case MoveData.MoveCategory.PHYSICAL:
case MoveData.MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveData.MoveCategory.PHYSICAL;
const power = new Utils.NumberHolder(move.power);
const sourceTeraType = user.getTeraType();
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MoveData.MultiHitAttr) && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === user.id)) {
power.value = 60;
}
applyPreAttackAbAttrs(VariableMovePowerAbAttr, user, target, move, power);
if (user.getAlly()?.hasAbilityWithAttr(AllyMoveCategoryPowerBoostAbAttr)) {
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, user, target, move, power);
}
const fieldAuras = new Set(
this.scene.getField(true)
.map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr) as FieldMoveTypePowerBoostAbAttr[])
.flat(),
);
for (const aura of fieldAuras) {
// The only relevant values are `move` and the `power` holder
aura.applyPreAttack(null, null, null, move, [power]);
}
const alliedField: Pokemon[] = user instanceof PlayerPokemon ? this.scene.getPlayerField() : this.scene.getEnemyField();
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, user, move, power));
power.value *= typeChangeMovePowerMultiplier.value;
if (!typeless) {
if (target.hasAbilityWithAttr(TypeImmunityAbAttr)) {
//
}
applyPreDefendAbAttrsNoApply(TypeImmunityAbAttr, user, target, move, cancelled, typeMultiplier);
MoveData.applyMoveAttrs(MoveData.NeutralDamageAgainstFlyingTypeMultiplierAttr, user, target, move, typeMultiplier);
}
if (!cancelled.value) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, user, target, move, cancelled, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, user, move, cancelled, typeMultiplier));
}
if (cancelled.value) {
//user.stopMultiHit(target);
result = HitResult.NO_EFFECT;
} else {
const typeBoost = user.findTag(t => t instanceof TypeBoostTag && t.boostedType === move.type) as TypeBoostTag;
if (typeBoost) {
power.value *= typeBoost.boostValue;
if (typeBoost.oneUse) {
//user.removeTag(typeBoost.tagType);
}
}
const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(move.type, user.isGrounded()));
MoveData.applyMoveAttrs(MoveData.IgnoreWeatherTypeDebuffAttr, user, target, move, arenaAttackTypeMultiplier);
if (this.scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() && move.type === Type.GROUND && move.moveTarget === MoveData.MoveTarget.ALL_NEAR_OTHERS) {
power.value /= 2;
}
MoveData.applyMoveAttrs(MoveData.VariablePowerAttr, user, target, move, power);
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, new Utils.IntegerHolder(0), power);
if (!typeless) {
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
this.scene.applyModifiers(AttackTypeBoosterModifier, user.isPlayer(), user, move.type, power);
}
if (user.getTag(HelpingHandTag)) {
power.value *= 1.5;
}
let isCritical: boolean = true;
const critOnly = new Utils.BooleanHolder(false);
const critAlways = user.getTag(BattlerTagType.ALWAYS_CRIT);
MoveData.applyMoveAttrs(MoveData.CritOnlyAttr, user, target, move, critOnly);
applyAbAttrs(ConditionalCritAbAttr, user, null, critOnly, target, move);
if (isCritical) {
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, target, null, blockCrit);
if (blockCrit.value) {
isCritical = false;
}
}
const sourceAtk = new Utils.IntegerHolder(user.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, target, null, false));
const targetDef = new Utils.IntegerHolder(target.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, user, move, false));
const sourceAtkCrit = new Utils.IntegerHolder(user.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, target, null, isCritical));
const targetDefCrit = new Utils.IntegerHolder(target.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, user, move, isCritical));
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
applyAbAttrs(MultCritAbAttr, user, null, criticalMultiplier);
const screenMultiplier = new Utils.NumberHolder(1);
if (!isCritical) {
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier);
}
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0;
const sourceTypes = user.getTypes();
const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type);
const stabMultiplier = new Utils.NumberHolder(1);
if (sourceTeraType === Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value += 0.5;
} else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type) {
stabMultiplier.value += 0.5;
}
applyAbAttrs(StabBoostAbAttr, user, null, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
}
MoveData.applyMoveAttrs(MoveData.VariableAtkAttr, user, target, move, sourceAtk);
MoveData.applyMoveAttrs(MoveData.VariableDefAttr, user, target, move, targetDef);
MoveData.applyMoveAttrs(MoveData.VariableAtkAttr, user, target, move, sourceAtkCrit);
MoveData.applyMoveAttrs(MoveData.VariableDefAttr, user, target, move, targetDefCrit);
const effectPhase = this.scene.getCurrentPhase();
let numTargets = 1;
if (effectPhase instanceof MoveEffectPhase) {
numTargets = effectPhase.getTargets().length;
}
const twoStrikeMultiplier = new Utils.NumberHolder(1);
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, target, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
if (!isTypeImmune) {
damage1.value = Math.ceil(((((2 * user.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * twoStrikeMultiplier.value * 0.85); // low roll
damage2.value = Math.ceil(((((2 * user.level / 5 + 2) * power.value * sourceAtkCrit.value / targetDefCrit.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * twoStrikeMultiplier.value * criticalMultiplier.value); // high roll crit
if (isPhysical && user.status && user.status.effect === StatusEffect.BURN) {
if (!move.hasAttr(MoveData.BypassBurnDamageReductionAttr)) {
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BypassBurnDamageReductionAbAttr, user, burnDamageReductionCancelled);
if (!burnDamageReductionCancelled.value) {
damage1.value = Math.floor(damage1.value / 2);
damage2.value = Math.floor(damage2.value / 2);
}
}
}
applyPreAttackAbAttrs(DamageBoostAbAttr, user, target, move, damage1);
applyPreAttackAbAttrs(DamageBoostAbAttr, user, target, move, damage2);
/**
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
* The target has a {@link BattlerTagType} that this move interacts with
* AND
* The move doubles damage when used against that tag
*/
move.getAttrs(MoveData.HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
if (target.getTag(hta.tagType)) {
damage1.value *= 2;
damage2.value *= 2;
}
});
}
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && target.isGrounded() && move.type === Type.DRAGON) {
damage1.value = Math.floor(damage1.value / 2);
damage2.value = Math.floor(damage2.value / 2);
}
const fixedDamage = new Utils.IntegerHolder(0);
MoveData.applyMoveAttrs(MoveData.FixedDamageAttr, user, target, move, fixedDamage);
if (!isTypeImmune && fixedDamage.value) {
damage1.value = fixedDamage.value;
damage2.value = fixedDamage.value;
isCritical = false;
result = HitResult.EFFECTIVE;
}
if (!result) {
if (!typeMultiplier.value) {
result = move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT;
} else {
const oneHitKo = new Utils.BooleanHolder(false);
MoveData.applyMoveAttrs(MoveData.OneHitKOAttr, user, target, move, oneHitKo);
if (oneHitKo.value) {
result = HitResult.ONE_HIT_KO;
isCritical = false;
damage1.value = target.hp;
damage2.value = target.hp;
} else if (typeMultiplier.value >= 2) {
result = HitResult.SUPER_EFFECTIVE;
} else if (typeMultiplier.value >= 1) {
result = HitResult.EFFECTIVE;
} else {
result = HitResult.NOT_VERY_EFFECTIVE;
}
}
}
if (!fixedDamage.value) {
if (!user.isPlayer()) {
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage1);
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage2);
}
if (!target.isPlayer()) {
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage1);
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage2);
}
}
MoveData.applyMoveAttrs(MoveData.ModifiedDamageAttr, user, target, move, damage1);
MoveData.applyMoveAttrs(MoveData.ModifiedDamageAttr, user, target, move, damage2);
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage1);
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage2);
//console.log("damage (min)", damage1.value, move.name, power.value, sourceAtk, targetDef);
//console.log("damage (max)", damage2.value, move.name, power.value, sourceAtkCrit, targetDefCrit);
// In case of fatal damage, this tag would have gotten cleared before we could lapse it.
const destinyTag = target.getTag(BattlerTagType.DESTINY_BOND);
const oneHitKo = result === HitResult.ONE_HIT_KO;
if (damage1.value) {
if (target.getHpRatio() === 1) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, target, user, move, cancelled, damage1);
}
}
if (damage2.value) {
if (target.getHpRatio() === 1) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, target, user, move, cancelled, damage2);
}
}
}
break;
case MoveData.MoveCategory.STATUS:
if (!typeless) {
applyPreDefendAbAttrsNoApply(TypeImmunityAbAttr, target, user, move, cancelled, typeMultiplier);
}
if (!cancelled.value) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, target, user, move, cancelled, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, user, move, cancelled, typeMultiplier));
}
if (!typeMultiplier.value) {
return -1
}
result = cancelled.value || !typeMultiplier.value ? HitResult.NO_EFFECT : HitResult.STATUS;
break;
}
return [damage1.value, damage2.value]
}
calculateAccuracy(user: Pokemon, target: Pokemon, move: PokemonMove) {
if (this.scene.currentBattle.double && false) {
switch (move.getMove().moveTarget) {
case MoveData.MoveTarget.USER: // Targets yourself
return -1; // Moves targeting yourself always hit
case MoveData.MoveTarget.OTHER: // Targets one Pokemon
return move.getMove().accuracy
case MoveData.MoveTarget.ALL_OTHERS: // Targets all Pokemon
return move.getMove().accuracy;
case MoveData.MoveTarget.NEAR_OTHER: // Targets a Pokemon adjacent to the user
return move.getMove().accuracy;
case MoveData.MoveTarget.ALL_NEAR_OTHERS: // Targets all Pokemon adjacent to the user
return move.getMove().accuracy;
case MoveData.MoveTarget.NEAR_ENEMY: // Targets an opponent adjacent to the user
return move.getMove().accuracy;
case MoveData.MoveTarget.ALL_NEAR_ENEMIES: // Targets all opponents adjacent to the user
return move.getMove().accuracy;
case MoveData.MoveTarget.RANDOM_NEAR_ENEMY: // Targets a random opponent adjacent to the user
return move.getMove().accuracy;
case MoveData.MoveTarget.ALL_ENEMIES: // Targets all opponents
return move.getMove().accuracy;
case MoveData.MoveTarget.ATTACKER: // Counter move
return move.getMove().accuracy;
case MoveData.MoveTarget.NEAR_ALLY: // Targets an adjacent ally
return move.getMove().accuracy;
case MoveData.MoveTarget.ALLY: // Targets an ally
return move.getMove().accuracy;
case MoveData.MoveTarget.USER_OR_NEAR_ALLY: // Targets an ally or yourself
return move.getMove().accuracy;
case MoveData.MoveTarget.USER_AND_ALLIES: // Targets all on your side
return move.getMove().accuracy;
case MoveData.MoveTarget.ALL: // Targets everyone
return move.getMove().accuracy;
case MoveData.MoveTarget.USER_SIDE: // Targets your field
return move.getMove().accuracy;
case MoveData.MoveTarget.ENEMY_SIDE: // Targets enemy field
return -1; // Moves placing entry hazards always hit
case MoveData.MoveTarget.BOTH_SIDES: // Targets the entire field
return move.getMove().accuracy;
case MoveData.MoveTarget.PARTY: // Targets all of the Player's Pokemon, including ones that aren't active
return move.getMove().accuracy;
case MoveData.MoveTarget.CURSE:
return move.getMove().accuracy;
}
}
// Moves targeting the user and entry hazards can't miss
if ([MoveData.MoveTarget.USER, MoveData.MoveTarget.ENEMY_SIDE].includes(move.getMove().moveTarget)) {
return -1;
}
if (target == undefined) return move.getMove().accuracy;
// If either Pokemon has No Guard,
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr)) {
return -1;
}
// If the user should ignore accuracy on a target, check who the user targeted last turn and see if they match
if (user.getTag(BattlerTagType.IGNORE_ACCURACY) && (user.getLastXMoves().slice(1).find(() => true)?.targets || []).indexOf(target.getBattlerIndex()) !== -1) {
return -1;
}
const hiddenTag = target.getTag(SemiInvulnerableTag);
if (hiddenTag && !move.getMove().getAttrs(MoveData.HitsTagAttr).some(hta => hta.tagType === hiddenTag.tagType)) {
return 0;
}
const moveAccuracy = new Utils.NumberHolder(move.getMove().accuracy);
MoveData.applyMoveAttrs(MoveData.VariableAccuracyAttr, user, target, move.getMove(), moveAccuracy);
applyPreDefendAbAttrs(WonderSkinAbAttr, target, user, move.getMove(), { value: false }, moveAccuracy);
if (moveAccuracy.value === -1) {
return -1;
}
const isOhko = move.getMove().hasAttr(MoveData.OneHitKOAccuracyAttr);
if (!isOhko) {
user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
}
if (this.scene.arena.weather?.weatherType === WeatherType.FOG) {
moveAccuracy.value = Math.floor(moveAccuracy.value * 0.9);
}
if (!isOhko && this.scene.arena.getTag(ArenaTagType.GRAVITY)) {
moveAccuracy.value = Math.floor(moveAccuracy.value * 1.67);
}
const userAccuracyLevel = new Utils.IntegerHolder(user.summonData.battleStats[BattleStat.ACC]);
const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, target, null, userAccuracyLevel);
applyAbAttrs(IgnoreOpponentStatChangesAbAttr, user, null, targetEvasionLevel);
applyAbAttrs(IgnoreOpponentEvasionAbAttr, user, null, targetEvasionLevel);
MoveData.applyMoveAttrs(MoveData.IgnoreOpponentStatChangesAttr, user, target, move.getMove(), targetEvasionLevel);
this.scene.applyModifiers(TempBattleStatBoosterModifier, user.isPlayer(), TempBattleStat.ACC, userAccuracyLevel);
const accuracyMultiplier = new Utils.NumberHolder(1);
if (userAccuracyLevel.value !== targetEvasionLevel.value) {
accuracyMultiplier.value = userAccuracyLevel.value > targetEvasionLevel.value
? (3 + Math.min(userAccuracyLevel.value - targetEvasionLevel.value, 6)) / 3
: 3 / (3 + Math.min(targetEvasionLevel.value - userAccuracyLevel.value, 6));
}
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, user, BattleStat.ACC, accuracyMultiplier, move.getMove());
const evasionMultiplier = new Utils.NumberHolder(1);
applyBattleStatMultiplierAbAttrs(BattleStatMultiplierAbAttr, target, BattleStat.EVA, evasionMultiplier);
accuracyMultiplier.value /= evasionMultiplier.value;
return moveAccuracy.value * accuracyMultiplier.value
}
calcDamage(scene: BattleScene, user: PlayerPokemon, target: Pokemon, move: PokemonMove) {
/*
var power = move.getMove().power
var myAtk = 0
var theirDef = 0
var myAtkC = 0
var theirDefC = 0
switch (move.getMove().category) {
case MoveData.MoveCategory.PHYSICAL:
myAtk = user.getBattleStat(Stat.ATK, target, move.getMove())
myAtkC = user.getBattleStat(Stat.ATK, target, move.getMove(), true)
theirDef = target.getBattleStat(Stat.DEF, user, move.getMove())
theirDefC = target.getBattleStat(Stat.DEF, user, move.getMove(), true)
break;
case MoveData.MoveCategory.SPECIAL:
myAtk = user.getBattleStat(Stat.SPATK, target, move.getMove())
myAtkC = user.getBattleStat(Stat.SPATK, target, move.getMove(), true)
theirDef = target.getBattleStat(Stat.SPDEF, user, move.getMove())
theirDefC = target.getBattleStat(Stat.SPDEF, user, move.getMove(), true)
break;
case MoveData.MoveCategory.STATUS:
return "---"
}
var stabBonus = 1
var types = user.getTypes()
// Apply STAB bonus
for (var i = 0; i < types.length; i++) {
if (types[i] == move.getMove().type) {
stabBonus = 1.5
}
}
// Apply Tera Type bonus
if (stabBonus == 1.5) {
// STAB
if (move.getMove().type == user.getTeraType()) {
stabBonus = 2
}
} else if (move.getMove().type == user.getTeraType()) {
stabBonus = 1.5
}
// Apply adaptability
if (stabBonus == 2) {
// Tera-STAB
if (move.getMove().type == user.getTeraType()) {
stabBonus = 2.25
}
} else if (stabBonus == 1.5) {
// STAB or Tera
if (move.getMove().type == user.getTeraType()) {
stabBonus = 2
}
} else if (move.getMove().type == user.getTeraType()) {
// Adaptability
stabBonus = 1.5
}
var weatherBonus = 1
if (this.scene.arena.weather.weatherType == WeatherType.RAIN || this.scene.arena.weather.weatherType == WeatherType.HEAVY_RAIN) {
if (move.getMove().type == Type.WATER) {
weatherBonus = 1.5
}
if (move.getMove().type == Type.FIRE) {
weatherBonus = this.scene.arena.weather.weatherType == WeatherType.HEAVY_RAIN ? 0 : 0.5
}
}
if (this.scene.arena.weather.weatherType == WeatherType.SUNNY || this.scene.arena.weather.weatherType == WeatherType.HARSH_SUN) {
if (move.getMove().type == Type.FIRE) {
weatherBonus = 1.5
}
if (move.getMove().type == Type.WATER) {
weatherBonus = this.scene.arena.weather.weatherType == WeatherType.HARSH_SUN ? 0 : (move.moveId == Moves.HYDRO_STEAM ? 1.5 : 0.5)
}
}
var typeBonus = target.getAttackMoveEffectiveness(user, move)
var modifiers = stabBonus * weatherBonus
*/
var dmgHigh = 0
var dmgLow = 0
// dmgLow = (((2*user.level/5 + 2) * power * myAtk / theirDef)/50 + 2) * 0.85 * modifiers
// dmgHigh = (((2*user.level/5 + 2) * power * myAtkC / theirDefC)/50 + 2) * 1.5 * modifiers
var out = this.simulateAttack(scene, user, target, move.getMove())
var minHits = 1
var maxHits = 1
var mh = move.getMove().getAttrs(MoveData.MultiHitAttr)
for (var i = 0; i < mh.length; i++) {
var mh2 = mh[i] as MoveData.MultiHitAttr
switch (mh2.multiHitType) {
case MoveData.MultiHitType._2:
minHits = 2;
maxHits = 2;
case MoveData.MultiHitType._2_TO_5:
minHits = 2;
maxHits = 5;
case MoveData.MultiHitType._3:
minHits = 3;
maxHits = 3;
case MoveData.MultiHitType._10:
minHits = 10;
maxHits = 10;
case MoveData.MultiHitType.BEAT_UP:
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
// No status means the ally pokemon can contribute to Beat Up
minHits = party.reduce((total, pokemon) => {
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
}, 0);
maxHits = minHits
}
}
var h = user.getHeldItems()
for (var i = 0; i < h.length; i++) {
if (h[i].type instanceof PokemonMultiHitModifierType) {
minHits += h[i].getStackCount()
maxHits += h[i].getStackCount()
}
}
dmgLow = out[0] * minHits
dmgHigh = out[1] * maxHits
/*
if (user.hasAbility(Abilities.PARENTAL_BOND)) {
// Second hit deals 0.25x damage
dmgLow *= 1.25
dmgHigh *= 1.25
}
*/
var koText = ""
if (Math.floor(dmgLow) >= target.hp) {
koText = " (KO)"
} else if (Math.ceil(dmgHigh) >= target.hp) {
var percentChance = 1 - ((target.hp - dmgLow + 1) / (dmgHigh - dmgLow + 1))
koText = " (" + Math.round(percentChance * 100) + "% KO)"
}
if (target.getMoveEffectiveness(user, move) == undefined) {
return "---"
}
if (scene.damageDisplay == "Value")
return target.getMoveEffectiveness(user, move) + "x - " + (Math.round(dmgLow) == Math.round(dmgHigh) ? Math.round(dmgLow).toString() : Math.round(dmgLow) + "-" + Math.round(dmgHigh)) + koText
dmgLow = Math.round((dmgLow)/target.getBattleStat(Stat.HP)*100)
dmgHigh = Math.round((dmgHigh)/target.getBattleStat(Stat.HP)*100)
if (scene.damageDisplay == "Percent")
return target.getMoveEffectiveness(user, move) + "x - " + (dmgLow == dmgHigh ? dmgLow + "%" : dmgLow + "%-" + dmgHigh + "%") + koText
if (scene.damageDisplay == "Off")
return target.getMoveEffectiveness(user, move) + "x"
}
setCursor(cursor: integer): boolean { setCursor(cursor: integer): boolean {
const ui = this.getUi(); const ui = this.getUi();
@ -178,16 +723,22 @@ export default class FightUiHandler extends UiHandler {
if (hasMove) { if (hasMove) {
const pokemonMove = moveset[cursor]; const pokemonMove = moveset[cursor];
this.typeIcon.setTexture(`types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[pokemonMove.getMove().type].toLowerCase()).setScale(0.8); this.typeIcon.setTexture(`types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[pokemonMove.getMove().type].toLowerCase()).setScale(0.8);
this.moveCategoryIcon.setTexture("categories", MoveCategory[pokemonMove.getMove().category].toLowerCase()).setScale(1.0); this.moveCategoryIcon.setTexture("categories", MoveData.MoveCategory[pokemonMove.getMove().category].toLowerCase()).setScale(1.0);
const power = pokemonMove.getMove().power; const power = pokemonMove.getMove().power;
const accuracy = pokemonMove.getMove().accuracy; const accuracy = pokemonMove.getMove().accuracy;
const maxPP = pokemonMove.getMovePp(); const maxPP = pokemonMove.getMovePp();
const pp = maxPP - pokemonMove.ppUsed; const pp = maxPP - pokemonMove.ppUsed;
const accuracy1 = this.calculateAccuracy(pokemon, this.scene.getEnemyField()[0], pokemonMove)
const accuracy2 = this.calculateAccuracy(pokemon, this.scene.getEnemyField()[1], pokemonMove)
this.ppText.setText(`${Utils.padInt(pp, 2, " ")}/${Utils.padInt(maxPP, 2, " ")}`); this.ppText.setText(`${Utils.padInt(pp, 2, " ")}/${Utils.padInt(maxPP, 2, " ")}`);
this.powerText.setText(`${power >= 0 ? power : "---"}`); this.powerText.setText(`${power >= 0 ? power : "---"}`);
this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`); this.accuracyText.setText(`${accuracy >= 0 ? accuracy : "---"}`);
this.accuracyText.setText(`${accuracy1 >= 0 ? Math.round(accuracy1) : "---"}`);
if (this.scene.getEnemyField()[1] != undefined)
this.accuracyText.setText(`${accuracy1 >= 0 ? Math.round(accuracy1) : "---"}/${accuracy2 >= 0 ? Math.round(accuracy2) : "---"}`);
const ppPercentLeft = pp / maxPP; const ppPercentLeft = pp / maxPP;
@ -207,6 +758,7 @@ export default class FightUiHandler extends UiHandler {
pokemon.getOpponents().forEach((opponent) => { pokemon.getOpponents().forEach((opponent) => {
opponent.updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove)); opponent.updateEffectiveness(this.getEffectivenessText(pokemon, opponent, pokemonMove));
opponent.updateEffectiveness(this.calcDamage(this.scene, pokemon, opponent, pokemonMove));
}); });
} }
@ -312,3 +864,333 @@ export default class FightUiHandler extends UiHandler {
this.cursorObj = null; this.cursorObj = null;
} }
} }
export function simulateAttack(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move) {
let result: HitResult;
const damage1 = new Utils.NumberHolder(0);
const damage2 = new Utils.NumberHolder(0);
const defendingSidePlayField = target.isPlayer() ? scene.getPlayerField() : scene.getEnemyField();
const variableCategory = new Utils.IntegerHolder(move.category);
MoveData.applyMoveAttrs(MoveData.VariableMoveCategoryAttr, user, target, move, variableCategory);
const moveCategory = variableCategory.value as MoveData.MoveCategory;
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
MoveData.applyMoveAttrs(MoveData.VariableMoveTypeAttr, user, target, move);
applyPreAttackAbAttrs(MoveTypeChangeAttr, user, target, move, typeChangeMovePowerMultiplier);
const types = target.getTypes(true, true);
const cancelled = new Utils.BooleanHolder(false);
const typeless = move.hasAttr(MoveData.TypelessAttr);
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveData.MoveCategory.STATUS || move.getAttrs(MoveData.StatusMoveTypeImmunityAttr).find(attr => types.includes(attr.immuneType)))
? target.getAttackTypeEffectiveness(move.type, user, false, false)
: 1);
MoveData.applyMoveAttrs(MoveData.VariableMoveTypeMultiplierAttr, user, target, move, typeMultiplier);
if (typeless) {
typeMultiplier.value = 1;
}
if (types.find(t => move.isTypeImmune(user, target, t))) {
typeMultiplier.value = 0;
}
// Apply arena tags for conditional protection
if (!move.checkFlag(MoveData.MoveFlags.IGNORE_PROTECT, user, target) && !move.isAllyTarget()) {
const defendingSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
scene.arena.applyTagsForSide(ArenaTagType.QUICK_GUARD, defendingSide, cancelled, this, move.priority);
scene.arena.applyTagsForSide(ArenaTagType.WIDE_GUARD, defendingSide, cancelled, this, move.moveTarget);
scene.arena.applyTagsForSide(ArenaTagType.MAT_BLOCK, defendingSide, cancelled, this, move.category);
scene.arena.applyTagsForSide(ArenaTagType.CRAFTY_SHIELD, defendingSide, cancelled, this, move.category, move.moveTarget);
}
switch (moveCategory) {
case MoveData.MoveCategory.PHYSICAL:
case MoveData.MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveData.MoveCategory.PHYSICAL;
const power = new Utils.NumberHolder(move.power);
const sourceTeraType = user.getTeraType();
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type && power.value < 60 && move.priority <= 0 && !move.hasAttr(MoveData.MultiHitAttr) && !scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === user.id)) {
power.value = 60;
}
applyPreAttackAbAttrs(VariableMovePowerAbAttr, user, target, move, power);
if (user.getAlly()?.hasAbilityWithAttr(AllyMoveCategoryPowerBoostAbAttr)) {
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, user, target, move, power);
}
const fieldAuras = new Set(
scene.getField(true)
.map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr) as FieldMoveTypePowerBoostAbAttr[])
.flat(),
);
for (const aura of fieldAuras) {
// The only relevant values are `move` and the `power` holder
aura.applyPreAttack(null, null, null, move, [power]);
}
const alliedField: Pokemon[] = user instanceof PlayerPokemon ? scene.getPlayerField() : scene.getEnemyField();
alliedField.forEach(p => applyPreAttackAbAttrs(UserFieldMoveTypePowerBoostAbAttr, p, user, move, power));
power.value *= typeChangeMovePowerMultiplier.value;
if (!typeless) {
applyPreDefendAbAttrs(TypeImmunityAbAttr, user, target, move, cancelled, typeMultiplier);
MoveData.applyMoveAttrs(MoveData.NeutralDamageAgainstFlyingTypeMultiplierAttr, user, target, move, typeMultiplier);
}
if (!cancelled.value) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, user, target, move, cancelled, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, user, move, cancelled, typeMultiplier));
}
if (cancelled.value) {
//user.stopMultiHit(target);
result = HitResult.NO_EFFECT;
} else {
const typeBoost = user.findTag(t => t instanceof TypeBoostTag && t.boostedType === move.type) as TypeBoostTag;
if (typeBoost) {
power.value *= typeBoost.boostValue;
if (typeBoost.oneUse) {
//user.removeTag(typeBoost.tagType);
}
}
const arenaAttackTypeMultiplier = new Utils.NumberHolder(scene.arena.getAttackTypeMultiplier(move.type, user.isGrounded()));
MoveData.applyMoveAttrs(MoveData.IgnoreWeatherTypeDebuffAttr, user, target, move, arenaAttackTypeMultiplier);
if (scene.arena.getTerrainType() === TerrainType.GRASSY && target.isGrounded() && move.type === Type.GROUND && move.moveTarget === MoveData.MoveTarget.ALL_NEAR_OTHERS) {
power.value /= 2;
}
MoveData.applyMoveAttrs(MoveData.VariablePowerAttr, user, target, move, power);
scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, new Utils.IntegerHolder(0), power);
if (!typeless) {
scene.arena.applyTags(WeakenMoveTypeTag, move.type, power);
scene.applyModifiers(AttackTypeBoosterModifier, user.isPlayer(), user, move.type, power);
}
if (user.getTag(HelpingHandTag)) {
power.value *= 1.5;
}
let isCritical: boolean = true;
const critOnly = new Utils.BooleanHolder(false);
const critAlways = user.getTag(BattlerTagType.ALWAYS_CRIT);
MoveData.applyMoveAttrs(MoveData.CritOnlyAttr, user, target, move, critOnly);
applyAbAttrs(ConditionalCritAbAttr, user, null, critOnly, target, move);
if (isCritical) {
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, target, null, blockCrit);
if (blockCrit.value) {
isCritical = false;
}
}
const sourceAtk = new Utils.IntegerHolder(user.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, target, null, false));
const targetDef = new Utils.IntegerHolder(target.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, user, move, false));
const sourceAtkCrit = new Utils.IntegerHolder(user.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, target, null, isCritical));
const targetDefCrit = new Utils.IntegerHolder(target.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, user, move, isCritical));
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
applyAbAttrs(MultCritAbAttr, user, null, criticalMultiplier);
const screenMultiplier = new Utils.NumberHolder(1);
if (!isCritical) {
scene.arena.applyTagsForSide(WeakenMoveScreenTag, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, scene.currentBattle.double, screenMultiplier);
}
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0;
const sourceTypes = user.getTypes();
const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type);
const stabMultiplier = new Utils.NumberHolder(1);
if (sourceTeraType === Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value += 0.5;
} else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type) {
stabMultiplier.value += 0.5;
}
applyAbAttrs(StabBoostAbAttr, user, null, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
}
MoveData.applyMoveAttrs(MoveData.VariableAtkAttr, user, target, move, sourceAtk);
MoveData.applyMoveAttrs(MoveData.VariableDefAttr, user, target, move, targetDef);
MoveData.applyMoveAttrs(MoveData.VariableAtkAttr, user, target, move, sourceAtkCrit);
MoveData.applyMoveAttrs(MoveData.VariableDefAttr, user, target, move, targetDefCrit);
const effectPhase = scene.getCurrentPhase();
let numTargets = 1;
if (effectPhase instanceof MoveEffectPhase) {
numTargets = effectPhase.getTargets().length;
}
const twoStrikeMultiplier = new Utils.NumberHolder(1);
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, target, move, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
if (!isTypeImmune) {
damage1.value = Math.ceil(((((2 * user.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * twoStrikeMultiplier.value * 0.85); // low roll
damage2.value = Math.ceil(((((2 * user.level / 5 + 2) * power.value * sourceAtkCrit.value / targetDefCrit.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * twoStrikeMultiplier.value * criticalMultiplier.value); // high roll crit
if (isPhysical && user.status && user.status.effect === StatusEffect.BURN) {
if (!move.hasAttr(MoveData.BypassBurnDamageReductionAttr)) {
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BypassBurnDamageReductionAbAttr, user, burnDamageReductionCancelled);
if (!burnDamageReductionCancelled.value) {
damage1.value = Math.floor(damage1.value / 2);
damage2.value = Math.floor(damage2.value / 2);
}
}
}
applyPreAttackAbAttrs(DamageBoostAbAttr, user, target, move, damage1);
applyPreAttackAbAttrs(DamageBoostAbAttr, user, target, move, damage2);
/**
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
* The target has a {@link BattlerTagType} that this move interacts with
* AND
* The move doubles damage when used against that tag
*/
move.getAttrs(MoveData.HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
if (target.getTag(hta.tagType)) {
damage1.value *= 2;
damage2.value *= 2;
}
});
}
if (scene.arena.terrain?.terrainType === TerrainType.MISTY && target.isGrounded() && move.type === Type.DRAGON) {
damage1.value = Math.floor(damage1.value / 2);
damage2.value = Math.floor(damage2.value / 2);
}
const fixedDamage = new Utils.IntegerHolder(0);
MoveData.applyMoveAttrs(MoveData.FixedDamageAttr, user, target, move, fixedDamage);
if (!isTypeImmune && fixedDamage.value) {
damage1.value = fixedDamage.value;
damage2.value = fixedDamage.value;
isCritical = false;
result = HitResult.EFFECTIVE;
}
if (!result) {
if (!typeMultiplier.value) {
result = move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT;
} else {
const oneHitKo = new Utils.BooleanHolder(false);
MoveData.applyMoveAttrs(MoveData.OneHitKOAttr, user, target, move, oneHitKo);
if (oneHitKo.value) {
result = HitResult.ONE_HIT_KO;
isCritical = false;
damage1.value = target.hp;
damage2.value = target.hp;
} else if (typeMultiplier.value >= 2) {
result = HitResult.SUPER_EFFECTIVE;
} else if (typeMultiplier.value >= 1) {
result = HitResult.EFFECTIVE;
} else {
result = HitResult.NOT_VERY_EFFECTIVE;
}
}
}
if (!fixedDamage.value) {
if (!user.isPlayer()) {
scene.applyModifiers(EnemyDamageBoosterModifier, false, damage1);
scene.applyModifiers(EnemyDamageBoosterModifier, false, damage2);
}
if (!target.isPlayer()) {
scene.applyModifiers(EnemyDamageReducerModifier, false, damage1);
scene.applyModifiers(EnemyDamageReducerModifier, false, damage2);
}
}
MoveData.applyMoveAttrs(MoveData.ModifiedDamageAttr, user, target, move, damage1);
MoveData.applyMoveAttrs(MoveData.ModifiedDamageAttr, user, target, move, damage2);
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage1);
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, user, target, move, cancelled, damage2);
const destinyTag = target.getTag(BattlerTagType.DESTINY_BOND);
const oneHitKo = result === HitResult.ONE_HIT_KO;
if (damage1.value) {
if (target.getHpRatio() === 1) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, target, user, move, cancelled, damage1);
}
}
if (damage2.value) {
if (target.getHpRatio() === 1) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, target, user, move, cancelled, damage2);
}
}
}
break;
case MoveData.MoveCategory.STATUS:
if (!typeless) {
applyPreDefendAbAttrs(TypeImmunityAbAttr, target, user, move, cancelled, typeMultiplier);
}
if (!cancelled.value) {
applyPreDefendAbAttrs(MoveImmunityAbAttr, target, user, move, cancelled, typeMultiplier);
defendingSidePlayField.forEach((p) => applyPreDefendAbAttrs(FieldPriorityMoveImmunityAbAttr, p, user, move, cancelled, typeMultiplier));
}
if (!typeMultiplier.value) {
return -1
}
result = cancelled.value || !typeMultiplier.value ? HitResult.NO_EFFECT : HitResult.STATUS;
break;
}
return [damage1.value, damage2.value]
}
export function calcDamage(scene: BattleScene, user: PlayerPokemon, target: Pokemon, move: PokemonMove) {
var dmgHigh = 0
var dmgLow = 0
// dmgLow = (((2*user.level/5 + 2) * power * myAtk / theirDef)/50 + 2) * 0.85 * modifiers
// dmgHigh = (((2*user.level/5 + 2) * power * myAtkC / theirDefC)/50 + 2) * 1.5 * modifiers
var out = this.simulateAttack(scene, user, target, move.getMove())
var minHits = 1
var maxHits = 1
var mh = move.getMove().getAttrs(MoveData.MultiHitAttr)
for (var i = 0; i < mh.length; i++) {
var mh2 = mh[i] as MoveData.MultiHitAttr
switch (mh2.multiHitType) {
case MoveData.MultiHitType._2:
minHits = 2;
maxHits = 2;
case MoveData.MultiHitType._2_TO_5:
minHits = 2;
maxHits = 5;
case MoveData.MultiHitType._3:
minHits = 3;
maxHits = 3;
case MoveData.MultiHitType._10:
minHits = 10;
maxHits = 10;
case MoveData.MultiHitType.BEAT_UP:
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
// No status means the ally pokemon can contribute to Beat Up
minHits = party.reduce((total, pokemon) => {
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
}, 0);
maxHits = minHits
}
}
var h = user.getHeldItems()
for (var i = 0; i < h.length; i++) {
if (h[i].type instanceof PokemonMultiHitModifierType) {
minHits += h[i].getStackCount()
maxHits += h[i].getStackCount()
}
}
dmgLow = out[0] * minHits
dmgHigh = out[1] * maxHits
/*
if (user.hasAbility(Abilities.PARENTAL_BOND)) {
// Second hit deals 0.25x damage
dmgLow *= 1.25
dmgHigh *= 1.25
}
*/
var koText = ""
if (Math.floor(dmgLow) >= target.hp) {
koText = " (KO)"
} else if (Math.ceil(dmgHigh) >= target.hp) {
var percentChance = (target.hp - dmgLow + 1) / (dmgHigh - dmgLow + 1)
koText = " (" + Math.round(percentChance * 100) + "% KO)"
}
return (Math.round(dmgLow) == Math.round(dmgHigh) ? Math.round(dmgLow).toString() : Math.round(dmgLow) + "-" + Math.round(dmgHigh)) + koText
dmgLow = Math.round((dmgLow)/target.getBattleStat(Stat.HP)*100)
dmgHigh = Math.round((dmgHigh)/target.getBattleStat(Stat.HP)*100)
return (dmgLow == dmgHigh ? dmgLow + "%" : dmgLow + "%-" + dmgHigh + "%") + koText
return "???"
}

View File

@ -0,0 +1,105 @@
import { FormModalUiHandler } from "./form-modal-ui-handler";
import { ModalConfig } from "./modal-ui-handler";
import * as Utils from "../utils";
import { Mode } from "./ui";
import i18next from "i18next";
import * as LoggerTools from "../logger";
import { addTextObject, TextStyle } from "./text";
export default class LogNameFormUiHandler extends FormModalUiHandler {
name: string;
getModalTitle(config?: ModalConfig): string {
return (this.name ? this.name : "Manage Log");
}
getFields(config?: ModalConfig): string[] {
return [ "Name", "Author(s)", "Label" ];
}
getWidth(config?: ModalConfig): number {
return 160;
}
getMargin(config?: ModalConfig): [number, number, number, number] {
return [ 0, 0, 48, 0 ];
}
getButtonLabels(config?: ModalConfig): string[] {
return [ "Rename", "Export", "ExSheet", "Delete" ];
}
getReadableErrorMessage(error: string): string {
const colonIndex = error?.indexOf(":");
if (colonIndex > 0) {
error = error.slice(0, colonIndex);
}
switch (error) {
case "invalid username":
return i18next.t("menu:invalidLoginUsername");
case "invalid password":
return i18next.t("menu:invalidLoginPassword");
case "account doesn't exist":
return i18next.t("menu:accountNonExistent");
case "password doesn't match":
return i18next.t("menu:unmatchingPassword");
}
return super.getReadableErrorMessage(error);
}
setup(): void {
super.setup();
//const label = addTextObject(this.scene, 10, 87, "Clicking Export or ExSheets does NOT save any text you entered\nPress \"Rename\", then reopen this menu and click Export", TextStyle.TOOLTIP_CONTENT, { fontSize: "42px" });
//this.modalContainer.add(label);
this.inputs[0].maxLength = 99
this.inputs[1].maxLength = 200
}
show(args: any[]): boolean {
this.name = args[0].autofillfields[0]
if (super.show(args)) {
const config = args[0] as ModalConfig;
console.log("Shown", args)
const originalLoginAction = this.submitAction;
this.inputs[0].setText(args[0].autofillfields[0])
this.inputs[1].setText(args[0].autofillfields[1])
this.inputs[2].setText(args[0].autofillfields[2])
this.submitAction = (_) => {
console.log("submitAction")
// Prevent overlapping overrides on action modification
this.submitAction = originalLoginAction;
this.sanitizeInputs();
this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] });
const onFail = error => {
this.scene.ui.setMode(Mode.NAME_LOG, Object.assign(config, { errorMessage: error?.trim() }));
this.scene.ui.playError();
};
if (!this.inputs[0].text) {
//return onFail(i18next.t("menu:emptyUsername"));
}
console.log(`Calling LoggerTools.setFileInfo(${this.inputs[0].text}, ${this.inputs[1].text.split(",")})`)
LoggerTools.setFileInfo(this.inputs[0].text, this.inputs[1].text.split(","))
console.log(`Calling originalLoginAction()`)
originalLoginAction()
};
const exportaction1 = config.buttonActions[1]
config.buttonActions[1] = (_) => {
LoggerTools.setFileInfo(this.inputs[0].text, this.inputs[1].text.split(","))
exportaction1()
}
const exportaction2 = config.buttonActions[2]
config.buttonActions[2] = (_) => {
LoggerTools.setFileInfo(this.inputs[0].text, this.inputs[1].text.split(","))
exportaction2()
}
return true;
}
return false;
}
}

View File

@ -0,0 +1,355 @@
import i18next from "i18next";
import BattleScene from "../battle-scene";
import { Button } from "#enums/buttons";
import { GameMode } from "../game-mode";
import { PokemonHeldItemModifier } from "../modifier/modifier";
import { SessionSaveData } from "../system/game-data";
import PokemonData from "../system/pokemon-data";
import * as Utils from "../utils";
import MessageUiHandler from "./message-ui-handler";
import { TextStyle, addTextObject } from "./text";
import { Mode } from "./ui";
import { addWindow } from "./ui-theme";
import * as LoggerTools from "../logger"
import { loggedInUser } from "#app/account.js";
import { allpanels, biomePanelIDs } from "../loading-scene"
import { getBiomeName } from "#app/data/biomes.js";
import { Species } from "#app/enums/species.js";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species.js";
const sessionSlotCount = 5;
export type LogSelectCallback = (key: string) => void;
export default class LogSelectUiHandler extends MessageUiHandler {
private saveSlotSelectContainer: Phaser.GameObjects.Container;
private sessionSlotsContainer: Phaser.GameObjects.Container;
private saveSlotSelectMessageBox: Phaser.GameObjects.NineSlice;
private saveSlotSelectMessageBoxContainer: Phaser.GameObjects.Container;
private sessionSlots: SessionSlot[];
private selectCallback: LogSelectCallback;
private scrollCursor: integer = 0;
private cursorObj: Phaser.GameObjects.NineSlice;
private sessionSlotsContainerInitialY: number;
constructor(scene: BattleScene) {
super(scene, Mode.LOG_HANDLER);
}
setup() {
const ui = this.getUi();
this.saveSlotSelectContainer = this.scene.add.container(0, 0);
this.saveSlotSelectContainer.setVisible(false);
ui.add(this.saveSlotSelectContainer);
const loadSessionBg = this.scene.add.rectangle(0, 0, this.scene.game.canvas.width / 6, -this.scene.game.canvas.height / 6, 0x006860);
loadSessionBg.setOrigin(0, 0);
this.saveSlotSelectContainer.add(loadSessionBg);
this.sessionSlotsContainerInitialY = -this.scene.game.canvas.height / 6 + 8;
this.sessionSlotsContainer = this.scene.add.container(8, this.sessionSlotsContainerInitialY);
this.saveSlotSelectContainer.add(this.sessionSlotsContainer);
this.saveSlotSelectMessageBoxContainer = this.scene.add.container(0, 0);
this.saveSlotSelectMessageBoxContainer.setVisible(false);
this.saveSlotSelectContainer.add(this.saveSlotSelectMessageBoxContainer);
this.saveSlotSelectMessageBox = addWindow(this.scene, 1, -1, 318, 28);
this.saveSlotSelectMessageBox.setOrigin(0, 1);
this.saveSlotSelectMessageBoxContainer.add(this.saveSlotSelectMessageBox);
this.message = addTextObject(this.scene, 8, 8, "", TextStyle.WINDOW, { maxLines: 2 });
this.message.setOrigin(0, 0);
this.saveSlotSelectMessageBoxContainer.add(this.message);
this.sessionSlots = [];
}
show(args: any[]): boolean {
if ((args.length < 1 || !(args[0] instanceof Function))) {
return false;
}
super.show(args);
this.selectCallback = args[0] as LogSelectCallback;
this.saveSlotSelectContainer.setVisible(true);
this.populateSessionSlots();
this.setScrollCursor(0);
this.setCursor(0);
return true;
}
processInput(button: Button): boolean {
const ui = this.getUi();
let success = false;
let error = false;
if (button === Button.ACTION || button === Button.CANCEL) {
const originalCallback = this.selectCallback;
if (button === Button.ACTION) {
const cursor = this.cursor + this.scrollCursor;
this.selectCallback = null;
originalCallback(this.sessionSlots[cursor].key);
success = true;
} else {
this.selectCallback = null;
originalCallback(undefined);
success = true;
}
} else {
switch (button) {
case Button.UP:
if (this.cursor) {
success = this.setCursor(this.cursor - 1);
} else if (this.scrollCursor) {
success = this.setScrollCursor(this.scrollCursor - 1);
}
break;
case Button.DOWN:
if (this.cursor < 2) {
success = this.setCursor(this.cursor + 1);
} else if (this.scrollCursor < this.sessionSlots.length - 3) {
success = this.setScrollCursor(this.scrollCursor + 1);
}
break;
}
}
if (success) {
ui.playSelect();
} else if (error) {
ui.playError();
}
return success || error;
}
populateSessionSlots() {
var ui = this.getUi();
var ypos = 0;
LoggerTools.getLogs()
for (let s = 0; s < sessionSlotCount; s++) {
var found = false
for (var i = 0; i < LoggerTools.logs.length; i++) {
if (LoggerTools.logs[i][3] == s.toString()) {
found = true
const sessionSlot = new SessionSlot(this.scene, s, ypos);
ypos++
sessionSlot.load(LoggerTools.logs[i][1]);
this.scene.add.existing(sessionSlot);
this.sessionSlotsContainer.add(sessionSlot);
this.sessionSlots.push(sessionSlot);
}
}
if (!found) {
const sessionSlot = new SessionSlot(this.scene, s, ypos);
ypos++
sessionSlot.load(undefined);
this.scene.add.existing(sessionSlot);
this.sessionSlotsContainer.add(sessionSlot);
this.sessionSlots.push(sessionSlot);
}
}
for (var i = 0; i < LoggerTools.logs.length; i++) {
if (LoggerTools.logs[i][3] == "") {
const sessionSlot = new SessionSlot(this.scene, undefined, ypos);
ypos++
sessionSlot.load(LoggerTools.logs[i][1]);
this.scene.add.existing(sessionSlot);
this.sessionSlotsContainer.add(sessionSlot);
this.sessionSlots.push(sessionSlot);
}
}
}
showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
super.showText(text, delay, callback, callbackDelay, prompt, promptDelay);
if (text?.indexOf("\n") === -1) {
this.saveSlotSelectMessageBox.setSize(318, 28);
this.message.setY(-22);
} else {
this.saveSlotSelectMessageBox.setSize(318, 42);
this.message.setY(-37);
}
this.saveSlotSelectMessageBoxContainer.setVisible(!!text?.length);
}
setCursor(cursor: integer): boolean {
const changed = super.setCursor(cursor);
if (!this.cursorObj) {
this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", null, 296, 44, 6, 6, 6, 6);
this.cursorObj.setOrigin(0, 0);
this.sessionSlotsContainer.add(this.cursorObj);
}
this.cursorObj.setPosition(4, 4 + (cursor + this.scrollCursor) * 56);
return changed;
}
setScrollCursor(scrollCursor: integer): boolean {
const changed = scrollCursor !== this.scrollCursor;
if (changed) {
this.scrollCursor = scrollCursor;
this.setCursor(this.cursor);
this.scene.tweens.add({
targets: this.sessionSlotsContainer,
y: this.sessionSlotsContainerInitialY - 56 * scrollCursor,
duration: Utils.fixedInt(325),
ease: "Sine.easeInOut"
});
}
return changed;
}
clear() {
super.clear();
this.saveSlotSelectContainer.setVisible(false);
this.eraseCursor();
this.selectCallback = null;
this.clearSessionSlots();
}
eraseCursor() {
if (this.cursorObj) {
this.cursorObj.destroy();
}
this.cursorObj = null;
}
clearSessionSlots() {
this.sessionSlots.splice(0, this.sessionSlots.length);
this.sessionSlotsContainer.removeAll(true);
}
}
class SessionSlot extends Phaser.GameObjects.Container {
public slotId: integer;
public autoSlot: integer;
public hasData: boolean;
public wv: integer;
public key: string;
private loadingLabel: Phaser.GameObjects.Text;
constructor(scene: BattleScene, slotId: integer = undefined, ypos: integer, autoSlot?: integer) {
super(scene, 0, ypos * 56);
this.slotId = slotId;
this.autoSlot = autoSlot
this.setup();
}
setup() {
const slotWindow = addWindow(this.scene, 0, 0, 304, 52);
this.add(slotWindow);
this.loadingLabel = addTextObject(this.scene, 152, 26, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW);
this.loadingLabel.setOrigin(0.5, 0.5);
this.add(this.loadingLabel);
}
async setupWithData(data: LoggerTools.DRPD) {
this.remove(this.loadingLabel, true);
var lbl = `???`
lbl = data.title
if (this.slotId) {
lbl = `[${this.slotId}] ${lbl}`
}
console.log(data, this.slotId, this.autoSlot, lbl)
const gameModeLabel = addTextObject(this.scene, 8, 5, lbl, TextStyle.WINDOW);
this.add(gameModeLabel);
const timestampLabel = addTextObject(this.scene, 8, 19, data.date, TextStyle.WINDOW);
this.add(timestampLabel);
const playTimeLabel = addTextObject(this.scene, 8, 33, data.version + " / " + (data.label || "") + " / " + (data.uuid || ""), TextStyle.WINDOW);
this.add(playTimeLabel);
const pokemonIconsContainer = this.scene.add.container(144, 4);
if (false || data.starters)
data.starters.forEach((p: LoggerTools.PokeData, i: integer) => {
if (p == undefined)
return;
const iconContainer = this.scene.add.container(26 * i, 0);
iconContainer.setScale(0.75);
if (Utils.getEnumValues(Species)[p.id] == undefined)
return;
if (getPokemonSpecies(Utils.getEnumValues(Species)[p.id]) == undefined)
return;
const icon = this.scene.addPkIcon(getPokemonSpecies(Utils.getEnumValues(Species)[p.id]), 0, 0, 0, 0, 0);
const text = addTextObject(this.scene, 32, 20, `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(p.level, 1000)}`, TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" });
text.setShadow(0, 0, null);
text.setStroke("#424242", 14);
text.setOrigin(1, 0);
iconContainer.add(icon);
iconContainer.add(text);
pokemonIconsContainer.add(iconContainer);
});
this.add(pokemonIconsContainer);
//const modifiersModule = await import("../modifier/modifier");
const modifierIconsContainer = this.scene.add.container(148, 30);
modifierIconsContainer.setScale(0.5);
let visibleModifierIndex = 0;
this.add(modifierIconsContainer);
}
load(l?: string, slot?: integer): Promise<boolean> {
return new Promise<boolean>(resolve => {
if (l == undefined) {
this.hasData = false;
this.loadingLabel.setText("No data for this run");
resolve(false);
return;
}
this.key = l
if (slot) {
this.slotId = slot
}
this.setupWithData(JSON.parse(localStorage.getItem(l)))
resolve(true);
});
return new Promise<boolean>(resolve => {
this.scene.gameData.getSession(this.slotId, this.autoSlot).then(async sessionData => {
if (!sessionData) {
this.hasData = false;
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
resolve(false);
return;
}
this.hasData = true;
await this.setupWithData(undefined);
resolve(true);
});
});
}
}
interface SessionSlot {
scene: BattleScene;
}

View File

@ -22,6 +22,7 @@ enum MenuOptions {
MANAGE_DATA, MANAGE_DATA,
COMMUNITY, COMMUNITY,
SAVE_AND_QUIT, SAVE_AND_QUIT,
SAVE_AND_REFRESH,
LOG_OUT LOG_OUT
} }
@ -356,6 +357,23 @@ export default class MenuUiHandler extends MessageUiHandler {
error = true; error = true;
} }
break; break;
case MenuOptions.SAVE_AND_REFRESH:
if (this.scene.currentBattle) {
success = true;
if (this.scene.currentBattle.turn > 1) {
ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => {
ui.setOverlayMode(Mode.CONFIRM, () => this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => this.scene.reset(true)), () => {
ui.revertMode();
ui.showText(null, 0);
}, false, -98);
});
} else {
this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => this.scene.reset(true));
}
} else {
error = true;
}
break;
case MenuOptions.LOG_OUT: case MenuOptions.LOG_OUT:
success = true; success = true;
const doLogout = () => { const doLogout = () => {

Some files were not shown because too many files have changed in this diff Show More