Merge branch 'pagefaultgames:main' into main

This commit is contained in:
NxKarim 2024-04-21 17:17:00 -06:00 committed by GitHub
commit 94ab2f208e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 320 additions and 84 deletions

View File

@ -7,8 +7,11 @@ PokéRogue is a browser based Pokémon fangame heavily inspired by the roguelite
If you have the motivation and experience with Typescript/Javascript (or are willing to learn) please feel free to fork the repository and make pull requests with contributions. If you don't know what to work on but want to help, reference the below **To-Do** section or the **#vote** channel in the discord.
### 💻 Environment Setup
node: 18.3.0
#### Prerequisites
- node: 18.3.0
- npm: [how to install](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
#### Running Locally
1. Clone the repo and in the root directory run `npm install`
- *if you run into any errors, reach out in the **#dev-corner** channel in discord*
2. Run `npm run start:dev` to locally run the project in `localhost:8000`

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,30 +4,30 @@
"image": "1024-stellar.png",
"format": "RGBA8888",
"size": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"scale": 0.5,
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"spriteSourceSize": {
"x": 9,
"x": 0,
"y": 0,
"w": 78,
"h": 96
"w": 115,
"h": 119
},
"frame": {
"x": 0,
"y": 0,
"w": 78,
"h": 96
"w": 115,
"h": 119
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:c9ee64bda72f2dadb06109338796ccac:1313f1218b7da2c57ad9f290d1323840:c1508f3b01ae78a28a1267fd6caa4f7b$"
"smartupdate": "$TexturePacker:SmartUpdate:bc663acf2e62803fce6c3a525dc8dd98:ccd7d0de8a487235cfbd6f372afa931f:c1508f3b01ae78a28a1267fd6caa4f7b$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -4,30 +4,30 @@
"image": "1024-stellar.png",
"format": "RGBA8888",
"size": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"scale": 0.333,
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"spriteSourceSize": {
"x": 5,
"x": 0,
"y": 0,
"w": 86,
"h": 96
"w": 115,
"h": 119
},
"frame": {
"x": 0,
"y": 0,
"w": 86,
"h": 96
"w": 115,
"h": 119
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:ac5e775f77477eeabd029932804747c4:f7a112a87c35dc81cb0da88b7cbb39e8:c1508f3b01ae78a28a1267fd6caa4f7b$"
"smartupdate": "$TexturePacker:SmartUpdate:210ba1c2e6e58501571ae226d073a3c5:f12bdf191842f7ec3a4be98a43fb8121:c1508f3b01ae78a28a1267fd6caa4f7b$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -4,30 +4,30 @@
"image": "1024-stellar.png",
"format": "RGBA8888",
"size": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"scale": 0.333,
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"spriteSourceSize": {
"x": 5,
"x": 0,
"y": 0,
"w": 86,
"h": 96
"w": 115,
"h": 119
},
"frame": {
"x": 0,
"y": 0,
"w": 86,
"h": 96
"w": 115,
"h": 119
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:2bd25bae0fabcfbc35e24bd578a7b4b5:aec60788a0d77f38fb599d721e41a0d6:c1508f3b01ae78a28a1267fd6caa4f7b$"
"smartupdate": "$TexturePacker:SmartUpdate:210ba1c2e6e58501571ae226d073a3c5:f12bdf191842f7ec3a4be98a43fb8121:c1508f3b01ae78a28a1267fd6caa4f7b$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -4,30 +4,30 @@
"image": "1024-stellar.png",
"format": "RGBA8888",
"size": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"scale": 0.5,
"scale": 1,
"frames": [
{
"filename": "0001.png",
"rotated": false,
"trimmed": false,
"sourceSize": {
"w": 96,
"h": 96
"w": 119,
"h": 119
},
"spriteSourceSize": {
"x": 9,
"x": 0,
"y": 0,
"w": 78,
"h": 96
"w": 115,
"h": 119
},
"frame": {
"x": 0,
"y": 0,
"w": 78,
"h": 96
"w": 115,
"h": 119
}
}
]
@ -36,6 +36,6 @@
"meta": {
"app": "https://www.codeandweb.com/texturepacker",
"version": "3.0",
"smartupdate": "$TexturePacker:SmartUpdate:a7e89af07a22475413df24b510c193f7:45261af90c4a51e3dc73cccb894a2aad:c1508f3b01ae78a28a1267fd6caa4f7b$"
"smartupdate": "$TexturePacker:SmartUpdate:3510deaf42eaa3ee2fdfa22c00a2b30b:3beb6b12ca1bb50ad260593b41939f27:c1508f3b01ae78a28a1267fd6caa4f7b$"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -17,7 +17,7 @@ import { TextStyle, addTextObject } from './ui/text';
import { Moves } from "./data/enums/moves";
import { allMoves } from "./data/move";
import { initMoves } from './data/move';
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave } from './modifier/modifier-type';
import { ModifierPoolType, getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getModifierPoolForType } from './modifier/modifier-type';
import AbilityBar from './ui/ability-bar';
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, IncrementMovePriorityAbAttr, applyAbAttrs, initAbilities } from './data/ability';
import { Abilities } from "./data/enums/abilities";
@ -795,7 +795,10 @@ export default class BattleScene extends SceneBase {
this.trainer.setVisible(true);
if (reloadI18n) {
const localizable: Localizable[] = [ ...allMoves ];
const localizable: Localizable[] = [
...allMoves,
...Utils.getEnumValues(ModifierPoolType).map(mpt => getModifierPoolForType(mpt)).map(mp => Object.values(mp).flat().map(mt => mt.modifierType).filter(mt => 'localize' in mt).map(lpb => lpb as unknown as Localizable)).flat()
];
for (let item of localizable)
item.localize();
}
@ -975,6 +978,9 @@ export default class BattleScene extends SceneBase {
case Species.DEERLING:
case Species.SAWSBUCK:
case Species.VIVILLON:
case Species.FLABEBE:
case Species.FLOETTE:
case Species.FLORGES:
case Species.ORICORIO:
case Species.SQUAWKABILLY:
case Species.TATSUGIRI:

View File

@ -2134,6 +2134,9 @@ export class SuppressFieldAbilitiesAbAttr extends AbAttr {
}
}
export class AlwaysHitAbAttr extends AbAttr { }
export class UncopiableAbilityAbAttr extends AbAttr {
constructor() {
super(false);
@ -2615,7 +2618,9 @@ export function initAbilities() {
new Ability(Abilities.SNIPER, "Sniper (N)", "Powers up moves if they become critical hits when attacking.", 4),
new Ability(Abilities.MAGIC_GUARD, "Magic Guard", "The Pokémon only takes damage from attacks.", 4)
.attr(BlockNonDirectDamageAbAttr),
new Ability(Abilities.NO_GUARD, "No Guard (N)", "The Pokémon employs no-guard tactics to ensure incoming and outgoing attacks always land.", 4),
new Ability(Abilities.NO_GUARD, "No Guard", "The Pokémon employs no-guard tactics to ensure incoming and outgoing attacks always land.", 4)
.attr(AlwaysHitAbAttr)
.attr(DoubleBattleChanceAbAttr),
new Ability(Abilities.STALL, "Stall (N)", "The Pokémon moves after all other Pokémon do.", 4),
new Ability(Abilities.TECHNICIAN, "Technician", "Powers up the Pokémon's weaker moves.", 4)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.power <= 60, 1.5),

View File

@ -1024,6 +1024,39 @@ export class SaltCuredTag extends BattlerTag {
}
}
export class CursedTag extends BattlerTag {
private sourceIndex: integer;
constructor(sourceId: integer) {
super(BattlerTagType.CURSED, BattlerTagLapseType.TURN_END, 1, Moves.CURSE, sourceId);
}
onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' has been cursed!'));
this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex();
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
const ret = lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
if (ret) {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.SALT_CURE));
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled);
if (!cancelled.value) {
pokemon.damageAndUpdate(Math.floor(pokemon.getMaxHp() / 4));
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` is hurt by the ${this.getMoveName()}!`));
}
}
return ret;
}
}
export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves, sourceId: integer): BattlerTag {
switch (tagType) {
case BattlerTagType.RECHARGING:
@ -1121,6 +1154,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount - 1, sourceMove);
case BattlerTagType.SALT_CURED:
return new SaltCuredTag(sourceId);
case BattlerTagType.CURSED:
return new CursedTag(sourceId);
case BattlerTagType.CHARGED:
return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true);
case BattlerTagType.NONE:

