Merge branch 'main' into main

This commit is contained in:
NxKarim 2024-04-22 02:36:46 -06:00 committed by GitHub
commit 5569571438
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 3441 additions and 1156 deletions

View File

@ -37,7 +37,7 @@ import SettingsUiHandler from './ui/settings-ui-handler';
import MessageUiHandler from './ui/message-ui-handler';
import { Species } from './data/enums/species';
import InvertPostFX from './pipelines/invert';
import { Achv, ModifierAchv, achvs } from './system/achv';
import { Achv, ModifierAchv, MoneyAchv, achvs } from './system/achv';
import { Voucher, vouchers } from './system/voucher';
import { Gender } from './data/gender';
import UIPlugin from 'phaser3-rex-plugins/templates/ui/ui-plugin';
@ -796,6 +796,7 @@ export default class BattleScene extends SceneBase {
if (reloadI18n) {
const localizable: Localizable[] = [
...allSpecies,
...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()
];
@ -1679,6 +1680,12 @@ export default class BattleScene extends SceneBase {
this.phaseQueue.push(new TurnInitPhase(this));
}
addMoney(amount: integer): void {
this.money = Math.min(this.money + amount, Number.MAX_SAFE_INTEGER);
this.updateMoneyText();
this.validateAchvs(MoneyAchv);
}
getWaveMoneyAmount(moneyMultiplier: number): integer {
const waveIndex = this.currentBattle.waveIndex;
const waveSetIndex = Math.ceil(waveIndex / 10) - 1;

View File

@ -157,10 +157,8 @@ export default class Battle {
const moneyAmount = new Utils.IntegerHolder(scene.currentBattle.moneyScattered);
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
scene.money += moneyAmount.value;
scene.updateMoneyText();
scene.validateAchvs(MoneyAchv);
scene.addMoney(moneyAmount.value);
scene.queueMessage(`You picked up ₽${moneyAmount.value.toLocaleString('en-US')}!`, null, true);
scene.currentBattle.moneyScattered = 0;

View File

@ -9,7 +9,8 @@ import { BattlerTag } from "./battler-tags";
import { BattlerTagType } from "./enums/battler-tag-type";
import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, allMoves } from "./move";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves } from "./move";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { Stat } from "./pokemon-stat";
import { PokemonHeldItemModifier } from "../modifier/modifier";
@ -541,6 +542,16 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr {
}
}
export class ReverseDrainAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (!!move.getMove().getAttrs(HitHealAttr).length || !!move.getMove().getAttrs(StrengthSapHealAttr).length ) {
pokemon.scene.queueMessage(getPokemonMessage(attacker, ` sucked up the liquid ooze!`));
return true;
}
return false;
}
}
export class PostDefendStatChangeAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition;
private stat: BattleStat;
@ -566,6 +577,29 @@ export class PostDefendStatChangeAbAttr extends PostDefendAbAttr {
}
}
export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition;
private tagType: ArenaTagType;
constructor(condition: PokemonDefendCondition, tagType: ArenaTagType) {
super(true);
this.condition = condition;
this.tagType = tagType;
}
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) {
const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag;
if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) {
pokemon.scene.arena.addTag(this.tagType, 0, undefined, pokemon.id, pokemon.isPlayer() ? ArenaTagSide.ENEMY : ArenaTagSide.PLAYER);
return true;
}
}
return false;
}
}
export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr {
private condition: PokemonDefendCondition;
private tagType: BattlerTagType;
@ -1611,6 +1645,43 @@ function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition {
};
}
function getAnticipationCondition(): AbAttrCondition {
return (pokemon: Pokemon) => {
for (let opponent of pokemon.getOpponents()) {
for (let move of opponent.moveset) {
// move is super effective
if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type) >= 2) {
return true;
}
// move is a OHKO
if (move.getMove().findAttr(attr => attr instanceof OneHitKOAttr)) {
return true;
}
// edge case for hidden power, type is computed
if (move.getMove().id === Moves.HIDDEN_POWER) {
const iv_val = Math.floor(((opponent.ivs[Stat.HP] & 1)
+(opponent.ivs[Stat.ATK] & 1) * 2
+(opponent.ivs[Stat.DEF] & 1) * 4
+(opponent.ivs[Stat.SPD] & 1) * 8
+(opponent.ivs[Stat.SPATK] & 1) * 16
+(opponent.ivs[Stat.SPDEF] & 1) * 32) * 15/63);
const type = [
Type.FIGHTING, Type.FLYING, Type.POISON, Type.GROUND,
Type.ROCK, Type.BUG, Type.GHOST, Type.STEEL,
Type.FIRE, Type.WATER, Type.GRASS, Type.ELECTRIC,
Type.PSYCHIC, Type.ICE, Type.DRAGON, Type.DARK][iv_val];
if (pokemon.getAttackTypeEffectiveness(type) >= 2) {
return true;
}
}
}
}
return false;
};
}
export class PostWeatherChangeAbAttr extends AbAttr {
applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean {
return false;
@ -2530,7 +2601,8 @@ export function initAbilities() {
new Ability(Abilities.MARVEL_SCALE, "Marvel Scale", "The Pokémon's marvelous scales boost the Defense stat if it has a status condition.", 3)
.conditionalAttr(pokemon => !!pokemon.status, BattleStatMultiplierAbAttr, BattleStat.DEF, 1.5)
.ignorable(),
new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze (N)", "The oozed liquid has a strong stench, which damages attackers using any draining move.", 3),
new Ability(Abilities.LIQUID_OOZE, "Liquid Ooze", "The oozed liquid has a strong stench, which damages attackers using any draining move.", 3)
.attr(ReverseDrainAbAttr),
new Ability(Abilities.OVERGROW, "Overgrow", "Powers up Grass-type moves when the Pokémon's HP is low.", 3)
.attr(LowHpMoveTypePowerBoostAbAttr, Type.GRASS),
new Ability(Abilities.BLAZE, "Blaze", "Powers up Fire-type moves when the Pokémon's HP is low.", 3)
@ -2637,7 +2709,8 @@ export function initAbilities() {
new Ability(Abilities.AFTERMATH, "Aftermath", "Damages the attacker if it contacts the Pokémon with a finishing hit.", 4)
.attr(PostFaintContactDamageAbAttr,4)
.bypassFaint(),
new Ability(Abilities.ANTICIPATION, "Anticipation (N)", "The Pokémon can sense an opposing Pokémon's dangerous moves.", 4),
new Ability(Abilities.ANTICIPATION, "Anticipation", "The Pokémon can sense an opposing Pokémon's dangerous moves.", 4)
.conditionalAttr(getAnticipationCondition(), PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' shuddered!')),
new Ability(Abilities.FOREWARN, "Forewarn (N)", "When it enters a battle, the Pokémon can tell one of the moves an opposing Pokémon has.", 4),
new Ability(Abilities.UNAWARE, "Unaware", "When attacking, the Pokémon ignores the target Pokémon's stat changes.", 4)
.attr(IgnoreOpponentStatChangesAbAttr)
@ -3001,7 +3074,7 @@ export function initAbilities() {
.attr(NoFusionAbilityAbAttr),
new Ability(Abilities.STALWART, "Stalwart (N)", "Ignores the effects of opposing Pokémon's Abilities and moves that draw in moves.", 8),
new Ability(Abilities.STEAM_ENGINE, "Steam Engine", "Boosts the Pokémon's Speed stat drastically if hit by a Fire- or Water-type move.", 8)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.FIRE || move.type === Type.WATER, BattleStat.SPD, 6),
.attr(PostDefendStatChangeAbAttr, (target, user, move) => (move.type === Type.FIRE || move.type === Type.WATER) && move.category !== MoveCategory.STATUS, BattleStat.SPD, 6),
new Ability(Abilities.PUNK_ROCK, "Punk Rock", "Boosts the power of sound-based moves. The Pokémon also takes half the damage from these kinds of moves.", 8)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SOUND_BASED), 1.3)
.attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5)
@ -3141,7 +3214,9 @@ export function initAbilities() {
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5),
new Ability(Abilities.SUPREME_OVERLORD, "Supreme Overlord (N)", "When the Pokémon enters a battle, its Attack and Sp. Atk stats are slightly boosted for each of the allies in its party that have already been defeated.", 9),
new Ability(Abilities.COSTAR, "Costar (N)", "When the Pokémon enters a battle, it copies an ally's stat changes.", 9),
new Ability(Abilities.TOXIC_DEBRIS, "Toxic Debris (N)", "Scatters poison spikes at the feet of the opposing team when the Pokémon takes damage from physical moves.", 9),
new Ability(Abilities.TOXIC_DEBRIS, "Toxic Debris", "Scatters poison spikes at the feet of the opposing team when the Pokémon takes damage from physical moves.", 9)
.attr(PostDefendApplyArenaTrapTagAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL, ArenaTagType.TOXIC_SPIKES)
.bypassFaint(),
new Ability(Abilities.ARMOR_TAIL, "Armor Tail", "The mysterious tail covering the Pokémon's head makes opponents unable to use priority moves against the Pokémon or its allies.", 9)
.attr(FieldPriorityMoveImmunityAbAttr)
.ignorable(),

