Merge pull request #4085 from ben-lear/global-trade-system
Update MEs branch with latest beta
This commit is contained in:
commit
cc893daf73
|
@ -15,8 +15,8 @@ on:
|
||||||
types: [checks_requested]
|
types: [checks_requested]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-tests: # Define a job named "run-tests"
|
run-misc-tests: # Define a job named "run-tests"
|
||||||
name: Run tests # Human-readable name for the job
|
name: Run misc tests # Human-readable name for the job
|
||||||
runs-on: ubuntu-latest # Specify the latest Ubuntu runner for the job
|
runs-on: ubuntu-latest # Specify the latest Ubuntu runner for the job
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
@ -31,5 +31,75 @@ jobs:
|
||||||
- name: Install Node.js dependencies # Step to install Node.js dependencies
|
- name: Install Node.js dependencies # Step to install Node.js dependencies
|
||||||
run: npm ci # Use 'npm ci' to install dependencies
|
run: npm ci # Use 'npm ci' to install dependencies
|
||||||
|
|
||||||
- name: tests # Step to run tests
|
- name: pre-test # pre-test to check overrides
|
||||||
run: npm run test:silent
|
run: npx vitest run --project pre
|
||||||
|
- name: test misc
|
||||||
|
run: npx vitest --project misc
|
||||||
|
|
||||||
|
run-abilities-tests:
|
||||||
|
name: Run abilities tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Install Node.js dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: pre-test
|
||||||
|
run: npx vitest run --project pre
|
||||||
|
- name: test abilities
|
||||||
|
run: npx vitest --project abilities
|
||||||
|
|
||||||
|
run-items-tests:
|
||||||
|
name: Run items tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Install Node.js dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: pre-test
|
||||||
|
run: npx vitest run --project pre
|
||||||
|
- name: test items
|
||||||
|
run: npx vitest --project items
|
||||||
|
|
||||||
|
run-moves-tests:
|
||||||
|
name: Run moves tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Install Node.js dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: pre-test
|
||||||
|
run: npx vitest run --project pre
|
||||||
|
- name: test moves
|
||||||
|
run: npx vitest --project moves
|
||||||
|
|
||||||
|
run-battle-tests:
|
||||||
|
name: Run battle tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Install Node.js dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: pre-test
|
||||||
|
run: npx vitest run --project pre
|
||||||
|
- name: test battle
|
||||||
|
run: npx vitest --project battle
|
|
@ -4,7 +4,8 @@ import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This script creates a test boilerplate file for a move or ability.
|
* This script creates a test boilerplate file for a move or ability.
|
||||||
* @param {string} type - The type of test to create. Either "move" or "ability".
|
* @param {string} type - The type of test to create. Either "move", "ability",
|
||||||
|
* or "item".
|
||||||
* @param {string} fileName - The name of the file to create.
|
* @param {string} fileName - The name of the file to create.
|
||||||
* @example npm run create-test move tackle
|
* @example npm run create-test move tackle
|
||||||
*/
|
*/
|
||||||
|
@ -19,7 +20,7 @@ const type = args[0]; // "move" or "ability"
|
||||||
let fileName = args[1]; // The file name
|
let fileName = args[1]; // The file name
|
||||||
|
|
||||||
if (!type || !fileName) {
|
if (!type || !fileName) {
|
||||||
console.error('Please provide both a type ("move" or "ability") and a file name.');
|
console.error('Please provide both a type ("move", "ability", or "item") and a file name.');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +41,11 @@ if (type === 'move') {
|
||||||
} else if (type === 'ability') {
|
} else if (type === 'ability') {
|
||||||
dir = path.join(__dirname, 'src', 'test', 'abilities');
|
dir = path.join(__dirname, 'src', 'test', 'abilities');
|
||||||
description = `Abilities - ${formattedName}`;
|
description = `Abilities - ${formattedName}`;
|
||||||
|
} else if (type === "item") {
|
||||||
|
dir = path.join(__dirname, 'src', 'test', 'items');
|
||||||
|
description = `Items - ${formattedName}`;
|
||||||
} else {
|
} else {
|
||||||
console.error('Invalid type. Please use "move" or "ability".');
|
console.error('Invalid type. Please use "move", "ability", or "item".');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,4 +102,4 @@ describe("${description}", () => {
|
||||||
// Write the template content to the file
|
// Write the template content to the file
|
||||||
fs.writeFileSync(filePath, content, 'utf8');
|
fs.writeFileSync(filePath, content, 'utf8');
|
||||||
|
|
||||||
console.log(`File created at: ${filePath}`);
|
console.log(`File created at: ${filePath}`);
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"1": {
|
||||||
|
"529cc5": "8153c7",
|
||||||
|
"d65a94": "5ad662",
|
||||||
|
"3a73ad": "6b3aad",
|
||||||
|
"bd216b": "21bd69",
|
||||||
|
"5a193a": "195a2a",
|
||||||
|
"193a63": "391963",
|
||||||
|
"295a84": "472984"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"529cc5": "ffedb6",
|
||||||
|
"d65a94": "e67d2f",
|
||||||
|
"3a73ad": "ebc582",
|
||||||
|
"bd216b": "b35131",
|
||||||
|
"31313a": "3d1519",
|
||||||
|
"5a193a": "752e2e",
|
||||||
|
"193a63": "705040",
|
||||||
|
"295a84": "ad875a",
|
||||||
|
"4a4a52": "57211a"
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 38 KiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 43 KiB |
|
@ -1691,8 +1691,8 @@
|
||||||
],
|
],
|
||||||
"465": [
|
"465": [
|
||||||
0,
|
0,
|
||||||
2,
|
1,
|
||||||
2
|
1
|
||||||
],
|
],
|
||||||
"466": [
|
"466": [
|
||||||
1,
|
1,
|
||||||
|
@ -3980,6 +3980,11 @@
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
|
"465": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
"592": [
|
"592": [
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
@ -5690,7 +5695,7 @@
|
||||||
"465": [
|
"465": [
|
||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
2
|
1
|
||||||
],
|
],
|
||||||
"466": [
|
"466": [
|
||||||
2,
|
2,
|
||||||
|
@ -8008,6 +8013,11 @@
|
||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
|
"465": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
],
|
||||||
"592": [
|
"592": [
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|
|
@ -8,5 +8,14 @@
|
||||||
"bd216b": "21bd69",
|
"bd216b": "21bd69",
|
||||||
"31313a": "31313a",
|
"31313a": "31313a",
|
||||||
"d65a94": "5ad662"
|
"d65a94": "5ad662"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"5a193a": "752e2e",
|
||||||
|
"31313a": "3d1519",
|
||||||
|
"d65a94": "e67d2f",
|
||||||
|
"3a73ad": "ebc582",
|
||||||
|
"295a84": "ad875a",
|
||||||
|
"bd216b": "b35131",
|
||||||
|
"193a63": "705040"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 34 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"1": {
|
||||||
|
"193a63": "391963",
|
||||||
|
"295a84": "472984",
|
||||||
|
"3a73ad": "6b3aad",
|
||||||
|
"000000": "000000",
|
||||||
|
"5a193a": "195a2a",
|
||||||
|
"bd216b": "21bd69",
|
||||||
|
"31313a": "31313a",
|
||||||
|
"d65a94": "5ad662"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"5a193a": "752e2e",
|
||||||
|
"31313a": "3d1519",
|
||||||
|
"d65a94": "e67d2f",
|
||||||
|
"3a73ad": "ebc582",
|
||||||
|
"295a84": "ad875a",
|
||||||
|
"bd216b": "b35131",
|
||||||
|
"193a63": "705040"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"1": {
|
||||||
|
"529cc5": "8153c7",
|
||||||
|
"d65a94": "5ad662",
|
||||||
|
"3a73ad": "6b3aad",
|
||||||
|
"bd216b": "21bd69",
|
||||||
|
"5a193a": "195a2a",
|
||||||
|
"193a63": "391963",
|
||||||
|
"295a84": "472984"
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"529cc5": "ffedb6",
|
||||||
|
"d65a94": "e67d2f",
|
||||||
|
"3a73ad": "ebc582",
|
||||||
|
"bd216b": "b35131",
|
||||||
|
"31313a": "3d1519",
|
||||||
|
"5a193a": "752e2e",
|
||||||
|
"193a63": "705040",
|
||||||
|
"295a84": "ad875a",
|
||||||
|
"4a4a52": "57211a"
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -874,7 +874,7 @@ export default class BattleScene extends SceneBase {
|
||||||
overrideModifiers(this, false);
|
overrideModifiers(this, false);
|
||||||
overrideHeldItems(this, pokemon, false);
|
overrideHeldItems(this, pokemon, false);
|
||||||
if (boss && !dataSource) {
|
if (boss && !dataSource) {
|
||||||
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967295));
|
const secondaryIvs = Utils.getIvsFromId(Utils.randSeedInt(4294967296));
|
||||||
|
|
||||||
for (let s = 0; s < pokemon.ivs.length; s++) {
|
for (let s = 0; s < pokemon.ivs.length; s++) {
|
||||||
pokemon.ivs[s] = Math.round(Phaser.Math.Linear(Math.min(pokemon.ivs[s], secondaryIvs[s]), Math.max(pokemon.ivs[s], secondaryIvs[s]), 0.75));
|
pokemon.ivs[s] = Math.round(Phaser.Math.Linear(Math.min(pokemon.ivs[s], secondaryIvs[s]), Math.max(pokemon.ivs[s], secondaryIvs[s]), 0.75));
|
||||||
|
@ -994,6 +994,16 @@ export default class BattleScene extends SceneBase {
|
||||||
this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym();
|
this.offsetGym = this.gameMode.isClassic && this.getGeneratedOffsetGym();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random number using the current battle's seed
|
||||||
|
*
|
||||||
|
* This calls {@linkcode Battle.randSeedInt}(`scene`, {@linkcode range}, {@linkcode min}) in `src/battle.ts`
|
||||||
|
* which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`
|
||||||
|
*
|
||||||
|
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
|
||||||
|
* @param min The minimum integer to pick, default `0`
|
||||||
|
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
|
||||||
|
*/
|
||||||
randBattleSeedInt(range: integer, min: integer = 0): integer {
|
randBattleSeedInt(range: integer, min: integer = 0): integer {
|
||||||
return this.currentBattle?.randSeedInt(this, range, min);
|
return this.currentBattle?.randSeedInt(this, range, min);
|
||||||
}
|
}
|
||||||
|
@ -1145,7 +1155,8 @@ export default class BattleScene extends SceneBase {
|
||||||
doubleTrainer = false;
|
doubleTrainer = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, doubleTrainer ? TrainerVariant.DOUBLE : Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
|
const variant = doubleTrainer ? TrainerVariant.DOUBLE : (Utils.randSeedInt(2) ? TrainerVariant.FEMALE : TrainerVariant.DEFAULT);
|
||||||
|
newTrainer = trainerData !== undefined ? trainerData.toTrainer(this) : new Trainer(this, trainerType, variant);
|
||||||
this.field.add(newTrainer);
|
this.field.add(newTrainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2727,7 +2738,7 @@ export default class BattleScene extends SceneBase {
|
||||||
if (mods.length < 1) {
|
if (mods.length < 1) {
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
const rand = Math.floor(Utils.randSeedInt(mods.length));
|
const rand = Utils.randSeedInt(mods.length);
|
||||||
return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))];
|
return [mods[rand], ...shuffleModifiers(mods.filter((_, i) => i !== rand))];
|
||||||
};
|
};
|
||||||
modifiers = shuffleModifiers(modifiers);
|
modifiers = shuffleModifiers(modifiers);
|
||||||
|
|
|
@ -362,6 +362,12 @@ export default class Battle {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random number using the current battle's seed. Calls {@linkcode Utils.randSeedInt}
|
||||||
|
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
|
||||||
|
* @param min The minimum integer to pick, default `0`
|
||||||
|
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
|
||||||
|
*/
|
||||||
randSeedInt(scene: BattleScene, range: number, min: number = 0): number {
|
randSeedInt(scene: BattleScene, range: number, min: number = 0): number {
|
||||||
if (range <= 1) {
|
if (range <= 1) {
|
||||||
return min;
|
return min;
|
||||||
|
|
|
@ -2642,7 +2642,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr {
|
||||||
if (simulated) {
|
if (simulated) {
|
||||||
return defender.canAddTag(BattlerTagType.CONFUSED);
|
return defender.canAddTag(BattlerTagType.CONFUSED);
|
||||||
} else {
|
} else {
|
||||||
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedInt(3, 2), move.id, defender.id);
|
return defender.addTag(BattlerTagType.CONFUSED, pokemon.randSeedIntRange(2, 5), move.id, defender.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -5333,8 +5333,10 @@ export function initAbilities() {
|
||||||
.attr(FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 4 / 3),
|
.attr(FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 4 / 3),
|
||||||
new Ability(Abilities.AURA_BREAK, 6)
|
new Ability(Abilities.AURA_BREAK, 6)
|
||||||
.ignorable()
|
.ignorable()
|
||||||
.conditionalAttr(target => target.hasAbility(Abilities.DARK_AURA), FieldMoveTypePowerBoostAbAttr, Type.DARK, 9 / 16)
|
.conditionalAttr(pokemon => pokemon.scene.getField(true).some(p => p.hasAbility(Abilities.DARK_AURA)), FieldMoveTypePowerBoostAbAttr, Type.DARK, 9 / 16)
|
||||||
.conditionalAttr(target => target.hasAbility(Abilities.FAIRY_AURA), FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 9 / 16),
|
.conditionalAttr(pokemon => pokemon.scene.getField(true).some(p => p.hasAbility(Abilities.FAIRY_AURA)), FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 9 / 16)
|
||||||
|
.conditionalAttr(pokemon => pokemon.scene.getField(true).some(p => p.hasAbility(Abilities.DARK_AURA) || p.hasAbility(Abilities.FAIRY_AURA)),
|
||||||
|
PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonAuraBreak", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })),
|
||||||
new Ability(Abilities.PRIMORDIAL_SEA, 6)
|
new Ability(Abilities.PRIMORDIAL_SEA, 6)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN)
|
||||||
|
|
|
@ -486,7 +486,7 @@ export class ConfusedTag extends BattlerTag {
|
||||||
if (pokemon.randSeedInt(3) === 0) {
|
if (pokemon.randSeedInt(3) === 0) {
|
||||||
const atk = pokemon.getEffectiveStat(Stat.ATK);
|
const atk = pokemon.getEffectiveStat(Stat.ATK);
|
||||||
const def = pokemon.getEffectiveStat(Stat.DEF);
|
const def = pokemon.getEffectiveStat(Stat.DEF);
|
||||||
const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
|
const damage = Utils.toDmgValue(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedIntRange(85, 100) / 100));
|
||||||
pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself"));
|
pokemon.scene.queueMessage(i18next.t("battlerTags:confusedLapseHurtItself"));
|
||||||
pokemon.damageAndUpdate(damage);
|
pokemon.damageAndUpdate(damage);
|
||||||
pokemon.battleData.hitCount++;
|
pokemon.battleData.hitCount++;
|
||||||
|
|
|
@ -15,7 +15,7 @@ export const EGG_SEED = 1073741824;
|
||||||
// Rates for specific random properties in 1/x
|
// Rates for specific random properties in 1/x
|
||||||
const DEFAULT_SHINY_RATE = 128;
|
const DEFAULT_SHINY_RATE = 128;
|
||||||
const GACHA_SHINY_UP_SHINY_RATE = 64;
|
const GACHA_SHINY_UP_SHINY_RATE = 64;
|
||||||
const SAME_SPECIES_EGG_SHINY_RATE = 24;
|
const SAME_SPECIES_EGG_SHINY_RATE = 12;
|
||||||
const SAME_SPECIES_EGG_HA_RATE = 8;
|
const SAME_SPECIES_EGG_HA_RATE = 8;
|
||||||
const MANAPHY_EGG_MANAPHY_RATE = 8;
|
const MANAPHY_EGG_MANAPHY_RATE = 8;
|
||||||
const GACHA_EGG_HA_RATE = 192;
|
const GACHA_EGG_HA_RATE = 192;
|
||||||
|
|
|
@ -757,7 +757,10 @@ export default class Move implements Localizable {
|
||||||
|
|
||||||
const fieldAuras = new Set(
|
const fieldAuras = new Set(
|
||||||
source.scene.getField(true)
|
source.scene.getField(true)
|
||||||
.map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr) as FieldMoveTypePowerBoostAbAttr[])
|
.map((p) => p.getAbilityAttrs(FieldMoveTypePowerBoostAbAttr).filter(attr => {
|
||||||
|
const condition = attr.getCondition();
|
||||||
|
return (!condition || condition(p));
|
||||||
|
}) as FieldMoveTypePowerBoostAbAttr[])
|
||||||
.flat(),
|
.flat(),
|
||||||
);
|
);
|
||||||
for (const aura of fieldAuras) {
|
for (const aura of fieldAuras) {
|
||||||
|
@ -4400,7 +4403,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
||||||
|
|
||||||
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
|
const moveChance = this.getMoveChance(user, target, move, this.selfTarget, true);
|
||||||
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
|
if (moveChance < 0 || moveChance === 100 || user.randSeedInt(100) < moveChance) {
|
||||||
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedInt(this.turnCountMax - this.turnCountMin, this.turnCountMin), move.id, user.id);
|
return (this.selfTarget ? user : target).addTag(this.tagType, user.randSeedIntRange(this.turnCountMin, this.turnCountMax), move.id, user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -6234,6 +6237,8 @@ const userSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target:
|
||||||
|
|
||||||
const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(Abilities.COMATOSE);
|
const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP || target.hasAbility(Abilities.COMATOSE);
|
||||||
|
|
||||||
|
const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => user.scene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined;
|
||||||
|
|
||||||
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
export type MoveAttrFilter = (attr: MoveAttr) => boolean;
|
||||||
|
|
||||||
function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): Promise<void> {
|
function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): Promise<void> {
|
||||||
|
@ -6972,7 +6977,8 @@ export function initMoves() {
|
||||||
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
new SelfStatusMove(Moves.PROTECT, Type.NORMAL, -1, 10, -1, 4, 2)
|
new SelfStatusMove(Moves.PROTECT, Type.NORMAL, -1, 10, -1, 4, 2)
|
||||||
.attr(ProtectAttr),
|
.attr(ProtectAttr)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.MACH_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 2)
|
new AttackMove(Moves.MACH_PUNCH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 2)
|
||||||
.punchingMove(),
|
.punchingMove(),
|
||||||
new StatusMove(Moves.SCARY_FACE, Type.NORMAL, 100, 10, -1, 0, 2)
|
new StatusMove(Moves.SCARY_FACE, Type.NORMAL, 100, 10, -1, 0, 2)
|
||||||
|
@ -7023,7 +7029,8 @@ export function initMoves() {
|
||||||
.windMove()
|
.windMove()
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
new SelfStatusMove(Moves.DETECT, Type.FIGHTING, -1, 5, -1, 4, 2)
|
new SelfStatusMove(Moves.DETECT, Type.FIGHTING, -1, 5, -1, 4, 2)
|
||||||
.attr(ProtectAttr),
|
.attr(ProtectAttr)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.BONE_RUSH, Type.GROUND, MoveCategory.PHYSICAL, 25, 90, 10, -1, 0, 2)
|
new AttackMove(Moves.BONE_RUSH, Type.GROUND, MoveCategory.PHYSICAL, 25, 90, 10, -1, 0, 2)
|
||||||
.attr(MultiHitAttr)
|
.attr(MultiHitAttr)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
|
@ -7041,7 +7048,8 @@ export function initMoves() {
|
||||||
.attr(HitHealAttr)
|
.attr(HitHealAttr)
|
||||||
.triageMove(),
|
.triageMove(),
|
||||||
new SelfStatusMove(Moves.ENDURE, Type.NORMAL, -1, 10, -1, 4, 2)
|
new SelfStatusMove(Moves.ENDURE, Type.NORMAL, -1, 10, -1, 4, 2)
|
||||||
.attr(ProtectAttr, BattlerTagType.ENDURING),
|
.attr(ProtectAttr, BattlerTagType.ENDURING)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2)
|
new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2)
|
||||||
.attr(StatStageChangeAttr, [ Stat.ATK ], -2),
|
.attr(StatStageChangeAttr, [ Stat.ATK ], -2),
|
||||||
new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2)
|
new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2)
|
||||||
|
@ -7788,7 +7796,8 @@ export function initMoves() {
|
||||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.ACC ], 1, true),
|
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.ACC ], 1, true),
|
||||||
new StatusMove(Moves.WIDE_GUARD, Type.ROCK, -1, 10, -1, 3, 5)
|
new StatusMove(Moves.WIDE_GUARD, Type.ROCK, -1, 10, -1, 3, 5)
|
||||||
.target(MoveTarget.USER_SIDE)
|
.target(MoveTarget.USER_SIDE)
|
||||||
.attr(AddArenaTagAttr, ArenaTagType.WIDE_GUARD, 1, true, true),
|
.attr(AddArenaTagAttr, ArenaTagType.WIDE_GUARD, 1, true, true)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new StatusMove(Moves.GUARD_SPLIT, Type.PSYCHIC, -1, 10, -1, 0, 5)
|
new StatusMove(Moves.GUARD_SPLIT, Type.PSYCHIC, -1, 10, -1, 0, 5)
|
||||||
.attr(AverageStatsAttr, [ Stat.DEF, Stat.SPDEF ], "moveTriggers:sharedGuard"),
|
.attr(AverageStatsAttr, [ Stat.DEF, Stat.SPDEF ], "moveTriggers:sharedGuard"),
|
||||||
new StatusMove(Moves.POWER_SPLIT, Type.PSYCHIC, -1, 10, -1, 0, 5)
|
new StatusMove(Moves.POWER_SPLIT, Type.PSYCHIC, -1, 10, -1, 0, 5)
|
||||||
|
@ -7876,7 +7885,8 @@ export function initMoves() {
|
||||||
.attr(PositiveStatStagePowerAttr),
|
.attr(PositiveStatStagePowerAttr),
|
||||||
new StatusMove(Moves.QUICK_GUARD, Type.FIGHTING, -1, 15, -1, 3, 5)
|
new StatusMove(Moves.QUICK_GUARD, Type.FIGHTING, -1, 15, -1, 3, 5)
|
||||||
.target(MoveTarget.USER_SIDE)
|
.target(MoveTarget.USER_SIDE)
|
||||||
.attr(AddArenaTagAttr, ArenaTagType.QUICK_GUARD, 1, true, true),
|
.attr(AddArenaTagAttr, ArenaTagType.QUICK_GUARD, 1, true, true)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new SelfStatusMove(Moves.ALLY_SWITCH, Type.PSYCHIC, -1, 15, -1, 2, 5)
|
new SelfStatusMove(Moves.ALLY_SWITCH, Type.PSYCHIC, -1, 15, -1, 2, 5)
|
||||||
.ignoresProtect()
|
.ignoresProtect()
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
|
@ -8047,7 +8057,8 @@ export function initMoves() {
|
||||||
new StatusMove(Moves.MAT_BLOCK, Type.FIGHTING, -1, 10, -1, 0, 6)
|
new StatusMove(Moves.MAT_BLOCK, Type.FIGHTING, -1, 10, -1, 0, 6)
|
||||||
.target(MoveTarget.USER_SIDE)
|
.target(MoveTarget.USER_SIDE)
|
||||||
.attr(AddArenaTagAttr, ArenaTagType.MAT_BLOCK, 1, true, true)
|
.attr(AddArenaTagAttr, ArenaTagType.MAT_BLOCK, 1, true, true)
|
||||||
.condition(new FirstMoveCondition()),
|
.condition(new FirstMoveCondition())
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.BELCH, Type.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6)
|
new AttackMove(Moves.BELCH, Type.POISON, MoveCategory.SPECIAL, 120, 90, 10, -1, 0, 6)
|
||||||
.condition((user, target, move) => user.battleData.berriesEaten.length > 0),
|
.condition((user, target, move) => user.battleData.berriesEaten.length > 0),
|
||||||
new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, -1, 0, 6)
|
new StatusMove(Moves.ROTOTILLER, Type.GROUND, -1, 10, -1, 0, 6)
|
||||||
|
@ -8105,7 +8116,8 @@ export function initMoves() {
|
||||||
.triageMove(),
|
.triageMove(),
|
||||||
new StatusMove(Moves.CRAFTY_SHIELD, Type.FAIRY, -1, 10, -1, 3, 6)
|
new StatusMove(Moves.CRAFTY_SHIELD, Type.FAIRY, -1, 10, -1, 3, 6)
|
||||||
.target(MoveTarget.USER_SIDE)
|
.target(MoveTarget.USER_SIDE)
|
||||||
.attr(AddArenaTagAttr, ArenaTagType.CRAFTY_SHIELD, 1, true, true),
|
.attr(AddArenaTagAttr, ArenaTagType.CRAFTY_SHIELD, 1, true, true)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new StatusMove(Moves.FLOWER_SHIELD, Type.FAIRY, -1, 10, -1, 0, 6)
|
new StatusMove(Moves.FLOWER_SHIELD, Type.FAIRY, -1, 10, -1, 0, 6)
|
||||||
.target(MoveTarget.ALL)
|
.target(MoveTarget.ALL)
|
||||||
.attr(StatStageChangeAttr, [ Stat.DEF ], 1, false, (user, target, move) => target.getTypes().includes(Type.GRASS) && !target.getTag(SemiInvulnerableTag)),
|
.attr(StatStageChangeAttr, [ Stat.DEF ], 1, false, (user, target, move) => target.getTypes().includes(Type.GRASS) && !target.getTag(SemiInvulnerableTag)),
|
||||||
|
@ -8130,7 +8142,8 @@ export function initMoves() {
|
||||||
.target(MoveTarget.BOTH_SIDES)
|
.target(MoveTarget.BOTH_SIDES)
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6)
|
new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6)
|
||||||
.attr(ProtectAttr, BattlerTagType.KINGS_SHIELD),
|
.attr(ProtectAttr, BattlerTagType.KINGS_SHIELD)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new StatusMove(Moves.PLAY_NICE, Type.NORMAL, -1, 20, -1, 0, 6)
|
new StatusMove(Moves.PLAY_NICE, Type.NORMAL, -1, 20, -1, 0, 6)
|
||||||
.attr(StatStageChangeAttr, [ Stat.ATK ], -1),
|
.attr(StatStageChangeAttr, [ Stat.ATK ], -1),
|
||||||
new StatusMove(Moves.CONFIDE, Type.NORMAL, -1, 20, -1, 0, 6)
|
new StatusMove(Moves.CONFIDE, Type.NORMAL, -1, 20, -1, 0, 6)
|
||||||
|
@ -8153,7 +8166,8 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.MYSTICAL_FIRE, Type.FIRE, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 6)
|
new AttackMove(Moves.MYSTICAL_FIRE, Type.FIRE, MoveCategory.SPECIAL, 75, 100, 10, 100, 0, 6)
|
||||||
.attr(StatStageChangeAttr, [ Stat.SPATK ], -1),
|
.attr(StatStageChangeAttr, [ Stat.SPATK ], -1),
|
||||||
new SelfStatusMove(Moves.SPIKY_SHIELD, Type.GRASS, -1, 10, -1, 4, 6)
|
new SelfStatusMove(Moves.SPIKY_SHIELD, Type.GRASS, -1, 10, -1, 4, 6)
|
||||||
.attr(ProtectAttr, BattlerTagType.SPIKY_SHIELD),
|
.attr(ProtectAttr, BattlerTagType.SPIKY_SHIELD)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new StatusMove(Moves.AROMATIC_MIST, Type.FAIRY, -1, 20, -1, 0, 6)
|
new StatusMove(Moves.AROMATIC_MIST, Type.FAIRY, -1, 20, -1, 0, 6)
|
||||||
.attr(StatStageChangeAttr, [ Stat.SPDEF ], 1)
|
.attr(StatStageChangeAttr, [ Stat.SPDEF ], 1)
|
||||||
.target(MoveTarget.NEAR_ALLY),
|
.target(MoveTarget.NEAR_ALLY),
|
||||||
|
@ -8349,7 +8363,8 @@ export function initMoves() {
|
||||||
new AttackMove(Moves.FIRST_IMPRESSION, Type.BUG, MoveCategory.PHYSICAL, 90, 100, 10, -1, 2, 7)
|
new AttackMove(Moves.FIRST_IMPRESSION, Type.BUG, MoveCategory.PHYSICAL, 90, 100, 10, -1, 2, 7)
|
||||||
.condition(new FirstMoveCondition()),
|
.condition(new FirstMoveCondition()),
|
||||||
new SelfStatusMove(Moves.BANEFUL_BUNKER, Type.POISON, -1, 10, -1, 4, 7)
|
new SelfStatusMove(Moves.BANEFUL_BUNKER, Type.POISON, -1, 10, -1, 4, 7)
|
||||||
.attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER),
|
.attr(ProtectAttr, BattlerTagType.BANEFUL_BUNKER)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.SPIRIT_SHACKLE, Type.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 7)
|
new AttackMove(Moves.SPIRIT_SHACKLE, Type.GHOST, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 7)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, true)
|
.attr(AddBattlerTagAttr, BattlerTagType.TRAPPED, false, false, 1, 1, true)
|
||||||
.makesContact(false),
|
.makesContact(false),
|
||||||
|
@ -8592,6 +8607,7 @@ export function initMoves() {
|
||||||
/* Unused */
|
/* Unused */
|
||||||
new SelfStatusMove(Moves.MAX_GUARD, Type.NORMAL, -1, 10, -1, 4, 8)
|
new SelfStatusMove(Moves.MAX_GUARD, Type.NORMAL, -1, 10, -1, 4, 8)
|
||||||
.attr(ProtectAttr)
|
.attr(ProtectAttr)
|
||||||
|
.condition(failIfLastCondition)
|
||||||
.ignoresVirtual(),
|
.ignoresVirtual(),
|
||||||
/* End Unused */
|
/* End Unused */
|
||||||
new AttackMove(Moves.DYNAMAX_CANNON, Type.DRAGON, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8)
|
new AttackMove(Moves.DYNAMAX_CANNON, Type.DRAGON, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8)
|
||||||
|
@ -8770,7 +8786,8 @@ export function initMoves() {
|
||||||
.target(MoveTarget.USER_AND_ALLIES)
|
.target(MoveTarget.USER_AND_ALLIES)
|
||||||
.ignoresProtect(),
|
.ignoresProtect(),
|
||||||
new SelfStatusMove(Moves.OBSTRUCT, Type.DARK, 100, 10, -1, 4, 8)
|
new SelfStatusMove(Moves.OBSTRUCT, Type.DARK, 100, 10, -1, 4, 8)
|
||||||
.attr(ProtectAttr, BattlerTagType.OBSTRUCT),
|
.attr(ProtectAttr, BattlerTagType.OBSTRUCT)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.FALSE_SURRENDER, Type.DARK, MoveCategory.PHYSICAL, 80, -1, 10, -1, 0, 8),
|
new AttackMove(Moves.FALSE_SURRENDER, Type.DARK, MoveCategory.PHYSICAL, 80, -1, 10, -1, 0, 8),
|
||||||
new AttackMove(Moves.METEOR_ASSAULT, Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 5, -1, 0, 8)
|
new AttackMove(Moves.METEOR_ASSAULT, Type.FIGHTING, MoveCategory.PHYSICAL, 150, 100, 5, -1, 0, 8)
|
||||||
.attr(RechargeAttr)
|
.attr(RechargeAttr)
|
||||||
|
@ -9058,10 +9075,10 @@ export function initMoves() {
|
||||||
.attr(TeraBlastCategoryAttr)
|
.attr(TeraBlastCategoryAttr)
|
||||||
.attr(TeraBlastTypeAttr)
|
.attr(TeraBlastTypeAttr)
|
||||||
.attr(TeraBlastPowerAttr)
|
.attr(TeraBlastPowerAttr)
|
||||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR))
|
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1, true, (user, target, move) => user.isTerastallized() && user.isOfType(Type.STELLAR)),
|
||||||
.partial(),
|
|
||||||
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
|
new SelfStatusMove(Moves.SILK_TRAP, Type.BUG, -1, 10, -1, 4, 9)
|
||||||
.attr(ProtectAttr, BattlerTagType.SILK_TRAP),
|
.attr(ProtectAttr, BattlerTagType.SILK_TRAP)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9)
|
new AttackMove(Moves.AXE_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 90, 10, 30, 0, 9)
|
||||||
.attr(MissEffectAttr, crashDamageFunc)
|
.attr(MissEffectAttr, crashDamageFunc)
|
||||||
.attr(NoEffectAttr, crashDamageFunc)
|
.attr(NoEffectAttr, crashDamageFunc)
|
||||||
|
@ -9253,7 +9270,8 @@ export function initMoves() {
|
||||||
.attr(PreMoveMessageAttr, doublePowerChanceMessageFunc)
|
.attr(PreMoveMessageAttr, doublePowerChanceMessageFunc)
|
||||||
.attr(DoublePowerChanceAttr),
|
.attr(DoublePowerChanceAttr),
|
||||||
new SelfStatusMove(Moves.BURNING_BULWARK, Type.FIRE, -1, 10, -1, 4, 9)
|
new SelfStatusMove(Moves.BURNING_BULWARK, Type.FIRE, -1, 10, -1, 4, 9)
|
||||||
.attr(ProtectAttr, BattlerTagType.BURNING_BULWARK),
|
.attr(ProtectAttr, BattlerTagType.BURNING_BULWARK)
|
||||||
|
.condition(failIfLastCondition),
|
||||||
new AttackMove(Moves.THUNDERCLAP, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9)
|
new AttackMove(Moves.THUNDERCLAP, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9)
|
||||||
.condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct?
|
.condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct?
|
||||||
new AttackMove(Moves.MIGHTY_CLEAVE, Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9)
|
new AttackMove(Moves.MIGHTY_CLEAVE, Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9)
|
||||||
|
|
|
@ -1803,7 +1803,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.fusionSpecies = this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
|
this.fusionSpecies = this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true);
|
||||||
this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? this.fusionSpecies.ability2 ? 2 : 1 : this.fusionSpecies.ability2 ? randAbilityIndex : 0);
|
this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? 2 : this.fusionSpecies.ability2 !== this.fusionSpecies.ability1 ? randAbilityIndex : 0);
|
||||||
this.fusionShiny = this.shiny;
|
this.fusionShiny = this.shiny;
|
||||||
this.fusionVariant = this.variant;
|
this.fusionVariant = this.variant;
|
||||||
|
|
||||||
|
@ -2361,7 +2361,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
if (!isTypeImmune) {
|
if (!isTypeImmune) {
|
||||||
const levelMultiplier = (2 * source.level / 5 + 2);
|
const levelMultiplier = (2 * source.level / 5 + 2);
|
||||||
const randomMultiplier = ((this.scene.randBattleSeedInt(16) + 85) / 100);
|
const randomMultiplier = (this.randSeedIntRange(85, 100) / 100);
|
||||||
damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
|
damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
|
||||||
* stabMultiplier.value
|
* stabMultiplier.value
|
||||||
* typeMultiplier
|
* typeMultiplier
|
||||||
|
@ -3531,12 +3531,30 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
fusionCanvas.remove();
|
fusionCanvas.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random number using the current battle's seed, or the global seed if `this.scene.currentBattle` is falsy
|
||||||
|
* <!-- @import "../battle".Battle -->
|
||||||
|
* This calls either {@linkcode BattleScene.randBattleSeedInt}({@linkcode range}, {@linkcode min}) in `src/battle-scene.ts`
|
||||||
|
* which calls {@linkcode Battle.randSeedInt}(`scene`, {@linkcode range}, {@linkcode min}) in `src/battle.ts`
|
||||||
|
* which calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts`,
|
||||||
|
* or it directly calls {@linkcode Utils.randSeedInt randSeedInt}({@linkcode range}, {@linkcode min}) in `src/utils.ts` if there is no current battle
|
||||||
|
*
|
||||||
|
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
|
||||||
|
* @param min The minimum integer to pick, default `0`
|
||||||
|
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
|
||||||
|
*/
|
||||||
randSeedInt(range: integer, min: integer = 0): integer {
|
randSeedInt(range: integer, min: integer = 0): integer {
|
||||||
return this.scene.currentBattle
|
return this.scene.currentBattle
|
||||||
? this.scene.randBattleSeedInt(range, min)
|
? this.scene.randBattleSeedInt(range, min)
|
||||||
: Utils.randSeedInt(range, min);
|
: Utils.randSeedInt(range, min);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random number using the current battle's seed, or the global seed if `this.scene.currentBattle` is falsy
|
||||||
|
* @param min The minimum integer to generate
|
||||||
|
* @param max The maximum integer to generate
|
||||||
|
* @returns a random integer between {@linkcode min} and {@linkcode max} inclusive
|
||||||
|
*/
|
||||||
randSeedIntRange(min: integer, max: integer): integer {
|
randSeedIntRange(min: integer, max: integer): integer {
|
||||||
return this.randSeedInt((max - min) + 1, min);
|
return this.randSeedInt((max - min) + 1, min);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
"postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!",
|
"postSummonTeravolt": "{{pokemonNameWithAffix}} is radiating a bursting aura!",
|
||||||
"postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!",
|
"postSummonDarkAura": "{{pokemonNameWithAffix}} is radiating a Dark Aura!",
|
||||||
"postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!",
|
"postSummonFairyAura": "{{pokemonNameWithAffix}} is radiating a Fairy Aura!",
|
||||||
|
"postSummonAuraBreak": "{{pokemonNameWithAffix}} reversed all other Pokémon's auras!",
|
||||||
"postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!",
|
"postSummonNeutralizingGas": "{{pokemonNameWithAffix}}'s Neutralizing Gas filled the area!",
|
||||||
"postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!",
|
"postSummonAsOneGlastrier": "{{pokemonNameWithAffix}} has two Abilities!",
|
||||||
"postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!",
|
"postSummonAsOneSpectrier": "{{pokemonNameWithAffix}} has two Abilities!",
|
||||||
|
|
|
@ -51,5 +51,7 @@
|
||||||
"renamePokemon": "Rename Pokémon",
|
"renamePokemon": "Rename Pokémon",
|
||||||
"rename": "Rename",
|
"rename": "Rename",
|
||||||
"nickname": "Nickname",
|
"nickname": "Nickname",
|
||||||
"errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect."
|
"errorServerDown": "Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.",
|
||||||
|
"noSaves": "You don't have any save files on record!",
|
||||||
|
"tooManySaves": "You have too many save files on record!"
|
||||||
}
|
}
|
|
@ -47,10 +47,14 @@
|
||||||
"description": "Changes a Pokémon's nature to {{natureName}} and permanently unlocks the nature for the starter."
|
"description": "Changes a Pokémon's nature to {{natureName}} and permanently unlocks the nature for the starter."
|
||||||
},
|
},
|
||||||
"DoubleBattleChanceBoosterModifierType": {
|
"DoubleBattleChanceBoosterModifierType": {
|
||||||
"description": "Doubles the chance of an encounter being a double battle for {{battleCount}} battles."
|
"description": "Quadruples the chance of an encounter being a double battle for up to {{battleCount}} battles."
|
||||||
},
|
},
|
||||||
"TempStatStageBoosterModifierType": {
|
"TempStatStageBoosterModifierType": {
|
||||||
"description": "Increases the {{stat}} of all party members by 1 stage for 5 battles."
|
"description": "Increases the {{stat}} of all party members by {{amount}} for up to 5 battles.",
|
||||||
|
"extra": {
|
||||||
|
"stage": "1 stage",
|
||||||
|
"percentage": "30%"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"AttackTypeBoosterModifierType": {
|
"AttackTypeBoosterModifierType": {
|
||||||
"description": "Increases the power of a Pokémon's {{moveType}}-type moves by 20%."
|
"description": "Increases the power of a Pokémon's {{moveType}}-type moves by 20%."
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
"name_female": "ワンパンウーマン"
|
"name_female": "ワンパンウーマン"
|
||||||
},
|
},
|
||||||
"HealAchv": {
|
"HealAchv": {
|
||||||
"description": "一つの 技や 特性や 持っているアイテムで\n{{healAmount}}{{HP}}を 一気に 回復する"
|
"description": "一つの 技や 特性や 持たせたアイテムで\n{{HP}}{{healAmount}}を 一気に 回復する"
|
||||||
},
|
},
|
||||||
"250_HEAL": {
|
"250_HEAL": {
|
||||||
"name": "回復発見者"
|
"name": "回復発見者"
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
"name": "ジョーイさん"
|
"name": "ジョーイさん"
|
||||||
},
|
},
|
||||||
"LevelAchv": {
|
"LevelAchv": {
|
||||||
"description": "一つの ポケモンを Lv{{level}}まで レベルアップする"
|
"description": "一つの ポケモンを Lv.{{level}}まで 上げる"
|
||||||
},
|
},
|
||||||
"LV_100": {
|
"LV_100": {
|
||||||
"name": "まだまだだよ"
|
"name": "まだまだだよ"
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
"name": "マスターリーグチャンピオン"
|
"name": "マスターリーグチャンピオン"
|
||||||
},
|
},
|
||||||
"TRANSFER_MAX_STAT_STAGE": {
|
"TRANSFER_MAX_STAT_STAGE": {
|
||||||
"name": "同力",
|
"name": "連係プレー",
|
||||||
"description": "少なくとも 一つの 能力を 最大まで あげて\n他の 手持ちポケモンに バトンタッチする"
|
"description": "少なくとも 一つの 能力を 最大まで あげて\n他の 手持ちポケモンに バトンタッチする"
|
||||||
},
|
},
|
||||||
"MAX_FRIENDSHIP": {
|
"MAX_FRIENDSHIP": {
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
"description": "一つの 手持ちポケモンを メガシンカさせる"
|
"description": "一つの 手持ちポケモンを メガシンカさせる"
|
||||||
},
|
},
|
||||||
"GIGANTAMAX": {
|
"GIGANTAMAX": {
|
||||||
"name": "太―くて 堪らない",
|
"name": "太ーくて堪らない",
|
||||||
"description": "一つの 手持ちポケモンを キョダイマックスさせる"
|
"description": "一つの 手持ちポケモンを キョダイマックスさせる"
|
||||||
},
|
},
|
||||||
"TERASTALLIZE": {
|
"TERASTALLIZE": {
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
"description": "一つの 手持ちポケモンを ステラ・テラスタルさせる"
|
"description": "一つの 手持ちポケモンを ステラ・テラスタルさせる"
|
||||||
},
|
},
|
||||||
"SPLICE": {
|
"SPLICE": {
|
||||||
"name": "インフィニット・フュジョン",
|
"name": "インフィニット・フュージョン",
|
||||||
"description": "遺伝子のくさびで 二つの ポケモンを 吸収合体させる"
|
"description": "遺伝子のくさびで 二つの ポケモンを 吸収合体させる"
|
||||||
},
|
},
|
||||||
"MINI_BLACK_HOLE": {
|
"MINI_BLACK_HOLE": {
|
||||||
|
@ -205,7 +205,7 @@
|
||||||
"description": "{{type}}タイプの 単一タイプチャレンジを クリアする"
|
"description": "{{type}}タイプの 単一タイプチャレンジを クリアする"
|
||||||
},
|
},
|
||||||
"MONO_NORMAL": {
|
"MONO_NORMAL": {
|
||||||
"name": "凡人"
|
"name": "超凡人"
|
||||||
},
|
},
|
||||||
"MONO_FIGHTING": {
|
"MONO_FIGHTING": {
|
||||||
"name": "八千以上だ!!"
|
"name": "八千以上だ!!"
|
||||||
|
@ -223,7 +223,7 @@
|
||||||
"name": "タケシの挑戦状"
|
"name": "タケシの挑戦状"
|
||||||
},
|
},
|
||||||
"MONO_BUG": {
|
"MONO_BUG": {
|
||||||
"name": "チョウチョウせん者"
|
"name": "チョウチョウ戦者"
|
||||||
},
|
},
|
||||||
"MONO_GHOST": {
|
"MONO_GHOST": {
|
||||||
"name": "貞子ちゃん"
|
"name": "貞子ちゃん"
|
||||||
|
|
|
@ -1 +1,43 @@
|
||||||
{}
|
{
|
||||||
|
"activeBattleEffects": "場の効果",
|
||||||
|
"player": "味方",
|
||||||
|
"neutral": "場の全員",
|
||||||
|
"enemy": "相手",
|
||||||
|
|
||||||
|
"sunny": "晴れ",
|
||||||
|
"rain": "雨",
|
||||||
|
"sandstorm": "砂あらし",
|
||||||
|
"hail": "あられ",
|
||||||
|
"snow": "雪",
|
||||||
|
"fog": "きり",
|
||||||
|
"heavyRain": "強い雨",
|
||||||
|
"harshSun": "大日照り",
|
||||||
|
"strongWinds": "乱気流",
|
||||||
|
|
||||||
|
"misty": "ミストフィールド",
|
||||||
|
"electric": "エレキフィールド",
|
||||||
|
"grassy": "グラスフィールド",
|
||||||
|
"psychic": "サイコフィールド",
|
||||||
|
|
||||||
|
"mudSport": "どろあそび",
|
||||||
|
"waterSport": "みずあそび",
|
||||||
|
"spikes": "まきびし",
|
||||||
|
"toxicSpikes": "どくびし",
|
||||||
|
"mist": "しろいきり",
|
||||||
|
"futureSight": "みらいよち",
|
||||||
|
"doomDesire": "はめつのねがい",
|
||||||
|
"wish": "ねがいごと",
|
||||||
|
"stealthRock": "ステルスロック",
|
||||||
|
"stickyWeb": "ねばねばネット",
|
||||||
|
"trickRoom": "トリックルーム",
|
||||||
|
"gravity": "じゅうりょく",
|
||||||
|
"reflect": "リフレクター",
|
||||||
|
"lightScreen": "ひかりのかべ",
|
||||||
|
"auroraVeil": "オーロラベール",
|
||||||
|
"quickGuard": "ファストガード",
|
||||||
|
"wideGuard": "ワイドガード",
|
||||||
|
"matBlock": "たたみがえし",
|
||||||
|
"craftyShield": "トリックガード",
|
||||||
|
"tailwind": "おいかぜ",
|
||||||
|
"happyHour": "ハッピータイム"
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,150 @@
|
||||||
{}
|
{
|
||||||
|
"music": "BGM: ",
|
||||||
|
"missing_entries": "{{name}}",
|
||||||
|
"battle_kanto_champion": "B2W2 戦闘!チャンピオン(カントー)",
|
||||||
|
"battle_johto_champion": "B2W2 戦闘!チャンピオン(ジョウト)",
|
||||||
|
"battle_hoenn_champion_g5": "B2W2 戦闘!チャンピオン(ホウエン)",
|
||||||
|
"battle_hoenn_champion_g6": "ORAS 決戦!ダイゴ",
|
||||||
|
"battle_sinnoh_champion": "B2W2 戦闘!チャンピオン(シンオウ)",
|
||||||
|
"battle_champion_alder": "BW チャンピオン アデク",
|
||||||
|
"battle_champion_iris": "B2W2 戦闘!チャンピオンアイリス",
|
||||||
|
"battle_kalos_champion": "XY 戦闘!チャンピオン",
|
||||||
|
"battle_alola_champion": "USUM 頂上決戦!ハウ",
|
||||||
|
"battle_galar_champion": "SWSH 決戦!チャンピオンダンデ",
|
||||||
|
"battle_champion_geeta": "SV 戦闘!トップチャンピオン",
|
||||||
|
"battle_champion_nemona": "SV 戦闘!チャンピオンネモ",
|
||||||
|
"battle_champion_kieran": "SV 戦闘!チャンピオンスグリ",
|
||||||
|
"battle_hoenn_elite": "ORAS 戦闘!四天王",
|
||||||
|
"battle_unova_elite": "BW 戦闘!四天王",
|
||||||
|
"battle_kalos_elite": "XY 戦闘!四天王",
|
||||||
|
"battle_alola_elite": "SM 戦闘!四天王",
|
||||||
|
"battle_galar_elite": "SWSH 戦闘!ファイナルトーナメント!",
|
||||||
|
"battle_paldea_elite": "SV 戦闘!四天王",
|
||||||
|
"battle_bb_elite": "SV 戦闘!ブルベリーグ四天王",
|
||||||
|
"battle_final_encounter": "ポケダンDX レックウザ登場",
|
||||||
|
"battle_final": "BW 戦闘!ゲーチス",
|
||||||
|
"battle_kanto_gym": "B2W2 戦闘!ジムリーダー(カントー)",
|
||||||
|
"battle_johto_gym": "B2W2 戦闘!ジムリーダー(ジョウト)",
|
||||||
|
"battle_hoenn_gym": "B2W2 戦闘!ジムリーダー(ホウエン)",
|
||||||
|
"battle_sinnoh_gym": "B2W2 戦闘!ジムリーダー(シンオウ)",
|
||||||
|
"battle_unova_gym": "BW 戦闘!ジムリーダー",
|
||||||
|
"battle_kalos_gym": "XY 戦闘!ジムリーダー",
|
||||||
|
"battle_galar_gym": "SWSH 戦闘!ジムリーダー",
|
||||||
|
"battle_paldea_gym": "SV 戦闘!ジムリーダー",
|
||||||
|
"battle_legendary_kanto": "XY 戦闘!ミュウツー",
|
||||||
|
"battle_legendary_raikou": "HGSS 戦闘!ライコウ",
|
||||||
|
"battle_legendary_entei": "HGSS 戦闘!エンテイ",
|
||||||
|
"battle_legendary_suicune": "HGSS 戦闘!スイクン",
|
||||||
|
"battle_legendary_lugia": "HGSS 戦闘!ルギア",
|
||||||
|
"battle_legendary_ho_oh": "HGSS 戦闘!ホウオウ",
|
||||||
|
"battle_legendary_regis_g5": "B2W2 戦闘!レジロック・レジアイス・レジスチル",
|
||||||
|
"battle_legendary_regis_g6": "ORAS 戦闘!レジロック・レジアイス・レジスチル",
|
||||||
|
"battle_legendary_gro_kyo": "ORAS 戦闘!ゲンシカイキ",
|
||||||
|
"battle_legendary_rayquaza": "ORAS 戦闘!超古代ポケモン",
|
||||||
|
"battle_legendary_deoxys": "ORAS 戦闘!デオキシス",
|
||||||
|
"battle_legendary_lake_trio": "ORAS 戦闘!ユクシー・エムリット・アグノム",
|
||||||
|
"battle_legendary_sinnoh": "ORAS 戦闘!伝説のポケモン(シンオウ)",
|
||||||
|
"battle_legendary_dia_pal": "ORAS 戦闘!ディアルガ・パルキア",
|
||||||
|
"battle_legendary_origin_forme": "LA 戦い:ディアルガ・パルキア(オリジンフォルム)",
|
||||||
|
"battle_legendary_giratina": "ORAS 戦闘!ギラティナ",
|
||||||
|
"battle_legendary_arceus": "HGSS アルセウス",
|
||||||
|
"battle_legendary_unova": "BW 戦闘!伝説のポケモン",
|
||||||
|
"battle_legendary_kyurem": "BW 戦闘!キュレム",
|
||||||
|
"battle_legendary_res_zek": "BW 戦闘!ゼクロム・レシラム",
|
||||||
|
"battle_legendary_xern_yvel": "XY 戦闘!ゼルネアス・イベルタル・ジガルデ",
|
||||||
|
"battle_legendary_tapu": "SM 戦闘!カプ",
|
||||||
|
"battle_legendary_sol_lun": "SM 戦闘!ソルガレオ・ルナアーラ",
|
||||||
|
"battle_legendary_ub": "SM 戦闘!ウルトラビースト",
|
||||||
|
"battle_legendary_dusk_dawn": "USUM 戦闘!日食・月食ネクロズマ",
|
||||||
|
"battle_legendary_ultra_nec": "USUM 戦闘!ウルトラネクロズマ",
|
||||||
|
"battle_legendary_zac_zam": "SWSH 戦闘!ザシアン・ザマゼンタ",
|
||||||
|
"battle_legendary_glas_spec": "SWSH 戦闘!ブリザポス・レイスポス",
|
||||||
|
"battle_legendary_calyrex": "SWSH 戦闘!バドレックス",
|
||||||
|
"battle_legendary_riders": "SWSH 戦闘!豊穣の王",
|
||||||
|
"battle_legendary_birds_galar": "SWSH 戦闘!伝説のとりポケモン",
|
||||||
|
"battle_legendary_ruinous": "SV 戦闘!災厄ポケモン",
|
||||||
|
"battle_legendary_kor_mir": "SV 戦闘!エリアゼロのポケモン",
|
||||||
|
"battle_legendary_loyal_three": "SV 戦闘!ともっこ",
|
||||||
|
"battle_legendary_ogerpon": "SV 戦闘!オーガポン",
|
||||||
|
"battle_legendary_terapagos": "SV 戦闘!テラパゴス",
|
||||||
|
"battle_legendary_pecharunt": "SV 戦闘!モモワロウ",
|
||||||
|
"battle_rival": "BW 戦闘!チェレン・ベル",
|
||||||
|
"battle_rival_2": "BW 戦闘!N",
|
||||||
|
"battle_rival_3": "BW 決戦!N",
|
||||||
|
"battle_trainer": "BW 戦闘!トレーナー",
|
||||||
|
"battle_wild": "BW 戦闘!野生ポケモン",
|
||||||
|
"battle_wild_strong": "BW 戦闘!強い野生ポケモン",
|
||||||
|
"end_summit": "ポケダンDX 天空の塔 最上階",
|
||||||
|
"battle_rocket_grunt": "HGSS 戦闘!ロケット団",
|
||||||
|
"battle_aqua_magma_grunt": "ORAS 戦闘!アクア団・マグマ団",
|
||||||
|
"battle_galactic_grunt": "BDSP 戦闘!ギンガ団",
|
||||||
|
"battle_plasma_grunt": "BW 戦闘!プラズマ団",
|
||||||
|
"battle_flare_grunt": "XY 戦闘!フレア団",
|
||||||
|
"battle_aether_grunt": "SM 戦闘!エーテル財団トレーナー",
|
||||||
|
"battle_skull_grunt": "SM 戦闘!スカル団",
|
||||||
|
"battle_macro_grunt": "SWSH 戦闘!トレーナー",
|
||||||
|
"battle_galactic_admin": "BDSP 戦闘!ギンガ団幹部",
|
||||||
|
"battle_skull_admin": "SM 戦闘!スカル団幹部",
|
||||||
|
"battle_oleana": "SWSH 戦闘!オリーヴ",
|
||||||
|
"battle_rocket_boss": "USUM 戦闘!レインボーロケット団ボス",
|
||||||
|
"battle_aqua_magma_boss": "ORAS 戦闘!アクア団・マグマ団のリーダー",
|
||||||
|
"battle_galactic_boss": "BDSP 戦闘!ギンガ団ボス",
|
||||||
|
"battle_plasma_boss": "B2W2 戦闘!ゲーチス",
|
||||||
|
"battle_flare_boss": "XY 戦闘!フラダリ",
|
||||||
|
"battle_aether_boss": "SM 戦闘!ルザミーネ",
|
||||||
|
"battle_skull_boss": "SM 戦闘!スカル団ボス",
|
||||||
|
"battle_macro_boss": "SWSH 戦闘!ローズ",
|
||||||
|
|
||||||
|
"abyss": "ポケダン空 やみのかこう",
|
||||||
|
"badlands": "ポケダン空 こかつのたに",
|
||||||
|
"beach": "ポケダン空 しめったいわば",
|
||||||
|
"cave": "ポケダン空 そらのいただき(どうくつ)",
|
||||||
|
"construction_site": "ポケダン空 きょだいがんせきぐん",
|
||||||
|
"desert": "ポケダン空 きたのさばく",
|
||||||
|
"dojo": "ポケダン空 ガラガラどうじょう",
|
||||||
|
"end": "ポケダンDX 天空の塔",
|
||||||
|
"factory": "ポケダン空 かくされたいせき",
|
||||||
|
"fairy_cave": "ポケダン空 ほしのどうくつ",
|
||||||
|
"forest": "ポケダン空 くろのもり",
|
||||||
|
"grass": "ポケダン空 リンゴのもり",
|
||||||
|
"graveyard": "ポケダン空 しんぴのもり",
|
||||||
|
"ice_cave": "ポケダン空 だいひょうざん",
|
||||||
|
"island": "ポケダン空 えんがんのいわば",
|
||||||
|
"jungle": "Lmz - Jungle(ジャングル)",
|
||||||
|
"laboratory": "Firel - Laboratory(ラボラトリー)",
|
||||||
|
"lake": "ポケダン空 すいしょうのどうくつ",
|
||||||
|
"meadow": "ポケダン空 そらのいただき(もり)",
|
||||||
|
"metropolis": "Firel - Metropolis(大都市)",
|
||||||
|
"mountain": "ポケダン空 ツノやま",
|
||||||
|
"plains": "ポケダン空 そらのいただき(そうげん)",
|
||||||
|
"power_plant": "ポケダン空 エレキへいげん",
|
||||||
|
"ruins": "ポケダン空 ふういんのいわば",
|
||||||
|
"sea": "Andr06 - Marine Mystique(海の神秘性)",
|
||||||
|
"seabed": "Firel - Seabed(海底)",
|
||||||
|
"slum": "Andr06 - Sneaky Snom(ずるいユキハミ)",
|
||||||
|
"snowy_forest": "ポケダン空 そらのいただき(ゆきやま)",
|
||||||
|
"space": "Firel - Aether(エーテル)",
|
||||||
|
"swamp": "ポケダン空 とざされたうみ",
|
||||||
|
"tall_grass": "ポケダン空 のうむのもり",
|
||||||
|
"temple": "ポケダン空 ばんにんのどうくつ",
|
||||||
|
"town": "ポケダン空 ランダムダンジョン3",
|
||||||
|
"volcano": "ポケダン空 ねっすいのどうくつ",
|
||||||
|
"wasteland": "ポケダン空 まぼろしのだいち",
|
||||||
|
"encounter_ace_trainer": "BW 視線!エリートトレーナー",
|
||||||
|
"encounter_backpacker": "BW 視線!バックパッカー",
|
||||||
|
"encounter_clerk": "BW 視線!ビジネスマン",
|
||||||
|
"encounter_cyclist": "BW 視線!サイクリング",
|
||||||
|
"encounter_lass": "BW 視線!ミニスカート",
|
||||||
|
"encounter_parasol_lady": "BW 視線!パラソルおねえさん",
|
||||||
|
"encounter_pokefan": "BW 視線!だいすきクラブ",
|
||||||
|
"encounter_psychic": "BW 視線!サイキッカー",
|
||||||
|
"encounter_rich": "BW 視線!ジェントルマン",
|
||||||
|
"encounter_rival": "BW チェレンのテーマ",
|
||||||
|
"encounter_roughneck": "BW 視線!スキンヘッズ",
|
||||||
|
"encounter_scientist": "BW 視線!けんきゅういん",
|
||||||
|
"encounter_twins": "BW 視線!ふたごちゃん",
|
||||||
|
"encounter_youngster": "BW 視線!たんぱんこぞう",
|
||||||
|
"heal": "BW 回復",
|
||||||
|
"menu": "ポケダン空 ようこそ! ポケモンたちのせかいへ!",
|
||||||
|
"title": "ポケダン空 トップメニュー"
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"title": "チャレンジを 設定",
|
"title": "チャレンジの設定",
|
||||||
"illegalEvolution": "{{pokemon}}は このチャレンジで\n対象外の ポケモンに なってしまった!",
|
"illegalEvolution": "{{pokemon}}は このチャレンジで\n対象外の ポケモンに なってしまった!",
|
||||||
"singleGeneration": {
|
"singleGeneration": {
|
||||||
"name": "単一世代",
|
"name": "単一世代",
|
||||||
|
|
|
@ -1 +1,8 @@
|
||||||
{}
|
{
|
||||||
|
"start": "スタート",
|
||||||
|
"luckIndicator": "運:",
|
||||||
|
"shinyOnHover": "色違い",
|
||||||
|
"commonShiny": "ふつう",
|
||||||
|
"rareShiny": "レア",
|
||||||
|
"epicShiny": "超レア"
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,84 @@
|
||||||
{}
|
{
|
||||||
|
"blue_red_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Blue: Hey Red, let's show them what we're made of!\n$Red: ...\n$Blue: This is Pallet Town Power!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Blue: That was a great battle!\n$Red: ..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"red_blue_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Red: ...!\n$Blue: He never talks much.\n$Blue: But dont let that fool you! He is a champ after all!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Red: ...!\n$Blue: Next time we will beat you!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tate_liza_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Tate: Are you surprised?\n$Liza: We are two gym leaders at once!\n$Tate: We are twins!\n$Liza: We dont need to talk to understand each other!\n$Tate: Twice the power...\n$Liza: Can you handle it?"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Tate: What? Our combination was perfect!\n$Liza: Looks like we need to train more..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"liza_tate_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Liza: Hihihi... Are you surprised?\n$Tate: Yes, we are really two gym leaders at once!\n$Liza: This is my twin brother Tate!\n$Tate: And this is my twin sister Liza!\n$Liza: Don't you think we are a perfect combination?"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Liza: Are we...\n$Tate: ...not as strong as we thought?"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wallace_steven_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Steven: Wallace, let's show them the power of the champions!\n$Wallace: We will show you the power of Hoenn!\n$Steven: Let's go!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Steven: That was a great battle!\n$Wallace: We will win next time!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"steven_wallace_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Steven: Do you have any rare Pokémon?\n$Wallace: Steven... We are here for a battle, not to show off our Pokémon.\n$Steven: Oh... I see... Let's go then!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Steven: Now that we are done with the battle, let's show off our Pokémon!\n$Wallace: Steven..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"alder_iris_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Alder: We are the strongest trainers in Unova!\n$Iris: Fights against strong trainers are the best!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Alder: Wow! You are super strong!\n$Iris: We will win next time!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iris_alder_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?",
|
||||||
|
"1_female": "Iris: Welcome Challenger! I am THE Unova Champion!\n$Alder: Iris, aren't you a bit too excited?"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Iris: A loss like this is not easy to take...\n$Alder: But we will only get stronger with every loss!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"piers_marnie_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Marnie: Brother, let's show them the power of Spikemuth!\n$Piers: We bring darkness!"
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Marnie: You brought light to our darkness!\n$Piers: Its too bright..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"marnie_piers_double": {
|
||||||
|
"encounter": {
|
||||||
|
"1": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing...",
|
||||||
|
"1_female": "Piers: Ready for a concert?\n$Marnie: Brother... They are here to fight, not to sing..."
|
||||||
|
},
|
||||||
|
"victory": {
|
||||||
|
"1": "Piers: Now that was a great concert!\n$Marnie: Brother..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,10 @@
|
||||||
{}
|
{
|
||||||
|
"encounter": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.",
|
||||||
|
"encounter_female": "It appears the time has finally come once again.\nYou know why you have come here, do you not?\n$You were drawn here, because you have been here before.\nCountless times.\n$Though, perhaps it can be counted.\nTo be precise, this is in fact your {{cycleCount}} cycle.\n$Each cycle your mind reverts to its former state.\nEven so, somehow, remnants of your former selves remain.\n$Until now you have yet to succeed, but I sense a different presence in you this time.\n\n$You are the only one here, though it is as if there is… another.\n$Will you finally prove a formidable challenge to me?\nThe challenge I have longed after for millennia?\n$We begin.",
|
||||||
|
"firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.",
|
||||||
|
"secondStageWin": "…Magnificent.",
|
||||||
|
"key_ordinal_one": "st",
|
||||||
|
"key_ordinal_two": "nd",
|
||||||
|
"key_ordinal_few": "rd",
|
||||||
|
"key_ordinal_other": "th"
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,6 @@
|
||||||
{}
|
{
|
||||||
|
"ending": "@c{shock}You're back?@d{32} Does that mean…@d{96} you won?!\n@c{smile_ehalf}I should have known you had it in you.\n$@c{smile_eclosed}Of course… I always had that feeling.\n@c{smile}It's over now, right? You ended the loop.\n$@c{smile_ehalf}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$I'll be the only one to remember what you did.\n@c{angry_mopen}I'll try not to forget!\n$@c{smile_wave_wink}Just kidding!@d{64} @c{smile}I'd never forget.@d{32}\nYour legend will live on in our hearts.\n$@c{smile_wave}Anyway,@d{64} it's getting late…@d{96} I think?\nIt's hard to tell in this place.\n$Let's go home. @c{smile_wave_wink}Maybe tomorrow, we can have another battle, for old time's sake?",
|
||||||
|
"ending_female": "@c{smile}Oh? You won?@d{96} @c{smile_eclosed}I guess I should've known.\nBut, you're back now.\n$@c{smile}It's over.@d{64} You ended the loop.\n$@c{serious_smile_fists}You fulfilled your dream too, didn't you?\nYou didn't lose even once.\n$@c{neutral}I'm the only one who'll remember what you did.@d{96}\nI guess that's okay, isn't it?\n$@c{serious_smile_fists}Your legend will always live on in our hearts.\n$@c{smile_eclosed}Anyway, I've had about enough of this place, haven't you? Let's head home.\n$@c{serious_smile_fists}Maybe when we get back, we can have another battle?\nIf you're up to it.",
|
||||||
|
"ending_endless": "Congratulations on reaching the current end!\nMore content is coming soon.",
|
||||||
|
"ending_name": "Devs"
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"pp": "PP",
|
"pp": "PP",
|
||||||
"power": "いりょく",
|
"power": "威力",
|
||||||
"accuracy": "めいちゅう",
|
"accuracy": "命中",
|
||||||
"abilityFlyInText": " {{pokemonName}}の {{passive}}{{abilityName}}",
|
"abilityFlyInText": " {{pokemonName}}の\n{{passive}}:{{abilityName}}",
|
||||||
"passive": "Passive "
|
"passive": "パッシブ "
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
"noPokerus": "ポケルス - なし",
|
"noPokerus": "ポケルス - なし",
|
||||||
"sortByNumber": "No.",
|
"sortByNumber": "No.",
|
||||||
"sortByCost": "ポイント",
|
"sortByCost": "ポイント",
|
||||||
"sortByCandies": "飴の数",
|
"sortByCandies": "アメの数",
|
||||||
"sortByIVs": "個体値",
|
"sortByIVs": "個体値",
|
||||||
"sortByName": "名前"
|
"sortByName": "名前"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,26 +12,26 @@
|
||||||
"dailyRunAttempts": "デイリーラン",
|
"dailyRunAttempts": "デイリーラン",
|
||||||
"dailyRunWins": "デイリーラン勝利",
|
"dailyRunWins": "デイリーラン勝利",
|
||||||
"endlessRuns": "エンドレスラン",
|
"endlessRuns": "エンドレスラン",
|
||||||
"highestWaveEndless": "エンドレス最高波",
|
"highestWaveEndless": "エンドレス最高ラウンド",
|
||||||
"highestMoney": "最大貯金",
|
"highestMoney": "最大貯金",
|
||||||
"highestDamage": "最大ダメージ",
|
"highestDamage": "最大ダメージ",
|
||||||
"highestHPHealed": "最大HP回復",
|
"highestHPHealed": "最大HP回復",
|
||||||
"pokemonEncountered": "遭遇したポケモン",
|
"pokemonEncountered": "遭遇したポケモン",
|
||||||
"pokemonDefeated": "倒したポケモン",
|
"pokemonDefeated": "倒したポケモン",
|
||||||
"pokemonCaught": "捕まえたポケモン",
|
"pokemonCaught": "捕まえたポケモン",
|
||||||
"eggsHatched": "孵化したタマゴ",
|
"eggsHatched": "ふかしたタマゴ",
|
||||||
"subLegendsSeen": "見つけた順伝説ポケモン",
|
"subLegendsSeen": "見つけた順伝説ポケモン",
|
||||||
"subLegendsCaught": "捕まえた準伝説ポケモン",
|
"subLegendsCaught": "捕まえた準伝説ポケモン",
|
||||||
"subLegendsHatched": "孵化した準伝説ポケモン",
|
"subLegendsHatched": "ふかした準伝説ポケモン",
|
||||||
"legendsSeen": "見つけた伝説ポケモン",
|
"legendsSeen": "見つけた伝説ポケモン",
|
||||||
"legendsCaught": "捕まえた伝説ポケモン",
|
"legendsCaught": "捕まえた伝説ポケモン",
|
||||||
"legendsHatched": "孵化した伝説ポケモン",
|
"legendsHatched": "ふかした伝説ポケモン",
|
||||||
"mythicalsSeen": "見つけた幻ポケモン",
|
"mythicalsSeen": "見つけた幻ポケモン",
|
||||||
"mythicalsCaught": "捕まえた幻ポケモン",
|
"mythicalsCaught": "捕まえた幻ポケモン",
|
||||||
"mythicalsHatched": "孵化した幻ポケモン",
|
"mythicalsHatched": "ふかした幻ポケモン",
|
||||||
"shiniesSeen": "見つけた色違いポケモン",
|
"shiniesSeen": "見つけた色違いポケモン",
|
||||||
"shiniesCaught": "捕まえた色違いポケモン",
|
"shiniesCaught": "捕まえた色違いポケモン",
|
||||||
"shiniesHatched": "孵化した色違いポケモン",
|
"shiniesHatched": "ふかした色違いポケモン",
|
||||||
"pokemonFused": "吸収合体したポケモン",
|
"pokemonFused": "吸収合体したポケモン",
|
||||||
"trainersDefeated": "倒したトレーナー",
|
"trainersDefeated": "倒したトレーナー",
|
||||||
"eggsPulled": "引いたタマゴ",
|
"eggsPulled": "引いたタマゴ",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"Erratic": "60まんタイプ",
|
"Erratic": "60万タイプ",
|
||||||
"Fast": "80まんタイプ",
|
"Fast": "80万タイプ",
|
||||||
"Medium_Fast": "100まんタイプ",
|
"Medium_Fast": "100万タイプ",
|
||||||
"Medium_Slow": "105まんタイプ",
|
"Medium_Slow": "105万タイプ",
|
||||||
"Slow": "125まんタイプ",
|
"Slow": "125万タイプ",
|
||||||
"Fluctuating": "164まんタイプ"
|
"Fluctuating": "164万タイプ"
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,6 @@
|
||||||
"linkGoogle": "Google連携",
|
"linkGoogle": "Google連携",
|
||||||
"unlinkGoogle": "Google連携解除",
|
"unlinkGoogle": "Google連携解除",
|
||||||
"cancel": "キャンセル",
|
"cancel": "キャンセル",
|
||||||
"losingProgressionWarning": "戦闘開始からの データが 保存されません。\nよろしいですか?",
|
"losingProgressionWarning": "戦闘開始からの データが セーブされません。\nよろしいですか?",
|
||||||
"noEggs": "現在は タマゴを 孵化していません!"
|
"noEggs": "現在は タマゴを ふかしていません!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,7 +353,7 @@
|
||||||
"description": "やせいのポケモンがかくれとくせいをもつかくりつをおおきくふやす"
|
"description": "やせいのポケモンがかくれとくせいをもつかくりつをおおきくふやす"
|
||||||
},
|
},
|
||||||
"IV_SCANNER": {
|
"IV_SCANNER": {
|
||||||
"name": "こたいち たんちき",
|
"name": "こたいちスキャナー",
|
||||||
"description": "やせいのポケモンのこたいちをスキャンできる。スタックごとに2つのこたいちがあきらかになる。もっともたかいこたいちがさいしょにひょうじされる"
|
"description": "やせいのポケモンのこたいちをスキャンできる。スタックごとに2つのこたいちがあきらかになる。もっともたかいこたいちがさいしょにひょうじされる"
|
||||||
},
|
},
|
||||||
"DNA_SPLICERS": {
|
"DNA_SPLICERS": {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"surviveDamageApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で もちこたえた!",
|
"surviveDamageApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で もちこたえた!",
|
||||||
"turnHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 回復!",
|
"turnHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 体力を 回復した!",
|
||||||
"hitHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 回復!",
|
"hitHealApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 少し 体力を 回復した!",
|
||||||
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 復活した!",
|
"pokemonInstantReviveApply": "{{pokemonNameWithAffix}}は\n{{typeName}}で 復活した!",
|
||||||
"resetNegativeStatStageApply": "{{pokemonNameWithAffix}}は {{typeName}}で\n下がった能力が 元に戻った!",
|
"resetNegativeStatStageApply": "{{pokemonNameWithAffix}}は {{typeName}}で\n下がった能力が 元に戻った!",
|
||||||
"moneyInterestApply": "{{typeName}}から {{moneyAmount}}円 取得した!",
|
"moneyInterestApply": "{{typeName}}から {{moneyAmount}}円 取得した!",
|
||||||
"turnHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を 吸い取った!",
|
"turnHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を 吸い取った!",
|
||||||
"contactHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を うばい取った!",
|
"contactHeldItemTransferApply": "{{pokemonName}}の {{typeName}}が\n{{pokemonNameWithAffix}}の {{itemName}}を 奪い取った!",
|
||||||
"enemyTurnHealApply": "{{pokemonNameWithAffix}}は\n体力を 回復!",
|
"enemyTurnHealApply": "{{pokemonNameWithAffix}}は\n体力を 回復!",
|
||||||
"bypassSpeedChanceApply": "{{pokemonName}}は {{itemName}}で\n行動が はやくなった!"
|
"bypassSpeedChanceApply": "{{pokemonName}}は {{itemName}}で\n行動が はやくなった!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +1,69 @@
|
||||||
{
|
{
|
||||||
"hitWithRecoil": "{{pokemonName}}は\nはんどうによる ダメージを うけた!",
|
"hitWithRecoil": "{{pokemonName}}は\n反動による ダメージを 受けた!",
|
||||||
"cutHpPowerUpMove": "{{pokemonName}}は\nたいりょくを けずって パワーぜんかい!",
|
"cutHpPowerUpMove": "{{pokemonName}}は\n体力を 削って 技の 威力を 上がった!",
|
||||||
"absorbedElectricity": "{{pokemonName}}は\n でんきを きゅうしゅうした!",
|
"absorbedElectricity": "{{pokemonName}}は\n 電気を 吸収した!",
|
||||||
"switchedStatChanges": "{{pokemonName}}は あいてと じぶんの\nのうりょくへんかを いれかえた!",
|
"switchedStatChanges": "{{pokemonName}}は 相手と 自分の\n能力変化を 入れ替えた!",
|
||||||
"sharedGuard": "{{pokemonName}}は\nおたがいのガードを シェアした!",
|
"switchedTwoStatChanges": "{{pokemonName}}は 相手と 自分の {{firstStat}}と\n{{secondStat}}の 能力変化を 入れ替えた!",
|
||||||
"sharedPower": "{{pokemonName}}は\nおたがいのパワーを シェアした!",
|
"switchedStat": "{{pokemonName}}は 相手と {{stat}}を 入れ替えた!",
|
||||||
"goingAllOutForAttack": "{{pokemonName}}は\nほんきを だした!",
|
"sharedGuard": "{{pokemonName}}は\nお互いのガードを シェアした!",
|
||||||
"regainedHealth": "{{pokemonName}}は\nたいりょくを かいふくした!",
|
"sharedPower": "{{pokemonName}}は\nお互いのパワーを シェアした!",
|
||||||
"keptGoingAndCrashed": "いきおいあまって {{pokemonName}}は\nじめんに ぶつかった!",
|
"goingAllOutForAttack": "{{pokemonName}}は\n本気を 出した!",
|
||||||
"fled": "{{pokemonName}}は にげだした!",
|
"regainedHealth": "{{pokemonName}}は\n体力を 回復した!",
|
||||||
"cannotBeSwitchedOut": "{{pokemonName}}を\nもどすことが できない!",
|
"keptGoingAndCrashed": "勢い余って {{pokemonName}}は\n地面に ぶつかった!",
|
||||||
"swappedAbilitiesWithTarget": "{{pokemonName}}は\nおたがいの とくせいを いれかえた!",
|
"fled": "{{pokemonName}}は 逃げ出した!",
|
||||||
"coinsScatteredEverywhere": "こばんが あたりに ちらばった!",
|
"cannotBeSwitchedOut": "{{pokemonName}}を\n戻すことが できない!",
|
||||||
|
"swappedAbilitiesWithTarget": "{{pokemonName}}は\nお互いの 特性を 入れ替えた!",
|
||||||
|
"coinsScatteredEverywhere": "小判が 辺りに 散らばった!",
|
||||||
"attackedByItem": "{{pokemonName}}に\n{{itemName}}が おそいかかる!",
|
"attackedByItem": "{{pokemonName}}に\n{{itemName}}が おそいかかる!",
|
||||||
"whippedUpAWhirlwind": "{{pokemonName}}の まわりで\nくうきが うずをまく!",
|
"whippedUpAWhirlwind": "{{pokemonName}}の 周りで\n空気が 渦を巻く!",
|
||||||
"flewUpHigh": "{{pokemonName}}は\nそらたかく とびあがった!",
|
"flewUpHigh": "{{pokemonName}}は\n空高く 飛び上がった!",
|
||||||
"tookInSunlight": "{{pokemonName}}は\nひかりを きゅうしゅうした!",
|
"tookInSunlight": "{{pokemonName}}は\n光を 吸収した!",
|
||||||
"dugAHole": "{{pokemonName}}は\nじめんに もぐった!",
|
"dugAHole": "{{pokemonName}}は\n地面に 潜った!",
|
||||||
"loweredItsHead": "{{pokemonName}}は\nくびを ひっこめた!",
|
"loweredItsHead": "{{pokemonName}}は\n首を 引っ込めた!",
|
||||||
"isGlowing": "{{pokemonName}}を\nはげしいひかりが つつむ!",
|
"isGlowing": "{{pokemonName}}を\n激しい光が 包む!",
|
||||||
"bellChimed": "すずのおとが ひびきわたった!",
|
"bellChimed": "鈴の音が 響き渡った!",
|
||||||
"foresawAnAttack": "{{pokemonName}}は\nみらいに こうげきを よちした!",
|
"foresawAnAttack": "{{pokemonName}}は\n未来に 攻撃を 予知した!",
|
||||||
"hidUnderwater": "{{pokemonName}}は\nすいちゅうに みをひそめた!",
|
"isTighteningFocus": "{{pokemonName}}は\n集中力を 高めている!",
|
||||||
"soothingAromaWaftedThroughArea": "ここちよい かおりが ひろがった!",
|
"hidUnderwater": "{{pokemonName}}は\n水中に 身を潜めた!",
|
||||||
"sprangUp": "{{pokemonName}}は\nたかく とびはねた!",
|
"soothingAromaWaftedThroughArea": "心地よい 香りが 広がった!",
|
||||||
"choseDoomDesireAsDestiny": "{{pokemonName}}は\nはめつのねがいを みらいに たくした!",
|
"sprangUp": "{{pokemonName}}は\n高く 飛び跳ねた!",
|
||||||
"vanishedInstantly": "{{pokemonName}}の すがたが\nいっしゅんにして きえた!",
|
"choseDoomDesireAsDestiny": "{{pokemonName}}は\nはめつのねがいを 未来に 託した!",
|
||||||
"tookTargetIntoSky": "{{pokemonName}}は {{targetName}}を\nじょうくうに つれさった!",
|
"vanishedInstantly": "{{pokemonName}}の 姿が\n一瞬にして 消えた!",
|
||||||
"becameCloakedInFreezingLight": "{{pokemonName}}は\nつめたいひかりに つつまれた!",
|
"tookTargetIntoSky": "{{pokemonName}}は {{targetName}}を\n上空に 連れ去った!",
|
||||||
"becameCloakedInFreezingAir": "{{pokemonName}}は\nこごえるくうきに つつまれた!",
|
"becameCloakedInFreezingLight": "{{pokemonName}}は\n冷たい光に 包まれた!",
|
||||||
"isChargingPower": "{{pokemonName}}は\nパワーを ためこんでいる!",
|
"becameCloakedInFreezingAir": "{{pokemonName}}は\n凍える空気に 包まれた!",
|
||||||
"burnedItselfOut": "{{pokemonName}}の ほのうは\nもえつきた!",
|
"isChargingPower": "{{pokemonName}}は\nパワーを 溜め込んでいる!",
|
||||||
"startedHeatingUpBeak": "{{pokemonName}}は\nクチバシを かねつしはじめた!",
|
"burnedItselfOut": "{{pokemonName}}の 炎は 燃え尽きた!",
|
||||||
|
"startedHeatingUpBeak": "{{pokemonName}}は\nクチバシを 加熱し始めた!",
|
||||||
"setUpShellTrap": "{{pokemonName}}は\nトラップシェルを 仕掛けた!",
|
"setUpShellTrap": "{{pokemonName}}は\nトラップシェルを 仕掛けた!",
|
||||||
"isOverflowingWithSpacePower": "{{pokemonName}}に\nうちゅうの ちからが あふれだす!",
|
"isOverflowingWithSpacePower": "{{pokemonName}}に\n宇宙の 力が 溢れ出す!",
|
||||||
"usedUpAllElectricity": "{{pokemonName}}は\nでんきを つかいきった!",
|
"usedUpAllElectricity": "{{pokemonName}}は\n電気を 使い切った!",
|
||||||
"stoleItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を ぬすんだ!",
|
"stoleItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を 盗んだ!",
|
||||||
"incineratedItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を もやした!",
|
"incineratedItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を 燃やした!",
|
||||||
"knockedOffItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を はたきおとした!",
|
"knockedOffItem": "{{pokemonName}}は\n{{targetName}}の {{itemName}}を はたき落とした!",
|
||||||
"tookMoveAttack": "{{pokemonName}}は\n{{moveName}}の こうげきを うけた!",
|
"tookMoveAttack": "{{pokemonName}}は\n{{moveName}}の 攻撃を 受けた!",
|
||||||
"cutOwnHpAndMaximizedStat": "{{pokemonName}}は\nたいりょくを けずって {{statName}}ぜんかい!",
|
"cutOwnHpAndMaximizedStat": "{{pokemonName}}は\n体力を 削って {{statName}}全開!",
|
||||||
"copiedStatChanges": "{{pokemonName}}は {{targetName}}の\nのうりょくへんかを コピーした!",
|
"copiedStatChanges": "{{pokemonName}}は {{targetName}}の\n能力変化を コピーした!",
|
||||||
"magnitudeMessage": "マグニチュード{{magnitude}}!",
|
"magnitudeMessage": "マグニチュード{{magnitude}}!",
|
||||||
"tookAimAtTarget": "{{pokemonName}}は {{targetName}}に\nねらいを さだめた!",
|
"tookAimAtTarget": "{{pokemonName}}は {{targetName}}に\n狙いを 定めた!",
|
||||||
"transformedIntoType": "{{pokemonName}}は\n{{typeName}}タイプに なった!",
|
"transformedIntoType": "{{pokemonName}}は\n{{typeName}}タイプに なった!",
|
||||||
"copiedMove": "{{pokemonName}}は\n{{moveName}}を コピーした!",
|
"copiedMove": "{{pokemonName}}は\n{{moveName}}を コピーした!",
|
||||||
"sketchedMove": "{{pokemonName}}は\n{{moveName}}を スケッチした!",
|
"sketchedMove": "{{pokemonName}}は\n{{moveName}}を スケッチした!",
|
||||||
"acquiredAbility": "{{pokemonName}}の とくせいが\n{{abilityName}}に なった!",
|
"acquiredAbility": "{{pokemonName}}の 特性が\n{{abilityName}}に なった!",
|
||||||
"copiedTargetAbility": "{{pokemonName}}は\n{{targetName}}の {{abilityName}}を コピーした!",
|
"copiedTargetAbility": "{{pokemonName}}は\n{{targetName}}の {{abilityName}}を コピーした!",
|
||||||
"transformedIntoTarget": "{{pokemonName}}は\n{{targetName}}に へんしんした!",
|
"transformedIntoTarget": "{{pokemonName}}は\n{{targetName}}に 変身した!",
|
||||||
"tryingToTakeFoeDown": "{{pokemonName}}は あいてを\nみちづれに しようとしている!",
|
"tryingToTakeFoeDown": "{{pokemonName}}は 相手を\nみちづれに しようとしている!",
|
||||||
"addType": "{{pokemonName}}に\n{{typeName}}タイプが ついかされた!",
|
"addType": "{{pokemonName}}に\n{{typeName}}タイプが 追加された!",
|
||||||
"cannotUseMove": "{{pokemonName}}は\n{{moveName}}を つかえなかった!",
|
"cannotUseMove": "{{pokemonName}}は\n{{moveName}}を 使えなかった!",
|
||||||
"healHp": "{{pokemonName}}の\nたいりょくが かいふくした!",
|
"healHp": "{{pokemonName}}の\n体力が 回復した!",
|
||||||
"sacrificialFullRestore": "{{pokemonName}}の\nねがいごとが かなった!",
|
"sacrificialFullRestore": "{{pokemonName}}の\nいやしのねがいが 叶った!",
|
||||||
"invertStats": "{{pokemonName}}の\nのうりょくへんかが ぎゃくてんした!",
|
"invertStats": "{{pokemonName}}は\n能力変化が ひっくり返った!",
|
||||||
"resetStats": "{{pokemonName}}の\nのうりょくへんかが もとにもどった!",
|
"resetStats": "{{pokemonName}}の\n能力変化が 元に戻った!",
|
||||||
"faintCountdown": "{{pokemonName}}は\n{{turnCount}}ターンごに ほろびてしまう!",
|
"statEliminated": "全ての 能力変化が 元に戻った!",
|
||||||
|
"faintCountdown": "{{pokemonName}}は\n{{turnCount}}ターン後に 滅びてしまう!",
|
||||||
"copyType": "{{pokemonName}}は {{targetPokemonName}}と\n同じタイプに なった!",
|
"copyType": "{{pokemonName}}は {{targetPokemonName}}と\n同じタイプに なった!",
|
||||||
"suppressAbilities": "{{pokemonName}}の とくせいが きかなくなった!",
|
"suppressAbilities": "{{pokemonName}}の 特性が 効かなくなった!",
|
||||||
"revivalBlessing": "{{pokemonName}}は\n復活して 戦えるようになった!",
|
"revivalBlessing": "{{pokemonName}}は\n復活して 戦えるようになった!",
|
||||||
"swapArenaTags": "{{pokemonName}}は\nおたがいの ばのこうかを いれかえた!"
|
"swapArenaTags": "{{pokemonName}}は\nお互いの 場の 効果を 入れ替えた!",
|
||||||
|
"exposedMove": "{{pokemonName}}は {{targetPokemonName}}の\n正体を 見破った!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"SEND_OUT": "いれかえる",
|
"SEND_OUT": "入れ替える",
|
||||||
"SUMMARY": "つよさをみる",
|
"SUMMARY": "強さを見る",
|
||||||
"CANCEL": "やめる",
|
"CANCEL": "やめる",
|
||||||
"RELEASE": "にがす",
|
"RELEASE": "逃がす",
|
||||||
"APPLY": "つかう",
|
"APPLY": "使う",
|
||||||
"TEACH": "おしえる"
|
"TEACH": "教える"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"moveset": "わざ",
|
"moveset": "技",
|
||||||
"gender": "せいべつ:",
|
"gender": "性別:",
|
||||||
"ability": "とくせい:",
|
"ability": "特性:",
|
||||||
"nature": "せいかく:",
|
"nature": "性格:",
|
||||||
"form": "すがた:"
|
"form": "姿:"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,44 @@
|
||||||
{}
|
{
|
||||||
|
"pokemonInfo": "ポケモン情報",
|
||||||
|
"status": "ステータス",
|
||||||
|
"powerAccuracyCategory": "威力\n命中\n分類",
|
||||||
|
"type": "タイプ",
|
||||||
|
"unknownTrainer": "???",
|
||||||
|
"ot": "親",
|
||||||
|
"nature": "性格",
|
||||||
|
"expPoints": "経験値",
|
||||||
|
"nextLv": "次のレベルまで",
|
||||||
|
"cancel": "キャンセル",
|
||||||
|
"memoString": "{{natureFragment}}な性格。\n{{metFragment}}",
|
||||||
|
"metFragment": {
|
||||||
|
"normal": "{{biome}}で\nLv.{{level}}の時に出会った。",
|
||||||
|
"apparently": "{{biome}}で\nLv.{{level}}の時に出会ったようだ。"
|
||||||
|
},
|
||||||
|
"natureFragment": {
|
||||||
|
"Hardy": "{{nature}}",
|
||||||
|
"Lonely": "{{nature}}",
|
||||||
|
"Brave": "{{nature}}",
|
||||||
|
"Adamant": "{{nature}}",
|
||||||
|
"Naughty": "{{nature}}",
|
||||||
|
"Bold": "{{nature}}",
|
||||||
|
"Docile": "{{nature}}",
|
||||||
|
"Relaxed": "{{nature}}",
|
||||||
|
"Impish": "{{nature}}",
|
||||||
|
"Lax": "{{nature}}",
|
||||||
|
"Timid": "{{nature}}",
|
||||||
|
"Hasty": "{{nature}}",
|
||||||
|
"Serious": "{{nature}}",
|
||||||
|
"Jolly": "{{nature}}",
|
||||||
|
"Naive": "{{nature}}",
|
||||||
|
"Modest": "{{nature}}",
|
||||||
|
"Mild": "{{nature}}",
|
||||||
|
"Quiet": "{{nature}}",
|
||||||
|
"Bashful": "{{nature}}",
|
||||||
|
"Rash": "{{nature}}",
|
||||||
|
"Calm": "{{nature}}",
|
||||||
|
"Gentle": "{{nature}}",
|
||||||
|
"Sassy": "{{nature}}",
|
||||||
|
"Careful": "{{nature}}",
|
||||||
|
"Quirky": "{{nature}}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,10 +28,10 @@
|
||||||
"SPDshortened": "速さ",
|
"SPDshortened": "速さ",
|
||||||
"runInfo": "ラン情報",
|
"runInfo": "ラン情報",
|
||||||
"money": "お金",
|
"money": "お金",
|
||||||
"runLength": "ラン最高ウェーブ",
|
"runLength": "時間",
|
||||||
"viewHeldItems": "手持ちアイテム",
|
"viewHeldItems": "持たせたアイテム",
|
||||||
"hallofFameText": "殿堂へようこそ!",
|
"hallofFameText": "殿堂入り おめでとう!",
|
||||||
"hallofFameText_female": "殿堂へようこそ!",
|
"hallofFameText_female": "殿堂入り おめでとう!",
|
||||||
"viewHallOfFame": "殿堂登録を見る!",
|
"viewHallOfFame": "殿堂登録を見る!",
|
||||||
"viewEndingSplash": "クリア後のアートを見る!"
|
"viewEndingSplash": "クリア後のアートを見る!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"overwriteData": "選択した スロットに データを 上書きします?",
|
"overwriteData": "選択した スロットに データを 上書きします?",
|
||||||
"loading": "読込中…",
|
"loading": "読込中…",
|
||||||
"wave": "波",
|
"wave": "ラウンド",
|
||||||
"lv": "Lv",
|
"lv": "Lv",
|
||||||
"empty": "なし"
|
"empty": "なし"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,36 @@
|
||||||
{}
|
{
|
||||||
|
"battlesWon": "Battles Won!",
|
||||||
|
"joinTheDiscord": "Join the Discord!",
|
||||||
|
"infiniteLevels": "Infinite Levels!",
|
||||||
|
"everythingStacks": "Everything Stacks!",
|
||||||
|
"optionalSaveScumming": "Optional Save Scumming!",
|
||||||
|
"biomes": "35 Biomes!",
|
||||||
|
"openSource": "Open Source!",
|
||||||
|
"playWithSpeed": "Play with 5x Speed!",
|
||||||
|
"liveBugTesting": "Live Bug Testing!",
|
||||||
|
"heavyInfluence": "Heavy RoR2 Influence!",
|
||||||
|
"pokemonRiskAndPokemonRain": "Pokémon Risk and Pokémon Rain!",
|
||||||
|
"nowWithMoreSalt": "Now with 33% More Salt!",
|
||||||
|
"infiniteFusionAtHome": "Infinite Fusion at Home!",
|
||||||
|
"brokenEggMoves": "Broken Egg Moves!",
|
||||||
|
"magnificent": "Magnificent!",
|
||||||
|
"mubstitute": "Mubstitute!",
|
||||||
|
"thatsCrazy": "That's Crazy!",
|
||||||
|
"oranceJuice": "Orance Juice!",
|
||||||
|
"questionableBalancing": "Questionable Balancing!",
|
||||||
|
"coolShaders": "Cool Shaders!",
|
||||||
|
"aiFree": "AI-Free!",
|
||||||
|
"suddenDifficultySpikes": "Sudden Difficulty Spikes!",
|
||||||
|
"basedOnAnUnfinishedFlashGame": "Based on an Unfinished Flash Game!",
|
||||||
|
"moreAddictiveThanIntended": "More Addictive than Intended!",
|
||||||
|
"mostlyConsistentSeeds": "Mostly Consistent Seeds!",
|
||||||
|
"achievementPointsDontDoAnything": "Achievement Points Don't Do Anything!",
|
||||||
|
"youDoNotStartAtLevel": "You Do Not Start at Level 2000!",
|
||||||
|
"dontTalkAboutTheManaphyEggIncident": "Don't Talk About the Manaphy Egg Incident!",
|
||||||
|
"alsoTryPokengine": "Also Try Pokéngine!",
|
||||||
|
"alsoTryEmeraldRogue": "Also Try Emerald Rogue!",
|
||||||
|
"alsoTryRadicalRed": "Also Try Radical Red!",
|
||||||
|
"eeveeExpo": "Eevee Expo!",
|
||||||
|
"ynoproject": "YNOproject!",
|
||||||
|
"breedersInSpace": "Breeders in space!"
|
||||||
|
}
|
||||||
|
|
|
@ -16,17 +16,17 @@
|
||||||
"snowStartMessage": "雪が 降り始めた!",
|
"snowStartMessage": "雪が 降り始めた!",
|
||||||
"snowLapseMessage": "雪が 降っている!",
|
"snowLapseMessage": "雪が 降っている!",
|
||||||
"snowClearMessage": "雪が 止んだ!",
|
"snowClearMessage": "雪が 止んだ!",
|
||||||
"fogStartMessage": "足下に 霧(きり)が立ち込めた!",
|
"fogStartMessage": "足下に 霧が 立ち込めた!",
|
||||||
"fogLapseMessage": "足下に 霧(きり)が 立ち込めている!",
|
"fogLapseMessage": "足下に 霧が 立ち込めている!",
|
||||||
"fogClearMessage": "足下の 霧(きり)が消え去った!",
|
"fogClearMessage": "足下の 霧が 消え去った!",
|
||||||
"heavyRainStartMessage": "強い雨が 降り始めた!",
|
"heavyRainStartMessage": "強い雨が 降り始めた!",
|
||||||
"heavyRainLapseMessage": "強い雨が 降っている!",
|
"heavyRainLapseMessage": "強い雨が 降っている!",
|
||||||
"heavyRainClearMessage": "強い雨が あがった!",
|
"heavyRainClearMessage": "強い雨が あがった!",
|
||||||
"harshSunStartMessage": "日差しが とても強くなった!",
|
"harshSunStartMessage": "日差しが とても強くなった!",
|
||||||
"harshSunLapseMessage": "日差しが とても強い!",
|
"harshSunLapseMessage": "日差しが とても強い!",
|
||||||
"harshSunClearMessage": "日差しが 元に戻った!",
|
"harshSunClearMessage": "日差しが 元に戻った!",
|
||||||
"strongWindsStartMessage": "謎(なぞ)の 乱気流(らんきりゅう)が\nひこうポケモンを 護(まも)る!",
|
"strongWindsStartMessage": "謎の 乱気流が\nひこうポケモンを 護る!",
|
||||||
"strongWindsLapseMessage": "謎(なぞ)の 乱気流(らんきりゅう)の 勢(いきお)いは 止まらない!",
|
"strongWindsLapseMessage": "謎の 乱気流の 勢いは 止まらない!",
|
||||||
"strongWindsEffectMessage": "謎(なぞ)の 乱気流(らんきりゅう)が 攻撃(こうげき)を 弱(よわ)めた!",
|
"strongWindsEffectMessage": "謎の 乱気流が 攻撃を 弱めた!",
|
||||||
"strongWindsClearMessage": "謎(なぞ)の 乱気流(らんきりゅう)が おさまった!"
|
"strongWindsClearMessage": "謎の 乱気流が おさまった!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -440,37 +440,44 @@ export class RememberMoveModifierType extends PokemonModifierType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DoubleBattleChanceBoosterModifierType extends ModifierType {
|
export class DoubleBattleChanceBoosterModifierType extends ModifierType {
|
||||||
public battleCount: integer;
|
private maxBattles: number;
|
||||||
|
|
||||||
constructor(localeKey: string, iconImage: string, battleCount: integer) {
|
constructor(localeKey: string, iconImage: string, maxBattles: number) {
|
||||||
super(localeKey, iconImage, (_type, _args) => new Modifiers.DoubleBattleChanceBoosterModifier(this, this.battleCount), "lure");
|
super(localeKey, iconImage, (_type, _args) => new Modifiers.DoubleBattleChanceBoosterModifier(this, maxBattles), "lure");
|
||||||
|
|
||||||
this.battleCount = battleCount;
|
this.maxBattles = maxBattles;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.maxBattles
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TempStatStageBoosterModifierType extends ModifierType implements GeneratedPersistentModifierType {
|
export class TempStatStageBoosterModifierType extends ModifierType implements GeneratedPersistentModifierType {
|
||||||
private stat: TempBattleStat;
|
private stat: TempBattleStat;
|
||||||
private key: string;
|
private nameKey: string;
|
||||||
|
private quantityKey: string;
|
||||||
|
|
||||||
constructor(stat: TempBattleStat) {
|
constructor(stat: TempBattleStat) {
|
||||||
const key = TempStatStageBoosterModifierTypeGenerator.items[stat];
|
const nameKey = TempStatStageBoosterModifierTypeGenerator.items[stat];
|
||||||
super("", key, (_type, _args) => new Modifiers.TempStatStageBoosterModifier(this, this.stat));
|
super("", nameKey, (_type, _args) => new Modifiers.TempStatStageBoosterModifier(this, this.stat, 5));
|
||||||
|
|
||||||
this.stat = stat;
|
this.stat = stat;
|
||||||
this.key = key;
|
this.nameKey = nameKey;
|
||||||
|
this.quantityKey = (stat !== Stat.ACC) ? "percentage" : "stage";
|
||||||
}
|
}
|
||||||
|
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return i18next.t(`modifierType:TempStatStageBoosterItem.${this.key}`);
|
return i18next.t(`modifierType:TempStatStageBoosterItem.${this.nameKey}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDescription(_scene: BattleScene): string {
|
getDescription(_scene: BattleScene): string {
|
||||||
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { stat: i18next.t(getStatKey(this.stat)) });
|
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
|
||||||
|
stat: i18next.t(getStatKey(this.stat)),
|
||||||
|
amount: i18next.t(`modifierType:ModifierType.TempStatStageBoosterModifierType.extra.${this.quantityKey}`)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getPregenArgs(): any[] {
|
getPregenArgs(): any[] {
|
||||||
|
@ -1398,9 +1405,9 @@ export const modifierTypes = {
|
||||||
SUPER_REPEL: () => new DoubleBattleChanceBoosterModifierType('Super Repel', 10),
|
SUPER_REPEL: () => new DoubleBattleChanceBoosterModifierType('Super Repel', 10),
|
||||||
MAX_REPEL: () => new DoubleBattleChanceBoosterModifierType('Max Repel', 25),*/
|
MAX_REPEL: () => new DoubleBattleChanceBoosterModifierType('Max Repel', 25),*/
|
||||||
|
|
||||||
LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.LURE", "lure", 5),
|
LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.LURE", "lure", 10),
|
||||||
SUPER_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.SUPER_LURE", "super_lure", 10),
|
SUPER_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.SUPER_LURE", "super_lure", 15),
|
||||||
MAX_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.MAX_LURE", "max_lure", 25),
|
MAX_LURE: () => new DoubleBattleChanceBoosterModifierType("modifierType:ModifierType.MAX_LURE", "max_lure", 30),
|
||||||
|
|
||||||
SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterModifierTypeGenerator(),
|
SPECIES_STAT_BOOSTER: () => new SpeciesStatBoosterModifierTypeGenerator(),
|
||||||
|
|
||||||
|
@ -1408,9 +1415,12 @@ export const modifierTypes = {
|
||||||
|
|
||||||
DIRE_HIT: () => new class extends ModifierType {
|
DIRE_HIT: () => new class extends ModifierType {
|
||||||
getDescription(_scene: BattleScene): string {
|
getDescription(_scene: BattleScene): string {
|
||||||
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", { stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises") });
|
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
|
||||||
|
stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises"),
|
||||||
|
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}("modifierType:ModifierType.DIRE_HIT", "dire_hit", (type, _args) => new Modifiers.TempCritBoosterModifier(type)),
|
}("modifierType:ModifierType.DIRE_HIT", "dire_hit", (type, _args) => new Modifiers.TempCritBoosterModifier(type, 5)),
|
||||||
|
|
||||||
BASE_STAT_BOOSTER: () => new BaseStatBoosterModifierTypeGenerator(),
|
BASE_STAT_BOOSTER: () => new BaseStatBoosterModifierTypeGenerator(),
|
||||||
|
|
||||||
|
|
|
@ -292,70 +292,131 @@ export class AddVoucherModifier extends ConsumableModifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifier used for party-wide or passive items that start an initial
|
||||||
|
* {@linkcode battleCount} equal to {@linkcode maxBattles} that, for every
|
||||||
|
* battle, decrements. Typically, when {@linkcode battleCount} reaches 0, the
|
||||||
|
* modifier will be removed. If a modifier of the same type is to be added, it
|
||||||
|
* will reset {@linkcode battleCount} back to {@linkcode maxBattles} of the
|
||||||
|
* existing modifier instead of adding that modifier directly.
|
||||||
|
* @extends PersistentModifier
|
||||||
|
* @abstract
|
||||||
|
* @see {@linkcode add}
|
||||||
|
*/
|
||||||
export abstract class LapsingPersistentModifier extends PersistentModifier {
|
export abstract class LapsingPersistentModifier extends PersistentModifier {
|
||||||
protected battlesLeft: integer;
|
/** The maximum amount of battles the modifier will exist for */
|
||||||
|
private maxBattles: number;
|
||||||
|
/** The current amount of battles the modifier will exist for */
|
||||||
|
private battleCount: number;
|
||||||
|
|
||||||
constructor(type: ModifierTypes.ModifierType, battlesLeft?: integer, stackCount?: integer) {
|
constructor(type: ModifierTypes.ModifierType, maxBattles: number, battleCount?: number, stackCount?: integer) {
|
||||||
super(type, stackCount);
|
super(type, stackCount);
|
||||||
|
|
||||||
this.battlesLeft = battlesLeft!; // TODO: is this bang correct?
|
this.maxBattles = maxBattles;
|
||||||
|
this.battleCount = battleCount ?? this.maxBattles;
|
||||||
}
|
}
|
||||||
|
|
||||||
lapse(args: any[]): boolean {
|
/**
|
||||||
return !!--this.battlesLeft;
|
* Goes through existing modifiers for any that match the selected modifier,
|
||||||
|
* which will then either add it to the existing modifiers if none were found
|
||||||
|
* or, if one was found, it will refresh {@linkcode battleCount}.
|
||||||
|
* @param modifiers {@linkcode PersistentModifier} array of the player's modifiers
|
||||||
|
* @param _virtual N/A
|
||||||
|
* @param _scene N/A
|
||||||
|
* @returns true if the modifier was successfully added or applied, false otherwise
|
||||||
|
*/
|
||||||
|
add(modifiers: PersistentModifier[], _virtual: boolean, scene: BattleScene): boolean {
|
||||||
|
for (const modifier of modifiers) {
|
||||||
|
if (this.match(modifier)) {
|
||||||
|
const modifierInstance = modifier as LapsingPersistentModifier;
|
||||||
|
if (modifierInstance.getBattleCount() < modifierInstance.getMaxBattles()) {
|
||||||
|
modifierInstance.resetBattleCount();
|
||||||
|
scene.playSound("se/restore");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// should never get here
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiers.push(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lapse(_args: any[]): boolean {
|
||||||
|
this.battleCount--;
|
||||||
|
return this.battleCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
|
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
|
||||||
const container = super.getIcon(scene);
|
const container = super.getIcon(scene);
|
||||||
|
|
||||||
const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: "#f89890" });
|
// Linear interpolation on hue
|
||||||
|
const hue = Math.floor(120 * (this.battleCount / this.maxBattles) + 5);
|
||||||
|
|
||||||
|
// Generates the color hex code with a constant saturation and lightness but varying hue
|
||||||
|
const typeHex = Utils.hslToHex(hue, 0.50, 0.90);
|
||||||
|
const strokeHex = Utils.hslToHex(hue, 0.70, 0.30);
|
||||||
|
|
||||||
|
const battleCountText = addTextObject(scene, 27, 0, this.battleCount.toString(), TextStyle.PARTY, { fontSize: "66px", color: typeHex });
|
||||||
battleCountText.setShadow(0, 0);
|
battleCountText.setShadow(0, 0);
|
||||||
battleCountText.setStroke("#984038", 16);
|
battleCountText.setStroke(strokeHex, 16);
|
||||||
battleCountText.setOrigin(1, 0);
|
battleCountText.setOrigin(1, 0);
|
||||||
container.add(battleCountText);
|
container.add(battleCountText);
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBattlesLeft(): integer {
|
getBattleCount(): number {
|
||||||
return this.battlesLeft;
|
return this.battleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMaxStackCount(scene: BattleScene, forThreshold?: boolean): number {
|
resetBattleCount(): void {
|
||||||
return 99;
|
this.battleCount = this.maxBattles;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier {
|
|
||||||
constructor(type: ModifierTypes.DoubleBattleChanceBoosterModifierType, battlesLeft: integer, stackCount?: integer) {
|
|
||||||
super(type, battlesLeft, stackCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match(modifier: Modifier): boolean {
|
getMaxBattles(): number {
|
||||||
if (modifier instanceof DoubleBattleChanceBoosterModifier) {
|
return this.maxBattles;
|
||||||
// Check type id to not match different tiers of lures
|
|
||||||
return modifier.type.id === this.type.id && modifier.battlesLeft === this.battlesLeft;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
clone(): DoubleBattleChanceBoosterModifier {
|
|
||||||
return new DoubleBattleChanceBoosterModifier(this.type as ModifierTypes.DoubleBattleChanceBoosterModifierType, this.battlesLeft, this.stackCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getArgs(): any[] {
|
getArgs(): any[] {
|
||||||
return [ this.battlesLeft ];
|
return [ this.maxBattles, this.battleCount ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMaxStackCount(_scene: BattleScene, _forThreshold?: boolean): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifier used for passive items, specifically lures, that
|
||||||
|
* temporarily increases the chance of a double battle.
|
||||||
|
* @extends LapsingPersistentModifier
|
||||||
|
* @see {@linkcode apply}
|
||||||
|
*/
|
||||||
|
export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier {
|
||||||
|
constructor(type: ModifierType, maxBattles:number, battleCount?: number, stackCount?: integer) {
|
||||||
|
super(type, maxBattles, battleCount, stackCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
match(modifier: Modifier): boolean {
|
||||||
|
return (modifier instanceof DoubleBattleChanceBoosterModifier) && (modifier.getMaxBattles() === this.getMaxBattles());
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): DoubleBattleChanceBoosterModifier {
|
||||||
|
return new DoubleBattleChanceBoosterModifier(this.type as ModifierTypes.DoubleBattleChanceBoosterModifierType, this.getMaxBattles(), this.getBattleCount(), this.stackCount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the chance of a double battle occurring
|
* Modifies the chance of a double battle occurring
|
||||||
* @param args A single element array containing the double battle chance as a NumberHolder
|
* @param args [0] {@linkcode Utils.NumberHolder} for double battle chance
|
||||||
* @returns {boolean} Returns true if the modifier was applied
|
* @returns true if the modifier was applied
|
||||||
*/
|
*/
|
||||||
apply(args: any[]): boolean {
|
apply(args: any[]): boolean {
|
||||||
const doubleBattleChance = args[0] as Utils.NumberHolder;
|
const doubleBattleChance = args[0] as Utils.NumberHolder;
|
||||||
// This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt
|
// This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using Utils.randSeedInt
|
||||||
// A double battle will initiate if the generated number is 0
|
// A double battle will initiate if the generated number is 0
|
||||||
doubleBattleChance.value = Math.ceil(doubleBattleChance.value / 2);
|
doubleBattleChance.value = Math.ceil(doubleBattleChance.value / 4);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -369,16 +430,18 @@ export class DoubleBattleChanceBoosterModifier extends LapsingPersistentModifier
|
||||||
* @see {@linkcode apply}
|
* @see {@linkcode apply}
|
||||||
*/
|
*/
|
||||||
export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
|
export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
|
||||||
|
/** The stat whose stat stage multiplier will be temporarily increased */
|
||||||
private stat: TempBattleStat;
|
private stat: TempBattleStat;
|
||||||
private multiplierBoost: number;
|
/** The amount by which the stat stage itself or its multiplier will be increased by */
|
||||||
|
private boost: number;
|
||||||
|
|
||||||
constructor(type: ModifierType, stat: TempBattleStat, battlesLeft?: number, stackCount?: number) {
|
constructor(type: ModifierType, stat: TempBattleStat, maxBattles: number, battleCount?: number, stackCount?: number) {
|
||||||
super(type, battlesLeft ?? 5, stackCount);
|
super(type, maxBattles, battleCount, stackCount);
|
||||||
|
|
||||||
this.stat = stat;
|
this.stat = stat;
|
||||||
// Note that, because we want X Accuracy to maintain its original behavior,
|
// Note that, because we want X Accuracy to maintain its original behavior,
|
||||||
// it will increment as it did previously, directly to the stat stage.
|
// it will increment as it did previously, directly to the stat stage.
|
||||||
this.multiplierBoost = stat !== Stat.ACC ? 0.3 : 1;
|
this.boost = (stat !== Stat.ACC) ? 0.3 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
match(modifier: Modifier): boolean {
|
match(modifier: Modifier): boolean {
|
||||||
|
@ -390,11 +453,11 @@ export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
return new TempStatStageBoosterModifier(this.type, this.stat, this.battlesLeft, this.stackCount);
|
return new TempStatStageBoosterModifier(this.type, this.stat, this.getMaxBattles(), this.getBattleCount(), this.stackCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
getArgs(): any[] {
|
getArgs(): any[] {
|
||||||
return [ this.stat, this.battlesLeft ];
|
return [ this.stat, ...super.getArgs() ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -409,44 +472,14 @@ export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Increases the incoming stat stage matching {@linkcode stat} by {@linkcode multiplierBoost}.
|
* Increases the incoming stat stage matching {@linkcode stat} by {@linkcode boost}.
|
||||||
* @param args [0] {@linkcode TempBattleStat} N/A
|
* @param args [0] {@linkcode TempBattleStat} N/A
|
||||||
* [1] {@linkcode Utils.NumberHolder} that holds the resulting value of the stat stage multiplier
|
* [1] {@linkcode Utils.NumberHolder} that holds the resulting value of the stat stage multiplier
|
||||||
*/
|
*/
|
||||||
apply(args: any[]): boolean {
|
apply(args: any[]): boolean {
|
||||||
(args[1] as Utils.NumberHolder).value += this.multiplierBoost;
|
(args[1] as Utils.NumberHolder).value += this.boost;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Goes through existing modifiers for any that match the selected modifier,
|
|
||||||
* which will then either add it to the existing modifiers if none were found
|
|
||||||
* or, if one was found, it will refresh {@linkcode battlesLeft}.
|
|
||||||
* @param modifiers {@linkcode PersistentModifier} array of the player's modifiers
|
|
||||||
* @param _virtual N/A
|
|
||||||
* @param _scene N/A
|
|
||||||
* @returns true if the modifier was successfully added or applied, false otherwise
|
|
||||||
*/
|
|
||||||
add(modifiers: PersistentModifier[], _virtual: boolean, _scene: BattleScene): boolean {
|
|
||||||
for (const modifier of modifiers) {
|
|
||||||
if (this.match(modifier)) {
|
|
||||||
const modifierInstance = modifier as TempStatStageBoosterModifier;
|
|
||||||
if (modifierInstance.getBattlesLeft() < 5) {
|
|
||||||
modifierInstance.battlesLeft = 5;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// should never get here
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiers.push(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMaxStackCount(_scene: BattleScene, _forThreshold?: boolean): number {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -456,12 +489,12 @@ export class TempStatStageBoosterModifier extends LapsingPersistentModifier {
|
||||||
* @see {@linkcode apply}
|
* @see {@linkcode apply}
|
||||||
*/
|
*/
|
||||||
export class TempCritBoosterModifier extends LapsingPersistentModifier {
|
export class TempCritBoosterModifier extends LapsingPersistentModifier {
|
||||||
constructor(type: ModifierType, battlesLeft?: integer, stackCount?: number) {
|
constructor(type: ModifierType, maxBattles: number, battleCount?: number, stackCount?: number) {
|
||||||
super(type, battlesLeft || 5, stackCount);
|
super(type, maxBattles, battleCount, stackCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
return new TempCritBoosterModifier(this.type, this.stackCount);
|
return new TempCritBoosterModifier(this.type, this.getMaxBattles(), this.getBattleCount(), this.stackCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
match(modifier: Modifier): boolean {
|
match(modifier: Modifier): boolean {
|
||||||
|
@ -486,36 +519,6 @@ export class TempCritBoosterModifier extends LapsingPersistentModifier {
|
||||||
(args[0] as Utils.NumberHolder).value++;
|
(args[0] as Utils.NumberHolder).value++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Goes through existing modifiers for any that match the selected modifier,
|
|
||||||
* which will then either add it to the existing modifiers if none were found
|
|
||||||
* or, if one was found, it will refresh {@linkcode battlesLeft}.
|
|
||||||
* @param modifiers {@linkcode PersistentModifier} array of the player's modifiers
|
|
||||||
* @param _virtual N/A
|
|
||||||
* @param _scene N/A
|
|
||||||
* @returns true if the modifier was successfully added or applied, false otherwise
|
|
||||||
*/
|
|
||||||
add(modifiers: PersistentModifier[], _virtual: boolean, _scene: BattleScene): boolean {
|
|
||||||
for (const modifier of modifiers) {
|
|
||||||
if (this.match(modifier)) {
|
|
||||||
const modifierInstance = modifier as TempCritBoosterModifier;
|
|
||||||
if (modifierInstance.getBattlesLeft() < 5) {
|
|
||||||
modifierInstance.battlesLeft = 5;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// should never get here
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiers.push(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMaxStackCount(_scene: BattleScene, _forThreshold?: boolean): number {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MapModifier extends PersistentModifier {
|
export class MapModifier extends PersistentModifier {
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { ToggleDoublePositionPhase } from "./toggle-double-position-phase";
|
||||||
import { GameOverPhase } from "./game-over-phase";
|
import { GameOverPhase } from "./game-over-phase";
|
||||||
import { SwitchPhase } from "./switch-phase";
|
import { SwitchPhase } from "./switch-phase";
|
||||||
import { VictoryPhase } from "./victory-phase";
|
import { VictoryPhase } from "./victory-phase";
|
||||||
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
||||||
|
|
||||||
export class FaintPhase extends PokemonPhase {
|
export class FaintPhase extends PokemonPhase {
|
||||||
private preventEndure: boolean;
|
private preventEndure: boolean;
|
||||||
|
@ -59,6 +60,7 @@ export class FaintPhase extends PokemonPhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.scene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true);
|
this.scene.queueMessage(i18next.t("battle:fainted", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), null, true);
|
||||||
|
this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
|
||||||
|
|
||||||
if (pokemon.turnData?.attacksReceived?.length) {
|
if (pokemon.turnData?.attacksReceived?.length) {
|
||||||
const lastAttack = pokemon.turnData.attacksReceived[0];
|
const lastAttack = pokemon.turnData.attacksReceived[0];
|
||||||
|
|
|
@ -49,7 +49,9 @@ export class GameOverPhase extends BattlePhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.victory && this.scene.gameMode.isEndless) {
|
if (this.victory && this.scene.gameMode.isEndless) {
|
||||||
this.scene.ui.showDialogue(i18next.t("PGMmiscDialogue:ending_endless"), i18next.t("PGMmiscDialogue:ending_name"), 0, () => this.handleGameOver());
|
const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET;
|
||||||
|
const genderStr = PlayerGender[genderIndex].toLowerCase();
|
||||||
|
this.scene.ui.showDialogue(i18next.t("miscDialogue:ending_endless", { context: genderStr }), i18next.t("miscDialogue:ending_name"), 0, () => this.handleGameOver());
|
||||||
} else if (this.victory || !this.scene.enableRetries) {
|
} else if (this.victory || !this.scene.enableRetries) {
|
||||||
this.handleGameOver();
|
this.handleGameOver();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -378,16 +378,16 @@ export class MoveEffectPhase extends PokemonPhase {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here?
|
const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user, target);
|
||||||
|
|
||||||
if (moveAccuracy === -1) {
|
if (moveAccuracy === -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove());
|
const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove());
|
||||||
const rand = user.randSeedInt(100, 1);
|
const rand = user.randSeedInt(100);
|
||||||
|
|
||||||
return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct?
|
return rand < (moveAccuracy * accuracyMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the {@linkcode Pokemon} using this phase's invoked move */
|
/** Returns the {@linkcode Pokemon} using this phase's invoked move */
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import BattleScene from "#app/battle-scene.js";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { applyPreWeatherEffectAbAttrs, SuppressWeatherEffectAbAttr, PreWeatherDamageAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPostWeatherLapseAbAttrs, PostWeatherLapseAbAttr } from "#app/data/ability.js";
|
import { applyPreWeatherEffectAbAttrs, SuppressWeatherEffectAbAttr, PreWeatherDamageAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPostWeatherLapseAbAttrs, PostWeatherLapseAbAttr } from "#app/data/ability.js";
|
||||||
import { CommonAnim } from "#app/data/battle-anims.js";
|
import { CommonAnim } from "#app/data/battle-anims";
|
||||||
import { Weather, getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather.js";
|
import { Weather, getWeatherDamageMessage, getWeatherLapseMessage } from "#app/data/weather";
|
||||||
import { WeatherType } from "#app/enums/weather-type.js";
|
import { BattlerTagType } from "#app/enums/battler-tag-type.js";
|
||||||
import Pokemon, { HitResult } from "#app/field/pokemon.js";
|
import { WeatherType } from "#app/enums/weather-type";
|
||||||
import * as Utils from "#app/utils.js";
|
import Pokemon, { HitResult } from "#app/field/pokemon";
|
||||||
|
import * as Utils from "#app/utils";
|
||||||
import { CommonAnimPhase } from "./common-anim-phase";
|
import { CommonAnimPhase } from "./common-anim-phase";
|
||||||
|
|
||||||
export class WeatherEffectPhase extends CommonAnimPhase {
|
export class WeatherEffectPhase extends CommonAnimPhase {
|
||||||
|
@ -39,7 +40,7 @@ export class WeatherEffectPhase extends CommonAnimPhase {
|
||||||
applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled);
|
applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled);
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
|
||||||
|
|
||||||
if (cancelled.value) {
|
if (cancelled.value || pokemon.getTag(BattlerTagType.UNDERGROUND) || pokemon.getTag(BattlerTagType.UNDERWATER)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -861,6 +861,14 @@ export class GameData {
|
||||||
|
|
||||||
const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct?
|
const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct?
|
||||||
|
|
||||||
|
// TODO: Remove this block after save migration is implemented
|
||||||
|
if (settings.hasOwnProperty("REROLL_TARGET") && !settings.hasOwnProperty(SettingKeys.Shop_Cursor_Target)) {
|
||||||
|
settings[SettingKeys.Shop_Cursor_Target] = settings["REROLL_TARGET"];
|
||||||
|
delete settings["REROLL_TARGET"];
|
||||||
|
localStorage.setItem("settings", JSON.stringify(settings));
|
||||||
|
}
|
||||||
|
// End of block to remove
|
||||||
|
|
||||||
for (const setting of Object.keys(settings)) {
|
for (const setting of Object.keys(settings)) {
|
||||||
setSetting(this.scene, setting, settings[setting]);
|
setSetting(this.scene, setting, settings[setting]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
@ -33,31 +32,45 @@ describe("Abilities - Aura Break", () => {
|
||||||
game.override.enemySpecies(Species.SHUCKLE);
|
game.override.enemySpecies(Species.SHUCKLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reverses the effect of fairy aura", async () => {
|
it("reverses the effect of Fairy Aura", async () => {
|
||||||
const moveToCheck = allMoves[Moves.MOONBLAST];
|
const moveToCheck = allMoves[Moves.MOONBLAST];
|
||||||
const basePower = moveToCheck.power;
|
const basePower = moveToCheck.power;
|
||||||
|
|
||||||
game.override.ability(Abilities.FAIRY_AURA);
|
game.override.ability(Abilities.FAIRY_AURA);
|
||||||
vi.spyOn(moveToCheck, "calculateBattlePower");
|
vi.spyOn(moveToCheck, "calculateBattlePower");
|
||||||
|
|
||||||
await game.startBattle([Species.PIKACHU]);
|
await game.classicMode.startBattle([Species.PIKACHU]);
|
||||||
game.move.select(Moves.MOONBLAST);
|
game.move.select(Moves.MOONBLAST);
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier));
|
expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("reverses the effect of dark aura", async () => {
|
it("reverses the effect of Dark Aura", async () => {
|
||||||
const moveToCheck = allMoves[Moves.DARK_PULSE];
|
const moveToCheck = allMoves[Moves.DARK_PULSE];
|
||||||
const basePower = moveToCheck.power;
|
const basePower = moveToCheck.power;
|
||||||
|
|
||||||
game.override.ability(Abilities.DARK_AURA);
|
game.override.ability(Abilities.DARK_AURA);
|
||||||
vi.spyOn(moveToCheck, "calculateBattlePower");
|
vi.spyOn(moveToCheck, "calculateBattlePower");
|
||||||
|
|
||||||
await game.startBattle([Species.PIKACHU]);
|
await game.classicMode.startBattle([Species.PIKACHU]);
|
||||||
game.move.select(Moves.DARK_PULSE);
|
game.move.select(Moves.DARK_PULSE);
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase);
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier));
|
expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(expect.closeTo(basePower * auraBreakMultiplier));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("has no effect if neither Fairy Aura nor Dark Aura are present", async () => {
|
||||||
|
const moveToCheck = allMoves[Moves.MOONBLAST];
|
||||||
|
const basePower = moveToCheck.power;
|
||||||
|
|
||||||
|
game.override.ability(Abilities.BALL_FETCH);
|
||||||
|
vi.spyOn(moveToCheck, "calculateBattlePower");
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.PIKACHU]);
|
||||||
|
game.move.select(Moves.MOONBLAST);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
|
||||||
|
expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { WeatherType } from "#app/data/weather";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
|
||||||
|
describe("Weather - Hail", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.weather(WeatherType.HAIL)
|
||||||
|
.battleType("single")
|
||||||
|
.moveset(SPLASH_ONLY)
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.enemySpecies(Species.MAGIKARP);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("inflicts damage equal to 1/16 of Pokemon's max HP at turn end", async () => {
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
game.scene.getField(true).forEach(pokemon => {
|
||||||
|
expect(pokemon.hp).toBeLessThan(pokemon.getMaxHp() - Math.floor(pokemon.getMaxHp() / 16));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not inflict damage to a Pokemon that is underwater (Dive) or underground (Dig)", async () => {
|
||||||
|
game.override.moveset([Moves.DIG]);
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
game.move.select(Moves.DIG);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||||
|
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp() - Math.floor(enemyPokemon.getMaxHp() / 16));
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { WeatherType } from "#app/data/weather";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
|
||||||
|
describe("Weather - Sandstorm", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.weather(WeatherType.SANDSTORM)
|
||||||
|
.battleType("single")
|
||||||
|
.moveset(SPLASH_ONLY)
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.enemySpecies(Species.MAGIKARP);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("inflicts damage equal to 1/16 of Pokemon's max HP at turn end", async () => {
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
game.scene.getField(true).forEach(pokemon => {
|
||||||
|
expect(pokemon.hp).toBeLessThan(pokemon.getMaxHp() - Math.floor(pokemon.getMaxHp() / 16));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not inflict damage to a Pokemon that is underwater (Dive) or underground (Dig)", async () => {
|
||||||
|
game.override.moveset([Moves.DIVE]);
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP]);
|
||||||
|
|
||||||
|
game.move.select(Moves.DIVE);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp());
|
||||||
|
expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp() - Math.floor(enemyPokemon.getMaxHp() / 16));
|
||||||
|
});
|
||||||
|
});
|
|
@ -72,7 +72,7 @@ describe("Items - Dire Hit", () => {
|
||||||
await game.phaseInterceptor.to(BattleEndPhase);
|
await game.phaseInterceptor.to(BattleEndPhase);
|
||||||
|
|
||||||
const modifier = game.scene.findModifier(m => m instanceof TempCritBoosterModifier) as TempCritBoosterModifier;
|
const modifier = game.scene.findModifier(m => m instanceof TempCritBoosterModifier) as TempCritBoosterModifier;
|
||||||
expect(modifier.getBattlesLeft()).toBe(4);
|
expect(modifier.getBattleCount()).toBe(4);
|
||||||
|
|
||||||
// Forced DIRE_HIT to spawn in the first slot with override
|
// Forced DIRE_HIT to spawn in the first slot with override
|
||||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||||
|
@ -90,7 +90,7 @@ describe("Items - Dire Hit", () => {
|
||||||
for (const m of game.scene.modifiers) {
|
for (const m of game.scene.modifiers) {
|
||||||
if (m instanceof TempCritBoosterModifier) {
|
if (m instanceof TempCritBoosterModifier) {
|
||||||
count++;
|
count++;
|
||||||
expect((m as TempCritBoosterModifier).getBattlesLeft()).toBe(5);
|
expect((m as TempCritBoosterModifier).getBattleCount()).toBe(5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(count).toBe(1);
|
expect(count).toBe(1);
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { Moves } from "#app/enums/moves.js";
|
||||||
|
import { Species } from "#app/enums/species.js";
|
||||||
|
import { DoubleBattleChanceBoosterModifier } from "#app/modifier/modifier";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
import { SPLASH_ONLY } from "../utils/testUtils";
|
||||||
|
import { ShopCursorTarget } from "#app/enums/shop-cursor-target.js";
|
||||||
|
import { Mode } from "#app/ui/ui.js";
|
||||||
|
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler.js";
|
||||||
|
import { Button } from "#app/enums/buttons.js";
|
||||||
|
|
||||||
|
describe("Items - Double Battle Chance Boosters", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should guarantee double battle with 2 unique tiers", async () => {
|
||||||
|
game.override
|
||||||
|
.startingModifier([
|
||||||
|
{ name: "LURE" },
|
||||||
|
{ name: "SUPER_LURE" }
|
||||||
|
])
|
||||||
|
.startingWave(2);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyField().length).toBe(2);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("should guarantee double boss battle with 3 unique tiers", async () => {
|
||||||
|
game.override
|
||||||
|
.startingModifier([
|
||||||
|
{ name: "LURE" },
|
||||||
|
{ name: "SUPER_LURE" },
|
||||||
|
{ name: "MAX_LURE" }
|
||||||
|
])
|
||||||
|
.startingWave(10);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyField = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
expect(enemyField.length).toBe(2);
|
||||||
|
expect(enemyField[0].isBoss()).toBe(true);
|
||||||
|
expect(enemyField[1].isBoss()).toBe(true);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("should renew how many battles are left of existing booster when picking up new booster of same tier", async() => {
|
||||||
|
game.override
|
||||||
|
.startingModifier([{ name: "LURE" }])
|
||||||
|
.itemRewards([{ name: "LURE" }])
|
||||||
|
.moveset(SPLASH_ONLY)
|
||||||
|
.startingLevel(200);
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.PIKACHU
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
|
||||||
|
await game.doKillOpponents();
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BattleEndPhase");
|
||||||
|
|
||||||
|
const modifier = game.scene.findModifier(m => m instanceof DoubleBattleChanceBoosterModifier) as DoubleBattleChanceBoosterModifier;
|
||||||
|
expect(modifier.getBattleCount()).toBe(9);
|
||||||
|
|
||||||
|
// Forced LURE to spawn in the first slot with override
|
||||||
|
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||||
|
const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler;
|
||||||
|
// Traverse to first modifier slot
|
||||||
|
handler.setCursor(0);
|
||||||
|
handler.setRowCursor(ShopCursorTarget.REWARDS);
|
||||||
|
handler.processInput(Button.ACTION);
|
||||||
|
}, () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("NewBattlePhase"), true);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnInitPhase");
|
||||||
|
|
||||||
|
// Making sure only one booster is in the modifier list even after picking up another
|
||||||
|
let count = 0;
|
||||||
|
for (const m of game.scene.modifiers) {
|
||||||
|
if (m instanceof DoubleBattleChanceBoosterModifier) {
|
||||||
|
count++;
|
||||||
|
const modifierInstance = m as DoubleBattleChanceBoosterModifier;
|
||||||
|
expect(modifierInstance.getBattleCount()).toBe(modifierInstance.getMaxBattles());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(count).toBe(1);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
|
@ -10,12 +10,7 @@ import { Abilities } from "#app/enums/abilities";
|
||||||
import { TempStatStageBoosterModifier } from "#app/modifier/modifier";
|
import { TempStatStageBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import { Button } from "#app/enums/buttons";
|
import { Button } from "#app/enums/buttons";
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
|
||||||
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
|
||||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
|
||||||
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
|
||||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
|
||||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +41,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should provide a x1.3 stat stage multiplier", async() => {
|
it("should provide a x1.3 stat stage multiplier", async() => {
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([
|
||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -56,7 +51,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
|
|
||||||
game.move.select(Moves.TACKLE);
|
game.move.select(Moves.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase);
|
||||||
|
|
||||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
|
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
@ -66,7 +61,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }])
|
.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }])
|
||||||
.ability(Abilities.SIMPLE);
|
.ability(Abilities.SIMPLE);
|
||||||
|
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([
|
||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -89,7 +84,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
|
|
||||||
|
|
||||||
it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async() => {
|
it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async() => {
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([
|
||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -113,7 +108,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
it("should not increase past maximum stat stage multiplier", async() => {
|
it("should not increase past maximum stat stage multiplier", async() => {
|
||||||
game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }, { name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]);
|
game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }, { name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]);
|
||||||
|
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([
|
||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -138,7 +133,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
.startingLevel(200)
|
.startingLevel(200)
|
||||||
.itemRewards([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]);
|
.itemRewards([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]);
|
||||||
|
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([
|
||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -146,10 +141,10 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
|
|
||||||
await game.doKillOpponents();
|
await game.doKillOpponents();
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BattleEndPhase);
|
await game.phaseInterceptor.to("BattleEndPhase");
|
||||||
|
|
||||||
const modifier = game.scene.findModifier(m => m instanceof TempStatStageBoosterModifier) as TempStatStageBoosterModifier;
|
const modifier = game.scene.findModifier(m => m instanceof TempStatStageBoosterModifier) as TempStatStageBoosterModifier;
|
||||||
expect(modifier.getBattlesLeft()).toBe(4);
|
expect(modifier.getBattleCount()).toBe(4);
|
||||||
|
|
||||||
// Forced X_ATTACK to spawn in the first slot with override
|
// Forced X_ATTACK to spawn in the first slot with override
|
||||||
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => {
|
||||||
|
@ -158,16 +153,17 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||||
handler.setCursor(0);
|
handler.setCursor(0);
|
||||||
handler.setRowCursor(ShopCursorTarget.REWARDS);
|
handler.setRowCursor(ShopCursorTarget.REWARDS);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
}, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);
|
}, () => game.isCurrentPhase("CommandPhase") || game.isCurrentPhase("NewBattlePhase"), true);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to("TurnInitPhase");
|
||||||
|
|
||||||
// Making sure only one booster is in the modifier list even after picking up another
|
// Making sure only one booster is in the modifier list even after picking up another
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const m of game.scene.modifiers) {
|
for (const m of game.scene.modifiers) {
|
||||||
if (m instanceof TempStatStageBoosterModifier) {
|
if (m instanceof TempStatStageBoosterModifier) {
|
||||||
count++;
|
count++;
|
||||||
expect((m as TempStatStageBoosterModifier).getBattlesLeft()).toBe(5);
|
const modifierInstance = m as TempStatStageBoosterModifier;
|
||||||
|
expect(modifierInstance.getBattleCount()).toBe(modifierInstance.getMaxBattles());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(count).toBe(1);
|
expect(count).toBe(1);
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { Moves } from "#app/enums/moves";
|
||||||
|
import { Species } from "#app/enums/species";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import { SPLASH_ONLY } from "#test/utils/testUtils";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Dragon Cheer", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.enemyLevel(20)
|
||||||
|
.moveset([Moves.DRAGON_CHEER, Moves.TACKLE, Moves.SPLASH]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("increases the user's allies' critical hit ratio by one stage", async () => {
|
||||||
|
await game.classicMode.startBattle([Species.DRAGONAIR, Species.MAGIKARP]);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyField()[0];
|
||||||
|
|
||||||
|
vi.spyOn(enemy, "getCritStage");
|
||||||
|
|
||||||
|
game.move.select(Moves.DRAGON_CHEER, 0);
|
||||||
|
game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||||
|
|
||||||
|
// After Tackle
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("increases the user's Dragon-type allies' critical hit ratio by two stages", async () => {
|
||||||
|
await game.classicMode.startBattle([Species.MAGIKARP, Species.DRAGONAIR]);
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyField()[0];
|
||||||
|
|
||||||
|
vi.spyOn(enemy, "getCritStage");
|
||||||
|
|
||||||
|
game.move.select(Moves.DRAGON_CHEER, 0);
|
||||||
|
game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||||
|
|
||||||
|
// After Tackle
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
expect(enemy.getCritStage).toHaveReturnedWith(2); // getCritStage is called on defender
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("applies the effect based on the allies' type upon use of the move, and do not change if the allies' type changes later in battle", async () => {
|
||||||
|
await game.classicMode.startBattle([Species.DRAGONAIR, Species.MAGIKARP]);
|
||||||
|
|
||||||
|
const magikarp = game.scene.getPlayerField()[1];
|
||||||
|
const enemy = game.scene.getEnemyField()[0];
|
||||||
|
|
||||||
|
vi.spyOn(enemy, "getCritStage");
|
||||||
|
|
||||||
|
game.move.select(Moves.DRAGON_CHEER, 0);
|
||||||
|
game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||||
|
|
||||||
|
// After Tackle
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
// Change Magikarp's type to Dragon
|
||||||
|
vi.spyOn(magikarp, "getTypes").mockReturnValue([Type.DRAGON]);
|
||||||
|
expect(magikarp.getTypes()).toEqual([Type.DRAGON]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH, 0);
|
||||||
|
game.move.select(Moves.TACKLE, 1, BattlerIndex.ENEMY);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
|
expect(enemy.getCritStage).toHaveReturnedWith(1); // getCritStage is called on defender
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
|
@ -1,13 +1,12 @@
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
import { Abilities } from "#app/enums/abilities";
|
import { Abilities } from "#app/enums/abilities";
|
||||||
import { DamagePhase } from "#app/phases/damage-phase";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
import { Species } from "#app/enums/species";
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
describe("Moves - Glaive Rush", () => {
|
describe("Moves - Glaive Rush", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
|
@ -25,131 +24,142 @@ describe("Moves - Glaive Rush", () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override.battleType("single");
|
game.override
|
||||||
game.override.disableCrits();
|
.battleType("single")
|
||||||
game.override.enemySpecies(Species.MAGIKARP);
|
.disableCrits()
|
||||||
game.override.enemyAbility(Abilities.BALL_FETCH);
|
.enemySpecies(Species.MAGIKARP)
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.GLAIVE_RUSH));
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
game.override.starterSpecies(Species.KLINK);
|
.enemyMoveset(Array(4).fill(Moves.GLAIVE_RUSH))
|
||||||
game.override.ability(Abilities.UNNERVE);
|
.starterSpecies(Species.KLINK)
|
||||||
game.override.passiveAbility(Abilities.FUR_COAT);
|
.ability(Abilities.BALL_FETCH)
|
||||||
game.override.moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]);
|
.moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("takes double damage from attacks", async () => {
|
it("takes double damage from attacks", async () => {
|
||||||
await game.startBattle();
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
|
|
||||||
vi.spyOn(game.scene, "randBattleSeedInt").mockReturnValue(0);
|
|
||||||
game.move.select(Moves.SHADOW_SNEAK);
|
game.move.select(Moves.SHADOW_SNEAK);
|
||||||
await game.phaseInterceptor.to(DamagePhase);
|
await game.phaseInterceptor.to("DamagePhase");
|
||||||
const damageDealt = 1000 - enemy.hp;
|
const damageDealt = 1000 - enemy.hp;
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
game.move.select(Moves.SHADOW_SNEAK);
|
game.move.select(Moves.SHADOW_SNEAK);
|
||||||
await game.phaseInterceptor.to(DamagePhase);
|
await game.phaseInterceptor.to("DamagePhase");
|
||||||
expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3));
|
expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3));
|
||||||
|
|
||||||
}, 5000); // TODO: revert back to 20s
|
}, TIMEOUT);
|
||||||
|
|
||||||
it("always gets hit by attacks", async () => {
|
it("always gets hit by attacks", async () => {
|
||||||
await game.startBattle();
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
|
|
||||||
allMoves[Moves.AVALANCHE].accuracy = 0;
|
allMoves[Moves.AVALANCHE].accuracy = 0;
|
||||||
game.move.select(Moves.AVALANCHE);
|
game.move.select(Moves.AVALANCHE);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(enemy.hp).toBeLessThan(1000);
|
expect(enemy.hp).toBeLessThan(1000);
|
||||||
|
|
||||||
}, 20000);
|
}, TIMEOUT);
|
||||||
|
|
||||||
it("interacts properly with multi-lens", async () => {
|
it("interacts properly with multi-lens", async () => {
|
||||||
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]);
|
game.override
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.AVALANCHE));
|
.startingHeldItems([{ name: "MULTI_LENS", count: 2 }])
|
||||||
await game.startBattle();
|
.enemyMoveset(Array(4).fill(Moves.AVALANCHE));
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
const player = game.scene.getPlayerPokemon()!;
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
player.hp = 1000;
|
player.hp = 1000;
|
||||||
|
|
||||||
allMoves[Moves.AVALANCHE].accuracy = 0;
|
allMoves[Moves.AVALANCHE].accuracy = 0;
|
||||||
game.move.select(Moves.GLAIVE_RUSH);
|
game.move.select(Moves.GLAIVE_RUSH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(player.hp).toBeLessThan(1000);
|
expect(player.hp).toBeLessThan(1000);
|
||||||
player.hp = 1000;
|
player.hp = 1000;
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(player.hp).toBe(1000);
|
expect(player.hp).toBe(1000);
|
||||||
|
|
||||||
}, 20000);
|
}, TIMEOUT);
|
||||||
|
|
||||||
it("secondary effects only last until next move", async () => {
|
it("secondary effects only last until next move", async () => {
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK));
|
game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK));
|
||||||
await game.startBattle();
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
const player = game.scene.getPlayerPokemon()!;
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
player.hp = 1000;
|
player.hp = 1000;
|
||||||
allMoves[Moves.SHADOW_SNEAK].accuracy = 0;
|
allMoves[Moves.SHADOW_SNEAK].accuracy = 0;
|
||||||
|
|
||||||
game.move.select(Moves.GLAIVE_RUSH);
|
game.move.select(Moves.GLAIVE_RUSH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(player.hp).toBe(1000);
|
expect(player.hp).toBe(1000);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
const damagedHp = player.hp;
|
const damagedHp = player.hp;
|
||||||
expect(player.hp).toBeLessThan(1000);
|
expect(player.hp).toBeLessThan(1000);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(player.hp).toBe(damagedHp);
|
expect(player.hp).toBe(damagedHp);
|
||||||
|
|
||||||
}, 20000);
|
}, TIMEOUT);
|
||||||
|
|
||||||
it("secondary effects are removed upon switching", async () => {
|
it("secondary effects are removed upon switching", async () => {
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK));
|
game.override
|
||||||
game.override.starterSpecies(0);
|
.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK))
|
||||||
await game.startBattle([Species.KLINK, Species.FEEBAS]);
|
.starterSpecies(0);
|
||||||
|
await game.classicMode.startBattle([Species.KLINK, Species.FEEBAS]);
|
||||||
|
|
||||||
const player = game.scene.getPlayerPokemon()!;
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
allMoves[Moves.SHADOW_SNEAK].accuracy = 0;
|
allMoves[Moves.SHADOW_SNEAK].accuracy = 0;
|
||||||
|
|
||||||
game.move.select(Moves.GLAIVE_RUSH);
|
game.move.select(Moves.GLAIVE_RUSH);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(player.hp).toBe(player.getMaxHp());
|
expect(player.hp).toBe(player.getMaxHp());
|
||||||
|
|
||||||
game.doSwitchPokemon(1);
|
game.doSwitchPokemon(1);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
game.doSwitchPokemon(1);
|
game.doSwitchPokemon(1);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
expect(player.hp).toBe(player.getMaxHp());
|
expect(player.hp).toBe(player.getMaxHp());
|
||||||
|
|
||||||
}, 20000);
|
}, TIMEOUT);
|
||||||
|
|
||||||
it("secondary effects don't activate if move fails", async () => {
|
it("secondary effects don't activate if move fails", async () => {
|
||||||
game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]);
|
game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]);
|
||||||
await game.startBattle();
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
const player = game.scene.getPlayerPokemon()!;
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
const enemy = game.scene.getEnemyPokemon()!;
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
player.hp = 1000;
|
player.hp = 1000;
|
||||||
|
|
||||||
game.move.select(Moves.PROTECT);
|
game.move.select(Moves.PROTECT);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
game.move.select(Moves.SHADOW_SNEAK);
|
game.move.select(Moves.SHADOW_SNEAK);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.SPLASH));
|
game.override.enemyMoveset(Array(4).fill(Moves.SPLASH));
|
||||||
const damagedHP1 = 1000 - enemy.hp;
|
const damagedHP1 = 1000 - enemy.hp;
|
||||||
enemy.hp = 1000;
|
enemy.hp = 1000;
|
||||||
|
|
||||||
game.move.select(Moves.SHADOW_SNEAK);
|
game.move.select(Moves.SHADOW_SNEAK);
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
const damagedHP2 = 1000 - enemy.hp;
|
const damagedHP2 = 1000 - enemy.hp;
|
||||||
|
|
||||||
expect(damagedHP2).toBeGreaterThanOrEqual((damagedHP1 * 2) - 1);
|
expect(damagedHP2).toBeGreaterThanOrEqual((damagedHP1 * 2) - 1);
|
||||||
}, 20000);
|
}, TIMEOUT);
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { Moves } from "#enums/moves";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag";
|
import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag";
|
||||||
import { BerryPhase } from "#app/phases/berry-phase";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
|
||||||
const TIMEOUT = 20 * 1000;
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
@ -43,13 +44,13 @@ describe("Moves - Protect", () => {
|
||||||
test(
|
test(
|
||||||
"should protect the user from attacks",
|
"should protect the user from attacks",
|
||||||
async () => {
|
async () => {
|
||||||
await game.startBattle([Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.PROTECT);
|
game.move.select(Moves.PROTECT);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
|
@ -61,13 +62,13 @@ describe("Moves - Protect", () => {
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.CEASELESS_EDGE));
|
game.override.enemyMoveset(Array(4).fill(Moves.CEASELESS_EDGE));
|
||||||
vi.spyOn(allMoves[Moves.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100);
|
vi.spyOn(allMoves[Moves.CEASELESS_EDGE], "accuracy", "get").mockReturnValue(100);
|
||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.PROTECT);
|
game.move.select(Moves.PROTECT);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
||||||
expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeUndefined();
|
expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeUndefined();
|
||||||
|
@ -79,13 +80,13 @@ describe("Moves - Protect", () => {
|
||||||
async () => {
|
async () => {
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.CHARM));
|
game.override.enemyMoveset(Array(4).fill(Moves.CHARM));
|
||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.PROTECT);
|
game.move.select(Moves.PROTECT);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0);
|
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
|
@ -96,18 +97,38 @@ describe("Moves - Protect", () => {
|
||||||
async () => {
|
async () => {
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.TACHYON_CUTTER));
|
game.override.enemyMoveset(Array(4).fill(Moves.TACHYON_CUTTER));
|
||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD]);
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerPokemon()!;
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
game.move.select(Moves.PROTECT);
|
game.move.select(Moves.PROTECT);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp());
|
||||||
expect(enemyPokemon.turnData.hitCount).toBe(1);
|
expect(enemyPokemon.turnData.hitCount).toBe(1);
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should fail if the user is the last to move in the turn",
|
||||||
|
async () => {
|
||||||
|
game.override.enemyMoveset(Array(4).fill(Moves.PROTECT));
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
|
const leadPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.PROTECT);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||||
|
expect(leadPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
}, TIMEOUT
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { Species } from "#enums/species";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { BerryPhase } from "#app/phases/berry-phase";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
|
||||||
const TIMEOUT = 20 * 1000;
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
@ -42,19 +42,16 @@ describe("Moves - Quick Guard", () => {
|
||||||
test(
|
test(
|
||||||
"should protect the user and allies from priority moves",
|
"should protect the user and allies from priority moves",
|
||||||
async () => {
|
async () => {
|
||||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
await game.classicMode.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_GUARD);
|
game.move.select(Moves.QUICK_GUARD);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp()));
|
playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp()));
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -64,19 +61,16 @@ describe("Moves - Quick Guard", () => {
|
||||||
game.override.enemyAbility(Abilities.PRANKSTER);
|
game.override.enemyAbility(Abilities.PRANKSTER);
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.GROWL));
|
game.override.enemyMoveset(Array(4).fill(Moves.GROWL));
|
||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
await game.classicMode.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_GUARD);
|
game.move.select(Moves.QUICK_GUARD);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH, 1);
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
leadPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0));
|
playerPokemon.forEach(p => expect(p.getStatStage(Stat.ATK)).toBe(0));
|
||||||
}, TIMEOUT
|
}, TIMEOUT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -85,21 +79,40 @@ describe("Moves - Quick Guard", () => {
|
||||||
async () => {
|
async () => {
|
||||||
game.override.enemyMoveset(Array(4).fill(Moves.WATER_SHURIKEN));
|
game.override.enemyMoveset(Array(4).fill(Moves.WATER_SHURIKEN));
|
||||||
|
|
||||||
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
await game.classicMode.startBattle([Species.CHARIZARD, Species.BLASTOISE]);
|
||||||
|
|
||||||
const leadPokemon = game.scene.getPlayerField();
|
const playerPokemon = game.scene.getPlayerField();
|
||||||
const enemyPokemon = game.scene.getEnemyField();
|
const enemyPokemon = game.scene.getEnemyField();
|
||||||
|
|
||||||
game.move.select(Moves.QUICK_GUARD);
|
game.move.select(Moves.QUICK_GUARD);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
|
||||||
|
|
||||||
game.move.select(Moves.FOLLOW_ME, 1);
|
game.move.select(Moves.FOLLOW_ME, 1);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
leadPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp()));
|
playerPokemon.forEach(p => expect(p.hp).toBe(p.getMaxHp()));
|
||||||
enemyPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1));
|
enemyPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"should fail if the user is the last to move in the turn",
|
||||||
|
async () => {
|
||||||
|
game.override.battleType("single");
|
||||||
|
game.override.enemyMoveset(Array(4).fill(Moves.QUICK_GUARD));
|
||||||
|
|
||||||
|
await game.classicMode.startBattle([Species.CHARIZARD]);
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.QUICK_GUARD);
|
||||||
|
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
|
||||||
|
expect(enemyPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS);
|
||||||
|
expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
}, TIMEOUT
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -81,7 +81,7 @@ export default class GameManager {
|
||||||
constructor(phaserGame: Phaser.Game, bypassLogin: boolean = true) {
|
constructor(phaserGame: Phaser.Game, bypassLogin: boolean = true) {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
ErrorInterceptor.getInstance().clear();
|
ErrorInterceptor.getInstance().clear();
|
||||||
BattleScene.prototype.randBattleSeedInt = (arg) => arg-1;
|
BattleScene.prototype.randBattleSeedInt = (range, min: number = 0) => min + range - 1; // This simulates a max roll
|
||||||
this.gameWrapper = new GameWrapper(phaserGame, bypassLogin);
|
this.gameWrapper = new GameWrapper(phaserGame, bypassLogin);
|
||||||
this.scene = new BattleScene();
|
this.scene = new BattleScene();
|
||||||
this.phaseInterceptor = new PhaseInterceptor(this.scene);
|
this.phaseInterceptor = new PhaseInterceptor(this.scene);
|
||||||
|
|
|
@ -220,4 +220,5 @@ export default class MockContainer implements MockGameObject {
|
||||||
return this.list.find(v => v.name === key) ?? new MockContainer(this.textureManager, 0, 0);
|
return this.list.find(v => v.name === key) ?? new MockContainer(this.textureManager, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disableInteractive = vi.fn();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,21 @@ import { addTextObject, TextStyle } from "./text";
|
||||||
import { addWindow } from "./ui-theme";
|
import { addWindow } from "./ui-theme";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
|
||||||
|
interface BuildInteractableImageOpts {
|
||||||
|
scale?: number;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
origin?: { x: number; y: number };
|
||||||
|
}
|
||||||
|
|
||||||
export default class LoginFormUiHandler extends FormModalUiHandler {
|
export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
|
private readonly ERR_USERNAME: string = "invalid username";
|
||||||
|
private readonly ERR_PASSWORD: string = "invalid password";
|
||||||
|
private readonly ERR_ACCOUNT_EXIST: string = "account doesn't exist";
|
||||||
|
private readonly ERR_PASSWORD_MATCH: string = "password doesn't match";
|
||||||
|
private readonly ERR_NO_SAVES: string = "No save files found";
|
||||||
|
private readonly ERR_TOO_MANY_SAVES: string = "Too many save files found";
|
||||||
|
|
||||||
private googleImage: Phaser.GameObjects.Image;
|
private googleImage: Phaser.GameObjects.Image;
|
||||||
private discordImage: Phaser.GameObjects.Image;
|
private discordImage: Phaser.GameObjects.Image;
|
||||||
private usernameInfoImage: Phaser.GameObjects.Image;
|
private usernameInfoImage: Phaser.GameObjects.Image;
|
||||||
|
@ -21,8 +35,23 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(): void {
|
setup(): void {
|
||||||
|
|
||||||
super.setup();
|
super.setup();
|
||||||
|
this.buildExternalPartyContainer();
|
||||||
|
|
||||||
|
this.infoContainer = this.scene.add.container(0, 0);
|
||||||
|
|
||||||
|
this.usernameInfoImage = this.buildInteractableImage("settings_icon", "username-info-icon", {
|
||||||
|
x: 20,
|
||||||
|
scale: 0.5
|
||||||
|
});
|
||||||
|
|
||||||
|
this.infoContainer.add(this.usernameInfoImage);
|
||||||
|
this.getUi().add(this.infoContainer);
|
||||||
|
this.infoContainer.setVisible(false);
|
||||||
|
this.infoContainer.disableInteractive();
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildExternalPartyContainer() {
|
||||||
this.externalPartyContainer = this.scene.add.container(0, 0);
|
this.externalPartyContainer = this.scene.add.container(0, 0);
|
||||||
this.externalPartyContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 12, this.scene.game.canvas.height / 12), Phaser.Geom.Rectangle.Contains);
|
this.externalPartyContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 12, this.scene.game.canvas.height / 12), Phaser.Geom.Rectangle.Contains);
|
||||||
this.externalPartyTitle = addTextObject(this.scene, 0, 4, "", TextStyle.SETTINGS_LABEL);
|
this.externalPartyTitle = addTextObject(this.scene, 0, 4, "", TextStyle.SETTINGS_LABEL);
|
||||||
|
@ -31,23 +60,8 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
this.externalPartyContainer.add(this.externalPartyBg);
|
this.externalPartyContainer.add(this.externalPartyBg);
|
||||||
this.externalPartyContainer.add(this.externalPartyTitle);
|
this.externalPartyContainer.add(this.externalPartyTitle);
|
||||||
|
|
||||||
this.infoContainer = this.scene.add.container(0, 0);
|
this.googleImage = this.buildInteractableImage("google", "google-icon");
|
||||||
this.infoContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width / 12, this.scene.game.canvas.height / 12), Phaser.Geom.Rectangle.Contains);
|
this.discordImage = this.buildInteractableImage("discord", "discord-icon");
|
||||||
|
|
||||||
const googleImage = this.scene.add.image(0, 0, "google");
|
|
||||||
googleImage.setOrigin(0, 0);
|
|
||||||
googleImage.setScale(0.07);
|
|
||||||
googleImage.setInteractive();
|
|
||||||
googleImage.setName("google-icon");
|
|
||||||
this.googleImage = googleImage;
|
|
||||||
|
|
||||||
const discordImage = this.scene.add.image(20, 0, "discord");
|
|
||||||
discordImage.setOrigin(0, 0);
|
|
||||||
discordImage.setScale(0.07);
|
|
||||||
discordImage.setInteractive();
|
|
||||||
discordImage.setName("discord-icon");
|
|
||||||
|
|
||||||
this.discordImage = discordImage;
|
|
||||||
|
|
||||||
this.externalPartyContainer.add(this.googleImage);
|
this.externalPartyContainer.add(this.googleImage);
|
||||||
this.externalPartyContainer.add(this.discordImage);
|
this.externalPartyContainer.add(this.discordImage);
|
||||||
|
@ -55,59 +69,52 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
this.externalPartyContainer.add(this.googleImage);
|
this.externalPartyContainer.add(this.googleImage);
|
||||||
this.externalPartyContainer.add(this.discordImage);
|
this.externalPartyContainer.add(this.discordImage);
|
||||||
this.externalPartyContainer.setVisible(false);
|
this.externalPartyContainer.setVisible(false);
|
||||||
|
|
||||||
const usernameInfoImage = this.scene.add.image(20, 0, "settings_icon");
|
|
||||||
usernameInfoImage.setOrigin(0, 0);
|
|
||||||
usernameInfoImage.setScale(0.5);
|
|
||||||
usernameInfoImage.setInteractive();
|
|
||||||
usernameInfoImage.setName("username-info-icon");
|
|
||||||
this.usernameInfoImage = usernameInfoImage;
|
|
||||||
|
|
||||||
this.infoContainer.add(this.usernameInfoImage);
|
|
||||||
this.getUi().add(this.infoContainer);
|
|
||||||
this.infoContainer.setVisible(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getModalTitle(config?: ModalConfig): string {
|
override getModalTitle(_config?: ModalConfig): string {
|
||||||
return i18next.t("menu:login");
|
return i18next.t("menu:login");
|
||||||
}
|
}
|
||||||
|
|
||||||
getFields(config?: ModalConfig): string[] {
|
override getFields(_config?: ModalConfig): string[] {
|
||||||
return [ i18next.t("menu:username"), i18next.t("menu:password") ];
|
return [ i18next.t("menu:username"), i18next.t("menu:password") ];
|
||||||
}
|
}
|
||||||
|
|
||||||
getWidth(config?: ModalConfig): number {
|
override getWidth(_config?: ModalConfig): number {
|
||||||
return 160;
|
return 160;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMargin(config?: ModalConfig): [number, number, number, number] {
|
override getMargin(_config?: ModalConfig): [number, number, number, number] {
|
||||||
return [ 0, 0, 48, 0 ];
|
return [ 0, 0, 48, 0 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
getButtonLabels(config?: ModalConfig): string[] {
|
override getButtonLabels(_config?: ModalConfig): string[] {
|
||||||
return [ i18next.t("menu:login"), i18next.t("menu:register")];
|
return [ i18next.t("menu:login"), i18next.t("menu:register")];
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableErrorMessage(error: string): string {
|
override getReadableErrorMessage(error: string): string {
|
||||||
const colonIndex = error?.indexOf(":");
|
const colonIndex = error?.indexOf(":");
|
||||||
if (colonIndex > 0) {
|
if (colonIndex > 0) {
|
||||||
error = error.slice(0, colonIndex);
|
error = error.slice(0, colonIndex);
|
||||||
}
|
}
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case "invalid username":
|
case this.ERR_USERNAME:
|
||||||
return i18next.t("menu:invalidLoginUsername");
|
return i18next.t("menu:invalidLoginUsername");
|
||||||
case "invalid password":
|
case this.ERR_PASSWORD:
|
||||||
return i18next.t("menu:invalidLoginPassword");
|
return i18next.t("menu:invalidLoginPassword");
|
||||||
case "account doesn't exist":
|
case this.ERR_ACCOUNT_EXIST:
|
||||||
return i18next.t("menu:accountNonExistent");
|
return i18next.t("menu:accountNonExistent");
|
||||||
case "password doesn't match":
|
case this.ERR_PASSWORD_MATCH:
|
||||||
return i18next.t("menu:unmatchingPassword");
|
return i18next.t("menu:unmatchingPassword");
|
||||||
|
case this.ERR_NO_SAVES:
|
||||||
|
return i18next.t("menu:noSaves");
|
||||||
|
case this.ERR_TOO_MANY_SAVES:
|
||||||
|
return i18next.t("menu:tooManySaves");
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.getReadableErrorMessage(error);
|
return super.getReadableErrorMessage(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
show(args: any[]): boolean {
|
override show(args: any[]): boolean {
|
||||||
if (super.show(args)) {
|
if (super.show(args)) {
|
||||||
|
|
||||||
const config = args[0] as ModalConfig;
|
const config = args[0] as ModalConfig;
|
||||||
|
@ -148,17 +155,16 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
override clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
this.externalPartyContainer.setVisible(false);
|
this.externalPartyContainer.setVisible(false);
|
||||||
this.infoContainer.setVisible(false);
|
this.infoContainer.setVisible(false);
|
||||||
|
this.setMouseCursorStyle("default"); //reset cursor
|
||||||
|
|
||||||
this.discordImage.off("pointerdown");
|
[this.discordImage, this.googleImage, this.usernameInfoImage].forEach((img) => img.off("pointerdown"));
|
||||||
this.googleImage.off("pointerdown");
|
|
||||||
this.usernameInfoImage.off("pointerdown");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processExternalProvider(config: ModalConfig) : void {
|
private processExternalProvider(config: ModalConfig) : void {
|
||||||
this.externalPartyTitle.setText(i18next.t("menu:orUse") ?? "");
|
this.externalPartyTitle.setText(i18next.t("menu:orUse") ?? "");
|
||||||
this.externalPartyTitle.setX(20+this.externalPartyTitle.text.length);
|
this.externalPartyTitle.setX(20+this.externalPartyTitle.text.length);
|
||||||
this.externalPartyTitle.setVisible(true);
|
this.externalPartyTitle.setVisible(true);
|
||||||
|
@ -205,6 +211,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
label: dataKeys[i].replace(keyToFind, ""),
|
label: dataKeys[i].replace(keyToFind, ""),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.revertMode();
|
||||||
|
this.infoContainer.disableInteractive();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -213,8 +220,13 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
options: options,
|
options: options,
|
||||||
delay: 1000
|
delay: 1000
|
||||||
});
|
});
|
||||||
|
this.infoContainer.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.scene.game.canvas.width, this.scene.game.canvas.height), Phaser.Geom.Rectangle.Contains);
|
||||||
} else {
|
} else {
|
||||||
return onFail("You have too many save files to use this");
|
if (dataKeys.length > 2) {
|
||||||
|
return onFail(this.ERR_TOO_MANY_SAVES);
|
||||||
|
} else {
|
||||||
|
return onFail(this.ERR_NO_SAVES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -236,4 +248,21 @@ export default class LoginFormUiHandler extends FormModalUiHandler {
|
||||||
alpha: 1
|
alpha: 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private buildInteractableImage(texture: string, name: string, opts: BuildInteractableImageOpts = {}) {
|
||||||
|
const {
|
||||||
|
scale = 0.07,
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
origin = { x: 0, y: 0 }
|
||||||
|
} = opts;
|
||||||
|
const img = this.scene.add.image(x, y, texture);
|
||||||
|
img.setName(name);
|
||||||
|
img.setOrigin(origin.x, origin.y);
|
||||||
|
img.setScale(scale);
|
||||||
|
img.setInteractive();
|
||||||
|
this.addInteractionHoverEffect(img);
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,29 +57,35 @@ export abstract class ModalUiHandler extends UiHandler {
|
||||||
|
|
||||||
const buttonLabels = this.getButtonLabels();
|
const buttonLabels = this.getButtonLabels();
|
||||||
|
|
||||||
const buttonTopMargin = this.getButtonTopMargin();
|
|
||||||
|
|
||||||
for (const label of buttonLabels) {
|
for (const label of buttonLabels) {
|
||||||
const buttonLabel = addTextObject(this.scene, 0, 8, label, TextStyle.TOOLTIP_CONTENT);
|
this.addButton(label);
|
||||||
buttonLabel.setOrigin(0.5, 0.5);
|
|
||||||
|
|
||||||
const buttonBg = addWindow(this.scene, 0, 0, buttonLabel.getBounds().width + 8, 16, false, false, 0, 0, WindowVariant.THIN);
|
|
||||||
buttonBg.setOrigin(0.5, 0);
|
|
||||||
buttonBg.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonBg.width, buttonBg.height), Phaser.Geom.Rectangle.Contains);
|
|
||||||
|
|
||||||
const buttonContainer = this.scene.add.container(0, buttonTopMargin);
|
|
||||||
|
|
||||||
this.buttonBgs.push(buttonBg);
|
|
||||||
this.buttonContainers.push(buttonContainer);
|
|
||||||
|
|
||||||
buttonContainer.add(buttonBg);
|
|
||||||
buttonContainer.add(buttonLabel);
|
|
||||||
this.modalContainer.add(buttonContainer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modalContainer.setVisible(false);
|
this.modalContainer.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addButton(label: string) {
|
||||||
|
const buttonTopMargin = this.getButtonTopMargin();
|
||||||
|
const buttonLabel = addTextObject(this.scene, 0, 8, label, TextStyle.TOOLTIP_CONTENT);
|
||||||
|
buttonLabel.setOrigin(0.5, 0.5);
|
||||||
|
|
||||||
|
const buttonBg = addWindow(this.scene, 0, 0, buttonLabel.getBounds().width + 8, 16, false, false, 0, 0, WindowVariant.THIN);
|
||||||
|
buttonBg.setOrigin(0.5, 0);
|
||||||
|
buttonBg.setInteractive(new Phaser.Geom.Rectangle(0, 0, buttonBg.width, buttonBg.height), Phaser.Geom.Rectangle.Contains);
|
||||||
|
|
||||||
|
const buttonContainer = this.scene.add.container(0, buttonTopMargin);
|
||||||
|
|
||||||
|
this.buttonBgs.push(buttonBg);
|
||||||
|
this.buttonContainers.push(buttonContainer);
|
||||||
|
|
||||||
|
buttonContainer.add(buttonBg);
|
||||||
|
buttonContainer.add(buttonLabel);
|
||||||
|
|
||||||
|
this.addInteractionHoverEffect(buttonBg);
|
||||||
|
|
||||||
|
this.modalContainer.add(buttonContainer);
|
||||||
|
}
|
||||||
|
|
||||||
show(args: any[]): boolean {
|
show(args: any[]): boolean {
|
||||||
if (args.length >= 1 && "buttonActions" in args[0]) {
|
if (args.length >= 1 && "buttonActions" in args[0]) {
|
||||||
super.show(args);
|
super.show(args);
|
||||||
|
@ -135,4 +141,20 @@ export abstract class ModalUiHandler extends UiHandler {
|
||||||
|
|
||||||
this.buttonBgs.map(bg => bg.off("pointerdown"));
|
this.buttonBgs.map(bg => bg.off("pointerdown"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a hover effect to a game object which changes the cursor to a `pointer` and tints it slighly
|
||||||
|
* @param gameObject the game object to add hover events/effects to
|
||||||
|
*/
|
||||||
|
protected addInteractionHoverEffect(gameObject: Phaser.GameObjects.Image | Phaser.GameObjects.NineSlice | Phaser.GameObjects.Sprite) {
|
||||||
|
gameObject.on("pointerover", () => {
|
||||||
|
this.setMouseCursorStyle("pointer");
|
||||||
|
gameObject.setTint(0xbbbbbb);
|
||||||
|
});
|
||||||
|
|
||||||
|
gameObject.on("pointerout", () => {
|
||||||
|
this.setMouseCursorStyle("default");
|
||||||
|
gameObject.clearTint();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,15 @@ export default abstract class UiHandler {
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the style of the mouse cursor.
|
||||||
|
* @see {@link https://developer.mozilla.org/en-US/docs/Web/CSS/cursor}
|
||||||
|
* @param cursorStyle cursor style to apply
|
||||||
|
*/
|
||||||
|
protected setMouseCursorStyle(cursorStyle: "pointer" | "default") {
|
||||||
|
this.scene.input.manager.canvas.style.cursor = cursorStyle;
|
||||||
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
}
|
}
|
||||||
|
|
28
src/utils.ts
28
src/utils.ts
|
@ -1,5 +1,5 @@
|
||||||
import i18next from "i18next";
|
|
||||||
import { MoneyFormat } from "#enums/money-format";
|
import { MoneyFormat } from "#enums/money-format";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
export const MissingTextureKey = "__MISSING";
|
export const MissingTextureKey = "__MISSING";
|
||||||
|
|
||||||
|
@ -82,6 +82,12 @@ export function randInt(range: integer, min: integer = 0): integer {
|
||||||
return Math.floor(Math.random() * range) + min;
|
return Math.floor(Math.random() * range) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random number using the global seed, or the current battle's seed if called via `Battle.randSeedInt`
|
||||||
|
* @param range How large of a range of random numbers to choose from. If {@linkcode range} <= 1, returns {@linkcode min}
|
||||||
|
* @param min The minimum integer to pick, default `0`
|
||||||
|
* @returns A random integer between {@linkcode min} and ({@linkcode min} + {@linkcode range} - 1)
|
||||||
|
*/
|
||||||
export function randSeedInt(range: integer, min: integer = 0): integer {
|
export function randSeedInt(range: integer, min: integer = 0): integer {
|
||||||
if (range <= 1) {
|
if (range <= 1) {
|
||||||
return min;
|
return min;
|
||||||
|
@ -449,6 +455,26 @@ export function rgbaToInt(rgba: integer[]): integer {
|
||||||
return (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + rgba[3];
|
return (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + rgba[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provided valid HSV values, calculates and stitches together a string of that
|
||||||
|
* HSV color's corresponding hex code.
|
||||||
|
*
|
||||||
|
* Sourced from {@link https://stackoverflow.com/a/44134328}.
|
||||||
|
* @param h Hue in degrees, must be in a range of [0, 360]
|
||||||
|
* @param s Saturation percentage, must be in a range of [0, 1]
|
||||||
|
* @param l Ligthness percentage, must be in a range of [0, 1]
|
||||||
|
* @returns a string of the corresponding color hex code with a "#" prefix
|
||||||
|
*/
|
||||||
|
export function hslToHex(h: number, s: number, l: number): string {
|
||||||
|
const a = s * Math.min(l, 1 - l);
|
||||||
|
const f = (n: number) => {
|
||||||
|
const k = (n + h / 30) % 12;
|
||||||
|
const rgb = l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
|
||||||
|
return Math.round(rgb * 255).toString(16).padStart(2, "0");
|
||||||
|
};
|
||||||
|
return `#${f(0)}${f(8)}${f(4)}`;
|
||||||
|
}
|
||||||
|
|
||||||
/*This function returns true if the current lang is available for some functions
|
/*This function returns true if the current lang is available for some functions
|
||||||
If the lang is not in the function, it usually means that lang is going to use the default english version
|
If the lang is not in the function, it usually means that lang is going to use the default english version
|
||||||
This function is used in:
|
This function is used in:
|
||||||
|
|
|
@ -1,38 +1,43 @@
|
||||||
import { defineProject } from 'vitest/config';
|
import { defineProject, UserWorkspaceConfig } from 'vitest/config';
|
||||||
import { defaultConfig } from './vite.config';
|
import { defaultConfig } from './vite.config';
|
||||||
|
|
||||||
|
export const defaultProjectTestConfig: UserWorkspaceConfig["test"] = {
|
||||||
|
setupFiles: ['./src/test/vitest.setup.ts'],
|
||||||
|
server: {
|
||||||
|
deps: {
|
||||||
|
inline: ['vitest-canvas-mock'],
|
||||||
|
//@ts-ignore
|
||||||
|
optimizer: {
|
||||||
|
web: {
|
||||||
|
include: ['vitest-canvas-mock'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
environment: 'jsdom' as const,
|
||||||
|
environmentOptions: {
|
||||||
|
jsdom: {
|
||||||
|
resources: 'usable',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
threads: false,
|
||||||
|
trace: true,
|
||||||
|
restoreMocks: true,
|
||||||
|
watch: false,
|
||||||
|
coverage: {
|
||||||
|
provider: 'istanbul' as const,
|
||||||
|
reportsDirectory: 'coverage' as const,
|
||||||
|
reporters: ['text-summary', 'html'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default defineProject(({ mode }) => ({
|
export default defineProject(({ mode }) => ({
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
test: {
|
test: {
|
||||||
|
...defaultProjectTestConfig,
|
||||||
name: "main",
|
name: "main",
|
||||||
include: ["./src/test/**/*.{test,spec}.ts"],
|
include: ["./src/test/**/*.{test,spec}.ts"],
|
||||||
exclude: ["./src/test/pre.test.ts"],
|
exclude: ["./src/test/pre.test.ts"],
|
||||||
setupFiles: ['./src/test/vitest.setup.ts'],
|
|
||||||
server: {
|
|
||||||
deps: {
|
|
||||||
inline: ['vitest-canvas-mock'],
|
|
||||||
optimizer: {
|
|
||||||
web: {
|
|
||||||
include: ['vitest-canvas-mock'],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
environment: 'jsdom' as const,
|
|
||||||
environmentOptions: {
|
|
||||||
jsdom: {
|
|
||||||
resources: 'usable',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
threads: false,
|
|
||||||
trace: true,
|
|
||||||
restoreMocks: true,
|
|
||||||
watch: false,
|
|
||||||
coverage: {
|
|
||||||
provider: 'istanbul' as const,
|
|
||||||
reportsDirectory: 'coverage' as const,
|
|
||||||
reporters: ['text-summary', 'html'],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
esbuild: {
|
esbuild: {
|
||||||
pure: mode === 'production' ? [ 'console.log' ] : [],
|
pure: mode === 'production' ? [ 'console.log' ] : [],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { defineWorkspace } from "vitest/config";
|
import { defineWorkspace } from "vitest/config";
|
||||||
import { defaultConfig } from "./vite.config";
|
import { defaultConfig } from "./vite.config";
|
||||||
|
import { defaultProjectTestConfig } from "./vitest.config";
|
||||||
|
|
||||||
export default defineWorkspace([
|
export default defineWorkspace([
|
||||||
{
|
{
|
||||||
|
@ -10,5 +11,58 @@ export default defineWorkspace([
|
||||||
environment: "jsdom",
|
environment: "jsdom",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
...defaultConfig,
|
||||||
|
test: {
|
||||||
|
...defaultProjectTestConfig,
|
||||||
|
name: "misc",
|
||||||
|
include: [
|
||||||
|
"src/test/achievements/**/*.{test,spec}.ts",
|
||||||
|
"src/test/arena/**/*.{test,spec}.ts",
|
||||||
|
"src/test/battlerTags/**/*.{test,spec}.ts",
|
||||||
|
"src/test/eggs/**/*.{test,spec}.ts",
|
||||||
|
"src/test/field/**/*.{test,spec}.ts",
|
||||||
|
"src/test/inputs/**/*.{test,spec}.ts",
|
||||||
|
"src/test/localization/**/*.{test,spec}.ts",
|
||||||
|
"src/test/phases/**/*.{test,spec}.ts",
|
||||||
|
"src/test/settingMenu/**/*.{test,spec}.ts",
|
||||||
|
"src/test/sprites/**/*.{test,spec}.ts",
|
||||||
|
"src/test/ui/**/*.{test,spec}.ts",
|
||||||
|
"src/test/*.{test,spec}.ts",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultConfig,
|
||||||
|
test: {
|
||||||
|
...defaultProjectTestConfig,
|
||||||
|
name: "abilities",
|
||||||
|
include: ["src/test/abilities/**/*.{test,spec}.ts"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultConfig,
|
||||||
|
test: {
|
||||||
|
...defaultProjectTestConfig,
|
||||||
|
name: "battle",
|
||||||
|
include: ["src/test/battle/**/*.{test,spec}.ts"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultConfig,
|
||||||
|
test: {
|
||||||
|
...defaultProjectTestConfig,
|
||||||
|
name: "items",
|
||||||
|
include: ["src/test/items/**/*.{test,spec}.ts"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...defaultConfig,
|
||||||
|
test: {
|
||||||
|
...defaultProjectTestConfig,
|
||||||
|
name: "moves",
|
||||||
|
include: ["src/test/moves/**/*.{test,spec}.ts"],
|
||||||
|
},
|
||||||
|
},
|
||||||
"./vitest.config.ts",
|
"./vitest.config.ts",
|
||||||
]);
|
]);
|
||||||
|
|
Loading…
Reference in New Issue