mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-05 16:02:20 +02:00
Compare commits
6 Commits
7ae09a31a5
...
e66d7026d5
Author | SHA1 | Date | |
---|---|---|---|
|
e66d7026d5 | ||
|
b72b432ce3 | ||
|
99557b318f | ||
|
aed9b56a7b | ||
|
ecc17c5342 | ||
|
cd7e818a01 |
@ -93,10 +93,8 @@ export default class Battle {
|
||||
|
||||
private initBattleSpec(): void {
|
||||
let spec = BattleSpec.DEFAULT;
|
||||
if (this.gameMode.isClassic) {
|
||||
if (this.waveIndex === 200)
|
||||
if (this.gameMode.isWaveFinal(this.waveIndex) && this.gameMode.isClassic)
|
||||
spec = BattleSpec.FINAL_BOSS;
|
||||
}
|
||||
this.battleSpec = spec;
|
||||
}
|
||||
|
||||
@ -105,7 +103,7 @@ export default class Battle {
|
||||
let baseLevel = 1 + levelWaveIndex / 2 + Math.pow(levelWaveIndex / 25, 2);
|
||||
const bossMultiplier = 1.2;
|
||||
|
||||
if (!(this.waveIndex % 10)) {
|
||||
if (this.gameMode.isBoss(this.waveIndex)) {
|
||||
const ret = Math.floor(baseLevel * bossMultiplier);
|
||||
if (this.battleSpec === BattleSpec.FINAL_BOSS || !(this.waveIndex % 250))
|
||||
return Math.ceil(ret / 25) * 25;
|
||||
|
@ -13,7 +13,7 @@ import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, Stat
|
||||
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
|
||||
import { ArenaTagType } from "./enums/arena-tag-type";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
||||
import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier";
|
||||
import { Moves } from "./enums/moves";
|
||||
import { TerrainType } from "./terrain";
|
||||
import { SpeciesFormChangeManualTrigger } from "./pokemon-forms";
|
||||
@ -23,6 +23,7 @@ import { Command } from "../ui/command-ui-handler";
|
||||
import Battle from "#app/battle.js";
|
||||
import { ability } from "#app/locales/en/ability.js";
|
||||
import { PokeballType, getPokeballName } from "./pokeball";
|
||||
import { BerryModifierType } from "#app/modifier/modifier-type";
|
||||
|
||||
export class Ability implements Localizable {
|
||||
public id: Abilities;
|
||||
@ -127,7 +128,7 @@ export abstract class AbAttr {
|
||||
return null;
|
||||
}
|
||||
|
||||
getCondition(): AbAttrCondition {
|
||||
getCondition(): AbAttrCondition | null {
|
||||
return this.extraCondition || null;
|
||||
}
|
||||
|
||||
@ -2228,6 +2229,71 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* After the turn ends, try to create an extra item
|
||||
*/
|
||||
export class PostTurnLootAbAttr extends PostTurnAbAttr {
|
||||
/**
|
||||
* @param itemType - The type of item to create
|
||||
* @param procChance - Chance to create an item
|
||||
* @see {@linkcode applyPostTurn()}
|
||||
*/
|
||||
constructor(
|
||||
/** Extend itemType to add more options */
|
||||
private itemType: "EATEN_BERRIES" | "HELD_BERRIES",
|
||||
private procChance: (pokemon: Pokemon) => number
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
|
||||
const pass = Phaser.Math.RND.realInRange(0, 1);
|
||||
// Clamp procChance to [0, 1]. Skip if didn't proc (less than pass)
|
||||
if (Math.max(Math.min(this.procChance(pokemon), 1), 0) < pass) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.itemType === "EATEN_BERRIES") {
|
||||
return this.createEatenBerry(pokemon);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new berry chosen randomly from the berries the pokemon ate this battle
|
||||
* @param pokemon The pokemon with this ability
|
||||
* @returns whether a new berry was created
|
||||
*/
|
||||
createEatenBerry(pokemon: Pokemon): boolean {
|
||||
const berriesEaten = pokemon.battleData.berriesEaten;
|
||||
|
||||
if (!berriesEaten.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const randomIdx = Utils.randSeedInt(berriesEaten.length);
|
||||
const chosenBerry = new BerryModifierType(berriesEaten[randomIdx]);
|
||||
berriesEaten.splice(randomIdx) // Remove berry from memory
|
||||
|
||||
const berryModifier = pokemon.scene.findModifier(
|
||||
(m) => m instanceof BerryModifier && m.berryType === berriesEaten[randomIdx],
|
||||
pokemon.isPlayer()
|
||||
) as BerryModifier | undefined;
|
||||
|
||||
if (!berryModifier) {
|
||||
pokemon.scene.addModifier(new BerryModifier(chosenBerry, pokemon.id, berriesEaten[randomIdx], 1));
|
||||
} else {
|
||||
berryModifier.stackCount++;
|
||||
}
|
||||
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` harvested one ${chosenBerry.name}!`));
|
||||
pokemon.scene.updateModifiers(pokemon.isPlayer());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class MoodyAbAttr extends PostTurnAbAttr {
|
||||
constructor() {
|
||||
super(true);
|
||||
@ -3418,7 +3484,13 @@ export function initAbilities() {
|
||||
new Ability(Abilities.FLARE_BOOST, 5)
|
||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user.status?.effect === StatusEffect.BURN, 1.5),
|
||||
new Ability(Abilities.HARVEST, 5)
|
||||
.unimplemented(),
|
||||
.attr(
|
||||
PostTurnLootAbAttr,
|
||||
"EATEN_BERRIES",
|
||||
/** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */
|
||||
(pokemon) => 0.5 * (getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)(pokemon) ? 2 : 1)
|
||||
)
|
||||
.partial(),
|
||||
new Ability(Abilities.TELEPATHY, 5)
|
||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move.getMove() instanceof AttackMove)
|
||||
.ignorable(),
|
||||
|
@ -4892,7 +4892,7 @@ export function initMoves() {
|
||||
.attr(StatusEffectAttr, StatusEffect.FREEZE),
|
||||
new AttackMove(Moves.BLIZZARD, Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 10, 0, 1)
|
||||
.attr(BlizzardAccuracyAttr)
|
||||
.attr(StatusEffectAttr, StatusEffect.FREEZE) // TODO: 30% chance to hit protect/detect in hail
|
||||
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
||||
.windMove()
|
||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||
new AttackMove(Moves.PSYBEAM, Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 10, 0, 1)
|
||||
|
@ -147,8 +147,14 @@ export class GameMode implements GameModeConfig {
|
||||
return null;
|
||||
}
|
||||
|
||||
isWaveFinal(waveIndex: integer): boolean {
|
||||
switch (this.modeId) {
|
||||
/**
|
||||
* Checks if wave provided is the final for current or specified game mode
|
||||
* @param waveIndex
|
||||
* @param modeId game mode
|
||||
* @returns if the current wave is final for classic or daily OR a minor boss in endless
|
||||
*/
|
||||
isWaveFinal(waveIndex: integer, modeId: GameModes = this.modeId): boolean {
|
||||
switch (modeId) {
|
||||
case GameModes.CLASSIC:
|
||||
return waveIndex === 200;
|
||||
case GameModes.ENDLESS:
|
||||
@ -159,6 +165,45 @@ export class GameMode implements GameModeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Every 10 waves is a boss battle
|
||||
* @returns true if waveIndex is a multiple of 10
|
||||
*/
|
||||
isBoss(waveIndex: integer): boolean {
|
||||
return waveIndex % 10 === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every 50 waves of an Endless mode is a boss
|
||||
* At this time it is paradox pokemon
|
||||
* @returns true if waveIndex is a multiple of 50 in Endless
|
||||
*/
|
||||
isEndlessBoss(waveIndex: integer): boolean {
|
||||
return waveIndex % 50 &&
|
||||
(this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every 250 waves of an Endless mode is a minor boss
|
||||
* At this time it is Eternatus
|
||||
* @returns true if waveIndex is a multiple of 250 in Endless
|
||||
*/
|
||||
isEndlessMinorBoss(waveIndex: integer): boolean {
|
||||
return waveIndex % 250 === 0 &&
|
||||
(this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every 1000 waves of an Endless mode is a major boss
|
||||
* At this time it is Eternamax Eternatus
|
||||
* @returns true if waveIndex is a multiple of 1000 in Endless
|
||||
*/
|
||||
isEndlessMajorBoss(waveIndex: integer): boolean {
|
||||
return waveIndex % 1000 === 0 &&
|
||||
(this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS);
|
||||
}
|
||||
|
||||
|
||||
getClearScoreBonus(): integer {
|
||||
switch (this.modeId) {
|
||||
case GameModes.CLASSIC:
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||
|
||||
export const voucher: SimpleTranslationEntries = {
|
||||
"vouchers": "Vouchers",
|
||||
"eggVoucher": "Egg Voucher",
|
||||
"eggVoucherPlus": "Egg Voucher Plus",
|
||||
"eggVoucherPremium": "Egg Voucher Premium",
|
||||
"eggVoucherGold": "Egg Voucher Gold",
|
||||
"locked": "Locked",
|
||||
"defeatTrainer": "Defeat {{trainerName}}"
|
||||
"vouchers": "Gutschein",
|
||||
"eggVoucher": "Ei-Gutschein",
|
||||
"eggVoucherPlus": "Ei-Gutschein Plus",
|
||||
"eggVoucherPremium": "Ei-Gutschein Premium",
|
||||
"eggVoucherGold": "Ei-Gutschein Gold",
|
||||
"locked": "Gesperrt",
|
||||
"defeatTrainer": "Besiege {{trainerName}}"
|
||||
} as const;
|
@ -1,10 +1,10 @@
|
||||
import { SimpleTranslationEntries } from "#app/plugins/i18n";
|
||||
|
||||
export const battleMessageUiHandler: SimpleTranslationEntries = {
|
||||
"ivBest": "Best",
|
||||
"ivFantastic": "Fantastic",
|
||||
"ivVeryGood": "Very Good",
|
||||
"ivPrettyGood": "Pretty Good",
|
||||
"ivDecent": "Decent",
|
||||
"ivNoGood": "No Good",
|
||||
"ivBest": "Inmejorable",
|
||||
"ivFantastic": "Fantástico",
|
||||
"ivVeryGood": "Notable",
|
||||
"ivPrettyGood": "Genial",
|
||||
"ivDecent": "No está mal",
|
||||
"ivNoGood": "Cojea un poco",
|
||||
} as const;
|
@ -2,47 +2,47 @@ import { BerryTranslationEntries } from "#app/plugins/i18n";
|
||||
|
||||
export const berry: BerryTranslationEntries = {
|
||||
"SITRUS": {
|
||||
name: "Sitrus Berry",
|
||||
effect: "Restores 25% HP if HP is below 50%",
|
||||
name: "Baya Zidra",
|
||||
effect: "Restaura 25% PS si estos caen por debajo del 50%",
|
||||
},
|
||||
"LUM": {
|
||||
name: "Lum Berry",
|
||||
effect: "Cures any non-volatile status condition and confusion",
|
||||
name: "Baya Ziuela",
|
||||
effect: "Cura cualquier problema de estado",
|
||||
},
|
||||
"ENIGMA": {
|
||||
name: "Enigma Berry",
|
||||
effect: "Restores 25% HP if hit by a super effective move",
|
||||
name: "Baya Enigma",
|
||||
effect: "Restaura 25% PS si le alcanza un ataque supereficaz",
|
||||
},
|
||||
"LIECHI": {
|
||||
name: "Liechi Berry",
|
||||
effect: "Raises Attack if HP is below 25%",
|
||||
name: "Baya Lichi",
|
||||
effect: "Aumenta el ataque si los PS están por debajo de 25%",
|
||||
},
|
||||
"GANLON": {
|
||||
name: "Ganlon Berry",
|
||||
effect: "Raises Defense if HP is below 25%",
|
||||
name: "Baya Gonlan",
|
||||
effect: "Aumenta la defensa si los PS están por debajo de 25%",
|
||||
},
|
||||
"PETAYA": {
|
||||
name: "Petaya Berry",
|
||||
effect: "Raises Sp. Atk if HP is below 25%",
|
||||
name: "Baya Yapati",
|
||||
effect: "Aumenta el ataque especial si los PS están por debajo de 25%",
|
||||
},
|
||||
"APICOT": {
|
||||
name: "Apicot Berry",
|
||||
effect: "Raises Sp. Def if HP is below 25%",
|
||||
name: "Baya Aricoc",
|
||||
effect: "Aumenta la defensa especial si los PS están por debajo de 25%",
|
||||
},
|
||||
"SALAC": {
|
||||
name: "Salac Berry",
|
||||
effect: "Raises Speed if HP is below 25%",
|
||||
name: "Baya Aslac",
|
||||
effect: "Aumenta la velocidad si los PS están por debajo de 25%",
|
||||
},
|
||||
"LANSAT": {
|
||||
name: "Lansat Berry",
|
||||
effect: "Raises critical hit ratio if HP is below 25%",
|
||||
name: "Baya Zonlan",
|
||||
effect: "Aumenta el índice de golpe crítico si los PS están por debajo de 25%",
|
||||
},
|
||||
"STARF": {
|
||||
name: "Starf Berry",
|
||||
effect: "Sharply raises a random stat if HP is below 25%",
|
||||
name: "Baya Arabol",
|
||||
effect: "Aumenta mucho una estadística al azar si los PS están por debajo de 25%",
|
||||
},
|
||||
"LEPPA": {
|
||||
name: "Leppa Berry",
|
||||
effect: "Restores 10 PP to a move if its PP reaches 0",
|
||||
name: "Baya Zanama",
|
||||
effect: "Restaura 10 PP del primer movimiento cuyos PP bajen a 0",
|
||||
},
|
||||
} as const;
|
@ -9,7 +9,7 @@ export const menuUiHandler: SimpleTranslationEntries = {
|
||||
"EGG_GACHA": "Gacha de Huevos",
|
||||
"MANAGE_DATA": "Gestionar Datos",
|
||||
"COMMUNITY": "Comunidad",
|
||||
"SAVE_AND_QUIT": "Save and Quit",
|
||||
"SAVE_AND_QUIT": "Guardar y Salir",
|
||||
"LOG_OUT": "Cerrar Sesión",
|
||||
"slot": "Ranura {{slotNumber}}",
|
||||
"importSession": "Importar Sesión",
|
||||
|
@ -2,8 +2,8 @@ import { PokemonInfoTranslationEntries } from "#app/plugins/i18n";
|
||||
|
||||
export const pokemonInfo: PokemonInfoTranslationEntries = {
|
||||
Stat: {
|
||||
"HP": "PV",
|
||||
"HPshortened": "PV",
|
||||
"HP": "PS",
|
||||
"HPshortened": "PS",
|
||||
"ATK": "Ataque",
|
||||
"ATKshortened": "Ata",
|
||||
"DEF": "Defensa",
|
||||
@ -17,25 +17,25 @@ export const pokemonInfo: PokemonInfoTranslationEntries = {
|
||||
},
|
||||
|
||||
Type: {
|
||||
"UNKNOWN": "Unknown",
|
||||
"UNKNOWN": "Desconocido",
|
||||
"NORMAL": "Normal",
|
||||
"FIGHTING": "Fighting",
|
||||
"FLYING": "Flying",
|
||||
"POISON": "Poison",
|
||||
"GROUND": "Ground",
|
||||
"ROCK": "Rock",
|
||||
"BUG": "Bug",
|
||||
"GHOST": "Ghost",
|
||||
"STEEL": "Steel",
|
||||
"FIRE": "Fire",
|
||||
"WATER": "Water",
|
||||
"GRASS": "Grass",
|
||||
"ELECTRIC": "Electric",
|
||||
"PSYCHIC": "Psychic",
|
||||
"ICE": "Ice",
|
||||
"DRAGON": "Dragon",
|
||||
"DARK": "Dark",
|
||||
"FAIRY": "Fairy",
|
||||
"STELLAR": "Stellar",
|
||||
"FIGHTING": "Lucha",
|
||||
"FLYING": "Volador",
|
||||
"POISON": "Veneno",
|
||||
"GROUND": "Tierra",
|
||||
"ROCK": "Roca",
|
||||
"BUG": "Bicho",
|
||||
"GHOST": "Fantasma",
|
||||
"STEEL": "Acero",
|
||||
"FIRE": "Fuego",
|
||||
"WATER": "Agua",
|
||||
"GRASS": "Planta",
|
||||
"ELECTRIC": "Eléctrico",
|
||||
"PSYCHIC": "Psíquico",
|
||||
"ICE": "Hielo",
|
||||
"DRAGON": "Dragón",
|
||||
"DARK": "Siniestro",
|
||||
"FAIRY": "Hada",
|
||||
"STELLAR": "Astral",
|
||||
},
|
||||
} as const;
|
@ -2076,6 +2076,8 @@ export class TurnStartPhase extends FieldPhase {
|
||||
}
|
||||
}
|
||||
|
||||
this.scene.pushPhase(new BerryPhase(this.scene));
|
||||
|
||||
if (this.scene.arena.weather)
|
||||
this.scene.pushPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather));
|
||||
|
||||
@ -2090,6 +2092,42 @@ export class TurnStartPhase extends FieldPhase {
|
||||
}
|
||||
}
|
||||
|
||||
/** The phase after attacks where the pokemon eat berries */
|
||||
export class BerryPhase extends FieldPhase {
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
this.executeForAll((pokemon) => {
|
||||
const hasUsableBerry = !!this.scene.findModifier((m) => m instanceof BerryModifier && m.shouldApply([pokemon]), pokemon.isPlayer());
|
||||
|
||||
if (hasUsableBerry) {
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
|
||||
|
||||
if (cancelled.value) {
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, " is too\nnervous to eat berries!"));
|
||||
} else {
|
||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM));
|
||||
|
||||
for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon) as BerryModifier[]) {
|
||||
if (berryModifier.consumed) {
|
||||
if (!--berryModifier.stackCount) {
|
||||
this.scene.removeModifier(berryModifier);
|
||||
} else {
|
||||
berryModifier.consumed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.scene.updateModifiers(pokemon.isPlayer());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
export class TurnEndPhase extends FieldPhase {
|
||||
constructor(scene: BattleScene) {
|
||||
super(scene);
|
||||
@ -2108,10 +2146,6 @@ export class TurnEndPhase extends FieldPhase {
|
||||
pokemon.summonData.disabledMove = Moves.NONE;
|
||||
}
|
||||
|
||||
const hasUsableBerry = !!this.scene.findModifier(m => m instanceof BerryModifier && m.shouldApply([ pokemon ]), pokemon.isPlayer());
|
||||
if (hasUsableBerry)
|
||||
this.scene.unshiftPhase(new BerryPhase(this.scene, pokemon.getBattlerIndex()));
|
||||
|
||||
this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon);
|
||||
|
||||
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
|
||||
@ -4093,38 +4127,6 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
||||
}
|
||||
}
|
||||
|
||||
export class BerryPhase extends CommonAnimPhase {
|
||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex) {
|
||||
super(scene, battlerIndex, undefined, CommonAnim.USE_ITEM);
|
||||
}
|
||||
|
||||
start() {
|
||||
let berryModifiers: BerryModifier[];
|
||||
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
const cancelled = new Utils.BooleanHolder(false);
|
||||
pokemon.getOpponents().map(opp => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
|
||||
|
||||
if (cancelled.value)
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is too\nnervous to eat berries!'));
|
||||
else if ((berryModifiers = this.scene.applyModifiers(BerryModifier, this.player, pokemon) as BerryModifier[])) {
|
||||
for (let berryModifier of berryModifiers) {
|
||||
if (berryModifier.consumed) {
|
||||
if (!--berryModifier.stackCount)
|
||||
this.scene.removeModifier(berryModifier);
|
||||
else
|
||||
berryModifier.consumed = false;
|
||||
this.scene.updateModifiers(this.player);
|
||||
}
|
||||
}
|
||||
return super.start();
|
||||
}
|
||||
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
export class PokemonHealPhase extends CommonAnimPhase {
|
||||
private hpHealed: integer;
|
||||
private message: string;
|
||||
|
Loading…
Reference in New Issue
Block a user