View File

@ -249,7 +249,7 @@ export async function printPokemon() {
Math.max(abilities.indexOf(pokemon.abilities.find(a => a.slot === 3)?.ability.name), 0)
];
const pokemonSpecies = new PokemonSpecies(dexId, species.names.find(n => n.language.name === 'en').name, generationIndex, species.is_legendary && baseTotal < 660, species.is_legendary && baseTotal >= 660, species.is_mythical,
const pokemonSpecies = new PokemonSpecies(dexId, generationIndex, species.is_legendary && baseTotal < 660, species.is_legendary && baseTotal >= 660, species.is_mythical,
species.genera.find(g => g.language.name === 'en')?.genus, type1 as Type, type2 > -1 ? type2 as Type : null, pokemon.height / 10, pokemon.weight / 10, ability1 as Abilities, ability2 as Abilities, abilityHidden as Abilities,
baseTotal, baseStats[0], baseStats[1], baseStats[2], baseStats[3], baseStats[4], baseStats[5], species.capture_rate, species.base_happiness, pokemon.base_experience, growthRateMap[species.growth_rate.name],
species.gender_rate < 9 ? 100 - (species.gender_rate * 12.5) : null, species.has_gender_differences, species.forms_switchable);

View File

@ -8,7 +8,8 @@ import * as Utils from "../utils";
import { Moves } from "./enums/moves";
import { ChargeAttr, MoveFlags, allMoves } from "./move";
import { Type } from "./type";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, applyAbAttrs, DisguiseConfusionDamageInteractionAbAttr } from "./ability";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr,ReverseDrainAbAttr, DisguiseConfusionDamageInteractionAbAttr, applyAbAttrs } from "./ability";
import { Abilities } from "./enums/abilities";
import { BattlerTagType } from "./enums/battler-tag-type";
import { TerrainType } from "./terrain";
@ -299,7 +300,11 @@ export class SeedTag extends BattlerTag {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED));
const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(), damage, getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!'), false, true));
const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(),
!reverseDrain ? damage : damage * -1,
!reverseDrain ? getPokemonMessage(pokemon, '\'s health is\nsapped by Leech Seed!') : getPokemonMessage(source, '\'s Leech Seed\nsucked up the liquid ooze!'),
false, true));
}
}
}

