Merge branch 'beta' into mine

This commit is contained in:
Lylian BALL 2024-08-30 12:23:45 +02:00 committed by GitHub
commit 085c4af17d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 2629 additions and 2307 deletions

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

View File

@ -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 ];

View File

@ -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 ],

View File

@ -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,

View File

@ -3846,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)
] ]

View File

@ -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"
} }

View File

@ -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",

View File

@ -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;
} }

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }
} }

View File

@ -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:

View File

@ -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;
}; };

View 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;
}
}

View File

@ -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);
} }

View File

@ -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);

View File

@ -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;

View 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;
}
}

View File

@ -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),
]; ];
} }