mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-03 23:12:20 +02:00
Compare commits
19 Commits
1e97d1bbd3
...
f159b2e84d
Author | SHA1 | Date | |
---|---|---|---|
|
f159b2e84d | ||
|
170abd1dc7 | ||
|
a3c60c43b2 | ||
|
60aa61e56e | ||
|
db4a63dbb9 | ||
|
aeecb67f32 | ||
|
d1132a5765 | ||
|
f7169868f3 | ||
|
7eb6ba4dfd | ||
|
f1111dc0d2 | ||
|
3b9b0c4091 | ||
|
8bf44a2047 | ||
|
e4da48f51a | ||
|
b2cd21bcb1 | ||
|
9833b1da7e | ||
|
db9434ac11 | ||
|
c112abbcd2 | ||
|
e2503d0d3e | ||
|
3eb9215985 |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
BIN
public/images/items/inverse.png
Normal file
BIN
public/images/items/inverse.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 413 B |
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 888 B |
@ -35,36 +35,36 @@ interface BiomeDepths {
|
|||||||
export const biomeLinks: BiomeLinks = {
|
export const biomeLinks: BiomeLinks = {
|
||||||
[Biome.TOWN]: Biome.PLAINS,
|
[Biome.TOWN]: Biome.PLAINS,
|
||||||
[Biome.PLAINS]: [ Biome.GRASS, Biome.METROPOLIS, Biome.LAKE ],
|
[Biome.PLAINS]: [ Biome.GRASS, Biome.METROPOLIS, Biome.LAKE ],
|
||||||
[Biome.GRASS]: Biome.TALL_GRASS,
|
[Biome.GRASS]: [ Biome.TALL_GRASS, [ Biome.CONSTRUCTION_SITE, 2 ] ],
|
||||||
[Biome.TALL_GRASS]: [ Biome.FOREST, Biome.CAVE ],
|
[Biome.TALL_GRASS]: [ Biome.FOREST, Biome.CAVE ],
|
||||||
[Biome.SLUM]: Biome.CONSTRUCTION_SITE,
|
[Biome.SLUM]: Biome.CONSTRUCTION_SITE,
|
||||||
[Biome.FOREST]: [ Biome.JUNGLE, Biome.MEADOW ],
|
[Biome.FOREST]: [ Biome.JUNGLE, Biome.MEADOW ],
|
||||||
[Biome.SEA]: [ Biome.SEABED, Biome.ICE_CAVE ],
|
[Biome.SEA]: [ Biome.SEABED, Biome.ICE_CAVE ],
|
||||||
[Biome.SWAMP]: [ Biome.GRAVEYARD, Biome.TALL_GRASS ],
|
[Biome.SWAMP]: [ Biome.GRAVEYARD, Biome.TALL_GRASS ],
|
||||||
[Biome.BEACH]: [ Biome.SEA, [ Biome.ISLAND, 4 ] ],
|
[Biome.BEACH]: [ Biome.SEA, [ Biome.ISLAND, 3 ] ],
|
||||||
[Biome.LAKE]: [ Biome.BEACH, Biome.SWAMP, Biome.CONSTRUCTION_SITE ],
|
[Biome.LAKE]: [ Biome.BEACH, Biome.SWAMP, Biome.CONSTRUCTION_SITE ],
|
||||||
[Biome.SEABED]: [ Biome.CAVE, [ Biome.VOLCANO, 4 ] ],
|
[Biome.SEABED]: [ Biome.CAVE, [ Biome.VOLCANO, 3 ] ],
|
||||||
[Biome.MOUNTAIN]: [ Biome.VOLCANO, [ Biome.WASTELAND, 3 ] ],
|
[Biome.MOUNTAIN]: [ Biome.VOLCANO, [ Biome.DOJO, 2] [ Biome.WASTELAND, 2 ] ],
|
||||||
[Biome.BADLANDS]: [ Biome.DESERT, Biome.MOUNTAIN ],
|
[Biome.BADLANDS]: [ Biome.DESERT, Biome.MOUNTAIN ],
|
||||||
[Biome.CAVE]: [ Biome.BADLANDS, Biome.LAKE ],
|
[Biome.CAVE]: [ Biome.BADLANDS, Biome.LAKE ],
|
||||||
[Biome.DESERT]: Biome.RUINS,
|
[Biome.DESERT]: [ Biome.RUINS, [ Biome.CONSTRUCTION_SITE, 2 ] ],
|
||||||
[Biome.ICE_CAVE]: Biome.SNOWY_FOREST,
|
[Biome.ICE_CAVE]: Biome.SNOWY_FOREST,
|
||||||
[Biome.MEADOW]: [ Biome.PLAINS, [ Biome.FAIRY_CAVE, 2 ] ],
|
[Biome.MEADOW]: [ Biome.PLAINS, Biome.FAIRY_CAVE ],
|
||||||
[Biome.POWER_PLANT]: Biome.FACTORY,
|
[Biome.POWER_PLANT]: Biome.FACTORY,
|
||||||
[Biome.VOLCANO]: [ Biome.BEACH, [ Biome.ICE_CAVE, 4 ] ],
|
[Biome.VOLCANO]: [ Biome.BEACH, [ Biome.ICE_CAVE, 3 ] ],
|
||||||
[Biome.GRAVEYARD]: Biome.ABYSS,
|
[Biome.GRAVEYARD]: Biome.ABYSS,
|
||||||
[Biome.DOJO]: [ Biome.PLAINS, [ Biome.TEMPLE, 3 ] ],
|
[Biome.DOJO]: [ Biome.PLAINS, [ Biome.JUNGLE, 2], [ Biome.TEMPLE, 2 ] ],
|
||||||
[Biome.FACTORY]: [ Biome.PLAINS, [ Biome.LABORATORY, 4 ] ],
|
[Biome.FACTORY]: [ Biome.TALL_GRASS, [ Biome.LABORATORY, 3 ] ],
|
||||||
[Biome.RUINS]: [ Biome.FOREST ],
|
[Biome.RUINS]: [ Biome.FOREST ],
|
||||||
[Biome.WASTELAND]: Biome.BADLANDS,
|
[Biome.WASTELAND]: Biome.BADLANDS,
|
||||||
[Biome.ABYSS]: [ Biome.CAVE, [ Biome.SPACE, 3 ], [ Biome.WASTELAND, 3 ] ],
|
[Biome.ABYSS]: [ Biome.CAVE, [ Biome.SPACE, 3 ], [ Biome.WASTELAND, 3 ] ],
|
||||||
[Biome.SPACE]: Biome.RUINS,
|
[Biome.SPACE]: Biome.RUINS,
|
||||||
[Biome.CONSTRUCTION_SITE]: [ Biome.DOJO, Biome.POWER_PLANT ],
|
[Biome.CONSTRUCTION_SITE]: [ Biome.POWER_PLANT, [ Biome.DOJO, 2 ] ],
|
||||||
[Biome.JUNGLE]: [ Biome.TEMPLE ],
|
[Biome.JUNGLE]: [ Biome.TEMPLE ],
|
||||||
[Biome.FAIRY_CAVE]: [ Biome.ICE_CAVE, [ Biome.SPACE, 3 ] ],
|
[Biome.FAIRY_CAVE]: [ Biome.ICE_CAVE, [ Biome.SPACE, 2 ] ],
|
||||||
[Biome.TEMPLE]: [ Biome.SWAMP, [ Biome.RUINS, 3 ] ],
|
[Biome.TEMPLE]: [ Biome.DESERT, [ Biome.SWAMP, 2 ], [ Biome.RUINS, 2 ] ],
|
||||||
[Biome.METROPOLIS]: Biome.SLUM,
|
[Biome.METROPOLIS]: Biome.SLUM,
|
||||||
[Biome.SNOWY_FOREST]: [ Biome.FOREST, Biome.LAKE, Biome.MOUNTAIN ],
|
[Biome.SNOWY_FOREST]: [ Biome.FOREST, Biome.MOUNTAIN, [ Biome.LAKE, 2 ] ],
|
||||||
[Biome.ISLAND]: Biome.SEA,
|
[Biome.ISLAND]: Biome.SEA,
|
||||||
[Biome.LABORATORY]: Biome.CONSTRUCTION_SITE
|
[Biome.LABORATORY]: Biome.CONSTRUCTION_SITE
|
||||||
};
|
};
|
||||||
@ -7663,6 +7663,12 @@ export function initBiomes() {
|
|||||||
biomeDepths[Biome.TOWN] = [ 0, 1 ];
|
biomeDepths[Biome.TOWN] = [ 0, 1 ];
|
||||||
|
|
||||||
const traverseBiome = (biome: Biome, depth: integer) => {
|
const traverseBiome = (biome: Biome, depth: integer) => {
|
||||||
|
if (biome === Biome.END) {
|
||||||
|
const biomeList = Object.keys(Biome).filter(key => !isNaN(Number(key)));
|
||||||
|
biomeList.pop(); // Removes Biome.END from the list
|
||||||
|
const randIndex = Utils.randInt(biomeList.length, 2); // Will never be Biome.TOWN or Biome.PLAINS
|
||||||
|
biome = Biome[biomeList[randIndex]];
|
||||||
|
}
|
||||||
const linkedBiomes: (Biome | [ Biome, integer ])[] = Array.isArray(biomeLinks[biome])
|
const linkedBiomes: (Biome | [ Biome, integer ])[] = Array.isArray(biomeLinks[biome])
|
||||||
? biomeLinks[biome] as (Biome | [ Biome, integer ])[]
|
? biomeLinks[biome] as (Biome | [ Biome, integer ])[]
|
||||||
: [ biomeLinks[biome] as Biome ];
|
: [ biomeLinks[biome] as Biome ];
|
||||||
|
@ -55,6 +55,11 @@ export enum ChallengeType {
|
|||||||
* @see {@link Challenge.applyFixedBattle}
|
* @see {@link Challenge.applyFixedBattle}
|
||||||
*/
|
*/
|
||||||
FIXED_BATTLES,
|
FIXED_BATTLES,
|
||||||
|
/**
|
||||||
|
* Modifies the effectiveness of Type matchups in battle
|
||||||
|
* @see {@linkcode Challenge.applyTypeEffectiveness}
|
||||||
|
*/
|
||||||
|
TYPE_EFFECTIVENESS,
|
||||||
/**
|
/**
|
||||||
* Modifies what level the AI pokemon are. UNIMPLEMENTED.
|
* Modifies what level the AI pokemon are. UNIMPLEMENTED.
|
||||||
*/
|
*/
|
||||||
@ -327,6 +332,15 @@ export abstract class Challenge {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for TYPE_EFFECTIVENESS challenges. Derived classes should alter this.
|
||||||
|
* @param effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move.
|
||||||
|
* @returns Whether this function did anything.
|
||||||
|
*/
|
||||||
|
applyTypeEffectiveness(effectiveness: Utils.NumberHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An apply function for AI_LEVEL challenges. Derived classes should alter this.
|
* An apply function for AI_LEVEL challenges. Derived classes should alter this.
|
||||||
* @param level {@link Utils.IntegerHolder} The generated level.
|
* @param level {@link Utils.IntegerHolder} The generated level.
|
||||||
@ -651,10 +665,7 @@ export class FreshStartChallenge extends Challenge {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override getDifficulty(): number {
|
||||||
* @overrides
|
|
||||||
*/
|
|
||||||
getDifficulty(): number {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,6 +677,38 @@ export class FreshStartChallenge extends Challenge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements an inverse battle challenge.
|
||||||
|
*/
|
||||||
|
export class InverseBattleChallenge extends Challenge {
|
||||||
|
constructor() {
|
||||||
|
super(Challenges.INVERSE_BATTLE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadChallenge(source: InverseBattleChallenge | any): InverseBattleChallenge {
|
||||||
|
const newChallenge = new InverseBattleChallenge();
|
||||||
|
newChallenge.value = source.value;
|
||||||
|
newChallenge.severity = source.severity;
|
||||||
|
return newChallenge;
|
||||||
|
}
|
||||||
|
|
||||||
|
override getDifficulty(): number {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTypeEffectiveness(effectiveness: Utils.NumberHolder): boolean {
|
||||||
|
if (effectiveness.value < 1) {
|
||||||
|
effectiveness.value = 2;
|
||||||
|
return true;
|
||||||
|
} else if (effectiveness.value > 1) {
|
||||||
|
effectiveness.value = 0.5;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lowers the amount of starter points available.
|
* Lowers the amount of starter points available.
|
||||||
*/
|
*/
|
||||||
@ -785,6 +828,14 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
|||||||
* @returns True if any challenge was successfully applied.
|
* @returns True if any challenge was successfully applied.
|
||||||
*/
|
*/
|
||||||
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FIXED_BATTLES, waveIndex: Number, battleConfig: FixedBattleConfig): boolean;
|
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.FIXED_BATTLES, waveIndex: Number, battleConfig: FixedBattleConfig): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify type effectiveness.
|
||||||
|
* @param gameMode {@linkcode GameMode} The current gameMode
|
||||||
|
* @param challengeType {@linkcode ChallengeType} ChallengeType.TYPE_EFFECTIVENESS
|
||||||
|
* @param effectiveness {@linkcode Utils.NumberHolder} The current effectiveness of the move.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType.TYPE_EFFECTIVENESS, effectiveness: Utils.NumberHolder): boolean;
|
||||||
/**
|
/**
|
||||||
* Apply all challenges that modify what level AI are.
|
* Apply all challenges that modify what level AI are.
|
||||||
* @param gameMode {@link GameMode} The current gameMode
|
* @param gameMode {@link GameMode} The current gameMode
|
||||||
@ -866,6 +917,9 @@ export function applyChallenges(gameMode: GameMode, challengeType: ChallengeType
|
|||||||
case ChallengeType.FIXED_BATTLES:
|
case ChallengeType.FIXED_BATTLES:
|
||||||
ret ||= c.applyFixedBattle(args[0], args[1]);
|
ret ||= c.applyFixedBattle(args[0], args[1]);
|
||||||
break;
|
break;
|
||||||
|
case ChallengeType.TYPE_EFFECTIVENESS:
|
||||||
|
ret ||= c.applyTypeEffectiveness(args[0]);
|
||||||
|
break;
|
||||||
case ChallengeType.AI_LEVEL:
|
case ChallengeType.AI_LEVEL:
|
||||||
ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]);
|
ret ||= c.applyLevelChange(args[0], args[1], args[2], args[3]);
|
||||||
break;
|
break;
|
||||||
@ -907,6 +961,8 @@ export function copyChallenge(source: Challenge | any): Challenge {
|
|||||||
return LowerStarterPointsChallenge.loadChallenge(source);
|
return LowerStarterPointsChallenge.loadChallenge(source);
|
||||||
case Challenges.FRESH_START:
|
case Challenges.FRESH_START:
|
||||||
return FreshStartChallenge.loadChallenge(source);
|
return FreshStartChallenge.loadChallenge(source);
|
||||||
|
case Challenges.INVERSE_BATTLE:
|
||||||
|
return InverseBattleChallenge.loadChallenge(source);
|
||||||
}
|
}
|
||||||
throw new Error("Unknown challenge copied");
|
throw new Error("Unknown challenge copied");
|
||||||
}
|
}
|
||||||
@ -918,5 +974,6 @@ export function initChallenges() {
|
|||||||
new SingleGenerationChallenge(),
|
new SingleGenerationChallenge(),
|
||||||
new SingleTypeChallenge(),
|
new SingleTypeChallenge(),
|
||||||
new FreshStartChallenge(),
|
new FreshStartChallenge(),
|
||||||
|
new InverseBattleChallenge(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ export const speciesEggMoves = {
|
|||||||
[Species.SHELLDER]: [ Moves.ROCK_BLAST, Moves.WATER_SHURIKEN, Moves.BANEFUL_BUNKER, Moves.BONE_RUSH ],
|
[Species.SHELLDER]: [ Moves.ROCK_BLAST, Moves.WATER_SHURIKEN, Moves.BANEFUL_BUNKER, Moves.BONE_RUSH ],
|
||||||
[Species.GASTLY]: [ Moves.SLUDGE_BOMB, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE ],
|
[Species.GASTLY]: [ Moves.SLUDGE_BOMB, Moves.AURA_SPHERE, Moves.NASTY_PLOT, Moves.ASTRAL_BARRAGE ],
|
||||||
[Species.ONIX]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.HEAVY_SLAM, Moves.DIAMOND_STORM ],
|
[Species.ONIX]: [ Moves.SHORE_UP, Moves.BODY_PRESS, Moves.HEAVY_SLAM, Moves.DIAMOND_STORM ],
|
||||||
[Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.SPORE ],
|
[Species.DROWZEE]: [ Moves.BADDY_BAD, Moves.STRENGTH_SAP, Moves.LUMINA_CRASH, Moves.DARK_VOID ],
|
||||||
[Species.KRABBY]: [ Moves.FIRE_LASH, Moves.PLAY_ROUGH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ],
|
[Species.KRABBY]: [ Moves.FIRE_LASH, Moves.PLAY_ROUGH, Moves.IVY_CUDGEL, Moves.SHELL_SMASH ],
|
||||||
[Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.OVERHEAT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
|
[Species.VOLTORB]: [ Moves.NASTY_PLOT, Moves.OVERHEAT, Moves.FROST_BREATH, Moves.ELECTRO_DRIFT ],
|
||||||
[Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ],
|
[Species.EXEGGCUTE]: [ Moves.FICKLE_BEAM, Moves.APPLE_ACID, Moves.TRICK_ROOM, Moves.LUMINA_CRASH ],
|
||||||
@ -125,7 +125,7 @@ export const speciesEggMoves = {
|
|||||||
[Species.SUICUNE]: [ Moves.RECOVER, Moves.NASTY_PLOT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
|
[Species.SUICUNE]: [ Moves.RECOVER, Moves.NASTY_PLOT, Moves.FREEZE_DRY, Moves.STEAM_ERUPTION ],
|
||||||
[Species.LARVITAR]: [ Moves.DRAGON_DANCE, Moves.MOUNTAIN_GALE, Moves.SHORE_UP, Moves.DIAMOND_STORM ],
|
[Species.LARVITAR]: [ Moves.DRAGON_DANCE, Moves.MOUNTAIN_GALE, Moves.SHORE_UP, Moves.DIAMOND_STORM ],
|
||||||
[Species.LUGIA]: [ Moves.NASTY_PLOT, Moves.LUMINA_CRASH, Moves.AURA_SPHERE, Moves.OBLIVION_WING ],
|
[Species.LUGIA]: [ Moves.NASTY_PLOT, Moves.LUMINA_CRASH, Moves.AURA_SPHERE, Moves.OBLIVION_WING ],
|
||||||
[Species.HO_OH]: [ Moves.FLOATY_FALL, Moves.SOLAR_BLADE, Moves.REVIVAL_BLESSING, Moves.BOLT_BEAK ],
|
[Species.HO_OH]: [ Moves.FLOATY_FALL, Moves.PRECIPICE_BLADES, Moves.REVIVAL_BLESSING, Moves.BOLT_BEAK ],
|
||||||
[Species.CELEBI]: [ Moves.PHOTON_GEYSER, Moves.MATCHA_GOTCHA, Moves.REVIVAL_BLESSING, Moves.QUIVER_DANCE ],
|
[Species.CELEBI]: [ Moves.PHOTON_GEYSER, Moves.MATCHA_GOTCHA, Moves.REVIVAL_BLESSING, Moves.QUIVER_DANCE ],
|
||||||
[Species.TREECKO]: [ Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.SECRET_SWORD, Moves.DRAGON_ENERGY ],
|
[Species.TREECKO]: [ Moves.NASTY_PLOT, Moves.APPLE_ACID, Moves.SECRET_SWORD, Moves.DRAGON_ENERGY ],
|
||||||
[Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.KNOCK_OFF, Moves.V_CREATE ],
|
[Species.TORCHIC]: [ Moves.HIGH_JUMP_KICK, Moves.SUPERCELL_SLAM, Moves.KNOCK_OFF, Moves.V_CREATE ],
|
||||||
@ -249,7 +249,7 @@ export const speciesEggMoves = {
|
|||||||
[Species.CRESSELIA]: [ Moves.COSMIC_POWER, Moves.SECRET_SWORD, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ],
|
[Species.CRESSELIA]: [ Moves.COSMIC_POWER, Moves.SECRET_SWORD, Moves.SIZZLY_SLIDE, Moves.LUMINA_CRASH ],
|
||||||
[Species.PHIONE]: [ Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.SPLISHY_SPLASH, Moves.QUIVER_DANCE ],
|
[Species.PHIONE]: [ Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.SPLISHY_SPLASH, Moves.QUIVER_DANCE ],
|
||||||
[Species.MANAPHY]: [ Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.SPLISHY_SPLASH, Moves.QUIVER_DANCE ],
|
[Species.MANAPHY]: [ Moves.BOUNCY_BUBBLE, Moves.FREEZE_DRY, Moves.SPLISHY_SPLASH, Moves.QUIVER_DANCE ],
|
||||||
[Species.DARKRAI]: [ Moves.FIERY_WRATH, Moves.MOONBLAST, Moves.SEARING_SHOT, Moves.SPORE ],
|
[Species.DARKRAI]: [ Moves.FIERY_WRATH, Moves.MOONBLAST, Moves.FIERY_DANCE, Moves.MAKE_IT_RAIN ],
|
||||||
[Species.SHAYMIN]: [ Moves.MATCHA_GOTCHA, Moves.FIERY_DANCE, Moves.AEROBLAST, Moves.QUIVER_DANCE ],
|
[Species.SHAYMIN]: [ Moves.MATCHA_GOTCHA, Moves.FIERY_DANCE, Moves.AEROBLAST, Moves.QUIVER_DANCE ],
|
||||||
[Species.ARCEUS]: [ Moves.NO_RETREAT, Moves.COLLISION_COURSE, Moves.ASTRAL_BARRAGE, Moves.MULTI_ATTACK ],
|
[Species.ARCEUS]: [ Moves.NO_RETREAT, Moves.COLLISION_COURSE, Moves.ASTRAL_BARRAGE, Moves.MULTI_ATTACK ],
|
||||||
[Species.VICTINI]: [ Moves.BLUE_FLARE, Moves.BOLT_STRIKE, Moves.LUSTER_PURGE, Moves.VICTORY_DANCE ],
|
[Species.VICTINI]: [ Moves.BLUE_FLARE, Moves.BOLT_STRIKE, Moves.LUSTER_PURGE, Moves.VICTORY_DANCE ],
|
||||||
|
@ -4,7 +4,7 @@ import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTr
|
|||||||
import { getPokemonNameWithAffix } from "../messages";
|
import { getPokemonNameWithAffix } from "../messages";
|
||||||
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon";
|
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon";
|
||||||
import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect";
|
import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect";
|
||||||
import { getTypeResistances, Type } from "./type";
|
import { getTypeDamageMultiplier, Type } from "./type";
|
||||||
import { Constructor } from "#app/utils";
|
import { Constructor } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { WeatherType } from "./weather";
|
import { WeatherType } from "./weather";
|
||||||
@ -35,8 +35,11 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
|||||||
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
import { StatChangePhase } from "#app/phases/stat-change-phase";
|
||||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
|
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||||
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
|
import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms";
|
||||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase.js";
|
import { NumberHolder } from "#app/utils";
|
||||||
|
import { GameMode } from "#app/game-mode";
|
||||||
|
import { applyChallenges, ChallengeType } from "./challenge";
|
||||||
|
|
||||||
export enum MoveCategory {
|
export enum MoveCategory {
|
||||||
PHYSICAL,
|
PHYSICAL,
|
||||||
@ -4194,9 +4197,13 @@ export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultipli
|
|||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const multiplier = args[0] as Utils.NumberHolder;
|
const multiplier = args[0] as Utils.NumberHolder;
|
||||||
if (target.isOfType(Type.WATER)) {
|
if (target.isOfType(Type.WATER)) {
|
||||||
multiplier.value *= 4; // Increased twice because initial reduction against water
|
const effectivenessAgainstWater = new Utils.NumberHolder(getTypeDamageMultiplier(move.type, Type.WATER));
|
||||||
|
applyChallenges(user.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, effectivenessAgainstWater);
|
||||||
|
if (effectivenessAgainstWater.value !== 0) {
|
||||||
|
multiplier.value *= 2 / effectivenessAgainstWater.value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -6217,7 +6224,7 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const userTypes = user.getTypes();
|
const userTypes = user.getTypes();
|
||||||
const validTypes = getTypeResistances(moveData.type).filter(t => !userTypes.includes(t)); // valid types are ones that are not already the user's types
|
const validTypes = this.getTypeResistances(user.scene.gameMode, moveData.type).filter(t => !userTypes.includes(t)); // valid types are ones that are not already the user's types
|
||||||
if (!validTypes.length) {
|
if (!validTypes.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -6229,6 +6236,25 @@ export class ResistLastMoveTypeAttr extends MoveEffectAttr {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the types resisting a given type. Used by Conversion 2
|
||||||
|
* @returns An array populated with Types, or an empty array if no resistances exist (Unknown or Stellar type)
|
||||||
|
*/
|
||||||
|
getTypeResistances(gameMode: GameMode, type: number): Type[] {
|
||||||
|
const typeResistances: Type[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < Object.keys(Type).length; i++) {
|
||||||
|
const multiplier = new NumberHolder(1);
|
||||||
|
multiplier.value = getTypeDamageMultiplier(type, i);
|
||||||
|
applyChallenges(gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||||
|
if (multiplier.value < 1) {
|
||||||
|
typeResistances.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeResistances;
|
||||||
|
}
|
||||||
|
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user, target, move) => {
|
return (user, target, move) => {
|
||||||
const moveHistory = target.getLastXMoves();
|
const moveHistory = target.getLastXMoves();
|
||||||
@ -7954,7 +7980,8 @@ export function initMoves() {
|
|||||||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6)
|
new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6)
|
||||||
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
||||||
.attr(WaterSuperEffectTypeMultiplierAttr),
|
.attr(WaterSuperEffectTypeMultiplierAttr)
|
||||||
|
.partial(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell.
|
||||||
new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6)
|
new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6)
|
||||||
.soundBased()
|
.soundBased()
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
|
@ -1132,7 +1132,7 @@ export function initSpecies() {
|
|||||||
),
|
),
|
||||||
new PokemonSpecies(Species.SNORLAX, 1, false, false, false, "Sleeping Pokémon", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, GrowthRate.SLOW, 87.5, false, true,
|
new PokemonSpecies(Species.SNORLAX, 1, false, false, false, "Sleeping Pokémon", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, GrowthRate.SLOW, 87.5, false, true,
|
||||||
new PokemonForm("Normal", "", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, false, null, true),
|
new PokemonForm("Normal", "", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, false, null, true),
|
||||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, Type.GRASS, 35, 460, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 640, 200, 135, 85, 80, 125, 15, 25, 50, 189),
|
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 35, 460, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 640, 200, 135, 85, 80, 125, 15, 25, 50, 189),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.ARTICUNO, 1, true, false, false, "Freeze Pokémon", Type.ICE, Type.FLYING, 1.7, 55.4, Abilities.PRESSURE, Abilities.NONE, Abilities.SNOW_CLOAK, 580, 90, 85, 100, 95, 125, 85, 3, 35, 290, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(Species.ARTICUNO, 1, true, false, false, "Freeze Pokémon", Type.ICE, Type.FLYING, 1.7, 55.4, Abilities.PRESSURE, Abilities.NONE, Abilities.SNOW_CLOAK, 580, 90, 85, 100, 95, 125, 85, 3, 35, 290, GrowthRate.SLOW, null, false),
|
||||||
new PokemonSpecies(Species.ZAPDOS, 1, true, false, false, "Electric Pokémon", Type.ELECTRIC, Type.FLYING, 1.6, 52.6, Abilities.PRESSURE, Abilities.NONE, Abilities.STATIC, 580, 90, 90, 85, 125, 90, 100, 3, 35, 290, GrowthRate.SLOW, null, false),
|
new PokemonSpecies(Species.ZAPDOS, 1, true, false, false, "Electric Pokémon", Type.ELECTRIC, Type.FLYING, 1.6, 52.6, Abilities.PRESSURE, Abilities.NONE, Abilities.STATIC, 580, 90, 90, 85, 125, 90, 100, 3, 35, 290, GrowthRate.SLOW, null, false),
|
||||||
@ -2252,19 +2252,19 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(Species.THWACKEY, 8, false, false, false, "Beat Pokémon", Type.GRASS, null, 0.7, 14, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 420, 70, 85, 70, 55, 60, 80, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
new PokemonSpecies(Species.THWACKEY, 8, false, false, false, "Beat Pokémon", Type.GRASS, null, 0.7, 14, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 420, 70, 85, 70, 55, 60, 80, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||||
new PokemonSpecies(Species.RILLABOOM, 8, false, false, false, "Drummer Pokémon", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
new PokemonSpecies(Species.RILLABOOM, 8, false, false, false, "Drummer Pokémon", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||||
new PokemonForm("Normal", "", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, false, null, true),
|
new PokemonForm("Normal", "", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, false, null, true),
|
||||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 90, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 115, 65, 95, 80, 45, 50, 265),
|
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 90, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.SCORBUNNY, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.3, 4.5, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 310, 50, 71, 40, 40, 40, 69, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
new PokemonSpecies(Species.SCORBUNNY, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.3, 4.5, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 310, 50, 71, 40, 40, 40, 69, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||||
new PokemonSpecies(Species.RABOOT, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.6, 9, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 420, 65, 86, 60, 55, 60, 94, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
new PokemonSpecies(Species.RABOOT, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.6, 9, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 420, 65, 86, 60, 55, 60, 94, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||||
new PokemonSpecies(Species.CINDERACE, 8, false, false, false, "Striker Pokémon", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
new PokemonSpecies(Species.CINDERACE, 8, false, false, false, "Striker Pokémon", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||||
new PokemonForm("Normal", "", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, false, null, true),
|
new PokemonForm("Normal", "", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, false, null, true),
|
||||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 33, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 90, 151, 85, 85, 85, 134, 45, 50, 265),
|
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 33, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.SOBBLE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.3, 4, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 310, 50, 40, 40, 70, 40, 70, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
new PokemonSpecies(Species.SOBBLE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.3, 4, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 310, 50, 40, 40, 70, 40, 70, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||||
new PokemonSpecies(Species.DRIZZILE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.7, 11.5, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 420, 65, 60, 55, 95, 55, 90, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
new PokemonSpecies(Species.DRIZZILE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.7, 11.5, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 420, 65, 60, 55, 95, 55, 90, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
|
||||||
new PokemonSpecies(Species.INTELEON, 8, false, false, false, "Secret Agent Pokémon", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
new PokemonSpecies(Species.INTELEON, 8, false, false, false, "Secret Agent Pokémon", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
|
||||||
new PokemonForm("Normal", "", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, false, null, true),
|
new PokemonForm("Normal", "", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, false, null, true),
|
||||||
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 45.2, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 90, 90, 85, 150, 85, 130, 45, 50, 265),
|
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 45.2, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.SKWOVET, 8, false, false, false, "Cheeky Pokémon", Type.NORMAL, null, 0.3, 2.5, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 275, 70, 55, 55, 35, 35, 25, 255, 50, 55, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.SKWOVET, 8, false, false, false, "Cheeky Pokémon", Type.NORMAL, null, 0.3, 2.5, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 275, 70, 55, 55, 35, 35, 25, 255, 50, 55, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
new PokemonSpecies(Species.GREEDENT, 8, false, false, false, "Greedy Pokémon", Type.NORMAL, null, 0.6, 6, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 460, 120, 95, 95, 55, 75, 20, 90, 50, 161, GrowthRate.MEDIUM_FAST, 50, false),
|
new PokemonSpecies(Species.GREEDENT, 8, false, false, false, "Greedy Pokémon", Type.NORMAL, null, 0.6, 6, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 460, 120, 95, 95, 55, 75, 20, 90, 50, 161, GrowthRate.MEDIUM_FAST, 50, false),
|
||||||
@ -3470,7 +3470,7 @@ export const starterPassiveAbilities = {
|
|||||||
[Species.SUICUNE]: Abilities.UNAWARE,
|
[Species.SUICUNE]: Abilities.UNAWARE,
|
||||||
[Species.LARVITAR]: Abilities.SAND_RUSH,
|
[Species.LARVITAR]: Abilities.SAND_RUSH,
|
||||||
[Species.LUGIA]: Abilities.DELTA_STREAM,
|
[Species.LUGIA]: Abilities.DELTA_STREAM,
|
||||||
[Species.HO_OH]: Abilities.DROUGHT,
|
[Species.HO_OH]: Abilities.MAGIC_GUARD,
|
||||||
[Species.CELEBI]: Abilities.GRASSY_SURGE,
|
[Species.CELEBI]: Abilities.GRASSY_SURGE,
|
||||||
[Species.TREECKO]: Abilities.TINTED_LENS,
|
[Species.TREECKO]: Abilities.TINTED_LENS,
|
||||||
[Species.TORCHIC]: Abilities.RECKLESS,
|
[Species.TORCHIC]: Abilities.RECKLESS,
|
||||||
@ -3591,7 +3591,7 @@ export const starterPassiveAbilities = {
|
|||||||
[Species.HEATRAN]: Abilities.EARTH_EATER,
|
[Species.HEATRAN]: Abilities.EARTH_EATER,
|
||||||
[Species.REGIGIGAS]: Abilities.MINDS_EYE,
|
[Species.REGIGIGAS]: Abilities.MINDS_EYE,
|
||||||
[Species.GIRATINA]: Abilities.SHADOW_SHIELD,
|
[Species.GIRATINA]: Abilities.SHADOW_SHIELD,
|
||||||
[Species.CRESSELIA]: Abilities.UNAWARE,
|
[Species.CRESSELIA]: Abilities.SHADOW_SHIELD,
|
||||||
[Species.PHIONE]: Abilities.SIMPLE,
|
[Species.PHIONE]: Abilities.SIMPLE,
|
||||||
[Species.MANAPHY]: Abilities.PRIMORDIAL_SEA,
|
[Species.MANAPHY]: Abilities.PRIMORDIAL_SEA,
|
||||||
[Species.DARKRAI]: Abilities.UNNERVE,
|
[Species.DARKRAI]: Abilities.UNNERVE,
|
||||||
|
318
src/data/type.ts
318
src/data/type.ts
@ -23,7 +23,7 @@ export enum Type {
|
|||||||
|
|
||||||
export type TypeDamageMultiplier = 0 | 0.125 | 0.25 | 0.5 | 1 | 2 | 4 | 8;
|
export type TypeDamageMultiplier = 0 | 0.125 | 0.25 | 0.5 | 1 | 2 | 4 | 8;
|
||||||
|
|
||||||
export function getTypeDamageMultiplier(attackType: integer, defType: integer): TypeDamageMultiplier {
|
export function getTypeDamageMultiplier(attackType: Type, defType: Type): TypeDamageMultiplier {
|
||||||
if (attackType === Type.UNKNOWN || defType === Type.UNKNOWN) {
|
if (attackType === Type.UNKNOWN || defType === Type.UNKNOWN) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -33,26 +33,10 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.GHOST:
|
case Type.GHOST:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -60,25 +44,12 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.PSYCHIC:
|
case Type.PSYCHIC:
|
||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
return 1;
|
|
||||||
case Type.ROCK:
|
case Type.ROCK:
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
case Type.DARK:
|
case Type.DARK:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.FLYING:
|
case Type.FLYING:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -86,43 +57,20 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
case Type.ICE:
|
case Type.ICE:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
case Type.PSYCHIC:
|
case Type.PSYCHIC:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
return 1;
|
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
@ -130,7 +78,7 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -138,25 +86,13 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
case Type.ICE:
|
case Type.ICE:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
case Type.ROCK:
|
case Type.ROCK:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.ROCK:
|
case Type.ROCK:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -166,23 +102,13 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.WATER:
|
case Type.WATER:
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.ROCK:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.NORMAL:
|
case Type.NORMAL:
|
||||||
case Type.FLYING:
|
case Type.FLYING:
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -190,51 +116,26 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.ROCK:
|
case Type.ROCK:
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.GHOST:
|
case Type.GHOST:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
case Type.GHOST:
|
case Type.GHOST:
|
||||||
case Type.DARK:
|
case Type.DARK:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.FLYING:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
case Type.NORMAL:
|
case Type.NORMAL:
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.STEEL:
|
case Type.STEEL:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -242,11 +143,6 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.GHOST:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.DARK:
|
|
||||||
return 1;
|
|
||||||
case Type.NORMAL:
|
case Type.NORMAL:
|
||||||
case Type.FLYING:
|
case Type.FLYING:
|
||||||
case Type.ROCK:
|
case Type.ROCK:
|
||||||
@ -259,8 +155,9 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -268,16 +165,6 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.ROCK:
|
case Type.ROCK:
|
||||||
case Type.WATER:
|
case Type.WATER:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
return 1;
|
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
case Type.STEEL:
|
case Type.STEEL:
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
@ -286,33 +173,20 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.WATER:
|
case Type.WATER:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.STEEL:
|
case Type.STEEL:
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
case Type.WATER:
|
case Type.WATER:
|
||||||
case Type.ICE:
|
case Type.ICE:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -322,49 +196,24 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
case Type.ICE:
|
case Type.ICE:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
case Type.WATER:
|
case Type.WATER:
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
case Type.GROUND:
|
case Type.GROUND:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.FLYING:
|
case Type.FLYING:
|
||||||
case Type.STEEL:
|
case Type.STEEL:
|
||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.PSYCHIC:
|
case Type.PSYCHIC:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -372,25 +221,11 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.GHOST:
|
case Type.GHOST:
|
||||||
case Type.DARK:
|
case Type.DARK:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
case Type.PSYCHIC:
|
case Type.PSYCHIC:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.ICE:
|
case Type.ICE:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -399,24 +234,10 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.STEEL:
|
case Type.STEEL:
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DRAGON:
|
|
||||||
case Type.DARK:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.ICE:
|
case Type.ICE:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.DRAGON:
|
case Type.DRAGON:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -424,25 +245,13 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.DRAGON:
|
case Type.DRAGON:
|
||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FIGHTING:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.BUG:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.DARK:
|
|
||||||
return 1;
|
|
||||||
case Type.FIRE:
|
case Type.FIRE:
|
||||||
case Type.WATER:
|
case Type.WATER:
|
||||||
case Type.GRASS:
|
case Type.GRASS:
|
||||||
case Type.ELECTRIC:
|
case Type.ELECTRIC:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.DARK:
|
case Type.DARK:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
@ -450,106 +259,33 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer):
|
|||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.POISON:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.STEEL:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.DRAGON:
|
|
||||||
return 1;
|
|
||||||
case Type.GHOST:
|
case Type.GHOST:
|
||||||
case Type.DARK:
|
case Type.DARK:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
case Type.PSYCHIC:
|
case Type.PSYCHIC:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.FAIRY:
|
case Type.FAIRY:
|
||||||
switch (attackType) {
|
switch (attackType) {
|
||||||
case Type.POISON:
|
case Type.POISON:
|
||||||
case Type.STEEL:
|
case Type.STEEL:
|
||||||
return 2;
|
return 2;
|
||||||
case Type.NORMAL:
|
|
||||||
case Type.FLYING:
|
|
||||||
case Type.GROUND:
|
|
||||||
case Type.ROCK:
|
|
||||||
case Type.GHOST:
|
|
||||||
case Type.FIRE:
|
|
||||||
case Type.WATER:
|
|
||||||
case Type.GRASS:
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
case Type.ICE:
|
|
||||||
case Type.FAIRY:
|
|
||||||
return 1;
|
|
||||||
case Type.FIGHTING:
|
case Type.FIGHTING:
|
||||||
case Type.BUG:
|
case Type.BUG:
|
||||||
case Type.DARK:
|
case Type.DARK:
|
||||||
return 0.5;
|
return 0.5;
|
||||||
case Type.DRAGON:
|
case Type.DRAGON:
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
case Type.STELLAR:
|
case Type.STELLAR:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the types resisting a given type
|
|
||||||
* @returns An array populated with Types, or an empty array if no resistances exist (Unknown or Stellar type)
|
|
||||||
*/
|
|
||||||
export function getTypeResistances(type: number): Type[] {
|
|
||||||
switch (type) {
|
|
||||||
case Type.NORMAL:
|
|
||||||
return [Type.ROCK, Type.STEEL, Type.GHOST];
|
|
||||||
case Type.FIGHTING:
|
|
||||||
return [Type.FLYING, Type.POISON, Type.BUG, Type.PSYCHIC, Type.FAIRY, Type.GHOST];
|
|
||||||
case Type.FLYING:
|
|
||||||
return [Type.ROCK, Type.ELECTRIC, Type.STEEL];
|
|
||||||
case Type.POISON:
|
|
||||||
return [Type.POISON, Type.GROUND, Type.ROCK, Type.GHOST, Type.STEEL];
|
|
||||||
case Type.GROUND:
|
|
||||||
return [Type.BUG, Type.GRASS, Type.FLYING];
|
|
||||||
case Type.ROCK:
|
|
||||||
return [Type.FIGHTING, Type.GROUND, Type.STEEL];
|
|
||||||
case Type.BUG:
|
|
||||||
return [Type.FIGHTING, Type.FLYING, Type.POISON, Type.GHOST, Type.STEEL, Type.FIRE, Type.FAIRY];
|
|
||||||
case Type.GHOST:
|
|
||||||
return [Type.DARK, Type.NORMAL];
|
|
||||||
case Type.STEEL:
|
|
||||||
return [Type.STEEL, Type.FIRE, Type.WATER, Type.ELECTRIC];
|
|
||||||
case Type.FIRE:
|
|
||||||
return [Type.ROCK, Type.FIRE, Type.WATER, Type.DRAGON];
|
|
||||||
case Type.WATER:
|
|
||||||
return [Type.WATER, Type.GRASS, Type.DRAGON];
|
|
||||||
case Type.GRASS:
|
|
||||||
return [Type.FLYING, Type.POISON, Type.BUG, Type.STEEL, Type.FIRE, Type.GRASS, Type.DRAGON];
|
|
||||||
case Type.ELECTRIC:
|
|
||||||
return [Type.GRASS, Type.ELECTRIC, Type.DRAGON, Type.GROUND];
|
|
||||||
case Type.PSYCHIC:
|
|
||||||
return [Type.STEEL, Type.PSYCHIC];
|
|
||||||
case Type.ICE:
|
|
||||||
return [Type.STEEL, Type.FIRE, Type.WATER, Type.ICE];
|
|
||||||
case Type.DRAGON:
|
|
||||||
return [Type.STEEL, Type.FAIRY];
|
|
||||||
case Type.DARK:
|
|
||||||
return [Type.FIGHTING, Type.DARK, Type.FAIRY];
|
|
||||||
case Type.FAIRY:
|
|
||||||
return [Type.POISON, Type.STEEL, Type.FIRE];
|
|
||||||
case Type.UNKNOWN:
|
|
||||||
case Type.STELLAR:
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,5 +3,6 @@ export enum Challenges {
|
|||||||
SINGLE_TYPE,
|
SINGLE_TYPE,
|
||||||
LOWER_MAX_STARTER_COST,
|
LOWER_MAX_STARTER_COST,
|
||||||
LOWER_STARTER_POINTS,
|
LOWER_STARTER_POINTS,
|
||||||
FRESH_START
|
FRESH_START,
|
||||||
|
INVERSE_BATTLE,
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ import { BerryType } from "#enums/berry-type";
|
|||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
import { getPokemonNameWithAffix } from "#app/messages.js";
|
||||||
import { DamagePhase } from "#app/phases/damage-phase.js";
|
import { DamagePhase } from "#app/phases/damage-phase.js";
|
||||||
import { FaintPhase } from "#app/phases/faint-phase.js";
|
import { FaintPhase } from "#app/phases/faint-phase.js";
|
||||||
@ -1315,12 +1316,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const multiplier = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, defType));
|
||||||
return getTypeDamageMultiplier(moveType, defType);
|
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, multiplier);
|
||||||
|
return multiplier.value;
|
||||||
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
|
}).reduce((acc, cur) => acc * cur, 1) as TypeDamageMultiplier;
|
||||||
|
|
||||||
|
const typeMultiplierAgainstFlying = new Utils.NumberHolder(getTypeDamageMultiplier(moveType, Type.FLYING));
|
||||||
|
applyChallenges(this.scene.gameMode, ChallengeType.TYPE_EFFECTIVENESS, typeMultiplierAgainstFlying);
|
||||||
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
// Handle strong winds lowering effectiveness of types super effective against pure flying
|
||||||
if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(moveType, Type.FLYING) === 2) {
|
if (!ignoreStrongWinds && arena.weather?.weatherType === WeatherType.STRONG_WINDS && !arena.weather.isEffectSuppressed(this.scene) && this.isOfType(Type.FLYING) && typeMultiplierAgainstFlying.value === 2) {
|
||||||
multiplier /= 2;
|
multiplier /= 2;
|
||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
this.scene.queueMessage(i18next.t("weather:strongWindsEffectMessage"));
|
||||||
@ -3842,7 +3846,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
this.moveset = (formIndex !== undefined ? formIndex : this.formIndex)
|
this.moveset = (formIndex !== undefined ? formIndex : this.formIndex)
|
||||||
? [
|
? [
|
||||||
new PokemonMove(Moves.DYNAMAX_CANNON),
|
new PokemonMove(Moves.DYNAMAX_CANNON),
|
||||||
new PokemonMove(Moves.SLUDGE_BOMB),
|
new PokemonMove(Moves.CROSS_POISON),
|
||||||
new PokemonMove(Moves.FLAMETHROWER),
|
new PokemonMove(Moves.FLAMETHROWER),
|
||||||
new PokemonMove(Moves.RECOVER, 0, -4)
|
new PokemonMove(Moves.RECOVER, 0, -4)
|
||||||
]
|
]
|
||||||
@ -3852,6 +3856,9 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
new PokemonMove(Moves.FLAMETHROWER),
|
new PokemonMove(Moves.FLAMETHROWER),
|
||||||
new PokemonMove(Moves.COSMIC_POWER)
|
new PokemonMove(Moves.COSMIC_POWER)
|
||||||
];
|
];
|
||||||
|
if (this.scene.gameMode.hasChallenge(Challenges.INVERSE_BATTLE)) {
|
||||||
|
this.moveset[2] = new PokemonMove(Moves.THUNDERBOLT);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super.generateAndPopulateMoveset();
|
super.generateAndPopulateMoveset();
|
||||||
|
@ -269,5 +269,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "Hussa, noch einmal von vorn!",
|
"name": "Hussa, noch einmal von vorn!",
|
||||||
"description": "Schließe die 'Neuanfang' Herausforderung ab"
|
"description": "Schließe die 'Neuanfang' Herausforderung ab"
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "Spieglein, Spieglein an der Wand",
|
||||||
|
"description": "Schließe die 'Umkehrkampf' Herausforderung ab"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,5 +25,12 @@
|
|||||||
"desc": "Du kannst nur die ursprünglichen Starter verwenden, genau so, als hättest du gerade erst mit Pokérogue begonnen.",
|
"desc": "Du kannst nur die ursprünglichen Starter verwenden, genau so, als hättest du gerade erst mit Pokérogue begonnen.",
|
||||||
"value.0": "Aus",
|
"value.0": "Aus",
|
||||||
"value.1": "An"
|
"value.1": "An"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "Umkehrkampf",
|
||||||
|
"shortName": "Umkehrkampf",
|
||||||
|
"desc": "Die Typen-Effektivität wird umgekehrt, und kein Typ ist gegen einen anderen Typ immun.\nDeaktiviert die Erfolge anderer Herausforderungen.",
|
||||||
|
"value.0": "Aus",
|
||||||
|
"value.1": "An"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -260,5 +260,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "First Try!",
|
"name": "First Try!",
|
||||||
"description": "Complete the Fresh Start challenge."
|
"description": "Complete the Fresh Start challenge."
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "Mirror rorriM",
|
||||||
|
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -279,5 +279,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "First Try!",
|
"name": "First Try!",
|
||||||
"description": "Complete the Fresh Start challenge."
|
"description": "Complete the Fresh Start challenge."
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "Mirror rorriM",
|
||||||
|
"description": "Complete the Inverse Battle challenge.\n.egnellahc elttaB esrevnI eht etelpmoC"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,5 +25,12 @@
|
|||||||
"desc": "You can only use the original starters, and only as if you had just started PokéRogue.",
|
"desc": "You can only use the original starters, and only as if you had just started PokéRogue.",
|
||||||
"value.0": "Off",
|
"value.0": "Off",
|
||||||
"value.1": "On"
|
"value.1": "On"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "Inverse Battle",
|
||||||
|
"shortName": "Inverse",
|
||||||
|
"desc": "Type matchups are reversed and no type is immune to any other type.\nDisables other challenges' achievements.",
|
||||||
|
"value.0": "Off",
|
||||||
|
"value.1": "On"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,5 +2,9 @@
|
|||||||
"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": "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": null,
|
"encounter_female": null,
|
||||||
"firstStageWin": "I see. The presence I felt was indeed real.\nIt appears I no longer need to hold back.\n$Do not disappoint me.",
|
"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."
|
"secondStageWin": "…Magnificent.",
|
||||||
|
"key_ordinal_one": "st",
|
||||||
|
"key_ordinal_two": "nd",
|
||||||
|
"key_ordinal_few": "rd",
|
||||||
|
"key_ordinal_other": "th"
|
||||||
}
|
}
|
@ -14,8 +14,8 @@
|
|||||||
"importSlotSelect": "Select a slot to import to.",
|
"importSlotSelect": "Select a slot to import to.",
|
||||||
"exportSession": "Export Session",
|
"exportSession": "Export Session",
|
||||||
"exportSlotSelect": "Select a slot to export from.",
|
"exportSlotSelect": "Select a slot to export from.",
|
||||||
"importRunHistory":"Import Run History",
|
"importRunHistory": "Import Run History",
|
||||||
"exportRunHistory":"Export Run History",
|
"exportRunHistory": "Export Run History",
|
||||||
"importData": "Import Data",
|
"importData": "Import Data",
|
||||||
"exportData": "Export Data",
|
"exportData": "Export Data",
|
||||||
"consentPreferences": "Consent Preferences",
|
"consentPreferences": "Consent Preferences",
|
||||||
|
@ -170,5 +170,9 @@
|
|||||||
"CLASSIC_VICTORY": {
|
"CLASSIC_VICTORY": {
|
||||||
"name": "Imbatible",
|
"name": "Imbatible",
|
||||||
"description": "Completa el juego en modo clásico."
|
"description": "Completa el juego en modo clásico."
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "Espejo ojepsE",
|
||||||
|
"description": "Completa el reto de Combate Inverso.\n.osrevnI etabmoC ed oter le atelpmoC"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,5 +18,12 @@
|
|||||||
"name": "Monotipo",
|
"name": "Monotipo",
|
||||||
"desc": "Solo puedes usar Pokémon with the {{type}} type.",
|
"desc": "Solo puedes usar Pokémon with the {{type}} type.",
|
||||||
"desc_default": "Solo puedes usar Pokémon del tipo elegido."
|
"desc_default": "Solo puedes usar Pokémon del tipo elegido."
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "Combate Inverso",
|
||||||
|
"shortName": "Inverso",
|
||||||
|
"desc": "La efectividad de los tipos es invertida. No hay inmunidades entre tipos.\nEste reto deshabilita logros de otros retos.",
|
||||||
|
"value.0": "Desactivado",
|
||||||
|
"value.1": "Activado"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -274,5 +274,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "Du premier coup !",
|
"name": "Du premier coup !",
|
||||||
"description": "Terminer un challenge « Nouveau départ »."
|
"description": "Terminer un challenge « Nouveau départ »."
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "La teuté à verlan",
|
||||||
|
"description": "Terminer un challenge en Combat Inversé.\nMineter un lenjcha en Ba-con Versin."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,5 +25,12 @@
|
|||||||
"desc": "Vous ne pouvez choisir que les starters de base du jeu, comme si vous le recommenciez.",
|
"desc": "Vous ne pouvez choisir que les starters de base du jeu, comme si vous le recommenciez.",
|
||||||
"value.0": "Non",
|
"value.0": "Non",
|
||||||
"value.1": "Oui"
|
"value.1": "Oui"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "Combat Inversé",
|
||||||
|
"shortName": "Inversé",
|
||||||
|
"desc": "Les affinités de la table des types sont inversées et plus aucun type n’a d’immunité.\nDésactive les succès des autres challenges.",
|
||||||
|
"value.0": "Non",
|
||||||
|
"value.1": "Oui"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,6 +22,7 @@
|
|||||||
},
|
},
|
||||||
"freshStart": {
|
"freshStart": {
|
||||||
"name": "出直し",
|
"name": "出直し",
|
||||||
|
"shortName": "出直し",
|
||||||
"desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の 最初のパートナーしか 使えません",
|
"desc": "ポケローグを 始めた ばかりの ような ままで ゲーム開始の 最初のパートナーしか 使えません",
|
||||||
"value.0": "オフ",
|
"value.0": "オフ",
|
||||||
"value.1": "オン"
|
"value.1": "オン"
|
||||||
|
@ -260,5 +260,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "첫트!",
|
"name": "첫트!",
|
||||||
"description": "새 출발 챌린지 모드 클리어."
|
"description": "새 출발 챌린지 모드 클리어."
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "상성 전문가(였던 것)",
|
||||||
|
"description": "거꾸로 배틀 챌린지 모드 클리어."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,5 +25,12 @@
|
|||||||
"desc": "포켓로그를 처음 시작했던 때처럼 강화가 전혀 되지 않은 오리지널 스타팅 포켓몬만 고를 수 있습니다.",
|
"desc": "포켓로그를 처음 시작했던 때처럼 강화가 전혀 되지 않은 오리지널 스타팅 포켓몬만 고를 수 있습니다.",
|
||||||
"value.0": "해제",
|
"value.0": "해제",
|
||||||
"value.1": "설정"
|
"value.1": "설정"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "거꾸로 배틀",
|
||||||
|
"shortName": "거꾸로",
|
||||||
|
"desc": "타입 상성이 반대로 바뀌고 면역 타입은 약점 타입이 됩니다.\n설정 시 다른 챌린지 업적은 달성할 수 없습니다.",
|
||||||
|
"value.0": "해제",
|
||||||
|
"value.1": "설정"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -264,5 +264,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "De Primeira!",
|
"name": "De Primeira!",
|
||||||
"description": "Complete o desafio de novo começo."
|
"description": "Complete o desafio de novo começo."
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "A torre da derrotA",
|
||||||
|
"description": "Complete o desafio da Batalha Inversa.\n.asrevnI ahlataB ad oifased o etelpmoC"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,5 +25,12 @@
|
|||||||
"desc": "Você só pode usar os iniciais originais, como se tivesse acabado de começar o PokéRogue.",
|
"desc": "Você só pode usar os iniciais originais, como se tivesse acabado de começar o PokéRogue.",
|
||||||
"value.0": "Desligado",
|
"value.0": "Desligado",
|
||||||
"value.1": "Ligado"
|
"value.1": "Ligado"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "Batalha Inversa",
|
||||||
|
"shortName": "Inversa",
|
||||||
|
"desc": "Fraquezas e resistências de tipos são invertidas e nenhum tipo é imune a outro tipo.\nDesativa as conquistas de outros desafios.",
|
||||||
|
"value.0": "Desligado",
|
||||||
|
"value.1": "Ligado"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -268,5 +268,9 @@
|
|||||||
"FRESH_START": {
|
"FRESH_START": {
|
||||||
"name": "初次尝试!",
|
"name": "初次尝试!",
|
||||||
"description": "完成初次尝试挑战"
|
"description": "完成初次尝试挑战"
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "镜子子镜",
|
||||||
|
"description": "完成逆转之战挑战\n战挑战之转逆成完"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,5 +25,12 @@
|
|||||||
"desc": "你只能使用御三家,就像是你第一次玩宝可梦肉鸽一样。",
|
"desc": "你只能使用御三家,就像是你第一次玩宝可梦肉鸽一样。",
|
||||||
"value.0": "关闭",
|
"value.0": "关闭",
|
||||||
"value.1": "开启"
|
"value.1": "开启"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "逆转之战",
|
||||||
|
"shortName": "逆转之战",
|
||||||
|
"desc": "属性相克关系被反转,且没有任何属性对其他属性免疫。\n禁用其他挑战的成就。",
|
||||||
|
"value.0": "关闭",
|
||||||
|
"value.1": "开启"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -252,5 +252,9 @@
|
|||||||
},
|
},
|
||||||
"MONO_FAIRY": {
|
"MONO_FAIRY": {
|
||||||
"name": "林克,醒醒!"
|
"name": "林克,醒醒!"
|
||||||
|
},
|
||||||
|
"INVERSE_BATTLE": {
|
||||||
|
"name": "鏡子子鏡",
|
||||||
|
"description": "完成逆轉之戰挑戰\n戰挑戰之轉逆成完"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,5 +19,12 @@
|
|||||||
"name": "單屬性",
|
"name": "單屬性",
|
||||||
"desc": "你只能使用{{type}}\n屬性的寶可夢",
|
"desc": "你只能使用{{type}}\n屬性的寶可夢",
|
||||||
"desc_default": "你只能使用所選\n屬性的寶可夢"
|
"desc_default": "你只能使用所選\n屬性的寶可夢"
|
||||||
|
},
|
||||||
|
"inverseBattle": {
|
||||||
|
"name": "逆轉之戰",
|
||||||
|
"shortName": "逆轉之戰",
|
||||||
|
"desc": "屬性相克關系被反轉,且沒有任何屬性對其他屬性免疫。\n禁用其他挑戰的成就。",
|
||||||
|
"value.0": "關閉",
|
||||||
|
"value.1": "開啓"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -791,10 +791,10 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge
|
|||||||
super("", EvolutionItem[evolutionItem].toLowerCase(), (_type, args) => new Modifiers.EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
|
super("", EvolutionItem[evolutionItem].toLowerCase(), (_type, args) => new Modifiers.EvolutionItemModifier(this, (args[0] as PlayerPokemon).id),
|
||||||
(pokemon: PlayerPokemon) => {
|
(pokemon: PlayerPokemon) => {
|
||||||
if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem
|
if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem
|
||||||
&& (!e.condition || e.condition.predicate(pokemon)) && (e.preFormKey === pokemon.getFormKey())).length && (pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX)) {
|
&& (!e.condition || e.condition.predicate(pokemon)) && (e.preFormKey === null || e.preFormKey === pokemon.getFormKey())).length && (pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX)) {
|
||||||
return null;
|
return null;
|
||||||
} else if (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem
|
} else if (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem
|
||||||
&& (!e.condition || e.condition.predicate(pokemon)) && (e.preFormKey === pokemon.getFusionFormKey())).length && (pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX)) {
|
&& (!e.condition || e.condition.predicate(pokemon)) && (e.preFormKey === null || e.preFormKey === pokemon.getFusionFormKey())).length && (pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import BattleScene from "#app/battle-scene.js";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { BattleType, BattlerIndex } from "#app/battle.js";
|
import { BattleType, BattlerIndex } from "#app/battle";
|
||||||
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability.js";
|
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
|
||||||
import { getCharVariantFromDialogue } from "#app/data/dialogue.js";
|
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config.js";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { getRandomWeatherType } from "#app/data/weather.js";
|
import { getRandomWeatherType } from "#app/data/weather";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec.js";
|
import { BattleSpec } from "#app/enums/battle-spec";
|
||||||
import { PlayerGender } from "#app/enums/player-gender.js";
|
import { PlayerGender } from "#app/enums/player-gender";
|
||||||
import { Species } from "#app/enums/species.js";
|
import { Species } from "#app/enums/species";
|
||||||
import { EncounterPhaseEvent } from "#app/events/battle-scene.js";
|
import { EncounterPhaseEvent } from "#app/events/battle-scene";
|
||||||
import Pokemon, { FieldPosition } from "#app/field/pokemon.js";
|
import Pokemon, { FieldPosition } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages.js";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { regenerateModifierPoolThresholds, ModifierPoolType } from "#app/modifier/modifier-type.js";
|
import { regenerateModifierPoolThresholds, ModifierPoolType } from "#app/modifier/modifier-type";
|
||||||
import { IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier.js";
|
import { IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
||||||
import { achvs } from "#app/system/achv.js";
|
import { achvs } from "#app/system/achv";
|
||||||
import { handleTutorial, Tutorial } from "#app/tutorial.js";
|
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||||
import { Mode } from "#app/ui/ui.js";
|
import { Mode } from "#app/ui/ui";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
import * as Utils from "#app/utils.js";
|
import * as Utils from "#app/utils";
|
||||||
import { CheckSwitchPhase } from "./check-switch-phase";
|
import { CheckSwitchPhase } from "./check-switch-phase";
|
||||||
import { GameOverPhase } from "./game-over-phase";
|
import { GameOverPhase } from "./game-over-phase";
|
||||||
import { PostSummonPhase } from "./post-summon-phase";
|
import { PostSummonPhase } from "./post-summon-phase";
|
||||||
@ -358,24 +358,29 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
case BattleSpec.FINAL_BOSS:
|
case BattleSpec.FINAL_BOSS:
|
||||||
const enemy = this.scene.getEnemyPokemon();
|
const enemy = this.scene.getEnemyPokemon();
|
||||||
this.scene.ui.showText(this.getEncounterMessage(), null, () => {
|
this.scene.ui.showText(this.getEncounterMessage(), null, () => {
|
||||||
|
const localizationKey = "battleSpecDialogue:encounter";
|
||||||
|
if (this.scene.ui.shouldSkipDialogue(localizationKey)) {
|
||||||
|
// Logging mirrors logging found in dialogue-ui-handler
|
||||||
|
console.log(`Dialogue ${localizationKey} skipped`);
|
||||||
|
this.doEncounterCommon(false);
|
||||||
|
} else {
|
||||||
const count = 5643853 + this.scene.gameData.gameStats.classicSessionsPlayed;
|
const count = 5643853 + this.scene.gameData.gameStats.classicSessionsPlayed;
|
||||||
//The two lines below check if English ordinals (1st, 2nd, 3rd, Xth) are used and determine which one to use.
|
// The line below checks if an English ordinal is necessary or not based on whether an entry for encounterLocalizationKey exists in the language or not.
|
||||||
//Otherwise, it defaults to an empty string.
|
const ordinalUsed = !i18next.exists(localizationKey, {fallbackLng: []}) || i18next.resolvedLanguage === "en" ? i18next.t("battleSpecDialogue:key", { count: count, ordinal: true }) : "";
|
||||||
//As of 08-07-24: Spanish and Italian default to the English translations
|
const cycleCount = count.toLocaleString() + ordinalUsed;
|
||||||
const ordinalUse = ["en", "es", "it"];
|
|
||||||
const currentLanguage = i18next.resolvedLanguage ?? "en";
|
|
||||||
const ordinalIndex = (ordinalUse.includes(currentLanguage)) ? ["st", "nd", "rd"][((count + 90) % 100 - 10) % 10 - 1] ?? "th" : "";
|
|
||||||
const cycleCount = count.toLocaleString() + ordinalIndex;
|
|
||||||
const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET;
|
const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET;
|
||||||
const genderStr = PlayerGender[genderIndex].toLowerCase();
|
const genderStr = PlayerGender[genderIndex].toLowerCase();
|
||||||
const encounterDialogue = i18next.t("battleSpecDialogue:encounter", { context: genderStr, cycleCount: cycleCount });
|
const encounterDialogue = i18next.t(localizationKey, { context: genderStr, cycleCount: cycleCount });
|
||||||
|
if (!this.scene.gameData.getSeenDialogues()[localizationKey]) {
|
||||||
|
this.scene.gameData.saveSeenDialogue(localizationKey);
|
||||||
|
}
|
||||||
this.scene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => {
|
this.scene.ui.showDialogue(encounterDialogue, enemy?.species.name, null, () => {
|
||||||
this.doEncounterCommon(false);
|
this.doEncounterCommon(false);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}, 1500, true);
|
}, 1500, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,9 @@ import { pokemonEvolutions } from "#app/data/pokemon-evolutions";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
import { Challenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge.js";
|
import { Challenge, FreshStartChallenge, InverseBattleChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge";
|
||||||
import { ConditionFn } from "#app/@types/common.js";
|
import { Challenges } from "#app/enums/challenges";
|
||||||
|
import { ConditionFn } from "#app/@types/common";
|
||||||
|
|
||||||
export enum AchvTier {
|
export enum AchvTier {
|
||||||
COMMON,
|
COMMON,
|
||||||
@ -137,8 +138,8 @@ export class ModifierAchv extends Achv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ChallengeAchv extends Achv {
|
export class ChallengeAchv extends Achv {
|
||||||
constructor(localizationKey: string, name: string, description: string, iconImage: string, score: integer, challengeFunc: (challenge: Challenge) => boolean) {
|
constructor(localizationKey: string, name: string, description: string, iconImage: string, score: integer, challengeFunc: (challenge: Challenge, scene: BattleScene) => boolean) {
|
||||||
super(localizationKey, name, description, iconImage, score, (_scene: BattleScene, args: any[]) => challengeFunc((args[0] as Challenge)));
|
super(localizationKey, name, description, iconImage, score, (_scene: BattleScene, args: any[]) => challengeFunc(args[0] as Challenge, _scene));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,6 +276,8 @@ export function getAchievementDescription(localizationKey: string): string {
|
|||||||
return i18next.t("achv:MonoType.description", { context: genderStr, "type": i18next.t(`pokemonInfo:Type.${localizationKey.slice(5)}`) });
|
return i18next.t("achv:MonoType.description", { context: genderStr, "type": i18next.t(`pokemonInfo:Type.${localizationKey.slice(5)}`) });
|
||||||
case "FRESH_START":
|
case "FRESH_START":
|
||||||
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
return i18next.t("achv:FRESH_START.description", { context: genderStr });
|
||||||
|
case "INVERSE_BATTLE":
|
||||||
|
return i18next.t("achv:INVERSE_BATTLE.description", { context: genderStr });
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -323,34 +326,35 @@ export const achvs = {
|
|||||||
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
||||||
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150),
|
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150),
|
||||||
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, c => c.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, c => c.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
||||||
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, c => c instanceof SingleGenerationChallenge && c.value === 1),
|
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 1 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, c => c instanceof SingleGenerationChallenge && c.value === 2),
|
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 2 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, c => c instanceof SingleGenerationChallenge && c.value === 3),
|
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 3 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_FOUR_VICTORY: new ChallengeAchv("MONO_GEN_FOUR", "", "MONO_GEN_FOUR.description", "ribbon_gen4", 100, c => c instanceof SingleGenerationChallenge && c.value === 4),
|
MONO_GEN_FOUR_VICTORY: new ChallengeAchv("MONO_GEN_FOUR", "", "MONO_GEN_FOUR.description", "ribbon_gen4", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 4 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_FIVE_VICTORY: new ChallengeAchv("MONO_GEN_FIVE", "", "MONO_GEN_FIVE.description", "ribbon_gen5", 100, c => c instanceof SingleGenerationChallenge && c.value === 5),
|
MONO_GEN_FIVE_VICTORY: new ChallengeAchv("MONO_GEN_FIVE", "", "MONO_GEN_FIVE.description", "ribbon_gen5", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 5 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_SIX_VICTORY: new ChallengeAchv("MONO_GEN_SIX", "", "MONO_GEN_SIX.description", "ribbon_gen6", 100, c => c instanceof SingleGenerationChallenge && c.value === 6),
|
MONO_GEN_SIX_VICTORY: new ChallengeAchv("MONO_GEN_SIX", "", "MONO_GEN_SIX.description", "ribbon_gen6", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 6 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv("MONO_GEN_SEVEN", "", "MONO_GEN_SEVEN.description", "ribbon_gen7", 100, c => c instanceof SingleGenerationChallenge && c.value === 7),
|
MONO_GEN_SEVEN_VICTORY: new ChallengeAchv("MONO_GEN_SEVEN", "", "MONO_GEN_SEVEN.description", "ribbon_gen7", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 7 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv("MONO_GEN_EIGHT", "", "MONO_GEN_EIGHT.description", "ribbon_gen8", 100, c => c instanceof SingleGenerationChallenge && c.value === 8),
|
MONO_GEN_EIGHT_VICTORY: new ChallengeAchv("MONO_GEN_EIGHT", "", "MONO_GEN_EIGHT.description", "ribbon_gen8", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 8 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_NINE_VICTORY: new ChallengeAchv("MONO_GEN_NINE", "", "MONO_GEN_NINE.description", "ribbon_gen9", 100, c => c instanceof SingleGenerationChallenge && c.value === 9),
|
MONO_GEN_NINE_VICTORY: new ChallengeAchv("MONO_GEN_NINE", "", "MONO_GEN_NINE.description", "ribbon_gen9", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 9 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_NORMAL: new ChallengeAchv("MONO_NORMAL", "", "MONO_NORMAL.description", "silk_scarf", 100, c => c instanceof SingleTypeChallenge && c.value === 1),
|
MONO_NORMAL: new ChallengeAchv("MONO_NORMAL", "", "MONO_NORMAL.description", "silk_scarf", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 1 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_FIGHTING: new ChallengeAchv("MONO_FIGHTING", "", "MONO_FIGHTING.description", "black_belt", 100, c => c instanceof SingleTypeChallenge && c.value === 2),
|
MONO_FIGHTING: new ChallengeAchv("MONO_FIGHTING", "", "MONO_FIGHTING.description", "black_belt", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 2 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_FLYING: new ChallengeAchv("MONO_FLYING", "", "MONO_FLYING.description", "sharp_beak", 100, c => c instanceof SingleTypeChallenge && c.value === 3),
|
MONO_FLYING: new ChallengeAchv("MONO_FLYING", "", "MONO_FLYING.description", "sharp_beak", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 3 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_POISON: new ChallengeAchv("MONO_POISON", "", "MONO_POISON.description", "poison_barb", 100, c => c instanceof SingleTypeChallenge && c.value === 4),
|
MONO_POISON: new ChallengeAchv("MONO_POISON", "", "MONO_POISON.description", "poison_barb", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 4 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GROUND: new ChallengeAchv("MONO_GROUND", "", "MONO_GROUND.description", "soft_sand", 100, c => c instanceof SingleTypeChallenge && c.value === 5),
|
MONO_GROUND: new ChallengeAchv("MONO_GROUND", "", "MONO_GROUND.description", "soft_sand", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 5 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_ROCK: new ChallengeAchv("MONO_ROCK", "", "MONO_ROCK.description", "hard_stone", 100, c => c instanceof SingleTypeChallenge && c.value === 6),
|
MONO_ROCK: new ChallengeAchv("MONO_ROCK", "", "MONO_ROCK.description", "hard_stone", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 6 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_BUG: new ChallengeAchv("MONO_BUG", "", "MONO_BUG.description", "silver_powder", 100, c => c instanceof SingleTypeChallenge && c.value === 7),
|
MONO_BUG: new ChallengeAchv("MONO_BUG", "", "MONO_BUG.description", "silver_powder", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 7 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GHOST: new ChallengeAchv("MONO_GHOST", "", "MONO_GHOST.description", "spell_tag", 100, c => c instanceof SingleTypeChallenge && c.value === 8),
|
MONO_GHOST: new ChallengeAchv("MONO_GHOST", "", "MONO_GHOST.description", "spell_tag", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 8 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_STEEL: new ChallengeAchv("MONO_STEEL", "", "MONO_STEEL.description", "metal_coat", 100, c => c instanceof SingleTypeChallenge && c.value === 9),
|
MONO_STEEL: new ChallengeAchv("MONO_STEEL", "", "MONO_STEEL.description", "metal_coat", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 9 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_FIRE: new ChallengeAchv("MONO_FIRE", "", "MONO_FIRE.description", "charcoal", 100, c => c instanceof SingleTypeChallenge && c.value === 10),
|
MONO_FIRE: new ChallengeAchv("MONO_FIRE", "", "MONO_FIRE.description", "charcoal", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 10 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_WATER: new ChallengeAchv("MONO_WATER", "", "MONO_WATER.description", "mystic_water", 100, c => c instanceof SingleTypeChallenge && c.value === 11),
|
MONO_WATER: new ChallengeAchv("MONO_WATER", "", "MONO_WATER.description", "mystic_water", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 11 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GRASS: new ChallengeAchv("MONO_GRASS", "", "MONO_GRASS.description", "miracle_seed", 100, c => c instanceof SingleTypeChallenge && c.value === 12),
|
MONO_GRASS: new ChallengeAchv("MONO_GRASS", "", "MONO_GRASS.description", "miracle_seed", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 12 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_ELECTRIC: new ChallengeAchv("MONO_ELECTRIC", "", "MONO_ELECTRIC.description", "magnet", 100, c => c instanceof SingleTypeChallenge && c.value === 13),
|
MONO_ELECTRIC: new ChallengeAchv("MONO_ELECTRIC", "", "MONO_ELECTRIC.description", "magnet", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 13 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_PSYCHIC: new ChallengeAchv("MONO_PSYCHIC", "", "MONO_PSYCHIC.description", "twisted_spoon", 100, c => c instanceof SingleTypeChallenge && c.value === 14),
|
MONO_PSYCHIC: new ChallengeAchv("MONO_PSYCHIC", "", "MONO_PSYCHIC.description", "twisted_spoon", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 14 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_ICE: new ChallengeAchv("MONO_ICE", "", "MONO_ICE.description", "never_melt_ice", 100, c => c instanceof SingleTypeChallenge && c.value === 15),
|
MONO_ICE: new ChallengeAchv("MONO_ICE", "", "MONO_ICE.description", "never_melt_ice", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 15 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_DRAGON: new ChallengeAchv("MONO_DRAGON", "", "MONO_DRAGON.description", "dragon_fang", 100, c => c instanceof SingleTypeChallenge && c.value === 16),
|
MONO_DRAGON: new ChallengeAchv("MONO_DRAGON", "", "MONO_DRAGON.description", "dragon_fang", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 16 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_DARK: new ChallengeAchv("MONO_DARK", "", "MONO_DARK.description", "black_glasses", 100, c => c instanceof SingleTypeChallenge && c.value === 17),
|
MONO_DARK: new ChallengeAchv("MONO_DARK", "", "MONO_DARK.description", "black_glasses", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 17 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, c => c instanceof SingleTypeChallenge && c.value === 18),
|
MONO_FAIRY: new ChallengeAchv("MONO_FAIRY", "", "MONO_FAIRY.description", "fairy_feather", 100, (c, scene) => c instanceof SingleTypeChallenge && c.value === 18 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, c => c instanceof FreshStartChallenge && c.value === 1),
|
FRESH_START: new ChallengeAchv("FRESH_START", "", "FRESH_START.description", "reviver_seed", 100, (c, scene) => c instanceof FreshStartChallenge && c.value > 0 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
|
INVERSE_BATTLE: new ChallengeAchv("INVERSE_BATTLE", "", "INVERSE_BATTLE.description", "inverse", 100, c => c instanceof InverseBattleChallenge && c.value > 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
export function initAchievements() {
|
export function initAchievements() {
|
||||||
|
@ -243,6 +243,8 @@ export class StarterPrefs {
|
|||||||
if (pStr !== StarterPrefers_private_latest) {
|
if (pStr !== StarterPrefers_private_latest) {
|
||||||
// something changed, store the update
|
// something changed, store the update
|
||||||
localStorage.setItem(`starterPrefs_${loggedInUser?.username}`, pStr);
|
localStorage.setItem(`starterPrefs_${loggedInUser?.username}`, pStr);
|
||||||
|
// update the latest prefs
|
||||||
|
StarterPrefers_private_latest = pStr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
203
src/test/battle/inverse_battle.test.ts
Normal file
203
src/test/battle/inverse_battle.test.ts
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { Type } from "#app/data/type";
|
||||||
|
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||||
|
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const TIMEOUT = 20 * 1000;
|
||||||
|
|
||||||
|
describe("Inverse Battle", () => {
|
||||||
|
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.challengeMode.addChallenge(Challenges.INVERSE_BATTLE, 1, 1);
|
||||||
|
|
||||||
|
game.override
|
||||||
|
.battleType("single")
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("1. immune types are 2x effective - Thunderbolt against Ground Type", async () => {
|
||||||
|
game.override.enemySpecies(Species.SANDSHREW);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("2. 2x effective types are 0.5x effective - Thunderbolt against Flying Type", async () => {
|
||||||
|
game.override.enemySpecies(Species.PIDGEY);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(0.5);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("3. 0.5x effective types are 2x effective - Thunderbolt against Electric Type", async () => {
|
||||||
|
game.override.enemySpecies(Species.CHIKORITA);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.THUNDERBOLT])).toBe(2);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("4. Stealth Rock follows the inverse matchups - Stealth Rock against Charizard deals 1/32 of max HP", async () => {
|
||||||
|
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0);
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.CHARIZARD)
|
||||||
|
.enemyLevel(100);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const charizard = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
const maxHp = charizard.getMaxHp();
|
||||||
|
const damage_prediction = Math.max(Math.round(charizard.getMaxHp() / 32), 1);
|
||||||
|
console.log("Damage calcuation before round: " + charizard.getMaxHp() / 32);
|
||||||
|
const currentHp = charizard.hp;
|
||||||
|
const expectedHP = maxHp - damage_prediction;
|
||||||
|
|
||||||
|
console.log("Charizard's max HP: " + maxHp, "Damage: " + damage_prediction, "Current HP: " + currentHp, "Expected HP: " + expectedHP);
|
||||||
|
expect(currentHp).toBeGreaterThan(maxHp * 31 / 32 - 1);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("5. Freeze Dry is 2x effective against Water Type like other Ice type Move - Freeze Dry against Squirtle", async () => {
|
||||||
|
game.override.enemySpecies(Species.SQUIRTLE);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FREEZE_DRY])).toBe(2);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("6. Water Absorb should heal against water moves - Water Absorb against Water gun", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.WATER_GUN])
|
||||||
|
.enemyAbility(Abilities.WATER_ABSORB);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
enemy.hp = enemy.getMaxHp() - 1;
|
||||||
|
game.move.select(Moves.WATER_GUN);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(MoveEndPhase);
|
||||||
|
|
||||||
|
expect(enemy.hp).toBe(enemy.getMaxHp());
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("7. Fire type does not get burned - Will-O-Wisp against Charmander", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.WILL_O_WISP])
|
||||||
|
.enemySpecies(Species.CHARMANDER);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.WILL_O_WISP);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
await game.move.forceHit();
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(MoveEndPhase);
|
||||||
|
|
||||||
|
expect(enemy.status?.effect).not.toBe(StatusEffect.BURN);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("8. Electric type does not get paralyzed - Nuzzle against Pikachu", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.NUZZLE])
|
||||||
|
.enemySpecies(Species.PIKACHU)
|
||||||
|
.enemyLevel(50);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.NUZZLE);
|
||||||
|
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(MoveEndPhase);
|
||||||
|
|
||||||
|
expect(enemy.status?.effect).not.toBe(StatusEffect.PARALYSIS);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
|
||||||
|
it("10. Anticipation should trigger on 2x effective moves - Anticipation against Thunderbolt", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.THUNDERBOLT])
|
||||||
|
.enemySpecies(Species.SANDSHREW)
|
||||||
|
.enemyAbility(Abilities.ANTICIPATION);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()?.summonData.abilitiesApplied[0]).toBe(Abilities.ANTICIPATION);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("11. Conversion 2 should change the type to the resistive type - Conversion 2 against Dragonite", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.CONVERSION_2])
|
||||||
|
.enemyMoveset([Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW, Moves.DRAGON_CLAW]);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.CONVERSION_2);
|
||||||
|
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
|
|
||||||
|
expect(player.getTypes()[0]).toBe(Type.DRAGON);
|
||||||
|
}, TIMEOUT);
|
||||||
|
|
||||||
|
it("12. Flying Press should be 0.25x effective against Grass + Dark Type - Flying Press against Meowscarada", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([Moves.FLYING_PRESS])
|
||||||
|
.enemySpecies(Species.MEOWSCARADA);
|
||||||
|
|
||||||
|
await game.challengeMode.startBattle();
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
expect(enemy.getMoveEffectiveness(player, allMoves[Moves.FLYING_PRESS])).toBe(0.25);
|
||||||
|
}, TIMEOUT);
|
||||||
|
});
|
87
src/test/moves/sparkly_swirl.test.ts
Normal file
87
src/test/moves/sparkly_swirl.test.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { allMoves } from "#app/data/move.js";
|
||||||
|
import { StatusEffect } from "#app/enums/status-effect.js";
|
||||||
|
import { CommandPhase } from "#app/phases/command-phase.js";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
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 - Sparkly Swirl", () => {
|
||||||
|
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
|
||||||
|
.enemySpecies(Species.SHUCKLE)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemyMoveset(SPLASH_ONLY)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.moveset([Moves.SPARKLY_SWIRL, Moves.SPLASH])
|
||||||
|
.ability(Abilities.BALL_FETCH);
|
||||||
|
|
||||||
|
vi.spyOn(allMoves[Moves.SPARKLY_SWIRL], "accuracy", "get").mockReturnValue(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cure status effect of the user, its ally, and all party pokemon", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.statusEffect(StatusEffect.BURN);
|
||||||
|
await game.classicMode.startBattle([Species.RATTATA, Species.RATTATA, Species.RATTATA]);
|
||||||
|
const [leftPlayer, rightPlayer, partyPokemon] = game.scene.getParty();
|
||||||
|
const leftOpp = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
vi.spyOn(leftPlayer, "resetStatus");
|
||||||
|
vi.spyOn(rightPlayer, "resetStatus");
|
||||||
|
vi.spyOn(partyPokemon, "resetStatus");
|
||||||
|
|
||||||
|
game.move.select(Moves.SPARKLY_SWIRL, 0, leftOpp.getBattlerIndex());
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(leftPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||||
|
expect(rightPlayer.resetStatus).toHaveBeenCalledOnce();
|
||||||
|
expect(partyPokemon.resetStatus).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
|
expect(leftPlayer.status?.effect).toBeUndefined();
|
||||||
|
expect(rightPlayer.status?.effect).toBeUndefined();
|
||||||
|
expect(partyPokemon.status?.effect).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not cure status effect of the target/target's allies", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyStatusEffect(StatusEffect.BURN);
|
||||||
|
await game.classicMode.startBattle([Species.RATTATA, Species.RATTATA]);
|
||||||
|
const [leftOpp, rightOpp] = game.scene.getEnemyField();
|
||||||
|
|
||||||
|
vi.spyOn(leftOpp, "resetStatus");
|
||||||
|
vi.spyOn(rightOpp, "resetStatus");
|
||||||
|
|
||||||
|
game.move.select(Moves.SPARKLY_SWIRL, 0, leftOpp.getBattlerIndex());
|
||||||
|
await game.phaseInterceptor.to(CommandPhase);
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(leftOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||||
|
expect(rightOpp.resetStatus).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
expect(leftOpp.status?.effect).toBeTruthy();
|
||||||
|
expect(rightOpp.status?.effect).toBeTruthy();
|
||||||
|
|
||||||
|
expect(leftOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||||
|
expect(rightOpp.status?.effect).toBe(StatusEffect.BURN);
|
||||||
|
});
|
||||||
|
});
|
@ -40,6 +40,7 @@ import fs from "fs";
|
|||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
import { ClassicModeHelper } from "./helpers/classicModeHelper";
|
import { ClassicModeHelper } from "./helpers/classicModeHelper";
|
||||||
import { DailyModeHelper } from "./helpers/dailyModeHelper";
|
import { DailyModeHelper } from "./helpers/dailyModeHelper";
|
||||||
|
import { ChallengeModeHelper } from "./helpers/challengeModeHelper";
|
||||||
import { MoveHelper } from "./helpers/moveHelper";
|
import { MoveHelper } from "./helpers/moveHelper";
|
||||||
import { OverridesHelper } from "./helpers/overridesHelper";
|
import { OverridesHelper } from "./helpers/overridesHelper";
|
||||||
import { SettingsHelper } from "./helpers/settingsHelper";
|
import { SettingsHelper } from "./helpers/settingsHelper";
|
||||||
@ -57,6 +58,7 @@ export default class GameManager {
|
|||||||
public readonly move: MoveHelper;
|
public readonly move: MoveHelper;
|
||||||
public readonly classicMode: ClassicModeHelper;
|
public readonly classicMode: ClassicModeHelper;
|
||||||
public readonly dailyMode: DailyModeHelper;
|
public readonly dailyMode: DailyModeHelper;
|
||||||
|
public readonly challengeMode: ChallengeModeHelper;
|
||||||
public readonly settings: SettingsHelper;
|
public readonly settings: SettingsHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,6 +79,7 @@ export default class GameManager {
|
|||||||
this.move = new MoveHelper(this);
|
this.move = new MoveHelper(this);
|
||||||
this.classicMode = new ClassicModeHelper(this);
|
this.classicMode = new ClassicModeHelper(this);
|
||||||
this.dailyMode = new DailyModeHelper(this);
|
this.dailyMode = new DailyModeHelper(this);
|
||||||
|
this.challengeMode = new ChallengeModeHelper(this);
|
||||||
this.settings = new SettingsHelper(this);
|
this.settings = new SettingsHelper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
78
src/test/utils/helpers/challengeModeHelper.ts
Normal file
78
src/test/utils/helpers/challengeModeHelper.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { BattleStyle } from "#app/enums/battle-style";
|
||||||
|
import { Species } from "#app/enums/species";
|
||||||
|
import overrides from "#app/overrides";
|
||||||
|
import { EncounterPhase } from "#app/phases/encounter-phase";
|
||||||
|
import { SelectStarterPhase } from "#app/phases/select-starter-phase";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import { generateStarter } from "../gameManagerUtils";
|
||||||
|
import { GameManagerHelper } from "./gameManagerHelper";
|
||||||
|
import { Challenge } from "#app/data/challenge";
|
||||||
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
import { copyChallenge } from "data/challenge";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to handle Challenge mode specifics
|
||||||
|
*/
|
||||||
|
export class ChallengeModeHelper extends GameManagerHelper {
|
||||||
|
|
||||||
|
challenges: Challenge[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a challenge to the challenge mode helper.
|
||||||
|
* @param id - The challenge id.
|
||||||
|
* @param value - The challenge value.
|
||||||
|
* @param severity - The challenge severity.
|
||||||
|
*/
|
||||||
|
addChallenge(id: Challenges, value: number, severity: number) {
|
||||||
|
const challenge = copyChallenge({ id, value, severity });
|
||||||
|
this.challenges.push(challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the Challenge game to the summon phase.
|
||||||
|
* @param gameMode - Optional game mode to set.
|
||||||
|
* @returns A promise that resolves when the summon phase is reached.
|
||||||
|
*/
|
||||||
|
async runToSummon(species?: Species[]) {
|
||||||
|
await this.game.runToTitle();
|
||||||
|
|
||||||
|
this.game.onNextPrompt("TitlePhase", Mode.TITLE, () => {
|
||||||
|
this.game.scene.gameMode.challenges = this.challenges;
|
||||||
|
const starters = generateStarter(this.game.scene, species);
|
||||||
|
const selectStarterPhase = new SelectStarterPhase(this.game.scene);
|
||||||
|
this.game.scene.pushPhase(new EncounterPhase(this.game.scene, false));
|
||||||
|
selectStarterPhase.initBattle(starters);
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.game.phaseInterceptor.run(EncounterPhase);
|
||||||
|
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0) {
|
||||||
|
this.game.removeEnemyHeldItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transitions to the start of a battle.
|
||||||
|
* @param species - Optional array of species to start the battle with.
|
||||||
|
* @returns A promise that resolves when the battle is started.
|
||||||
|
*/
|
||||||
|
async startBattle(species?: Species[]) {
|
||||||
|
await this.runToSummon(species);
|
||||||
|
|
||||||
|
if (this.game.scene.battleStyle === BattleStyle.SWITCH) {
|
||||||
|
this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||||
|
this.game.setMode(Mode.MESSAGE);
|
||||||
|
this.game.endPhase();
|
||||||
|
}, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase));
|
||||||
|
|
||||||
|
this.game.onNextPrompt("CheckSwitchPhase", Mode.CONFIRM, () => {
|
||||||
|
this.game.setMode(Mode.MESSAGE);
|
||||||
|
this.game.endPhase();
|
||||||
|
}, () => this.game.isCurrentPhase(CommandPhase) || this.game.isCurrentPhase(TurnInitPhase));
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.game.phaseInterceptor.to(CommandPhase);
|
||||||
|
console.log("==================[New Turn]==================");
|
||||||
|
}
|
||||||
|
}
|
@ -77,7 +77,21 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected setupOptions() {
|
protected setupOptions() {
|
||||||
const options = this.config?.options || [];
|
const configOptions = this.config?.options ?? [];
|
||||||
|
|
||||||
|
let options: OptionSelectItem[];
|
||||||
|
|
||||||
|
// for performance reasons, this limits how many options we can see at once. Without this, it would try to make text options for every single options
|
||||||
|
// which makes the performance take a hit. If there's not enough options to do this (set to 10 at the moment) and the ui mode !== Mode.AUTO_COMPLETE,
|
||||||
|
// this is ignored and the original code is untouched, with the options array being all the options from the config
|
||||||
|
if (configOptions.length >= 10 && this.scene.ui.getMode() === Mode.AUTO_COMPLETE) {
|
||||||
|
const optionsScrollTotal = configOptions.length;
|
||||||
|
const optionStartIndex = this.scrollCursor;
|
||||||
|
const optionEndIndex = Math.min(optionsScrollTotal, optionStartIndex + (!optionStartIndex || this.scrollCursor + (this.config?.maxOptions! - 1) >= optionsScrollTotal ? this.config?.maxOptions! - 1 : this.config?.maxOptions! - 2));
|
||||||
|
options = configOptions.slice(optionStartIndex, optionEndIndex + 2);
|
||||||
|
} else {
|
||||||
|
options = configOptions;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.optionSelectText) {
|
if (this.optionSelectText) {
|
||||||
this.optionSelectText.destroy();
|
this.optionSelectText.destroy();
|
||||||
@ -192,6 +206,19 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler {
|
|||||||
} else {
|
} else {
|
||||||
ui.playError();
|
ui.playError();
|
||||||
}
|
}
|
||||||
|
} else if (button === Button.SUBMIT && ui.getMode() === Mode.AUTO_COMPLETE) {
|
||||||
|
// this is here to differentiate between a Button.SUBMIT vs Button.ACTION within the autocomplete handler
|
||||||
|
// this is here because Button.ACTION is picked up as z on the keyboard, meaning if you're typing and hit z, it'll select the option you've chosen
|
||||||
|
success = true;
|
||||||
|
const option = this.config?.options[this.cursor + (this.scrollCursor - (this.scrollCursor ? 1 : 0))];
|
||||||
|
if (option?.handler()) {
|
||||||
|
if (!option.keepOpen) {
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
|
playSound = !option.overrideSound;
|
||||||
|
} else {
|
||||||
|
ui.playError();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.UP:
|
case Button.UP:
|
||||||
|
@ -64,12 +64,15 @@ export default class AdminUiHandler extends FormModalUiHandler {
|
|||||||
Utils.apiPost("admin/account/discord-link", `username=${encodeURIComponent(this.inputs[0].text)}&discordId=${encodeURIComponent(this.inputs[1].text)}`, "application/x-www-form-urlencoded", true)
|
Utils.apiPost("admin/account/discord-link", `username=${encodeURIComponent(this.inputs[0].text)}&discordId=${encodeURIComponent(this.inputs[1].text)}`, "application/x-www-form-urlencoded", true)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return response.text();
|
console.error(response);
|
||||||
}
|
}
|
||||||
return response.json();
|
this.inputs[0].setText("");
|
||||||
|
this.inputs[1].setText("");
|
||||||
|
this.scene.ui.revertMode();
|
||||||
})
|
})
|
||||||
.then(response => {
|
.catch((err) => {
|
||||||
this.scene.ui.setMode(Mode.ADMIN, config);
|
console.error(err);
|
||||||
|
this.scene.ui.revertMode();
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
45
src/ui/autocomplete-ui-handler.ts
Normal file
45
src/ui/autocomplete-ui-handler.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { Button } from "#enums/buttons";
|
||||||
|
import BattleScene from "../battle-scene";
|
||||||
|
import AbstractOptionSelectUiHandler from "./abstact-option-select-ui-handler";
|
||||||
|
import { Mode } from "./ui";
|
||||||
|
|
||||||
|
export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler {
|
||||||
|
modalContainer: Phaser.GameObjects.Container;
|
||||||
|
constructor(scene: BattleScene, mode: Mode = Mode.OPTION_SELECT) {
|
||||||
|
super(scene, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWindowWidth(): integer {
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
show(args: any[]): boolean {
|
||||||
|
if (args[0].modalContainer) {
|
||||||
|
const { modalContainer } = args[0];
|
||||||
|
const show = super.show(args);
|
||||||
|
this.modalContainer = modalContainer;
|
||||||
|
this.setupOptions();
|
||||||
|
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected setupOptions() {
|
||||||
|
super.setupOptions();
|
||||||
|
if (this.modalContainer) {
|
||||||
|
this.optionSelectContainer.setSize(this.optionSelectContainer.height, Math.max(this.optionSelectText.displayWidth + 24, this.getWindowWidth()));
|
||||||
|
this.optionSelectContainer.setPositionRelative(this.modalContainer, this.optionSelectBg.width, this.optionSelectBg.height + 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processInput(button: Button): boolean {
|
||||||
|
// the cancel and action button are here because if you're typing, x and z are used for cancel/action. This means you could be typing something and accidentally cancel/select when you don't mean to
|
||||||
|
// the submit button is therefore used to select a choice (the enter button), though this does not work on my local dev testing for phones, as for my phone/keyboard combo, the enter and z key are both
|
||||||
|
// bound to Button.ACTION, which makes this not work on mobile
|
||||||
|
if (button !== Button.CANCEL && button !== Button.ACTION) {
|
||||||
|
return super.processInput(button);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,8 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||||||
public movesWindowContainer: Phaser.GameObjects.Container;
|
public movesWindowContainer: Phaser.GameObjects.Container;
|
||||||
public nameBoxContainer: Phaser.GameObjects.Container;
|
public nameBoxContainer: Phaser.GameObjects.Container;
|
||||||
|
|
||||||
|
public readonly wordWrapWidth: number = 1780;
|
||||||
|
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
super(scene, Mode.MESSAGE);
|
super(scene, Mode.MESSAGE);
|
||||||
}
|
}
|
||||||
@ -63,7 +65,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||||||
const message = addTextObject(this.scene, 0, 0, "", TextStyle.MESSAGE, {
|
const message = addTextObject(this.scene, 0, 0, "", TextStyle.MESSAGE, {
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
wordWrap: {
|
wordWrap: {
|
||||||
width: 1780
|
width: this.wordWrapWidth
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
messageContainer.add(message);
|
messageContainer.add(message);
|
||||||
@ -129,7 +131,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
this.commandWindow.setVisible(false);
|
this.commandWindow.setVisible(false);
|
||||||
this.movesWindowContainer.setVisible(false);
|
this.movesWindowContainer.setVisible(false);
|
||||||
this.message.setWordWrapWidth(1780);
|
this.message.setWordWrapWidth(this.wordWrapWidth);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -161,7 +163,9 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showDialogue(text: string, name?: string, delay?: integer | null, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
|
showDialogue(text: string, name?: string, delay?: integer | null, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) {
|
||||||
name && this.showNameText(name);
|
if (name) {
|
||||||
|
this.showNameText(name);
|
||||||
|
}
|
||||||
super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay);
|
super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import BattleScene, { bypassLogin } from "../battle-scene";
|
|||||||
import { TextStyle, addTextObject, getTextStyleOptions } from "./text";
|
import { TextStyle, addTextObject, getTextStyleOptions } from "./text";
|
||||||
import { Mode } from "./ui";
|
import { Mode } from "./ui";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { addWindow } from "./ui-theme";
|
import { addWindow, WindowVariant } from "./ui-theme";
|
||||||
import MessageUiHandler from "./message-ui-handler";
|
import MessageUiHandler from "./message-ui-handler";
|
||||||
import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler";
|
import { OptionSelectConfig, OptionSelectItem } from "./abstact-option-select-ui-handler";
|
||||||
import { Tutorial, handleTutorial } from "../tutorial";
|
import { Tutorial, handleTutorial } from "../tutorial";
|
||||||
@ -11,6 +11,7 @@ import i18next from "i18next";
|
|||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
import { GameDataType } from "#enums/game-data-type";
|
import { GameDataType } from "#enums/game-data-type";
|
||||||
import BgmBar from "#app/ui/bgm-bar";
|
import BgmBar from "#app/ui/bgm-bar";
|
||||||
|
import AwaitableUiHandler from "./awaitable-ui-handler";
|
||||||
|
|
||||||
enum MenuOptions {
|
enum MenuOptions {
|
||||||
GAME_SETTINGS,
|
GAME_SETTINGS,
|
||||||
@ -31,6 +32,10 @@ const githubUrl = "https://github.com/pagefaultgames/pokerogue";
|
|||||||
const redditUrl = "https://www.reddit.com/r/pokerogue";
|
const redditUrl = "https://www.reddit.com/r/pokerogue";
|
||||||
|
|
||||||
export default class MenuUiHandler extends MessageUiHandler {
|
export default class MenuUiHandler extends MessageUiHandler {
|
||||||
|
private readonly textPadding = 8;
|
||||||
|
private readonly defaultMessageBoxWidth = 220;
|
||||||
|
private readonly defaultWordWrapWidth = 1224;
|
||||||
|
|
||||||
private menuContainer: Phaser.GameObjects.Container;
|
private menuContainer: Phaser.GameObjects.Container;
|
||||||
private menuMessageBoxContainer: Phaser.GameObjects.Container;
|
private menuMessageBoxContainer: Phaser.GameObjects.Container;
|
||||||
private menuOverlay: Phaser.GameObjects.Rectangle;
|
private menuOverlay: Phaser.GameObjects.Rectangle;
|
||||||
@ -46,17 +51,20 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
protected manageDataConfig: OptionSelectConfig;
|
protected manageDataConfig: OptionSelectConfig;
|
||||||
protected communityConfig: OptionSelectConfig;
|
protected communityConfig: OptionSelectConfig;
|
||||||
|
|
||||||
|
// Windows for the default message box and the message box for testing dialogue
|
||||||
|
private menuMessageBox: Phaser.GameObjects.NineSlice;
|
||||||
|
private dialogueMessageBox: Phaser.GameObjects.NineSlice;
|
||||||
|
|
||||||
protected scale: number = 0.1666666667;
|
protected scale: number = 0.1666666667;
|
||||||
|
|
||||||
public bgmBar: BgmBar;
|
public bgmBar: BgmBar;
|
||||||
|
|
||||||
|
|
||||||
constructor(scene: BattleScene, mode: Mode | null = null) {
|
constructor(scene: BattleScene, mode: Mode | null = null) {
|
||||||
super(scene, mode);
|
super(scene, mode);
|
||||||
|
|
||||||
this.excludedMenus = () => [
|
this.excludedMenus = () => [
|
||||||
{ condition: [Mode.COMMAND, Mode.TITLE].includes(mode ?? Mode.TITLE), options: [ MenuOptions.EGG_GACHA, MenuOptions.EGG_LIST] },
|
{ condition: [Mode.COMMAND, Mode.TITLE].includes(mode ?? Mode.TITLE), options: [MenuOptions.EGG_GACHA, MenuOptions.EGG_LIST] },
|
||||||
{ condition: bypassLogin, options: [ MenuOptions.LOG_OUT ] }
|
{ condition: bypassLogin, options: [MenuOptions.LOG_OUT] }
|
||||||
];
|
];
|
||||||
|
|
||||||
this.menuOptions = Utils.getEnumKeys(MenuOptions)
|
this.menuOptions = Utils.getEnumKeys(MenuOptions)
|
||||||
@ -98,8 +106,8 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
render() {
|
render() {
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
this.excludedMenus = () => [
|
this.excludedMenus = () => [
|
||||||
{ condition: ![Mode.COMMAND, Mode.TITLE].includes(ui.getModeChain()[0]), options: [ MenuOptions.EGG_GACHA, MenuOptions.EGG_LIST] },
|
{ condition: ![Mode.COMMAND, Mode.TITLE].includes(ui.getModeChain()[0]), options: [MenuOptions.EGG_GACHA, MenuOptions.EGG_LIST] },
|
||||||
{ condition: bypassLogin, options: [ MenuOptions.LOG_OUT ] }
|
{ condition: bypassLogin, options: [MenuOptions.LOG_OUT] }
|
||||||
];
|
];
|
||||||
|
|
||||||
this.menuOptions = Utils.getEnumKeys(MenuOptions)
|
this.menuOptions = Utils.getEnumKeys(MenuOptions)
|
||||||
@ -115,12 +123,12 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
this.menuBg = addWindow(this.scene,
|
this.menuBg = addWindow(this.scene,
|
||||||
(this.scene.game.canvas.width / 6) - (this.optionSelectText.displayWidth + 25),
|
(this.scene.game.canvas.width / 6) - (this.optionSelectText.displayWidth + 25),
|
||||||
0,
|
0,
|
||||||
this.optionSelectText.displayWidth + 19+24*this.scale,
|
this.optionSelectText.displayWidth + 19 + 24 * this.scale,
|
||||||
(this.scene.game.canvas.height / 6) - 2
|
(this.scene.game.canvas.height / 6) - 2
|
||||||
);
|
);
|
||||||
this.menuBg.setOrigin(0, 0);
|
this.menuBg.setOrigin(0, 0);
|
||||||
|
|
||||||
this.optionSelectText.setPositionRelative(this.menuBg, 10+24*this.scale, 6);
|
this.optionSelectText.setPositionRelative(this.menuBg, 10 + 24 * this.scale, 6);
|
||||||
|
|
||||||
this.menuContainer.add(this.menuBg);
|
this.menuContainer.add(this.menuBg);
|
||||||
|
|
||||||
@ -131,20 +139,27 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
this.menuMessageBoxContainer = this.scene.add.container(0, 130);
|
this.menuMessageBoxContainer = this.scene.add.container(0, 130);
|
||||||
this.menuMessageBoxContainer.setName("menu-message-box");
|
this.menuMessageBoxContainer.setName("menu-message-box");
|
||||||
this.menuMessageBoxContainer.setVisible(false);
|
this.menuMessageBoxContainer.setVisible(false);
|
||||||
this.menuContainer.add(this.menuMessageBoxContainer);
|
|
||||||
|
|
||||||
const menuMessageBox = addWindow(this.scene, 0, -0, 220, 48);
|
// Window for general messages
|
||||||
menuMessageBox.setOrigin(0, 0);
|
this.menuMessageBox = addWindow(this.scene, 0, 0, this.defaultMessageBoxWidth, 48);
|
||||||
this.menuMessageBoxContainer.add(menuMessageBox);
|
this.menuMessageBox.setOrigin(0, 0);
|
||||||
|
this.menuMessageBoxContainer.add(this.menuMessageBox);
|
||||||
|
|
||||||
const menuMessageText = addTextObject(this.scene, 8, 8, "", TextStyle.WINDOW, { maxLines: 2 });
|
// Full-width window used for testing dialog messages in debug mode
|
||||||
|
this.dialogueMessageBox = addWindow(this.scene, -this.textPadding, 0, this.scene.game.canvas.width / 6 + this.textPadding * 2, 49, false, false, 0, 0, WindowVariant.THIN);
|
||||||
|
this.dialogueMessageBox.setOrigin(0, 0);
|
||||||
|
this.menuMessageBoxContainer.add(this.dialogueMessageBox);
|
||||||
|
|
||||||
|
const menuMessageText = addTextObject(this.scene, this.textPadding, this.textPadding, "", TextStyle.WINDOW, { maxLines: 2 });
|
||||||
menuMessageText.setName("menu-message");
|
menuMessageText.setName("menu-message");
|
||||||
menuMessageText.setWordWrapWidth(1224);
|
|
||||||
menuMessageText.setOrigin(0, 0);
|
menuMessageText.setOrigin(0, 0);
|
||||||
this.menuMessageBoxContainer.add(menuMessageText);
|
this.menuMessageBoxContainer.add(menuMessageText);
|
||||||
|
|
||||||
this.message = menuMessageText;
|
this.message = menuMessageText;
|
||||||
|
|
||||||
|
// By default we use the general purpose message window
|
||||||
|
this.setDialogTestMode(false);
|
||||||
|
|
||||||
this.menuContainer.add(this.menuMessageBoxContainer);
|
this.menuContainer.add(this.menuMessageBoxContainer);
|
||||||
|
|
||||||
const manageDataOptions: any[] = []; // TODO: proper type
|
const manageDataOptions: any[] = []; // TODO: proper type
|
||||||
@ -155,7 +170,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
const config: OptionSelectConfig = {
|
const config: OptionSelectConfig = {
|
||||||
options: new Array(5).fill(null).map((_, i) => i).filter(slotFilter).map(i => {
|
options: new Array(5).fill(null).map((_, i) => i).filter(slotFilter).map(i => {
|
||||||
return {
|
return {
|
||||||
label: i18next.t("menuUiHandler:slot", {slotNumber: i+1}),
|
label: i18next.t("menuUiHandler:slot", { slotNumber: i + 1 }),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
callback(i);
|
callback(i);
|
||||||
ui.revertMode();
|
ui.revertMode();
|
||||||
@ -257,8 +272,55 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
keepOpen: true
|
keepOpen: true
|
||||||
|
});
|
||||||
|
if (Utils.isLocal || Utils.isBeta) { // this should make sure we don't have this option in live
|
||||||
|
manageDataOptions.push({
|
||||||
|
label: "Test Dialogue",
|
||||||
|
handler: () => {
|
||||||
|
ui.playSelect();
|
||||||
|
const prefilledText = "";
|
||||||
|
const buttonAction: any = {};
|
||||||
|
buttonAction["buttonActions"] = [
|
||||||
|
(sanitizedName: string) => {
|
||||||
|
ui.revertMode();
|
||||||
|
ui.playSelect();
|
||||||
|
const dialogueTestName = sanitizedName;
|
||||||
|
const dialogueName = decodeURIComponent(escape(atob(dialogueTestName)));
|
||||||
|
const handler = ui.getHandler() as AwaitableUiHandler;
|
||||||
|
handler.tutorialActive = true;
|
||||||
|
const interpolatorOptions: any = {};
|
||||||
|
const splitArr = dialogueName.split(" "); // this splits our inputted text into words to cycle through later
|
||||||
|
const translatedString = splitArr[0]; // this is our outputted i18 string
|
||||||
|
const regex = RegExp("\\{\\{(\\w*)\\}\\}", "g"); // this is a regex expression to find all the text between {{ }} in the i18 output
|
||||||
|
const matches = i18next.t(translatedString).match(regex) ?? [];
|
||||||
|
if (matches.length > 0) {
|
||||||
|
for (let match = 0; match < matches.length; match++) {
|
||||||
|
// we add 1 here because splitArr[0] is our first value for the translatedString, and after that is where the variables are
|
||||||
|
// the regex here in the replace (/\W/g) is to remove the {{ and }} and just give us all alphanumeric characters
|
||||||
|
if (typeof splitArr[match + 1] !== "undefined") {
|
||||||
|
interpolatorOptions[matches[match].replace(/\W/g, "")] = i18next.t(splitArr[match + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Switch to the dialog test window
|
||||||
|
this.setDialogTestMode(true);
|
||||||
|
ui.showText(String(i18next.t(translatedString, interpolatorOptions)), null, () => this.scene.ui.showText("", 0, () => {
|
||||||
|
handler.tutorialActive = false;
|
||||||
|
// Go back to the default message window
|
||||||
|
this.setDialogTestMode(false);
|
||||||
|
}), null, true);
|
||||||
},
|
},
|
||||||
{
|
() => {
|
||||||
|
ui.revertMode();
|
||||||
|
}
|
||||||
|
];
|
||||||
|
ui.setMode(Mode.TEST_DIALOGUE, buttonAction, prefilledText);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
keepOpen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
manageDataOptions.push({
|
||||||
label: i18next.t("menuUiHandler:cancel"),
|
label: i18next.t("menuUiHandler:cancel"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.revertMode();
|
||||||
@ -421,7 +483,7 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
break;
|
break;
|
||||||
case MenuOptions.MANAGE_DATA:
|
case MenuOptions.MANAGE_DATA:
|
||||||
if (!bypassLogin && !this.manageDataConfig.options.some(o => o.label === i18next.t("menuUiHandler:linkDiscord") || o.label === i18next.t("menuUiHandler:unlinkDiscord"))) {
|
if (!bypassLogin && !this.manageDataConfig.options.some(o => o.label === i18next.t("menuUiHandler:linkDiscord") || o.label === i18next.t("menuUiHandler:unlinkDiscord"))) {
|
||||||
this.manageDataConfig.options.splice(this.manageDataConfig.options.length-1, 0,
|
this.manageDataConfig.options.splice(this.manageDataConfig.options.length - 1, 0,
|
||||||
{
|
{
|
||||||
label: loggedInUser?.discordId === "" ? i18next.t("menuUiHandler:linkDiscord") : i18next.t("menuUiHandler:unlinkDiscord"),
|
label: loggedInUser?.discordId === "" ? i18next.t("menuUiHandler:linkDiscord") : i18next.t("menuUiHandler:unlinkDiscord"),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
@ -547,6 +609,21 @@ export default class MenuUiHandler extends MessageUiHandler {
|
|||||||
return success || error;
|
return success || error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch the message window style and size when we are replaying dialog for debug purposes
|
||||||
|
* In "dialog test mode", the window takes the whole width of the screen and the text
|
||||||
|
* is set up to wrap around the same way as the dialogue during the game
|
||||||
|
* @param isDialogMode whether to use the dialog test
|
||||||
|
*/
|
||||||
|
setDialogTestMode(isDialogMode: boolean) {
|
||||||
|
this.menuMessageBox.setVisible(!isDialogMode);
|
||||||
|
this.dialogueMessageBox.setVisible(isDialogMode);
|
||||||
|
// If we're testing dialog, we use the same word wrapping as the battle message handler
|
||||||
|
this.message.setWordWrapWidth(isDialogMode ? this.scene.ui.getMessageHandler().wordWrapWidth : this.defaultWordWrapWidth);
|
||||||
|
this.message.setX(isDialogMode ? this.textPadding + 1 : this.textPadding);
|
||||||
|
this.message.setY(isDialogMode ? this.textPadding + 0.4 : this.textPadding);
|
||||||
|
}
|
||||||
|
|
||||||
showText(text: string, delay?: number, callback?: Function, callbackDelay?: number, prompt?: boolean, promptDelay?: number): void {
|
showText(text: string, delay?: number, callback?: Function, callbackDelay?: number, prompt?: boolean, promptDelay?: number): void {
|
||||||
this.menuMessageBoxContainer.setVisible(!!text);
|
this.menuMessageBoxContainer.setVisible(!!text);
|
||||||
|
|
||||||
|
@ -374,23 +374,14 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||||||
case GameModes.CHALLENGE:
|
case GameModes.CHALLENGE:
|
||||||
modeText.appendText(`${i18next.t("gameMode:challenge")}`, false);
|
modeText.appendText(`${i18next.t("gameMode:challenge")}`, false);
|
||||||
modeText.appendText(`\t\t${i18next.t("runHistory:challengeRules")}: `);
|
modeText.appendText(`\t\t${i18next.t("runHistory:challengeRules")}: `);
|
||||||
const runChallenges = this.runInfo.challenges;
|
const rules: string[] = this.challengeParser();
|
||||||
const rules: string[] = [];
|
|
||||||
for (let i = 0; i < runChallenges.length; i++) {
|
|
||||||
if (runChallenges[i].id === Challenges.SINGLE_GENERATION && runChallenges[i].value !== 0) {
|
|
||||||
rules.push(i18next.t(`runHistory:challengeMonoGen${runChallenges[i].value}`));
|
|
||||||
} else if (runChallenges[i].id === Challenges.SINGLE_TYPE && runChallenges[i].value !== 0) {
|
|
||||||
rules.push(i18next.t(`pokemonInfo:Type.${Type[runChallenges[i].value-1]}` as const));
|
|
||||||
} else if (runChallenges[i].id === Challenges.FRESH_START && runChallenges[i].value !== 0) {
|
|
||||||
rules.push(i18next.t("challenges:freshStart.name"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rules) {
|
if (rules) {
|
||||||
for (let i = 0; i < rules.length; i++) {
|
for (let i = 0; i < rules.length; i++) {
|
||||||
|
const newline = i > 0 && i%2 === 0;
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
modeText.appendText(" + ", false);
|
modeText.appendText(" + ", newline);
|
||||||
}
|
}
|
||||||
modeText.appendText(rules[i], false);
|
modeText.appendText(rules[i], newline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -466,6 +457,34 @@ export default class RunInfoUiHandler extends UiHandler {
|
|||||||
this.runContainer.add(this.runInfoContainer);
|
this.runContainer.add(this.runInfoContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function parses the Challenges section of the Run Entry and returns a list of active challenge.
|
||||||
|
* @return string[] of active challenge names
|
||||||
|
*/
|
||||||
|
private challengeParser(): string[] {
|
||||||
|
const rules: string[] = [];
|
||||||
|
for (let i = 0; i < this.runInfo.challenges.length; i++) {
|
||||||
|
if (this.runInfo.challenges[i].value !== 0) {
|
||||||
|
switch (this.runInfo.challenges[i].id) {
|
||||||
|
case Challenges.SINGLE_GENERATION:
|
||||||
|
rules.push(i18next.t(`runHistory:challengeMonoGen${this.runInfo.challenges[i].value}`));
|
||||||
|
break;
|
||||||
|
case Challenges.SINGLE_TYPE:
|
||||||
|
rules.push(i18next.t(`pokemonInfo:Type.${Type[this.runInfo.challenges[i].value-1]}` as const));
|
||||||
|
break;
|
||||||
|
case Challenges.FRESH_START:
|
||||||
|
rules.push(i18next.t("challenges:freshStart.name"));
|
||||||
|
break;
|
||||||
|
case Challenges.INVERSE_BATTLE:
|
||||||
|
//
|
||||||
|
rules.push(i18next.t("challenges:inverseBattle.shortName").split("").reverse().join(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses and displays the run's player party.
|
* Parses and displays the run's player party.
|
||||||
* Default Information: Icon, Level, Nature, Ability, Passive, Shiny Status, Fusion Status, Stats, and Moves.
|
* Default Information: Icon, Level, Nature, Ability, Passive, Shiny Status, Fusion Status, Stats, and Moves.
|
||||||
|
@ -915,7 +915,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.allSpecies.forEach((species, s) => {
|
this.allSpecies.forEach((species, s) => {
|
||||||
const icon = this.starterContainers[s].icon;
|
const icon = this.starterContainers[s].icon;
|
||||||
const dexEntry = this.scene.gameData.dexData[species.speciesId];
|
const dexEntry = this.scene.gameData.dexData[species.speciesId];
|
||||||
this.starterPreferences[species.speciesId] = this.starterPreferences[species.speciesId] ?? {};
|
|
||||||
|
// Initialize the StarterAttributes for this species
|
||||||
|
this.starterPreferences[species.speciesId] = this.initStarterPrefs(species);
|
||||||
|
|
||||||
if (dexEntry.caughtAttr) {
|
if (dexEntry.caughtAttr) {
|
||||||
icon.clearTint();
|
icon.clearTint();
|
||||||
@ -942,6 +944,93 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the starter attributes for the given PokemonSpecies, after sanitizing them.
|
||||||
|
* If somehow a preference is set for a form, variant, gender, ability or nature
|
||||||
|
* that wasn't actually unlocked or is invalid it will be cleared here
|
||||||
|
*
|
||||||
|
* @param species The species to get Starter Preferences for
|
||||||
|
* @returns StarterAttributes for the species
|
||||||
|
*/
|
||||||
|
initStarterPrefs(species: PokemonSpecies): StarterAttributes {
|
||||||
|
const starterAttributes = this.starterPreferences[species.speciesId];
|
||||||
|
const dexEntry = this.scene.gameData.dexData[species.speciesId];
|
||||||
|
const starterData = this.scene.gameData.starterData[species.speciesId];
|
||||||
|
|
||||||
|
// no preferences or Pokemon wasn't caught, return empty attribute
|
||||||
|
if (!starterAttributes || !dexEntry.caughtAttr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const caughtAttr = dexEntry.caughtAttr;
|
||||||
|
|
||||||
|
const hasShiny = caughtAttr & DexAttr.SHINY;
|
||||||
|
const hasNonShiny = caughtAttr & DexAttr.NON_SHINY;
|
||||||
|
if (starterAttributes.shiny && !hasShiny) {
|
||||||
|
// shiny form wasn't unlocked, purging shiny and variant setting
|
||||||
|
delete starterAttributes.shiny;
|
||||||
|
delete starterAttributes.variant;
|
||||||
|
} else if (starterAttributes.shiny === false && !hasNonShiny) {
|
||||||
|
// non shiny form wasn't unlocked, purging shiny setting
|
||||||
|
delete starterAttributes.shiny;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starterAttributes.variant !== undefined && !isNaN(starterAttributes.variant)) {
|
||||||
|
const unlockedVariants = [
|
||||||
|
hasNonShiny,
|
||||||
|
hasShiny && caughtAttr & DexAttr.DEFAULT_VARIANT,
|
||||||
|
hasShiny && caughtAttr & DexAttr.VARIANT_2,
|
||||||
|
hasShiny && caughtAttr & DexAttr.VARIANT_3
|
||||||
|
];
|
||||||
|
if (!unlockedVariants[starterAttributes.variant + 1]) { // add 1 as -1 = non-shiny
|
||||||
|
// requested variant wasn't unlocked, purging setting
|
||||||
|
delete starterAttributes.variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starterAttributes.female !== undefined) {
|
||||||
|
if (!(starterAttributes.female ? caughtAttr & DexAttr.FEMALE : caughtAttr & DexAttr.MALE)) {
|
||||||
|
// requested gender wasn't unlocked, purging setting
|
||||||
|
delete starterAttributes.female;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starterAttributes.ability !== undefined) {
|
||||||
|
const speciesHasSingleAbility = species.ability2 === species.ability1;
|
||||||
|
const abilityAttr = starterData.abilityAttr;
|
||||||
|
const hasAbility1 = abilityAttr & AbilityAttr.ABILITY_1;
|
||||||
|
const hasAbility2 = abilityAttr & AbilityAttr.ABILITY_2;
|
||||||
|
const hasHiddenAbility = abilityAttr & AbilityAttr.ABILITY_HIDDEN;
|
||||||
|
// Due to a past bug it is possible that some Pokemon with a single ability have the ability2 flag
|
||||||
|
// In this case, we only count ability2 as valid if ability1 was not unlocked, otherwise we ignore it
|
||||||
|
const unlockedAbilities = [
|
||||||
|
hasAbility1,
|
||||||
|
speciesHasSingleAbility ? hasAbility2 && !hasAbility1 : hasAbility2,
|
||||||
|
hasHiddenAbility
|
||||||
|
];
|
||||||
|
if (!unlockedAbilities[starterAttributes.ability]) {
|
||||||
|
// requested ability wasn't unlocked, purging setting
|
||||||
|
delete starterAttributes.ability;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedForm = starterAttributes.form;
|
||||||
|
if (selectedForm !== undefined && (!species.forms[selectedForm]?.isStarterSelectable || !(caughtAttr & this.scene.gameData.getFormAttr(selectedForm)))) {
|
||||||
|
// requested form wasn't unlocked/isn't a starter form, purging setting
|
||||||
|
delete starterAttributes.form;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starterAttributes.nature !== undefined) {
|
||||||
|
const unlockedNatures = this.scene.gameData.getNaturesForAttr(dexEntry.natureAttr);
|
||||||
|
if (unlockedNatures.indexOf(starterAttributes.nature as unknown as Nature) < 0) {
|
||||||
|
// requested nature wasn't unlocked, purging setting
|
||||||
|
delete starterAttributes.nature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return starterAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the selections for all filters to their default starting value
|
* Set the selections for all filters to their default starting value
|
||||||
*/
|
*/
|
||||||
@ -1749,9 +1838,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
switch (button) {
|
switch (button) {
|
||||||
case Button.CYCLE_SHINY:
|
case Button.CYCLE_SHINY:
|
||||||
if (this.canCycleShiny) {
|
if (this.canCycleShiny) {
|
||||||
const newVariant = props.variant;
|
const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : props.variant;
|
||||||
starterAttributes.shiny = starterAttributes.shiny ? !starterAttributes.shiny : true;
|
starterAttributes.shiny = starterAttributes.shiny ? !starterAttributes.shiny : true;
|
||||||
this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, props.shiny ? 0 : undefined, undefined, undefined);
|
this.setSpeciesDetails(this.lastSpecies, !props.shiny, undefined, undefined, props.shiny ? 0 : newVariant, undefined, undefined);
|
||||||
if (starterAttributes.shiny) {
|
if (starterAttributes.shiny) {
|
||||||
this.scene.playSound("se/sparkle");
|
this.scene.playSound("se/sparkle");
|
||||||
// Set the variant label to the shiny tint
|
// Set the variant label to the shiny tint
|
||||||
@ -1760,10 +1849,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonShinyIcon.setTint(tint);
|
this.pokemonShinyIcon.setTint(tint);
|
||||||
this.pokemonShinyIcon.setVisible(true);
|
this.pokemonShinyIcon.setVisible(true);
|
||||||
} else {
|
} else {
|
||||||
// starterAttributes.variant = 0;
|
|
||||||
if (starterAttributes?.variant) {
|
|
||||||
delete starterAttributes.variant;
|
|
||||||
}
|
|
||||||
this.pokemonShinyIcon.setVisible(false);
|
this.pokemonShinyIcon.setVisible(false);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -2276,43 +2361,39 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
container.cost = this.scene.gameData.getSpeciesStarterValue(container.species.speciesId);
|
container.cost = this.scene.gameData.getSpeciesStarterValue(container.species.speciesId);
|
||||||
|
|
||||||
// First, ensure you have the caught attributes for the species else default to bigint 0
|
// First, ensure you have the caught attributes for the species else default to bigint 0
|
||||||
const isCaught = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0);
|
const caughtAttr = this.scene.gameData.dexData[container.species.speciesId]?.caughtAttr || BigInt(0);
|
||||||
|
const starterData = this.scene.gameData.starterData[container.species.speciesId];
|
||||||
// Define the variables based on whether their respective variants have been caught
|
|
||||||
const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3);
|
|
||||||
const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2);
|
|
||||||
const isVariantCaught = !!(isCaught & DexAttr.SHINY);
|
|
||||||
const isUncaught = !isCaught && !isVariantCaught && !isVariant2Caught && !isVariant3Caught;
|
|
||||||
const isPassiveUnlocked = this.scene.gameData.starterData[container.species.speciesId].passiveAttr > 0;
|
|
||||||
const isPassiveUnlockable = this.isPassiveAvailable(container.species.speciesId) && !isPassiveUnlocked;
|
|
||||||
const isCostReduced = this.scene.gameData.starterData[container.species.speciesId].valueReduction > 0;
|
|
||||||
const isCostReductionUnlockable = this.isValueReductionAvailable(container.species.speciesId);
|
|
||||||
const isFavorite = this.starterPreferences[container.species.speciesId]?.favorite ?? false;
|
|
||||||
|
|
||||||
const isWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount > 0;
|
|
||||||
const isNotWin = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === 0;
|
|
||||||
const isUndefined = this.scene.gameData.starterData[container.species.speciesId].classicWinCount === undefined;
|
|
||||||
const isHA = this.scene.gameData.starterData[container.species.speciesId].abilityAttr & AbilityAttr.ABILITY_HIDDEN;
|
|
||||||
const isEggPurchasable = this.isSameSpeciesEggAvailable(container.species.speciesId);
|
|
||||||
|
|
||||||
|
// Gen filter
|
||||||
const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation);
|
const fitsGen = this.filterBar.getVals(DropDownColumn.GEN).includes(container.species.generation);
|
||||||
|
|
||||||
|
// Type filter
|
||||||
const fitsType = this.filterBar.getVals(DropDownColumn.TYPES).some(type => container.species.isOfType((type as number) - 1));
|
const fitsType = this.filterBar.getVals(DropDownColumn.TYPES).some(type => container.species.isOfType((type as number) - 1));
|
||||||
|
|
||||||
|
// Caught / Shiny filter
|
||||||
|
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
|
||||||
|
const isShinyCaught = !!(caughtAttr & DexAttr.SHINY);
|
||||||
|
const isVariant1Caught = isShinyCaught && !!(caughtAttr & DexAttr.DEFAULT_VARIANT);
|
||||||
|
const isVariant2Caught = isShinyCaught && !!(caughtAttr & DexAttr.VARIANT_2);
|
||||||
|
const isVariant3Caught = isShinyCaught && !!(caughtAttr & DexAttr.VARIANT_3);
|
||||||
|
const isUncaught = !isNonShinyCaught && !isVariant1Caught && !isVariant2Caught && !isVariant3Caught;
|
||||||
const fitsCaught = this.filterBar.getVals(DropDownColumn.CAUGHT).some(caught => {
|
const fitsCaught = this.filterBar.getVals(DropDownColumn.CAUGHT).some(caught => {
|
||||||
if (caught === "SHINY3") {
|
if (caught === "SHINY3") {
|
||||||
return isVariant3Caught;
|
return isVariant3Caught;
|
||||||
} else if (caught === "SHINY2") {
|
} else if (caught === "SHINY2") {
|
||||||
return isVariant2Caught && !isVariant3Caught;
|
return isVariant2Caught && !isVariant3Caught;
|
||||||
} else if (caught === "SHINY") {
|
} else if (caught === "SHINY") {
|
||||||
return isVariantCaught && !isVariant2Caught && !isVariant3Caught;
|
return isVariant1Caught && !isVariant2Caught && !isVariant3Caught;
|
||||||
} else if (caught === "NORMAL") {
|
} else if (caught === "NORMAL") {
|
||||||
return isCaught && !isVariantCaught && !isVariant2Caught && !isVariant3Caught;
|
return isNonShinyCaught && !isVariant1Caught && !isVariant2Caught && !isVariant3Caught;
|
||||||
} else if (caught === "UNCAUGHT") {
|
} else if (caught === "UNCAUGHT") {
|
||||||
return isUncaught;
|
return isUncaught;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Passive Filter
|
||||||
|
const isPassiveUnlocked = starterData.passiveAttr > 0;
|
||||||
|
const isPassiveUnlockable = this.isPassiveAvailable(container.species.speciesId) && !isPassiveUnlocked;
|
||||||
const fitsPassive = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(unlocks => {
|
const fitsPassive = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(unlocks => {
|
||||||
if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.ON) {
|
if (unlocks.val === "PASSIVE" && unlocks.state === DropDownState.ON) {
|
||||||
return isPassiveUnlocked;
|
return isPassiveUnlocked;
|
||||||
@ -2325,6 +2406,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Cost Reduction Filter
|
||||||
|
const isCostReduced = starterData.valueReduction > 0;
|
||||||
|
const isCostReductionUnlockable = this.isValueReductionAvailable(container.species.speciesId);
|
||||||
const fitsCostReduction = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(unlocks => {
|
const fitsCostReduction = this.filterBar.getVals(DropDownColumn.UNLOCKS).some(unlocks => {
|
||||||
if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.ON) {
|
if (unlocks.val === "COST_REDUCTION" && unlocks.state === DropDownState.ON) {
|
||||||
return isCostReduced;
|
return isCostReduced;
|
||||||
@ -2337,6 +2421,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Favorite Filter
|
||||||
|
const isFavorite = this.starterPreferences[container.species.speciesId]?.favorite ?? false;
|
||||||
const fitsFavorite = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
const fitsFavorite = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
||||||
if (misc.val === "FAVORITE" && misc.state === DropDownState.ON) {
|
if (misc.val === "FAVORITE" && misc.state === DropDownState.ON) {
|
||||||
return isFavorite;
|
return isFavorite;
|
||||||
@ -2349,28 +2435,34 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ribbon / Classic Win Filter
|
||||||
|
const hasWon = starterData.classicWinCount > 0;
|
||||||
|
const hasNotWon = starterData.classicWinCount === 0;
|
||||||
|
const isUndefined = starterData.classicWinCount === undefined;
|
||||||
const fitsWin = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
const fitsWin = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
||||||
if (container.species.speciesId < 10) {
|
|
||||||
}
|
|
||||||
if (misc.val === "WIN" && misc.state === DropDownState.ON) {
|
if (misc.val === "WIN" && misc.state === DropDownState.ON) {
|
||||||
return isWin;
|
return hasWon;
|
||||||
} else if (misc.val === "WIN" && misc.state === DropDownState.EXCLUDE) {
|
} else if (misc.val === "WIN" && misc.state === DropDownState.EXCLUDE) {
|
||||||
return isNotWin || isUndefined;
|
return hasNotWon || isUndefined;
|
||||||
} else if (misc.val === "WIN" && misc.state === DropDownState.OFF) {
|
} else if (misc.val === "WIN" && misc.state === DropDownState.OFF) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// HA Filter
|
||||||
|
const hasHA = starterData.abilityAttr & AbilityAttr.ABILITY_HIDDEN;
|
||||||
const fitsHA = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
const fitsHA = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
||||||
if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.ON) {
|
if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.ON) {
|
||||||
return isHA;
|
return hasHA;
|
||||||
} else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.EXCLUDE) {
|
} else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.EXCLUDE) {
|
||||||
return !isHA;
|
return !hasHA;
|
||||||
} else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.OFF) {
|
} else if (misc.val === "HIDDEN_ABILITY" && misc.state === DropDownState.OFF) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Egg Purchasable Filter
|
||||||
|
const isEggPurchasable = this.isSameSpeciesEggAvailable(container.species.speciesId);
|
||||||
const fitsEgg = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
const fitsEgg = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
||||||
if (misc.val === "EGG" && misc.state === DropDownState.ON) {
|
if (misc.val === "EGG" && misc.state === DropDownState.ON) {
|
||||||
return isEggPurchasable;
|
return isEggPurchasable;
|
||||||
@ -2381,6 +2473,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pokerus Filter
|
||||||
const fitsPokerus = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
const fitsPokerus = this.filterBar.getVals(DropDownColumn.MISC).some(misc => {
|
||||||
if (misc.val === "POKERUS" && misc.state === DropDownState.ON) {
|
if (misc.val === "POKERUS" && misc.state === DropDownState.ON) {
|
||||||
return this.pokerusSpecies.includes(container.species);
|
return this.pokerusSpecies.includes(container.species);
|
||||||
@ -2579,56 +2672,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0;
|
this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0;
|
||||||
|
|
||||||
const starterAttributes : StarterAttributes | null = species ? {...this.starterPreferences[species.speciesId]} : null;
|
const starterAttributes : StarterAttributes | null = species ? {...this.starterPreferences[species.speciesId]} : null;
|
||||||
// validate starterAttributes
|
|
||||||
if (starterAttributes) {
|
|
||||||
// this may cause changes so we created a copy of the attributes before
|
|
||||||
if (starterAttributes.variant && !isNaN(starterAttributes.variant)) {
|
|
||||||
if (![
|
|
||||||
this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY, // TODO: is that bang correct?
|
|
||||||
this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT, // TODO: is that bang correct?
|
|
||||||
this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2, // TODO: is that bang correct?
|
|
||||||
this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3 // TODO: is that bang correct?
|
|
||||||
][starterAttributes.variant+1]) { // add 1 as -1 = non-shiny
|
|
||||||
// requested variant wasn't unlocked, purging setting
|
|
||||||
delete starterAttributes.variant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof starterAttributes.female !== "boolean" || !(starterAttributes.female ?
|
|
||||||
this.speciesStarterDexEntry!.caughtAttr & DexAttr.FEMALE : // TODO: is this bang correct?
|
|
||||||
this.speciesStarterDexEntry!.caughtAttr & DexAttr.MALE // TODO: is this bang correct?
|
|
||||||
)) {
|
|
||||||
// requested gender wasn't unlocked, purging setting
|
|
||||||
delete starterAttributes.female;
|
|
||||||
}
|
|
||||||
|
|
||||||
const abilityAttr = this.scene.gameData.starterData[species!.speciesId].abilityAttr; // TODO: is this bang correct?
|
|
||||||
if (![
|
|
||||||
abilityAttr & AbilityAttr.ABILITY_1,
|
|
||||||
species!.ability2 ? (abilityAttr & AbilityAttr.ABILITY_2) : abilityAttr & AbilityAttr.ABILITY_HIDDEN, // TODO: is this bang correct?
|
|
||||||
species!.ability2 && abilityAttr & AbilityAttr.ABILITY_HIDDEN // TODO: is this bang correct?
|
|
||||||
][starterAttributes.ability!]) { // TODO: is this bang correct?
|
|
||||||
// requested ability wasn't unlocked, purging setting
|
|
||||||
delete starterAttributes.ability;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(species?.forms[starterAttributes.form!]?.isStarterSelectable && this.speciesStarterDexEntry!.caughtAttr & this.scene.gameData.getFormAttr(starterAttributes.form!))) { // TODO: are those bangs correct?
|
|
||||||
// requested form wasn't unlocked/isn't a starter form, purging setting
|
|
||||||
delete starterAttributes.form;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr).indexOf(starterAttributes.nature as unknown as Nature) < 0) {
|
|
||||||
// requested nature wasn't unlocked, purging setting
|
|
||||||
delete starterAttributes.nature;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (starterAttributes?.nature) {
|
if (starterAttributes?.nature) {
|
||||||
// load default nature from stater save data, if set
|
// load default nature from stater save data, if set
|
||||||
this.natureCursor = starterAttributes.nature;
|
this.natureCursor = starterAttributes.nature;
|
||||||
}
|
}
|
||||||
if (starterAttributes?.ability && !isNaN(starterAttributes.ability)) {
|
if (starterAttributes?.ability && !isNaN(starterAttributes.ability)) {
|
||||||
// load default nature from stater save data, if set
|
// load default ability from stater save data, if set
|
||||||
this.abilityCursor = starterAttributes.ability;
|
this.abilityCursor = starterAttributes.ability;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2675,7 +2725,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonLuckText.setText(luck.toString());
|
this.pokemonLuckText.setText(luck.toString());
|
||||||
this.pokemonLuckText.setTint(getVariantTint(Math.min(luck - 1, 2) as Variant));
|
this.pokemonLuckText.setTint(getVariantTint(Math.min(luck - 1, 2) as Variant));
|
||||||
this.pokemonLuckLabelText.setVisible(this.pokemonLuckText.visible);
|
this.pokemonLuckLabelText.setVisible(this.pokemonLuckText.visible);
|
||||||
this.pokemonShinyIcon.setVisible(this.starterPreferences[species.speciesId]?.shiny ?? false);
|
|
||||||
|
|
||||||
//Growth translate
|
//Growth translate
|
||||||
let growthReadable = Utils.toReadableString(GrowthRate[species.growthRate]);
|
let growthReadable = Utils.toReadableString(GrowthRate[species.growthRate]);
|
||||||
@ -2699,12 +2748,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
|
this.pokemonHatchedIcon.setFrame(getEggTierForSpecies(species));
|
||||||
}
|
}
|
||||||
this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry.hatchedCount}`);
|
this.pokemonHatchedCountText.setText(`${this.speciesStarterDexEntry.hatchedCount}`);
|
||||||
|
|
||||||
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
|
const defaultDexAttr = this.getCurrentDexProps(species.speciesId);
|
||||||
const defaultProps = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
|
const defaultProps = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr);
|
||||||
const variant = defaultProps.variant;
|
const variant = defaultProps.variant;
|
||||||
const tint = getVariantTint(variant);
|
const tint = getVariantTint(variant);
|
||||||
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
|
this.pokemonShinyIcon.setFrame(getVariantIcon(variant));
|
||||||
this.pokemonShinyIcon.setTint(tint);
|
this.pokemonShinyIcon.setTint(tint);
|
||||||
|
this.pokemonShinyIcon.setVisible(defaultProps.shiny);
|
||||||
this.pokemonCaughtHatchedContainer.setVisible(true);
|
this.pokemonCaughtHatchedContainer.setVisible(true);
|
||||||
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
|
if (pokemonPrevolutions.hasOwnProperty(species.speciesId)) {
|
||||||
this.pokemonCaughtHatchedContainer.setY(16);
|
this.pokemonCaughtHatchedContainer.setY(16);
|
||||||
@ -2894,21 +2945,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
const dexEntry = this.scene.gameData.dexData[species.speciesId];
|
const dexEntry = this.scene.gameData.dexData[species.speciesId];
|
||||||
const abilityAttr = this.scene.gameData.starterData[species.speciesId].abilityAttr;
|
const abilityAttr = this.scene.gameData.starterData[species.speciesId].abilityAttr;
|
||||||
|
|
||||||
const isCaught = this.scene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0);
|
const caughtAttr = this.scene.gameData.dexData[species.speciesId]?.caughtAttr || BigInt(0);
|
||||||
const isVariant3Caught = !!(isCaught & DexAttr.VARIANT_3);
|
|
||||||
const isVariant2Caught = !!(isCaught & DexAttr.VARIANT_2);
|
|
||||||
const isDefaultVariantCaught = !!(isCaught & DexAttr.DEFAULT_VARIANT);
|
|
||||||
const isVariantCaught = !!(isCaught & DexAttr.SHINY);
|
|
||||||
const isMaleCaught = !!(isCaught & DexAttr.MALE);
|
|
||||||
const isFemaleCaught = !!(isCaught & DexAttr.FEMALE);
|
|
||||||
|
|
||||||
const starterAttributes = this.starterPreferences[species.speciesId];
|
|
||||||
|
|
||||||
|
if (!dexEntry.caughtAttr) {
|
||||||
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId));
|
const props = this.scene.gameData.getSpeciesDexAttrProps(species, this.getCurrentDexProps(species.speciesId));
|
||||||
const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
|
||||||
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
|
const defaultNature = this.scene.gameData.getSpeciesDefaultNature(species);
|
||||||
|
|
||||||
if (!dexEntry.caughtAttr) {
|
|
||||||
if (shiny === undefined || shiny !== props.shiny) {
|
if (shiny === undefined || shiny !== props.shiny) {
|
||||||
shiny = props.shiny;
|
shiny = props.shiny;
|
||||||
}
|
}
|
||||||
@ -2927,83 +2970,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
if (natureIndex === undefined || natureIndex !== defaultNature) {
|
if (natureIndex === undefined || natureIndex !== defaultNature) {
|
||||||
natureIndex = defaultNature;
|
natureIndex = defaultNature;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// compare current shiny, formIndex, female, variant, abilityIndex, natureIndex with the caught ones
|
|
||||||
// if the current ones are not caught, we need to find the next caught ones
|
|
||||||
if (shiny) {
|
|
||||||
if (!(isVariantCaught || isVariant2Caught || isVariant3Caught)) {
|
|
||||||
shiny = false;
|
|
||||||
starterAttributes.shiny = false;
|
|
||||||
variant = 0;
|
|
||||||
starterAttributes.variant = 0;
|
|
||||||
} else {
|
|
||||||
shiny = true;
|
|
||||||
starterAttributes.shiny = true;
|
|
||||||
if (variant === 0 && !isDefaultVariantCaught) {
|
|
||||||
if (isVariant2Caught) {
|
|
||||||
variant = 1;
|
|
||||||
starterAttributes.variant = 1;
|
|
||||||
} else if (isVariant3Caught) {
|
|
||||||
variant = 2;
|
|
||||||
starterAttributes.variant = 2;
|
|
||||||
} else {
|
|
||||||
variant = 0;
|
|
||||||
starterAttributes.variant = 0;
|
|
||||||
}
|
|
||||||
} else if (variant === 1 && !isVariant2Caught) {
|
|
||||||
if (isVariantCaught) {
|
|
||||||
variant = 0;
|
|
||||||
starterAttributes.variant = 0;
|
|
||||||
} else if (isVariant3Caught) {
|
|
||||||
variant = 2;
|
|
||||||
starterAttributes.variant = 2;
|
|
||||||
} else {
|
|
||||||
variant = 0;
|
|
||||||
starterAttributes.variant = 0;
|
|
||||||
}
|
|
||||||
} else if (variant === 2 && !isVariant3Caught) {
|
|
||||||
if (isVariantCaught) {
|
|
||||||
variant = 0;
|
|
||||||
starterAttributes.variant = 0;
|
|
||||||
} else if (isVariant2Caught) {
|
|
||||||
variant = 1;
|
|
||||||
starterAttributes.variant = 1;
|
|
||||||
} else {
|
|
||||||
variant = 0;
|
|
||||||
starterAttributes.variant = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (female) {
|
|
||||||
if (!isFemaleCaught) {
|
|
||||||
female = false;
|
|
||||||
starterAttributes.female = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!isMaleCaught) {
|
|
||||||
female = true;
|
|
||||||
starterAttributes.female = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (species.forms) {
|
|
||||||
const formCount = species.forms.length;
|
|
||||||
let newFormIndex = formIndex??0;
|
|
||||||
if (species.forms[newFormIndex]) {
|
|
||||||
const isValidForm = species.forms[newFormIndex].isStarterSelectable && dexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex);
|
|
||||||
if (!isValidForm) {
|
|
||||||
do {
|
|
||||||
newFormIndex = (newFormIndex + 1) % formCount;
|
|
||||||
if (species.forms[newFormIndex].isStarterSelectable && dexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while (newFormIndex !== props.formIndex);
|
|
||||||
formIndex = newFormIndex;
|
|
||||||
starterAttributes.form = formIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default?
|
this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default?
|
||||||
@ -3045,8 +3011,19 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
currentFilteredContainer.checkIconId(female, formIndex, shiny, variant);
|
currentFilteredContainer.checkIconId(female, formIndex, shiny, variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.canCycleShiny = isVariantCaught || isVariant2Caught || isVariant3Caught;
|
const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY);
|
||||||
|
const isShinyCaught = !!(caughtAttr & DexAttr.SHINY);
|
||||||
|
const isVariant1Caught = isShinyCaught && !!(caughtAttr & DexAttr.DEFAULT_VARIANT);
|
||||||
|
const isVariant2Caught = isShinyCaught && !!(caughtAttr & DexAttr.VARIANT_2);
|
||||||
|
const isVariant3Caught = isShinyCaught && !!(caughtAttr & DexAttr.VARIANT_3);
|
||||||
|
|
||||||
|
this.canCycleShiny = isNonShinyCaught && isShinyCaught;
|
||||||
|
this.canCycleVariant = !!shiny && [ isVariant1Caught, isVariant2Caught, isVariant3Caught].filter(v => v).length > 1;
|
||||||
|
|
||||||
|
const isMaleCaught = !!(caughtAttr & DexAttr.MALE);
|
||||||
|
const isFemaleCaught = !!(caughtAttr & DexAttr.FEMALE);
|
||||||
this.canCycleGender = isMaleCaught && isFemaleCaught;
|
this.canCycleGender = isMaleCaught && isFemaleCaught;
|
||||||
|
|
||||||
const hasAbility1 = abilityAttr & AbilityAttr.ABILITY_1;
|
const hasAbility1 = abilityAttr & AbilityAttr.ABILITY_1;
|
||||||
let hasAbility2 = abilityAttr & AbilityAttr.ABILITY_2;
|
let hasAbility2 = abilityAttr & AbilityAttr.ABILITY_2;
|
||||||
const hasHiddenAbility = abilityAttr & AbilityAttr.ABILITY_HIDDEN;
|
const hasHiddenAbility = abilityAttr & AbilityAttr.ABILITY_HIDDEN;
|
||||||
@ -3061,10 +3038,11 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.canCycleAbility = [ hasAbility1, hasAbility2, hasHiddenAbility ].filter(a => a).length > 1;
|
this.canCycleAbility = [ hasAbility1, hasAbility2, hasHiddenAbility ].filter(a => a).length > 1;
|
||||||
|
|
||||||
this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey))
|
this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey))
|
||||||
.map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(f => f).length > 1;
|
.map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(f => f).length > 1;
|
||||||
this.canCycleNature = this.scene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
|
this.canCycleNature = this.scene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1;
|
||||||
this.canCycleVariant = !!shiny && [ dexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, dexEntry.caughtAttr & DexAttr.VARIANT_2, dexEntry.caughtAttr & DexAttr.VARIANT_3].filter(v => v).length > 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dexEntry.caughtAttr && species.malePercent !== null) {
|
if (dexEntry.caughtAttr && species.malePercent !== null) {
|
||||||
@ -3442,39 +3420,55 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
return canStart;
|
return canStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this creates a temporary dex attr props that we use to check whether a pokemon is valid for a challenge.
|
/**
|
||||||
* when checking for certain challenges (i.e. mono type), we need to check for form changes AND evolutions
|
* Creates a temporary dex attr props that will be used to check whether a pokemon is valid for a challenge
|
||||||
* However, since some pokemon can evolve based on their intial gender/form, we need a way to look for that
|
* and to display the correct shiny, variant, and form based on the StarterPreferences
|
||||||
* This temporary dex attr will therefore ONLY look at gender and form, since there's no cases of shinies/variants
|
*
|
||||||
* having different evolutions to their non shiny/variant part, and so those can be ignored
|
* @param speciesId the id of the species to get props for
|
||||||
* Since the current form and gender is stored in the starter preferences, this is where we get the values from
|
* @returns the dex props
|
||||||
*/
|
*/
|
||||||
getCurrentDexProps(speciesId: number): bigint {
|
getCurrentDexProps(speciesId: number): bigint {
|
||||||
let props = 0n;
|
let props = 0n;
|
||||||
|
const caughtAttr = this.scene.gameData.dexData[speciesId].caughtAttr;
|
||||||
|
|
||||||
if (this.starterPreferences[speciesId]?.female) { // this checks the gender of the pokemon
|
/* this checks the gender of the pokemon; this works by checking a) that the starter preferences for the species exist, and if so, is it female. If so, it'll add DexAttr.FEMALE to our temp props
|
||||||
|
* It then checks b) if the caughtAttr for the pokemon is female and NOT male - this means that the ONLY gender we've gotten is female, and we need to add DexAttr.FEMALE to our temp props
|
||||||
|
* If neither of these pass, we add DexAttr.MALE to our temp props
|
||||||
|
*/
|
||||||
|
if (this.starterPreferences[speciesId]?.female || ((caughtAttr & DexAttr.FEMALE) > 0n && (caughtAttr & DexAttr.MALE) === 0n)) {
|
||||||
props += DexAttr.FEMALE;
|
props += DexAttr.FEMALE;
|
||||||
} else {
|
} else {
|
||||||
props += DexAttr.MALE;
|
props += DexAttr.MALE;
|
||||||
}
|
}
|
||||||
if (this.starterPreferences[speciesId]?.shiny) {
|
/* This part is very similar to above, but instead of for gender, it checks for shiny within starter preferences.
|
||||||
|
* If they're not there, it checks the caughtAttr for shiny only (i.e. SHINY === true && NON_SHINY === false)
|
||||||
|
*/
|
||||||
|
if (this.starterPreferences[speciesId]?.shiny || ((caughtAttr & DexAttr.SHINY) > 0n && (caughtAttr & DexAttr.NON_SHINY) === 0n)) {
|
||||||
props += DexAttr.SHINY;
|
props += DexAttr.SHINY;
|
||||||
if (this.starterPreferences[speciesId]?.variant) {
|
if (this.starterPreferences[speciesId]?.variant) {
|
||||||
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
|
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.variant)) * DexAttr.DEFAULT_VARIANT;
|
||||||
} else {
|
} else {
|
||||||
|
/* This calculates the correct variant if there's no starter preferences for it.
|
||||||
|
* This gets the lowest tier variant that you've caught (in line with other mechanics) and adds it to the temp props
|
||||||
|
*/
|
||||||
|
if ((caughtAttr & DexAttr.DEFAULT_VARIANT) > 0) {
|
||||||
props += DexAttr.DEFAULT_VARIANT;
|
props += DexAttr.DEFAULT_VARIANT;
|
||||||
}
|
}
|
||||||
|
if ((caughtAttr & DexAttr.VARIANT_2) > 0) {
|
||||||
|
props += DexAttr.VARIANT_2;
|
||||||
|
} else if ((caughtAttr & DexAttr.VARIANT_3) > 0) {
|
||||||
|
props += DexAttr.VARIANT_3;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
props += DexAttr.NON_SHINY;
|
props += DexAttr.NON_SHINY;
|
||||||
if (this.starterPreferences[speciesId]?.variant) {
|
|
||||||
delete this.starterPreferences[speciesId].variant;
|
|
||||||
}
|
|
||||||
props += DexAttr.DEFAULT_VARIANT; // we add the default variant here because non shiny versions are listed as default variant
|
props += DexAttr.DEFAULT_VARIANT; // we add the default variant here because non shiny versions are listed as default variant
|
||||||
}
|
}
|
||||||
if (this.starterPreferences[speciesId]?.form) { // this checks for the form of the pokemon
|
if (this.starterPreferences[speciesId]?.form) { // this checks for the form of the pokemon
|
||||||
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.form)) * DexAttr.DEFAULT_FORM;
|
props += BigInt(Math.pow(2, this.starterPreferences[speciesId]?.form)) * DexAttr.DEFAULT_FORM;
|
||||||
} else {
|
} else {
|
||||||
props += DexAttr.DEFAULT_FORM;
|
// Get the first unlocked form
|
||||||
|
props += this.scene.gameData.getFormAttr(this.scene.gameData.getFormIndex(caughtAttr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
|
147
src/ui/test-dialogue-ui-handler.ts
Normal file
147
src/ui/test-dialogue-ui-handler.ts
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import { FormModalUiHandler } from "./form-modal-ui-handler";
|
||||||
|
import { ModalConfig } from "./modal-ui-handler";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
|
import { OptionSelectItem } from "./abstact-option-select-ui-handler";
|
||||||
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
|
import { Mode } from "./ui";
|
||||||
|
|
||||||
|
export default class TestDialogueUiHandler extends FormModalUiHandler {
|
||||||
|
|
||||||
|
keys: string[];
|
||||||
|
|
||||||
|
constructor(scene, mode) {
|
||||||
|
super(scene, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
super.setup();
|
||||||
|
|
||||||
|
const flattenKeys = (object, topKey?: string, midleKey?: string[]): Array<any> => {
|
||||||
|
return Object.keys(object).map((t, i) => {
|
||||||
|
const value = Object.values(object)[i];
|
||||||
|
|
||||||
|
if (typeof value === "object" && !isNullOrUndefined(value)) { // we check for not null or undefined here because if the language json file has a null key, the typeof will still be an object, but that object will be null, causing issues
|
||||||
|
// If the value is an object, execute the same process
|
||||||
|
// si el valor es un objeto ejecuta el mismo proceso
|
||||||
|
|
||||||
|
return flattenKeys(value, topKey ?? t, topKey ? midleKey ? [...midleKey, t] : [t] : undefined).filter((t) => t.length > 0);
|
||||||
|
} else if (typeof value === "string" || isNullOrUndefined(value)) { // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
|
||||||
|
|
||||||
|
// Return in the format expected by i18next
|
||||||
|
return midleKey ? `${topKey}:${midleKey.map((m) => m).join(".")}.${t}` : `${topKey}:${t}`;
|
||||||
|
}
|
||||||
|
}).filter((t) => t);
|
||||||
|
};
|
||||||
|
|
||||||
|
const keysInArrays = flattenKeys(i18next.getDataByLanguage(String(i18next.resolvedLanguage))).filter((t) => t.length > 0); // Array of arrays
|
||||||
|
const keys = keysInArrays.flat(Infinity).map(String); // One array of string
|
||||||
|
this.keys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalTitle(config?: ModalConfig): string {
|
||||||
|
return "Test Dialogue";
|
||||||
|
}
|
||||||
|
|
||||||
|
getFields(config?: ModalConfig): string[] {
|
||||||
|
return [ "Dialogue" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidth(config?: ModalConfig): number {
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMargin(config?: ModalConfig): [number, number, number, number] {
|
||||||
|
return [ 0, 0, 48, 0 ];
|
||||||
|
}
|
||||||
|
|
||||||
|
getButtonLabels(config?: ModalConfig): string[] {
|
||||||
|
return [ "Check", "Cancel" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
getReadableErrorMessage(error: string): string {
|
||||||
|
const colonIndex = error?.indexOf(":");
|
||||||
|
if (colonIndex > 0) {
|
||||||
|
error = error.slice(0, colonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getReadableErrorMessage(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
show(args: any[]): boolean {
|
||||||
|
const ui = this.getUi();
|
||||||
|
const input = this.inputs[0];
|
||||||
|
input.setMaxLength(255);
|
||||||
|
|
||||||
|
input.on("keydown", (inputObject, evt: KeyboardEvent) => {
|
||||||
|
if (["escape", "space"].some((v) => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) && ui.getMode() === Mode.AUTO_COMPLETE) {
|
||||||
|
// Delete autocomplete list and recovery focus.
|
||||||
|
inputObject.on("blur", () => inputObject.node.focus(), { once: true });
|
||||||
|
ui.revertMode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
input.on("textchange", (inputObject, evt: InputEvent) => {
|
||||||
|
// Delete autocomplete.
|
||||||
|
if (ui.getMode() === Mode.AUTO_COMPLETE) {
|
||||||
|
ui.revertMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
let options: OptionSelectItem[] = [];
|
||||||
|
const splitArr = inputObject.text.split(" ");
|
||||||
|
const filteredKeys = this.keys.filter((command) => command.toLowerCase().includes(splitArr[splitArr.length - 1].toLowerCase()));
|
||||||
|
if (inputObject.text !== "" && filteredKeys.length > 0) {
|
||||||
|
// if performance is required, you could reduce the number of total results by changing the slice below to not have all ~8000 inputs going
|
||||||
|
options = filteredKeys.slice(0).map((value) => {
|
||||||
|
return {
|
||||||
|
label: value,
|
||||||
|
handler: () => {
|
||||||
|
// this is here to make sure that if you try to backspace then enter, the last known evt.data (backspace) is picked up
|
||||||
|
// this is because evt.data is null for backspace, so without this, the autocomplete windows just closes
|
||||||
|
if (!isNullOrUndefined(evt.data) || evt.inputType?.toLowerCase() === "deletecontentbackward") {
|
||||||
|
const separatedArray = inputObject.text.split(" ");
|
||||||
|
separatedArray[separatedArray.length - 1] = value;
|
||||||
|
inputObject.setText(separatedArray.join(" "));
|
||||||
|
}
|
||||||
|
ui.revertMode();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.length > 0) {
|
||||||
|
const modalOpts = {
|
||||||
|
options: options,
|
||||||
|
maxOptions: 5,
|
||||||
|
modalContainer: this.modalContainer
|
||||||
|
};
|
||||||
|
ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOpts);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if (super.show(args)) {
|
||||||
|
const config = args[0] as ModalConfig;
|
||||||
|
this.inputs[0].resize(1150, 116);
|
||||||
|
this.inputContainers[0].list[0].width = 200;
|
||||||
|
if (args[1] && typeof (args[1] as PlayerPokemon).getNameToRender === "function") {
|
||||||
|
this.inputs[0].text = (args[1] as PlayerPokemon).getNameToRender();
|
||||||
|
} else {
|
||||||
|
this.inputs[0].text = args[1];
|
||||||
|
}
|
||||||
|
this.submitAction = (_) => {
|
||||||
|
if (ui.getMode() === Mode.TEST_DIALOGUE) {
|
||||||
|
this.sanitizeInputs();
|
||||||
|
const sanitizedName = btoa(unescape(encodeURIComponent(this.inputs[0].text)));
|
||||||
|
config.buttonActions[0](sanitizedName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -49,6 +49,8 @@ import RenameFormUiHandler from "./rename-form-ui-handler";
|
|||||||
import AdminUiHandler from "./admin-ui-handler";
|
import AdminUiHandler from "./admin-ui-handler";
|
||||||
import RunHistoryUiHandler from "./run-history-ui-handler";
|
import RunHistoryUiHandler from "./run-history-ui-handler";
|
||||||
import RunInfoUiHandler from "./run-info-ui-handler";
|
import RunInfoUiHandler from "./run-info-ui-handler";
|
||||||
|
import TestDialogueUiHandler from "#app/ui/test-dialogue-ui-handler";
|
||||||
|
import AutoCompleteUiHandler from "./autocomplete-ui-handler";
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
@ -89,6 +91,8 @@ export enum Mode {
|
|||||||
RENAME_POKEMON,
|
RENAME_POKEMON,
|
||||||
RUN_HISTORY,
|
RUN_HISTORY,
|
||||||
RUN_INFO,
|
RUN_INFO,
|
||||||
|
TEST_DIALOGUE,
|
||||||
|
AUTO_COMPLETE,
|
||||||
ADMIN,
|
ADMIN,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,6 +131,8 @@ const noTransitionModes = [
|
|||||||
Mode.UNAVAILABLE,
|
Mode.UNAVAILABLE,
|
||||||
Mode.OUTDATED,
|
Mode.OUTDATED,
|
||||||
Mode.RENAME_POKEMON,
|
Mode.RENAME_POKEMON,
|
||||||
|
Mode.TEST_DIALOGUE,
|
||||||
|
Mode.AUTO_COMPLETE,
|
||||||
Mode.ADMIN,
|
Mode.ADMIN,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -191,6 +197,8 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||||||
new RenameFormUiHandler(scene),
|
new RenameFormUiHandler(scene),
|
||||||
new RunHistoryUiHandler(scene),
|
new RunHistoryUiHandler(scene),
|
||||||
new RunInfoUiHandler(scene),
|
new RunInfoUiHandler(scene),
|
||||||
|
new TestDialogueUiHandler(scene, Mode.TEST_DIALOGUE),
|
||||||
|
new AutoCompleteUiHandler(scene),
|
||||||
new AdminUiHandler(scene),
|
new AdminUiHandler(scene),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user