View File

@ -12,7 +12,7 @@ import * as Utils from "../utils";
import { WeatherType } from "./weather";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr } from "./ability";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr } from "./ability";
import { Abilities } from "./enums/abilities";
import { allAbilities } from './ability';
import { PokemonHeldItemModifier } from "../modifier/modifier";
@ -96,9 +96,6 @@ export default class Move implements Localizable {
constructor(id: Moves, type: Type, category: MoveCategory, defaultMoveTarget: MoveTarget, power: integer, accuracy: integer, pp: integer, chance: integer, priority: integer, generation: integer) {
this.id = id;
const i18nKey = Moves[id].split('_').filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join('') as unknown as string;
this.name = id ? i18next.t(`move:${i18nKey}.name`).toString() : '';
this.nameAppend = '';
this.type = type;
this.category = category;
@ -106,7 +103,6 @@ export default class Move implements Localizable {
this.power = power;
this.accuracy = accuracy;
this.pp = pp;
this.effect = id ? i18next.t(`move:${i18nKey}.effect`).toString() : '';
this.chance = chance;
this.priority = priority;
this.generation = generation;
@ -119,9 +115,11 @@ export default class Move implements Localizable {
this.setFlag(MoveFlags.IGNORE_PROTECT, true);
if (category === MoveCategory.PHYSICAL)
this.setFlag(MoveFlags.MAKES_CONTACT, true);
this.localize();
}
localize() {
localize(): void {
const i18nKey = Moves[this.id].split('_').filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join('') as unknown as string;
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`).toString()}${this.nameAppend}` : '';
@ -846,8 +844,12 @@ export class HitHealAttr extends MoveEffectAttr {
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const healAmount = Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1);
const reverseDrain = user.hasAbilityWithAttr(ReverseDrainAbAttr);
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(),
Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1), getPokemonMessage(target, ` had its\nenergy drained!`), false, true));
!reverseDrain ? healAmount : healAmount * -1,
!reverseDrain ? getPokemonMessage(target, ` had its\nenergy drained!`) : undefined,
false, true));
return true;
}
@ -862,9 +864,12 @@ export class StrengthSapHealAttr extends MoveEffectAttr {
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const healAmount = target.stats[Stat.ATK] * (Math.max(2, 2 + target.summonData.battleStats[BattleStat.ATK]) / Math.max(2, 2 - target.summonData.battleStats[BattleStat.ATK]));
const reverseDrain = user.hasAbilityWithAttr(ReverseDrainAbAttr);
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(),
target.stats[Stat.ATK] * (Math.max(2, 2 + target.summonData.battleStats[BattleStat.ATK]) / Math.max(2, 2 - target.summonData.battleStats[BattleStat.ATK])),
getPokemonMessage(user, ` regained\nhealth!`), false, true));
!reverseDrain ? healAmount : healAmount * -1,
!reverseDrain ? getPokemonMessage(user, ` regained\nhealth!`) : undefined,
false, true));
return true;
}
}
@ -2296,7 +2301,7 @@ export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultipli
}
}
export class OneHitKOAccuracyAttr extends MoveAttr {
export class OneHitKOAccuracyAttr extends VariableAccuracyAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const accuracy = args[0] as Utils.NumberHolder;
accuracy.value = 30 + 70 * Math.min(target.level / user.level, 0.5) * 2;
@ -3699,7 +3704,7 @@ export function initMoves() {
.punchingMove(),
new AttackMove(Moves.SCRATCH, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1),
new AttackMove(Moves.VISE_GRIP, Type.NORMAL, MoveCategory.PHYSICAL, 55, 100, 30, -1, 0, 1),
new AttackMove(Moves.GUILLOTINE, Type.NORMAL, MoveCategory.PHYSICAL, -1, 30, 5, -1, 0, 1)
new AttackMove(Moves.GUILLOTINE, Type.NORMAL, MoveCategory.PHYSICAL, 200, 30, 5, -1, 0, 1)
.attr(OneHitKOAttr)
.attr(OneHitKOAccuracyAttr),
new AttackMove(Moves.RAZOR_WIND, Type.NORMAL, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 1)
@ -3748,7 +3753,7 @@ export function initMoves() {
new AttackMove(Moves.HORN_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 65, 100, 25, -1, 0, 1),
new AttackMove(Moves.FURY_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, 0, 1)
.attr(MultiHitAttr),
new AttackMove(Moves.HORN_DRILL, Type.NORMAL, MoveCategory.PHYSICAL, -1, 30, 5, -1, 0, 1)
new AttackMove(Moves.HORN_DRILL, Type.NORMAL, MoveCategory.PHYSICAL, 200, 30, 5, -1, 0, 1)
.attr(OneHitKOAttr)
.attr(OneHitKOAccuracyAttr),
new AttackMove(Moves.TACKLE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 35, -1, 0, 1),
@ -3902,7 +3907,7 @@ export function initMoves() {
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND, true)
.makesContact(false)
.target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.FISSURE, Type.GROUND, MoveCategory.PHYSICAL, -1, 30, 5, -1, 0, 1)
new AttackMove(Moves.FISSURE, Type.GROUND, MoveCategory.PHYSICAL, 200, 30, 5, -1, 0, 1)
.attr(OneHitKOAttr)
.attr(OneHitKOAccuracyAttr)
.attr(HitsTagAttr, BattlerTagType.UNDERGROUND, false)
@ -4565,7 +4570,7 @@ export function initMoves() {
new AttackMove(Moves.SAND_TOMB, Type.GROUND, MoveCategory.PHYSICAL, 35, 85, 15, 100, 0, 3)
.attr(TrapAttr, BattlerTagType.SAND_TOMB)
.makesContact(false),
new AttackMove(Moves.SHEER_COLD, Type.ICE, MoveCategory.SPECIAL, -1, 30, 5, -1, 0, 3)
new AttackMove(Moves.SHEER_COLD, Type.ICE, MoveCategory.SPECIAL, 200, 30, 5, -1, 0, 3)
.attr(OneHitKOAttr)
.attr(OneHitKOAccuracyAttr),
new AttackMove(Moves.MUDDY_WATER, Type.WATER, MoveCategory.SPECIAL, 90, 85, 10, 30, 0, 3)
@ -5564,7 +5569,7 @@ export function initMoves() {
.bitingMove()
.attr(RemoveScreensAttr),
new AttackMove(Moves.STOMPING_TANTRUM, Type.GROUND, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 7)
.partial(),
.attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result == MoveResult.MISS || user.getLastXMoves(2)[1]?.result == MoveResult.FAIL ? 2 : 1),
new AttackMove(Moves.SHADOW_BONE, Type.GHOST, MoveCategory.PHYSICAL, 85, 100, 10, 20, 0, 7)
.attr(StatChangeAttr, BattleStat.DEF, -1)
.makesContact(false),
@ -5677,8 +5682,8 @@ export function initMoves() {
.attr(StatChangeAttr, BattleStat.SPD, -1)
.partial(),
new StatusMove(Moves.MAGIC_POWDER, Type.PSYCHIC, 100, 20, -1, 0, 8)
.powderMove()
.unimplemented(),
.attr(ChangeTypeAttr, Type.PSYCHIC)
.powderMove(),
new AttackMove(Moves.DRAGON_DARTS, Type.DRAGON, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 8)
.attr(MultiHitAttr, MultiHitType._2)
.makesContact(false)
@ -6162,7 +6167,7 @@ export function initMoves() {
.slicingMove(),
new AttackMove(Moves.HYDRO_STEAM, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9)
.partial(),
new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, 1, 90, 10, -1, 0, 9)
new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9)
.attr(TargetHalfHpDamageAttr),
new AttackMove(Moves.COLLISION_COURSE, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2 ? 5461/4096 : 1),
@ -6284,7 +6289,7 @@ export function initMoves() {
new AttackMove(Moves.ALLURING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 9)
.partial(),
new AttackMove(Moves.TEMPER_FLARE, Type.FIRE, MoveCategory.PHYSICAL, 75, 100, 10, -1, 0, 9)
.partial(),
.attr(MovePowerMultiplierAttr, (user, target, move) => user.getLastXMoves(2)[1]?.result == MoveResult.MISS || user.getLastXMoves(2)[1]?.result == MoveResult.FAIL ? 2 : 1),
new AttackMove(Moves.SUPERCELL_SLAM, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 95, 15, -1, 0, 9)
.attr(MissEffectAttr, crashDamageFunc)
.attr(NoEffectAttr, crashDamageFunc),

File diff suppressed because it is too large Load Diff

View File

@ -309,7 +309,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (this.shiny) {
const populateVariantColors = (key: string, back: boolean = false): Promise<void> => {
return new Promise(resolve => {
const battleSpritePath = this.getBattleSpriteAtlasPath(back, ignoreOverride);
const battleSpritePath = this.getBattleSpriteAtlasPath(back, ignoreOverride).replace('variant/', '').replace(/_[1-3]$/, '');
let variantSet: VariantSet;
let config = variantData;
battleSpritePath.split('/').map(p => config ? config = config[p] : null);
@ -326,9 +326,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
});
};
if (this.isPlayer())
Promise.all([ populateVariantColors(this.getBattleSpriteKey()), populateVariantColors(this.getBattleSpriteKey(true)) ]).then(() => updateFusionPaletteAndResolve());
Promise.all([ populateVariantColors(this.getBattleSpriteKey(false)), populateVariantColors(this.getBattleSpriteKey(true), true) ]).then(() => updateFusionPaletteAndResolve());
else
populateVariantColors(this.getBattleSpriteKey()).then(() => updateFusionPaletteAndResolve());
populateVariantColors(this.getBattleSpriteKey(false)).then(() => updateFusionPaletteAndResolve());
} else
updateFusionPaletteAndResolve();
});
@ -986,11 +986,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
let shinyThreshold = new Utils.IntegerHolder(32);
if (thresholdOverride === undefined) {
if (!this.hasTrainer()) {
if (new Date() < new Date('4/22/2024'))
shinyThreshold.value *= 3;
if (!this.hasTrainer())
this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
}
} else
shinyThreshold.value = thresholdOverride;

View File

@ -1,9 +1,11 @@
import { SimpleTranslationEntries } from "#app/plugins/i18n";
/**
* The menu namespace holds most miscellaneous text that isn't directly part of the game's
* contents or directly related to Pokemon data. This includes menu navigation, settings,
* account interactions, descriptive text, etc.
*/
export const menu = {
export const menu: SimpleTranslationEntries = {
"cancel": "Cancel",
"continue": "Continue",
"dailyRun": "Daily Run (Beta)",

View File

@ -1,6 +1,6 @@
import { MoveTranslations } from "#app/plugins/i18n";
import { MoveTranslationEntries } from "#app/plugins/i18n";
export const move: MoveTranslations = {
export const move: MoveTranslationEntries = {
"pound": {
name: "Pound",
effect: "The target is physically pounded with a long tail, a foreleg, or the like."

View File

@ -1,4 +1,6 @@
export const pokeball = {
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokeball: SimpleTranslationEntries = {
"pokeBall": "Poké Ball",
"greatBall": "Great Ball",
"ultraBall": "Ultra Ball",

1086
src/locales/en/pokemon.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
export const menu = {
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const menu: SimpleTranslationEntries = {
"cancel": "Annuler",
"continue": "Continuer",
"dailyRun": "Défi du jour (Bêta)",

View File

@ -1,6 +1,6 @@
import { MoveTranslations } from "#app/plugins/i18n";
import { MoveTranslationEntries } from "#app/plugins/i18n";
export const move: MoveTranslations = {
export const move: MoveTranslationEntries = {
"pound": {
name: "Écras'Face",
effect: "Le lanceur écrase la cible avec lun de ses membres, tels quune de ses pattes avant ou sa longue queue."

View File

@ -1,4 +1,6 @@
export const pokeball = {
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const pokeball: SimpleTranslationEntries = {
"pokeBall": "Poké Ball",
"greatBall": "Super Ball",
"ultraBall": "Hyper Ball",

1086
src/locales/fr/pokemon.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
export const menu = {
import { SimpleTranslationEntries } from "#app/plugins/i18n";
export const menu: SimpleTranslationEntries = {
"cancel": "Annulla",
"continue": "Continua",
"newGame": "Nuova Partita",

View File

@ -142,7 +142,7 @@ class AddPokeballModifierType extends ModifierType implements Localizable {
this.count = count;
}
localize() {
localize(): void {
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

@ -1578,9 +1578,7 @@ export class MoneyRewardModifier extends ConsumableModifier {
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
scene.money += moneyAmount.value;
scene.updateMoneyText();
scene.validateAchvs(MoneyAchv);
scene.addMoney(moneyAmount.value);
return true;
}
@ -1627,9 +1625,7 @@ export class DamageMoneyRewardModifier extends PokemonHeldItemModifier {
const scene = (args[0] as Pokemon).scene;
const moneyAmount = new Utils.IntegerHolder(Math.floor((args[1] as Utils.IntegerHolder).value * (0.5 * this.getStackCount())));
scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
scene.money += moneyAmount.value;
scene.updateMoneyText();
scene.validateAchvs(MoneyAchv);
scene.addMoney(moneyAmount.value);
return true;
}
@ -1651,9 +1647,7 @@ export class MoneyInterestModifier extends PersistentModifier {
apply(args: any[]): boolean {
const scene = args[0] as BattleScene;
const interestAmount = Math.floor(scene.money * 0.1 * this.getStackCount());
scene.money += interestAmount;
scene.updateMoneyText();
scene.validateAchvs(MoneyAchv);
scene.addMoney(interestAmount);
scene.queueMessage(`You received interest of ₽${interestAmount.toLocaleString('en-US')}\nfrom the ${this.type.name}!`, null, true);

View File

@ -2,7 +2,7 @@ import BattleScene, { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE, bypassLog
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
import * as Utils from './utils';
import { Moves } from "./data/enums/moves";
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, FixedDamageAttr } from "./data/move";
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveEffectAttr, MoveFlags, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets, MoveTargetSet, MoveEffectTrigger, CopyMoveAttr, AttackMove, SelfStatusMove, DelayedAttackAttr, RechargeAttr, PreMoveMessageAttr, HealStatusEffectAttr, IgnoreOpponentStatChangesAttr, NoEffectAttr, FixedDamageAttr, OneHitKOAccuracyAttr } from "./data/move";
import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat";
@ -30,20 +30,20 @@ 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, AlwaysHitAbAttr } from "./data/ability";
import { CheckTrappedAbAttr, 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";
import { BattleSpec } from "./enums/battle-spec";
import { Species } from "./data/enums/species";
import { HealAchv, LevelAchv, MoneyAchv, achvs } from "./system/achv";
import { HealAchv, LevelAchv, achvs } from "./system/achv";
import { TrainerSlot, trainerConfigs } from "./data/trainer-config";
import { TrainerType } from "./data/enums/trainer-type";
import { EggHatchPhase } from "./egg-hatch-phase";
import { Egg } from "./data/egg";
import { vouchers } from "./system/voucher";
import { loggedInUser, updateUserInfo } from "./account";
import { DexAttr, PlayerGender, SessionSaveData } from "./system/game-data";
import { PlayerGender, SessionSaveData } from "./system/game-data";
import { addPokeballCaptureStars, addPokeballOpenParticles } from "./field/anims";
import { SpeciesFormChangeActiveTrigger, SpeciesFormChangeManualTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangePreMoveTrigger } from "./data/pokemon-forms";
import { battleSpecDialogue, getCharVariantFromDialogue } from "./data/dialogue";
@ -2295,6 +2295,7 @@ export class MovePhase extends BattlePhase {
this.cancelled = activated;
break;
}
if (activated) {
this.scene.queueMessage(getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect)));
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.getBattlerIndex(), undefined, CommonAnim.POISON + (this.pokemon.status.effect - 1)));
@ -2317,6 +2318,7 @@ export class MovePhase extends BattlePhase {
showMoveText(): void {
if (this.move.getMove().getAttrs(ChargeAttr).length) {
this.scene.queueMessage(getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500);
const lastMove = this.pokemon.getLastXMoves() as TurnMove[];
if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER)
return;
@ -2512,12 +2514,15 @@ export class MoveEffectPhase extends PokemonPhase {
if (moveAccuracy.value === -1)
return true;
user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
const isOhko = !!this.move.getMove().getAttrs(OneHitKOAccuracyAttr).length;
if (!isOhko)
user.scene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
if (this.scene.arena.weather?.weatherType === WeatherType.FOG)
moveAccuracy.value = Math.floor(moveAccuracy.value * 0.9);
if (!this.move.getMove().getAttrs(OneHitKOAttr).length && this.scene.arena.getTag(ArenaTagType.GRAVITY))
if (!isOhko && this.scene.arena.getTag(ArenaTagType.GRAVITY))
moveAccuracy.value = Math.floor(moveAccuracy.value * 1.67);
const userAccuracyLevel = new Utils.IntegerHolder(user.summonData.battleStats[BattleStat.ACC]);
@ -3312,10 +3317,7 @@ export class MoneyRewardPhase extends BattlePhase {
this.scene.applyModifiers(MoneyMultiplierModifier, true, moneyAmount);
this.scene.money += moneyAmount.value;
this.scene.updateMoneyText();
this.scene.validateAchvs(MoneyAchv);
this.scene.addMoney(moneyAmount.value);
this.scene.ui.showText(`You got ₽${moneyAmount.value.toLocaleString('en-US')}\nfor winning!`, null, () => this.end(), null, true);
}
@ -3797,11 +3799,15 @@ export class PokemonHealPhase extends CommonAnimPhase {
const hasMessage = !!this.message;
let lastStatusEffect = StatusEffect.NONE;
if (!fullHp) {
if (!fullHp || this.hpHealed < 0) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
if (!this.revive)
this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
const healAmount = new Utils.NumberHolder(Math.floor(this.hpHealed * hpRestoreMultiplier.value));
if (healAmount.value < 0) {
pokemon.damageAndUpdate(healAmount.value * -1, HitResult.HEAL);
healAmount.value = 0;
}
// Prevent healing to full if specified (in case of healing tokens so Sturdy doesn't cause a softlock)
if (this.preventFullHeal && pokemon.hp + healAmount.value >= pokemon.getMaxHp())
healAmount.value = (pokemon.getMaxHp() - pokemon.hp) - 1;

View File

@ -9,12 +9,19 @@ import { move as frMove } from '../locales/fr/move';
import { pokeball as enPokeball } from '../locales/en/pokeball';
import { pokeball as frPokeball } from '../locales/fr/pokeball';
import { pokemon as enPokemon } from '../locales/en/pokemon';
import { pokemon as frPokemon } from '../locales/fr/pokemon';
export interface SimpleTranslationEntries {
[key: string]: string
}
export interface MoveTranslationEntry {
name: string,
effect: string
}
export interface MoveTranslations {
export interface MoveTranslationEntries {
[key: string]: MoveTranslationEntry
}
@ -54,6 +61,7 @@ export function initI18n(): void {
menu: enMenu,
move: enMove,
pokeball: enPokeball,
pokemon: enPokemon,
},
it: {
menu: itMenu,
@ -62,6 +70,7 @@ export function initI18n(): void {
menu: frMenu,
move: frMove,
pokeball: frPokeball,
pokemon: frPokemon,
}
},
});
@ -74,6 +83,7 @@ declare module 'i18next' {
menu: typeof enMenu;
move: typeof enMove;
pokeball: typeof enPokeball;
pokemon: typeof enPokemon;
};
}
}