View File

@ -49,6 +49,7 @@ export enum BattlerTagType {
BYPASS_SLEEP = "BYPASS_SLEEP",
IGNORE_FLYING = "IGNORE_FLYING",
SALT_CURED = "SALT_CURED",
CURSED = "CURSED",
CHARGED = "CHARGED",
GROUNDED = "GROUNDED"
}

View File

@ -2493,6 +2493,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
return -5;
case BattlerTagType.SEEDED:
case BattlerTagType.SALT_CURED:
case BattlerTagType.CURSED:
case BattlerTagType.FRENZY:
case BattlerTagType.TRAPPED:
case BattlerTagType.BIND:
@ -2527,6 +2528,34 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
}
}
export class CurseAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move:Move, args: any[]): boolean {
// Determine the correct target based on the user's type
if (!user.getTypes(true).includes(Type.GHOST)) {
// For non-Ghost types, target the user itself
target = user;
}
if (user.getTypes(true).includes(Type.GHOST)) {
if (target.getTag(BattlerTagType.CURSED)) {
user.scene.queueMessage('But it failed!');
return false;
}
let curseRecoilDamage = Math.floor(user.getMaxHp() / 2);
user.damageAndUpdate(curseRecoilDamage, HitResult.OTHER, false, true, true);
user.scene.queueMessage(getPokemonMessage(user, ' cut its own HP!'));
target.addTag(BattlerTagType.CURSED, 0, move.id, user.id);
return true;
} else {
target = user;
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.ATK, BattleStat.DEF], 1));
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.getBattlerIndex(), this.selfTarget, [BattleStat.SPD], -1));
return true;
}
}
}
export class LapseBattlerTagAttr extends MoveEffectAttr {
public tagTypes: BattlerTagType[];
@ -5132,6 +5161,7 @@ export function initMoves() {
.attr(StatChangeAttr, BattleStat.SPATK, 1, true)
.danceMove(),
new AttackMove(Moves.FREEZE_SHOCK, Type.ICE, MoveCategory.PHYSICAL, 140, 90, 5, 30, 0, 5)
.attr(ChargeAttr, ChargeAnim.FREEZE_SHOCK_CHARGING, 'became cloaked\nin a freezing light!')
.attr(StatusEffectAttr, StatusEffect.PARALYSIS)
.makesContact(false),
new AttackMove(Moves.ICE_BURN, Type.ICE, MoveCategory.SPECIAL, 140, 90, 5, 30, 0, 5)

View File

@ -1,4 +1,5 @@
import BattleScene from "../battle-scene";
import i18next from '../plugins/i18n';
export enum PokeballType {
POKEBALL,
@ -30,22 +31,22 @@ export function getPokeballName(type: PokeballType): string {
let ret: string;
switch (type) {
case PokeballType.POKEBALL:
ret = 'Poké Ball';
ret = i18next.t('pokeball:pokeBall');
break;
case PokeballType.GREAT_BALL:
ret = 'Great Ball';
ret = i18next.t('pokeball:greatBall');
break;
case PokeballType.ULTRA_BALL:
ret = 'Ultra Ball';
ret = i18next.t('pokeball:ultraBall');
break;
case PokeballType.ROGUE_BALL:
ret = 'Rogue Ball';
ret = i18next.t('pokeball:rogueBall');
break;
case PokeballType.MASTER_BALL:
ret = 'Master Ball';
ret = i18next.t('pokeball:masterBall');
break;
case PokeballType.LUXURY_BALL:
ret = 'Luxury Ball';
ret = i18next.t('pokeball:luxuryBall');
break;
}
return ret;

View File

@ -917,7 +917,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.TRUMBEAK, 14, null, null)
],
[Species.TRUMBEAK]: [
new SpeciesEvolution(Species.TOUCANNON, 36, null, null)
new SpeciesEvolution(Species.TOUCANNON, 28, null, null)
],
[Species.YUNGOOS]: [
new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.SHORT)

View File

@ -10919,8 +10919,8 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
],
[Species.CHESPIN]: [
[ 1, Moves.TACKLE ],
[ 1, Moves.VINE_WHIP ],
[ 1, Moves.GROWL ],
[ 1, Moves.VINE_WHIP ],
[ 8, Moves.ROLLOUT ],
[ 11, Moves.BITE ],
[ 15, Moves.LEECH_SEED ],
@ -10934,8 +10934,8 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
],
[Species.QUILLADIN]: [
[ 1, Moves.TACKLE ],
[ 1, Moves.VINE_WHIP ],
[ 1, Moves.GROWL ],
[ 1, Moves.VINE_WHIP ],
[ 8, Moves.ROLLOUT ],
[ 11, Moves.BITE ],
[ 15, Moves.LEECH_SEED ],

View File

@ -1473,7 +1473,7 @@ export function initSpecies() {
new PokemonSpecies(Species.PORYGON_Z, "Porygon-Z", 4, false, false, false, "Virtual Pokémon", Type.NORMAL, null, 0.9, 34, Abilities.ADAPTABILITY, Abilities.DOWNLOAD, Abilities.ANALYTIC, 535, 85, 80, 70, 135, 75, 90, 30, 50, 268, GrowthRate.MEDIUM_FAST, null, false),
new PokemonSpecies(Species.GALLADE, "Gallade", 4, false, false, false, "Blade Pokémon", Type.PSYCHIC, Type.FIGHTING, 1.6, 52, Abilities.STEADFAST, Abilities.SHARPNESS, Abilities.JUSTIFIED, 518, 68, 125, 65, 65, 115, 80, 45, 35, 259, GrowthRate.SLOW, 100, false, true,
new PokemonForm("Normal", "", Type.PSYCHIC, Type.FIGHTING, 1.6, 52, Abilities.STEADFAST, Abilities.SHARPNESS, Abilities.JUSTIFIED, 518, 68, 125, 65, 65, 115, 80, 45, 35, 259),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.PSYCHIC, Type.FIGHTING, 1.6, 56.4, Abilities.INNER_FOCUS, Abilities.INNER_FOCUS, Abilities.INNER_FOCUS, 618, 68, 165, 95, 65, 115, 110, 45, 35, 259),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.PSYCHIC, Type.FIGHTING, 1.6, 56.4, Abilities.SHARPNESS, Abilities.SHARPNESS, Abilities.SHARPNESS, 618, 68, 165, 95, 65, 115, 110, 45, 35, 259),
),
new PokemonSpecies(Species.PROBOPASS, "Probopass", 4, false, false, false, "Compass Pokémon", Type.ROCK, Type.STEEL, 1.4, 340, Abilities.STURDY, Abilities.MAGNET_PULL, Abilities.SAND_FORCE, 525, 60, 55, 145, 75, 150, 40, 60, 70, 184, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.DUSKNOIR, "Dusknoir", 4, false, false, false, "Gripper Pokémon", Type.GHOST, null, 2.2, 106.6, Abilities.PRESSURE, Abilities.NONE, Abilities.FRISK, 525, 45, 100, 135, 65, 135, 45, 45, 35, 263, GrowthRate.FAST, 50, false),
@ -3058,7 +3058,7 @@ export const speciesStarters = {
[Species.FUECOCO]: 3,
[Species.QUAXLY]: 3,
[Species.LECHONK]: 2,
[Species.TAROUNTULA]: 2,
[Species.TAROUNTULA]: 1,
[Species.NYMBLE]: 3,
[Species.PAWMI]: 3,
[Species.TANDEMAUS]: 4,

View File

@ -697,9 +697,9 @@ export const trainerConfigs: TrainerConfigs = {
[TrainerType.GIOVANNI]: new TrainerConfig(++t).initForGymLeader([ Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F ], Type.DARK).setBattleBgm('battle_kanto_gym'),
[TrainerType.FALKNER]: new TrainerConfig(++t).initForGymLeader([ Species.PIDGEY, Species.HOOTHOOT, Species.DODUO ], Type.FLYING).setBattleBgm('battle_johto_gym'),
[TrainerType.BUGSY]: new TrainerConfig(++t).initForGymLeader([ Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR ], Type.BUG).setBattleBgm('battle_johto_gym'),
[TrainerType.WHITNEY]: new TrainerConfig(++t).initForGymLeader([ Species.CLEFAIRY, Species.MILTANK ], Type.NORMAL).setBattleBgm('battle_johto_gym'),
[TrainerType.WHITNEY]: new TrainerConfig(++t).initForGymLeader([ Species.GIRAFARIG, Species.MILTANK ], Type.NORMAL).setBattleBgm('battle_johto_gym'),
[TrainerType.MORTY]: new TrainerConfig(++t).initForGymLeader([ Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE ], Type.GHOST).setBattleBgm('battle_johto_gym'),
[TrainerType.CHUCK]: new TrainerConfig(++t).initForGymLeader([ Species.POLIWRATH, ], Type.FIGHTING).setBattleBgm('battle_johto_gym'),
[TrainerType.CHUCK]: new TrainerConfig(++t).initForGymLeader([ Species.POLIWRATH, Species.MANKEY ], Type.FIGHTING).setBattleBgm('battle_johto_gym'),
[TrainerType.JASMINE]: new TrainerConfig(++t).initForGymLeader([ Species.MAGNEMITE, Species.STEELIX ], Type.STEEL).setBattleBgm('battle_johto_gym'),
[TrainerType.PRYCE]: new TrainerConfig(++t).initForGymLeader([ Species.SEEL, Species.SWINUB ], Type.ICE).setBattleBgm('battle_johto_gym'),
[TrainerType.CLAIR]: new TrainerConfig(++t).initForGymLeader([ Species.DRATINI, Species.HORSEA, Species.GYARADOS ], Type.DRAGON).setBattleBgm('battle_johto_gym'),

View File

@ -82,7 +82,11 @@ export class EggHatchPhase extends Phase {
this.eggContainer.add(this.eggLightraysOverlay);
this.eggHatchContainer.add(this.eggContainer);
const getPokemonSprite = () => this.scene.add.sprite(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2, `pkmn__sub`);
const getPokemonSprite = () => {
const ret = this.scene.add.sprite(this.eggHatchBg.displayWidth / 2, this.eggHatchBg.displayHeight / 2, `pkmn__sub`);
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
return ret;
};
this.eggHatchContainer.add((this.pokemonSprite = getPokemonSprite()));
@ -245,7 +249,7 @@ export class EggHatchPhase extends Phase {
this.scene.validateAchv(achvs.HATCH_SHINY);
this.eggContainer.setVisible(false);
this.pokemonSprite.play(this.pokemon.getSpriteKey(true));
this.pokemonSprite.pipelineData['ignoreTimeTint'] = true;
this.pokemonSprite.setPipelineData('ignoreTimeTint', true);
this.pokemonSprite.setPipelineData('spriteKey', this.pokemon.getSpriteKey());
this.pokemonSprite.setPipelineData('shiny', this.pokemon.shiny);
this.pokemonSprite.setPipelineData('variant', this.pokemon.variant);

View File

@ -71,7 +71,11 @@ export class EvolutionPhase extends Phase {
this.evolutionBgOverlay.setAlpha(0);
this.evolutionContainer.add(this.evolutionBgOverlay);
const getPokemonSprite = () => this.scene.addPokemonSprite(this.pokemon, this.evolutionBaseBg.displayWidth / 2, this.evolutionBaseBg.displayHeight / 2, `pkmn__sub`);
const getPokemonSprite = () => {
const ret = this.scene.addPokemonSprite(this.pokemon, this.evolutionBaseBg.displayWidth / 2, this.evolutionBaseBg.displayHeight / 2, `pkmn__sub`);
ret.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], ignoreTimeTint: true });
return ret;
};
this.evolutionContainer.add((this.pokemonSprite = getPokemonSprite()));
this.evolutionContainer.add((this.pokemonTintSprite = getPokemonSprite()));
@ -92,7 +96,10 @@ export class EvolutionPhase extends Phase {
[ this.pokemonSprite, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite ].map(sprite => {
sprite.play(this.pokemon.getSpriteKey(true));
sprite.setPipeline(this.scene.spritePipeline, { tone: [ 0.0, 0.0, 0.0, 0.0 ], hasShadow: false, teraColor: getTypeRgb(this.pokemon.getTeraType()) });
sprite.pipelineData['ignoreTimeTint'] = true;
sprite.setPipelineData('ignoreTimeTint', true);
sprite.setPipelineData('spriteKey', this.pokemon.getSpriteKey());
sprite.setPipelineData('shiny', this.pokemon.shiny);
sprite.setPipelineData('variant', this.pokemon.variant);
[ 'spriteColors', 'fusionSpriteColors' ].map(k => {
if (this.pokemon.summonData?.speciesForm)
k += 'Base';

View File

@ -1832,7 +1832,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false;
break;
case StatusEffect.FREEZE:
if (this.isOfType(Type.ICE))
if (this.isOfType(Type.ICE) || [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(this.scene?.arena.weather?.weatherType))
return false;
break;
case StatusEffect.BURN:

View File

@ -0,0 +1,8 @@
export const pokeball = {
"pokeBall": "Poké Ball",
"greatBall": "Great Ball",
"ultraBall": "Ultra Ball",
"rogueBall": "Rogue Ball",
"masterBall": "Master Ball",
"luxuryBall": "Luxury Ball",
} as const;

View File

@ -1443,7 +1443,7 @@ export const move: MoveTranslations = {
},
"healingWish": {
name: "Voeu Soin",
effect: "Un soin qui permet au lanceur de récupérer jusquà la moitié de ses PV max."
effect: "Le lanceur tombe K.O. pour soigner les altérations de statut et les PV du Pokémon qui viendra le remplacer sur le terrain."
},
"brine": {
name: "Saumure",

View File

@ -0,0 +1,8 @@
export const pokeball = {
"pokeBall": "Poké Ball",
"greatBall": "Super Ball",
"ultraBall": "Hyper Ball",
"rogueBall": "Rogue Ball",
"masterBall": "Master Ball",
"luxuryBall": "Luxe Ball",
} as const;

View File

@ -19,6 +19,7 @@ import { VoucherType, getVoucherTypeIcon, getVoucherTypeName } from '../system/v
import { FormChangeItem, SpeciesFormChangeItemTrigger, pokemonFormChanges } from '../data/pokemon-forms';
import { ModifierTier } from './modifier-tier';
import { Nature, getNatureName, getNatureStatMultiplier } from '#app/data/nature';
import { Localizable } from '#app/plugins/i18n';
const outputModifierData = false;
const useMaxWeightForOutput = false;
@ -131,10 +132,19 @@ export interface GeneratedPersistentModifierType {
getPregenArgs(): any[];
}
class AddPokeballModifierType extends ModifierType {
class AddPokeballModifierType extends ModifierType implements Localizable {
private pokeballType: PokeballType;
private count: integer;
constructor(pokeballType: PokeballType, count: integer, iconImage?: string) {
super(`${count}x ${getPokeballName(pokeballType)}`, `Receive ${getPokeballName(pokeballType)} x${count}\nCatch Rate: ${getPokeballCatchMultiplier(pokeballType) > -1 ? `${getPokeballCatchMultiplier(pokeballType)}x` : 'Certain'}`,
(_type, _args) => new Modifiers.AddPokeballModifier(this, pokeballType, count), iconImage, 'pb', 'pb_bounce_1');
super('', '', (_type, _args) => new Modifiers.AddPokeballModifier(this, pokeballType, count), iconImage, 'pb', 'pb_bounce_1');
this.pokeballType = pokeballType;
this.count = count;
}
localize() {
this.name = `${this.count}x ${getPokeballName(this.pokeballType)}`;
this.description = `Receive ${getPokeballName(this.pokeballType)} x${this.count}\nCatch Rate: ${getPokeballCatchMultiplier(this.pokeballType) > -1 ? `${getPokeballCatchMultiplier(this.pokeballType)}x` : 'Certain'}`;
}
}

View File

@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type";
import { CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs } from "./data/ability";
import { CheckTrappedAbAttr, MoveAbilityBypassAbAttr, IgnoreOpponentStatChangesAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, BattleStatMultiplierAbAttr, applyBattleStatMultiplierAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, applyPostBattleInitAbAttrs, PostBattleInitAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -379,6 +379,33 @@ export class UnavailablePhase extends Phase {
}
}
export class ReloadSessionPhase extends Phase {
constructor(scene: BattleScene) {
super(scene);
}
start(): void {
this.scene.ui.setMode(Mode.SESSION_RELOAD);
let delayElapsed = false;
let loaded = false;
this.scene.time.delayedCall(Utils.fixedInt(1500), () => {
if (loaded)
this.end();
else
delayElapsed = true;
});
this.scene.gameData.loadSystem().then(() => {
if (delayElapsed)
this.end();
else
loaded = true;
});
}
}
export class OutdatedPhase extends Phase {
constructor(scene: BattleScene) {
super(scene);
@ -767,6 +794,7 @@ export class EncounterPhase extends BattlePhase {
pokemon.resetBattleData();
}
if (!this.loaded)
this.scene.arena.trySetWeather(getRandomWeatherType(this.scene.arena), false);
const enemyField = this.scene.getEnemyField();
@ -2173,7 +2201,7 @@ export class MovePhase extends BattlePhase {
const targets = this.scene.getField(true).filter(p => {
if (this.targets.indexOf(p.getBattlerIndex()) > -1) {
const hiddenTag = p.getTag(HiddenTag);
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length && !p.hasAbilityWithAttr(AlwaysHitAbAttr) && !this.pokemon.hasAbilityWithAttr(AlwaysHitAbAttr))
return false;
return true;
}
@ -2467,6 +2495,9 @@ export class MoveEffectPhase extends PokemonPhase {
if (user.turnData.hitsLeft < user.turnData.hitCount)
return true;
if (user.hasAbilityWithAttr(AlwaysHitAbAttr) || target.hasAbilityWithAttr(AlwaysHitAbAttr))
return true;
const hiddenTag = target.getTag(HiddenTag);
if (hiddenTag && !this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
return false;
@ -3389,6 +3420,8 @@ export class GameOverPhase extends BattlePhase {
handleClearSession(): void {
this.scene.gameData.tryClearSession(this.scene, this.scene.sessionSlotId).then((success: boolean | [boolean, boolean]) => {
if (!success[0])
return this.scene.reset(true);
this.scene.time.delayedCall(1000, () => {
let firstClear = false;
if (this.victory && success[1]) {
@ -4296,7 +4329,7 @@ export class EggLapsePhase extends Phase {
const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => {
return --egg.hatchWaves < 1
})
});
if (eggsToHatch.length) {
this.scene.queueMessage('Oh?');

View File

@ -6,6 +6,9 @@ import { menu as frMenu } from '../locales/fr/menu';
import { move as enMove } from '../locales/en/move';
import { move as frMove } from '../locales/fr/move';
import { pokeball as enPokeball } from '../locales/en/pokeball';
import { pokeball as frPokeball } from '../locales/fr/pokeball';
export interface MoveTranslationEntry {
name: string,
effect: string
@ -50,6 +53,7 @@ export function initI18n(): void {
en: {
menu: enMenu,
move: enMove,
pokeball: enPokeball,
},
it: {
menu: itMenu,
@ -57,6 +61,7 @@ export function initI18n(): void {
fr: {
menu: frMenu,
move: frMove,
pokeball: frPokeball,
}
},
});
@ -68,6 +73,7 @@ declare module 'i18next' {
resources: {
menu: typeof enMenu;
move: typeof enMove;
pokeball: typeof enPokeball;
};
}
}

View File

@ -1,7 +1,7 @@
import BattleScene, { PokeballCounts, bypassLogin } from "../battle-scene";
import Pokemon, { EnemyPokemon, PlayerPokemon } from "../field/pokemon";
import { pokemonEvolutions, pokemonPrevolutions } from "../data/pokemon-evolutions";
import PokemonSpecies, { SpeciesFormKey, allSpecies, getPokemonSpecies, noStarterFormKeys, speciesStarters } from "../data/pokemon-species";
import PokemonSpecies, { allSpecies, getPokemonSpecies, noStarterFormKeys, speciesStarters } from "../data/pokemon-species";
import { Species, defaultStarterSpecies } from "../data/enums/species";
import * as Utils from "../utils";
import PokemonData from "./pokemon-data";
@ -27,7 +27,7 @@ import { Moves } from "../data/enums/moves";
import { speciesEggMoves } from "../data/egg-moves";
import { allMoves } from "../data/move";
import { TrainerVariant } from "../field/trainer";
import { OutdatedPhase, UnavailablePhase } from "#app/phases";
import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
import { Variant, variantData } from "#app/data/variant";
const saveKey = 'x0i2O7WRiANTqPmZ'; // Temporary; secure encryption is not yet necessary
@ -280,6 +280,9 @@ export class GameData {
if (error.startsWith('client version out of date')) {
this.scene.clearPhaseQueue();
this.scene.unshiftPhase(new OutdatedPhase(this.scene));
} else if (error.startsWith('session out of date')) {
this.scene.clearPhaseQueue();
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
}
console.error(error);
return resolve(false);
@ -340,7 +343,7 @@ export class GameData {
this.starterData[s].eggMoves = starterEggMoveData[s];
}
this.migrateStarterAbilities(systemData);
this.migrateStarterAbilities(systemData, this.starterData);
} else {
if ([ '1.0.0', '1.0.1' ].includes(systemData.gameVersion))
this.migrateStarterAbilities(systemData);
@ -551,6 +554,10 @@ export class GameData {
.then(response => response.text())
.then(error => {
if (error) {
if (error.startsWith('session out of date')) {
this.scene.clearPhaseQueue();
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
}
console.error(error);
return resolve(false);
}
@ -715,9 +722,19 @@ export class GameData {
Utils.apiFetch(`savedata/delete?datatype=${GameDataType.SESSION}&slot=${slotId}`, true).then(response => {
if (response.ok) {
loggedInUser.lastSessionSlot = -1;
return resolve(true);
resolve(true);
}
return response.text();
}).then(error => {
if (error) {
if (error.startsWith('session out of date')) {
this.scene.clearPhaseQueue();
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
}
console.error(error);
resolve(false);
}
resolve(true);
});
});
});
@ -735,12 +752,19 @@ export class GameData {
return resolve([false, false]);
const sessionData = this.getSessionSaveData(scene);
Utils.apiPost(`savedata/clear?slot=${slotId}`, JSON.stringify(sessionData)).then(response => {
if (response.ok) {
if (response.ok)
loggedInUser.lastSessionSlot = -1;
return response.json();
}).then(jsonResponse => {
if (!jsonResponse.error)
return resolve([true, jsonResponse.success as boolean]);
if (jsonResponse && jsonResponse.error.startsWith('session out of date')) {
this.scene.clearPhaseQueue();
this.scene.unshiftPhase(new ReloadSessionPhase(this.scene));
}
console.error(jsonResponse);
resolve([false, false]);
}).then(jsonResponse => resolve([true, jsonResponse.success as boolean]));
});
});
});
}
@ -1207,9 +1231,9 @@ export class GameData {
}
}
migrateStarterAbilities(systemData: SystemSaveData): void {
migrateStarterAbilities(systemData: SystemSaveData, initialStarterData?: StarterData): void {
const starterIds = Object.keys(this.starterData).map(s => parseInt(s) as Species);
const starterData = systemData.starterData;
const starterData = initialStarterData || systemData.starterData;
const dexData = systemData.dexData;
for (let s of starterIds) {
const dexAttr = dexData[s].caughtAttr;

View File

@ -336,10 +336,14 @@ export default class MenuUiHandler extends MessageUiHandler {
case Button.UP:
if (this.cursor)
success = this.setCursor(this.cursor - 1);
else
success = this.setCursor(this.menuOptions.length - 1);
break;
case Button.DOWN:
if (this.cursor + 1 < this.menuOptions.length)
success = this.setCursor(this.cursor + 1);
else
success = this.setCursor(0);
break;
}
}

View File

@ -0,0 +1,47 @@
import BattleScene from "../battle-scene";
import { ModalConfig, ModalUiHandler } from "./modal-ui-handler";
import { addTextObject, TextStyle } from "./text";
import { Mode } from "./ui";
export default class SessionReloadModalUiHandler extends ModalUiHandler {
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
}
getModalTitle(): string {
return '';
}
getWidth(): number {
return 160;
}
getHeight(): number {
return 32;
}
getMargin(): [number, number, number, number] {
return [ 0, 0, 48, 0 ];
}
getButtonLabels(): string[] {
return [ ];
}
setup(): void {
super.setup();
const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Your session is out of date.\nYour data will be reloaded…', TextStyle.WINDOW, { fontSize: '48px', align: 'center' });
label.setOrigin(0.5, 0.5);
this.modalContainer.add(label);
}
show(args: any[]): boolean {
const config: ModalConfig = {
buttonActions: []
};
return super.show([ config ]);
}
}

View File

@ -34,6 +34,7 @@ import TitleUiHandler from './title-ui-handler';
import SavingIconHandler from './saving-icon-handler';
import UnavailableModalUiHandler from './unavailable-modal-ui-handler';
import OutdatedModalUiHandler from './outdated-modal-ui-handler';
import SessionReloadModalUiHandler from './session-reload-modal-ui-handler';
export enum Mode {
MESSAGE,
@ -62,6 +63,7 @@ export enum Mode {
LOGIN_FORM,
REGISTRATION_FORM,
LOADING,
SESSION_RELOAD,
UNAVAILABLE,
OUTDATED
};
@ -90,6 +92,7 @@ const noTransitionModes = [
Mode.LOGIN_FORM,
Mode.REGISTRATION_FORM,
Mode.LOADING,
Mode.SESSION_RELOAD,
Mode.UNAVAILABLE,
Mode.OUTDATED
];
@ -141,6 +144,7 @@ export default class UI extends Phaser.GameObjects.Container {
new LoginFormUiHandler(scene),
new RegistrationFormUiHandler(scene),
new LoadingModalUiHandler(scene),
new SessionReloadModalUiHandler(scene),
new UnavailableModalUiHandler(scene),
new OutdatedModalUiHandler(scene)
];