Merge remote-tracking branch 'upstream/main' into feature/guard-dog

This commit is contained in:
Madi Simpson 2024-05-04 18:55:26 -07:00
commit 88d43c09b8
31 changed files with 2491 additions and 2096 deletions

View File

@ -19,7 +19,7 @@ If you have the motivation and experience with Typescript/Javascript (or are wil
### ❔ FAQ ### ❔ FAQ
**How do I test a new _______?** **How do I test a new _______?**
- In the `battle-scene.ts` file there are overrides for most values you'll need to change for testing - In the `src/overrides.ts` file there are overrides for most values you'll need to change for testing
## 🪧 To Do ## 🪧 To Do

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

BIN
public/images/ui/candy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -3,7 +3,7 @@
"short_name": "PokéRogue", "short_name": "PokéRogue",
"description": "A Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, and reaching Pokémon stats you never thought possible.", "description": "A Pokémon fangame heavily inspired by the roguelite genre. Battle endlessly while gathering stacking items, exploring many different biomes, and reaching Pokémon stats you never thought possible.",
"scope": "/", "scope": "/",
"start_url": "https://pokerogue.net", "start_url": "/",
"display": "fullscreen", "display": "fullscreen",
"background_color": "#8c8c8c", "background_color": "#8c8c8c",
"theme_color": "#8c8c8c", "theme_color": "#8c8c8c",

View File

@ -9,7 +9,7 @@ import { BattlerTag } from "./battler-tags";
import { BattlerTagType } from "./enums/battler-tag-type"; import { BattlerTagType } from "./enums/battler-tag-type";
import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { StatusEffect, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender"; import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves } from "./move"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves, StatusMove } from "./move";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag"; import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type"; import { ArenaTagType } from "./enums/arena-tag-type";
import { Stat } from "./pokemon-stat"; import { Stat } from "./pokemon-stat";
@ -828,13 +828,16 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr {
} }
export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr {
constructor() { private ability: Abilities;
constructor(ability: Abilities) {
super(); super();
this.ability = ability;
} }
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) { if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) {
attacker.summonData.ability = pokemon.getAbility().id; attacker.summonData.ability = this.ability;
return true; return true;
} }
@ -1820,6 +1823,53 @@ function getAnticipationCondition(): AbAttrCondition {
}; };
} }
export class ForewarnAbAttr extends PostSummonAbAttr {
constructor() {
super(true);
}
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
let maxPowerSeen = 0;
let maxMove = "";
let movePower = 0;
for (let opponent of pokemon.getOpponents()) {
for (let move of opponent.moveset) {
if (move.getMove() instanceof StatusMove) {
movePower = 1;
} else if (move.getMove().findAttr(attr => attr instanceof OneHitKOAttr)) {
movePower = 150;
} else if (move.getMove().id === Moves.COUNTER || move.getMove().id === Moves.MIRROR_COAT || move.getMove().id === Moves.METAL_BURST) {
movePower = 120;
} else if (move.getMove().power === -1) {
movePower = 80;
} else {
movePower = move.getMove().power;
}
if (movePower > maxPowerSeen) {
maxPowerSeen = movePower;
maxMove = move.getName();
}
}
}
pokemon.scene.queueMessage(getPokemonMessage(pokemon, " was forewarned about " + maxMove + "!"));
return true;
}
}
export class FriskAbAttr extends PostSummonAbAttr {
constructor() {
super(true);
}
applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
for (let opponent of pokemon.getOpponents()) {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, " frisked " + opponent.name + "\'s " + opponent.getAbility().name + "!"));
}
return true;
}
}
export class PostWeatherChangeAbAttr extends AbAttr { export class PostWeatherChangeAbAttr extends AbAttr {
applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean { applyPostWeatherChange(pokemon: Pokemon, passive: boolean, weather: WeatherType, args: any[]): boolean {
return false; return false;
@ -1970,13 +2020,19 @@ export class MoodyAbAttr extends PostTurnAbAttr {
} }
applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
// TODO: Edge case of not choosing to buff or debuff a stat that's already maxed
let selectableStats = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; let selectableStats = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD];
let increaseStat = selectableStats[Utils.randInt(selectableStats.length)]; let increaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] < 6);
selectableStats = selectableStats.filter(s => s !== increaseStat); let decreaseStatArray = selectableStats.filter(s => pokemon.summonData.battleStats[s] > -6);
let decreaseStat = selectableStats[Utils.randInt(selectableStats.length)];
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [increaseStat], 2)); if (increaseStatArray.length > 0) {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [decreaseStat], -1)); let increaseStat = increaseStatArray[Utils.randInt(increaseStatArray.length)];
decreaseStatArray = decreaseStatArray.filter(s => s !== increaseStat);
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [increaseStat], 2));
}
if (decreaseStatArray.length > 0) {
let decreaseStat = selectableStats[Utils.randInt(selectableStats.length)];
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [decreaseStat], -1));
}
return true; return true;
} }
} }
@ -2083,6 +2139,13 @@ export class StatChangeMultiplierAbAttr extends AbAttr {
} }
} }
export class StatChangeCopyAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, (args[0] as BattleStat[]), (args[1] as integer), true, false, false));
return true;
}
}
export class BypassBurnDamageReductionAbAttr extends AbAttr { export class BypassBurnDamageReductionAbAttr extends AbAttr {
constructor() { constructor() {
super(false); super(false);
@ -2903,7 +2966,7 @@ export function initAbilities() {
new Ability(Abilities.ANTICIPATION, 4) new Ability(Abilities.ANTICIPATION, 4)
.conditionalAttr(getAnticipationCondition(), PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' shuddered!')), .conditionalAttr(getAnticipationCondition(), PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' shuddered!')),
new Ability(Abilities.FOREWARN, 4) new Ability(Abilities.FOREWARN, 4)
.unimplemented(), .attr(ForewarnAbAttr),
new Ability(Abilities.UNAWARE, 4) new Ability(Abilities.UNAWARE, 4)
.attr(IgnoreOpponentStatChangesAbAttr) .attr(IgnoreOpponentStatChangesAbAttr)
.ignorable(), .ignorable(),
@ -2933,7 +2996,7 @@ export function initAbilities() {
new Ability(Abilities.HONEY_GATHER, 4) new Ability(Abilities.HONEY_GATHER, 4)
.unimplemented(), .unimplemented(),
new Ability(Abilities.FRISK, 4) new Ability(Abilities.FRISK, 4)
.unimplemented(), .attr(FriskAbAttr),
new Ability(Abilities.RECKLESS, 4) new Ability(Abilities.RECKLESS, 4)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.getAttrs(RecoilAttr).length && move.id !== Moves.STRUGGLE, 1.2), .attr(MovePowerBoostAbAttr, (user, target, move) => move.getAttrs(RecoilAttr).length && move.id !== Moves.STRUGGLE, 1.2),
new Ability(Abilities.MULTITYPE, 4) new Ability(Abilities.MULTITYPE, 4)
@ -3026,7 +3089,7 @@ export function initAbilities() {
new Ability(Abilities.INFILTRATOR, 5) new Ability(Abilities.INFILTRATOR, 5)
.unimplemented(), .unimplemented(),
new Ability(Abilities.MUMMY, 5) new Ability(Abilities.MUMMY, 5)
.attr(PostDefendAbilityGiveAbAttr) .attr(PostDefendAbilityGiveAbAttr, Abilities.MUMMY)
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.MOXIE, 5) new Ability(Abilities.MOXIE, 5)
.attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1), .attr(PostVictoryStatChangeAbAttr, BattleStat.ATK, 1),
@ -3359,6 +3422,7 @@ export function initAbilities() {
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, '\'s Neutralizing Gas filled the area!'))
.partial(), .partial(),
new Ability(Abilities.PASTEL_VEIL, 8) new Ability(Abilities.PASTEL_VEIL, 8)
.attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC)
@ -3397,7 +3461,7 @@ export function initAbilities() {
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr), .attr(UnsuppressableAbilityAbAttr),
new Ability(Abilities.LINGERING_AROMA, 9) new Ability(Abilities.LINGERING_AROMA, 9)
.attr(PostDefendAbilityGiveAbAttr) .attr(PostDefendAbilityGiveAbAttr, Abilities.LINGERING_AROMA)
.bypassFaint(), .bypassFaint(),
new Ability(Abilities.SEED_SOWER, 9) new Ability(Abilities.SEED_SOWER, 9)
.attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY), .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY),
@ -3478,7 +3542,7 @@ export function initAbilities() {
.attr(PostBiomeChangeTerrainChangeAbAttr, TerrainType.ELECTRIC) .attr(PostBiomeChangeTerrainChangeAbAttr, TerrainType.ELECTRIC)
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPATK, 4 / 3), .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPATK, 4 / 3),
new Ability(Abilities.OPPORTUNIST, 9) new Ability(Abilities.OPPORTUNIST, 9)
.unimplemented(), .attr(StatChangeCopyAbAttr),
new Ability(Abilities.CUD_CHEW, 9) new Ability(Abilities.CUD_CHEW, 9)
.unimplemented(), .unimplemented(),
new Ability(Abilities.SHARPNESS, 9) new Ability(Abilities.SHARPNESS, 9)

View File

@ -984,6 +984,19 @@ export class HideSpriteTag extends BattlerTag {
} }
} }
export class TypeImmuneTag extends BattlerTag {
public immuneType: Type;
constructor(tagType: BattlerTagType, sourceMove: Moves, immuneType: Type, length: number) {
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
}
}
export class MagnetRisenTag extends TypeImmuneTag {
constructor(tagType: BattlerTagType, sourceMove: Moves) {
super(tagType, sourceMove, Type.GROUND, 5);
}
}
export class TypeBoostTag extends BattlerTag { export class TypeBoostTag extends BattlerTag {
public boostedType: Type; public boostedType: Type;
public boostValue: number; public boostValue: number;
@ -1211,6 +1224,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
return new CursedTag(sourceId); return new CursedTag(sourceId);
case BattlerTagType.CHARGED: case BattlerTagType.CHARGED:
return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true); return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true);
case BattlerTagType.MAGNET_RISEN:
return new MagnetRisenTag(tagType, sourceMove);
case BattlerTagType.NONE: case BattlerTagType.NONE:
default: default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);

View File

@ -115,7 +115,7 @@ export const speciesEggMoves = {
[Species.PHANPY]: [ Moves.SHORE_UP, Moves.HEAD_SMASH, Moves.MOUNTAIN_GALE, Moves.VOLT_TACKLE ], [Species.PHANPY]: [ Moves.SHORE_UP, Moves.HEAD_SMASH, Moves.MOUNTAIN_GALE, Moves.VOLT_TACKLE ],
[Species.STANTLER]: [ Moves.HORN_LEECH, Moves.HIGH_JUMP_KICK, Moves.BULK_UP, Moves.HEAD_CHARGE ], [Species.STANTLER]: [ Moves.HORN_LEECH, Moves.HIGH_JUMP_KICK, Moves.BULK_UP, Moves.HEAD_CHARGE ],
[Species.SMEARGLE]: [ Moves.BATON_PASS, Moves.BURNING_BULWARK, Moves.SALT_CURE, Moves.SPORE ], [Species.SMEARGLE]: [ Moves.BATON_PASS, Moves.BURNING_BULWARK, Moves.SALT_CURE, Moves.SPORE ],
[Species.TYROGUE]: [ Moves.MACH_PUNCH, Moves.WICKED_TORQUE, Moves.METEOR_MASH, Moves.COLLISION_COURSE ], [Species.TYROGUE]: [ Moves.VICTORY_DANCE, Moves.WICKED_TORQUE, Moves.METEOR_MASH, Moves.COLLISION_COURSE ],
[Species.SMOOCHUM]: [ Moves.EXPANDING_FORCE, Moves.AURA_SPHERE, Moves.FREEZY_FROST, Moves.TAKE_HEART ], [Species.SMOOCHUM]: [ Moves.EXPANDING_FORCE, Moves.AURA_SPHERE, Moves.FREEZY_FROST, Moves.TAKE_HEART ],
[Species.ELEKID]: [ Moves.DRAIN_PUNCH, Moves.TIDY_UP, Moves.ICE_HAMMER, Moves.PLASMA_FISTS ], [Species.ELEKID]: [ Moves.DRAIN_PUNCH, Moves.TIDY_UP, Moves.ICE_HAMMER, Moves.PLASMA_FISTS ],
[Species.MAGBY]: [ Moves.STORED_POWER, Moves.EARTH_POWER, Moves.ARMOR_CANNON, Moves.FLEUR_CANNON ], [Species.MAGBY]: [ Moves.STORED_POWER, Moves.EARTH_POWER, Moves.ARMOR_CANNON, Moves.FLEUR_CANNON ],

View File

@ -77,8 +77,6 @@ export function getEggHatchWavesMessage(hatchWaves: integer): string {
} }
export function getEggGachaTypeDescriptor(scene: BattleScene, egg: Egg): string { export function getEggGachaTypeDescriptor(scene: BattleScene, egg: Egg): string {
if (egg.isManaphyEgg())
return '';
switch (egg.gachaType) { switch (egg.gachaType) {
case GachaType.LEGENDARY: case GachaType.LEGENDARY:
return `Legendary Rate Up (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, egg.timestamp)).getName()})`; return `Legendary Rate Up (${getPokemonSpecies(getLegendaryGachaSpeciesForTimestamp(scene, egg.timestamp)).getName()})`;

View File

@ -54,5 +54,6 @@ export enum BattlerTagType {
SALT_CURED = "SALT_CURED", SALT_CURED = "SALT_CURED",
CURSED = "CURSED", CURSED = "CURSED",
CHARGED = "CHARGED", CHARGED = "CHARGED",
GROUNDED = "GROUNDED" GROUNDED = "GROUNDED",
MAGNET_RISEN = "MAGNET_RISEN"
} }

View File

@ -1496,6 +1496,23 @@ export class StatChangeAttr extends MoveEffectAttr {
} }
} }
export class AcupressureStatChangeAttr extends MoveEffectAttr {
constructor() {
super();
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise<boolean> {
let randStats = [ BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD, BattleStat.ACC, BattleStat.EVA ];
randStats = randStats.filter(s => target.summonData.battleStats[s] < 6);
if (randStats.length > 0) {
let boostStat = [randStats[Utils.randInt(randStats.length)]];
user.scene.unshiftPhase(new StatChangePhase(user.scene, target.getBattlerIndex(), this.selfTarget, boostStat, 2));
return true;
}
return false;
}
}
export class GrowthStatChangeAttr extends StatChangeAttr { export class GrowthStatChangeAttr extends StatChangeAttr {
constructor() { constructor() {
super([ BattleStat.ATK, BattleStat.SPATK ], 1, true); super([ BattleStat.ATK, BattleStat.SPATK ], 1, true);
@ -2199,6 +2216,36 @@ export class VariableMoveTypeAttr extends MoveAttr {
} }
} }
export class TechnoBlastTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.GENESECT)) {
const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies.formIndex;
const type = (args[0] as Utils.IntegerHolder);
switch (form) {
case 1: // Shock Drive
type.value = Type.ELECTRIC;
break;
case 2: // Burn Drive
type.value = Type.FIRE;
break;
case 3: // Chill Drive
type.value = Type.ICE;
break;
case 4: // Douse Drive
type.value = Type.WATER;
break;
default:
type.value = Type.NORMAL;
break;
}
return true;
}
return false;
}
}
export class AuraWheelTypeAttr extends VariableMoveTypeAttr { export class AuraWheelTypeAttr extends VariableMoveTypeAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)) { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)) {
@ -3695,6 +3742,22 @@ export class LastResortAttr extends MoveAttr {
} }
} }
export class VariableTargetAttr extends MoveAttr {
private targetChangeFunc: (user: Pokemon, target: Pokemon, move: Move) => number;
constructor(targetChange: (user: Pokemon, target: Pokemon, move: Move) => number) {
super();
this.targetChangeFunc = targetChange;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const targetVal = args[0] as Utils.NumberHolder;
targetVal.value = this.targetChangeFunc(user, target, move);
return true;
}
}
const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY); const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY);
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune(); const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
@ -3775,7 +3838,10 @@ export type MoveTargetSet = {
} }
export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet { export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet {
const moveTarget = move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : []; const variableTarget = new Utils.NumberHolder(0);
user.getOpponents().forEach(p => applyMoveAttrs(VariableTargetAttr, user, p, allMoves[move], variableTarget));
const moveTarget = allMoves[move].getAttrs(VariableTargetAttr).length ? variableTarget.value : move ? allMoves[move].moveTarget : move === undefined ? MoveTarget.NEAR_ENEMY : [];
const opponents = user.getOpponents(); const opponents = user.getOpponents();
let set: Pokemon[] = []; let set: Pokemon[] = [];
@ -4705,8 +4771,7 @@ export function initMoves() {
.attr(StatusEffectAttr, StatusEffect.SLEEP) .attr(StatusEffectAttr, StatusEffect.SLEEP)
.soundBased(), .soundBased(),
new StatusMove(Moves.TICKLE, Type.NORMAL, 100, 20, -1, 0, 3) new StatusMove(Moves.TICKLE, Type.NORMAL, 100, 20, -1, 0, 3)
.attr(StatChangeAttr, BattleStat.ATK, -1) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.DEF ], -1),
.attr(StatChangeAttr, BattleStat.DEF, -1),
new SelfStatusMove(Moves.COSMIC_POWER, Type.PSYCHIC, -1, 20, -1, 0, 3) new SelfStatusMove(Moves.COSMIC_POWER, Type.PSYCHIC, -1, 20, -1, 0, 3)
.attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, true), .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], 1, true),
new AttackMove(Moves.WATER_SPOUT, Type.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 3) new AttackMove(Moves.WATER_SPOUT, Type.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 3)
@ -4828,7 +4893,7 @@ export function initMoves() {
.attr(AddArenaTagAttr, ArenaTagType.TAILWIND, 4, true) .attr(AddArenaTagAttr, ArenaTagType.TAILWIND, 4, true)
.target(MoveTarget.USER_SIDE), .target(MoveTarget.USER_SIDE),
new StatusMove(Moves.ACUPRESSURE, Type.NORMAL, -1, 30, -1, 0, 4) new StatusMove(Moves.ACUPRESSURE, Type.NORMAL, -1, 30, -1, 0, 4)
.attr(StatChangeAttr, BattleStat.RAND, 2) .attr(AcupressureStatChangeAttr)
.target(MoveTarget.USER_OR_NEAR_ALLY), .target(MoveTarget.USER_OR_NEAR_ALLY),
new AttackMove(Moves.METAL_BURST, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 4) new AttackMove(Moves.METAL_BURST, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 4)
.attr(CounterDamageAttr, (move: Move) => (move.category === MoveCategory.PHYSICAL || move.category === MoveCategory.SPECIAL), 1.5) .attr(CounterDamageAttr, (move: Move) => (move.category === MoveCategory.PHYSICAL || move.category === MoveCategory.SPECIAL), 1.5)
@ -4900,6 +4965,10 @@ export function initMoves() {
new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4) new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4)
.attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true),
new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4) new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4)
.attr(AddBattlerTagAttr, BattlerTagType.MAGNET_RISEN, true, true)
.condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) &&
!user.getTag(BattlerTagType.IGNORE_FLYING) && !user.getTag(BattlerTagType.INGRAIN) &&
!user.getTag(BattlerTagType.MAGNET_RISEN))
.unimplemented(), .unimplemented(),
new AttackMove(Moves.FLARE_BLITZ, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4) new AttackMove(Moves.FLARE_BLITZ, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4)
.attr(RecoilAttr, false, 0.33) .attr(RecoilAttr, false, 0.33)
@ -5299,7 +5368,7 @@ export function initMoves() {
.ballBombMove() .ballBombMove()
.target(MoveTarget.ALL_NEAR_OTHERS), .target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.TECHNO_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 5) new AttackMove(Moves.TECHNO_BLAST, Type.NORMAL, MoveCategory.SPECIAL, 120, 100, 5, -1, 0, 5)
.partial(), .attr(TechnoBlastTypeAttr),
new AttackMove(Moves.RELIC_SONG, Type.NORMAL, MoveCategory.SPECIAL, 75, 100, 10, 10, 0, 5) new AttackMove(Moves.RELIC_SONG, Type.NORMAL, MoveCategory.SPECIAL, 75, 100, 10, 10, 0, 5)
.attr(StatusEffectAttr, StatusEffect.SLEEP) .attr(StatusEffectAttr, StatusEffect.SLEEP)
.soundBased() .soundBased()
@ -5742,8 +5811,7 @@ export function initMoves() {
.ignoresAbilities() .ignoresAbilities()
.partial(), .partial(),
new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, 100, 0, 7) new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, 100, 0, 7)
.attr(StatChangeAttr, BattleStat.ATK, -1) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], -1),
.attr(StatChangeAttr, BattleStat.SPATK, -1),
new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7) new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7)
.attr(FlinchAttr), .attr(FlinchAttr),
new AttackMove(Moves.NATURES_MADNESS, Type.FAIRY, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 7) new AttackMove(Moves.NATURES_MADNESS, Type.FAIRY, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 7)
@ -5922,8 +5990,7 @@ export function initMoves() {
new AttackMove(Moves.BODY_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) new AttackMove(Moves.BODY_PRESS, Type.FIGHTING, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8)
.attr(DefAtkAttr), .attr(DefAtkAttr),
new StatusMove(Moves.DECORATE, Type.FAIRY, -1, 15, 100, 0, 8) new StatusMove(Moves.DECORATE, Type.FAIRY, -1, 15, 100, 0, 8)
.attr(StatChangeAttr, BattleStat.ATK, 2) .attr(StatChangeAttr, [ BattleStat.ATK, BattleStat.SPATK ], 2),
.attr(StatChangeAttr, BattleStat.SPATK, 2),
new AttackMove(Moves.DRUM_BEATING, Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8) new AttackMove(Moves.DRUM_BEATING, Type.GRASS, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 8)
.attr(StatChangeAttr, BattleStat.SPD, -1) .attr(StatChangeAttr, BattleStat.SPD, -1)
.makesContact(false), .makesContact(false),
@ -5973,7 +6040,8 @@ export function initMoves() {
new AttackMove(Moves.STEEL_BEAM, Type.STEEL, MoveCategory.SPECIAL, 140, 95, 5, -1, 0, 8) new AttackMove(Moves.STEEL_BEAM, Type.STEEL, MoveCategory.SPECIAL, 140, 95, 5, -1, 0, 8)
.attr(HalfSacrificialAttr), .attr(HalfSacrificialAttr),
new AttackMove(Moves.EXPANDING_FORCE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8) new AttackMove(Moves.EXPANDING_FORCE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8)
.partial(), .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 1.5 : 1)
.attr(VariableTargetAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.PSYCHIC && user.isGrounded() ? 6 : 3),
new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8) new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8)
.attr(ClearTerrainAttr) .attr(ClearTerrainAttr)
.condition((user, target, move) => !!user.scene.arena.terrain), .condition((user, target, move) => !!user.scene.arena.terrain),

View File

@ -82,7 +82,11 @@ export enum FormChangeItem {
SHADOW_REINS_OF_UNITY, SHADOW_REINS_OF_UNITY,
WELLSPRING_MASK, WELLSPRING_MASK,
HEARTHFLAME_MASK, HEARTHFLAME_MASK,
CORNERSTONE_MASK CORNERSTONE_MASK,
SHOCK_DRIVE,
BURN_DRIVE,
CHILL_DRIVE,
DOUSE_DRIVE
} }
export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean; export type SpeciesFormChangeConditionPredicate = (p: Pokemon) => boolean;
@ -542,6 +546,12 @@ export const pokemonFormChanges: PokemonFormChanges = {
new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangePostMoveTrigger(Moves.RELIC_SONG), true), new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangePostMoveTrigger(Moves.RELIC_SONG), true),
new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangeActiveTrigger(false), true) new SpeciesFormChange(Species.MELOETTA, 'pirouette', 'aria', new SpeciesFormChangeActiveTrigger(false), true)
], ],
[Species.GENESECT]: [
new SpeciesFormChange(Species.GENESECT, '', 'shock', new SpeciesFormChangeItemTrigger(FormChangeItem.SHOCK_DRIVE)),
new SpeciesFormChange(Species.GENESECT, '', 'burn', new SpeciesFormChangeItemTrigger(FormChangeItem.BURN_DRIVE)),
new SpeciesFormChange(Species.GENESECT, '', 'chill', new SpeciesFormChangeItemTrigger(FormChangeItem.CHILL_DRIVE)),
new SpeciesFormChange(Species.GENESECT, '', 'douse', new SpeciesFormChangeItemTrigger(FormChangeItem.DOUSE_DRIVE))
],
[Species.GRENINJA]: [ [Species.GRENINJA]: [
new SpeciesFormChange(Species.GRENINJA, 'battle-bond', 'ash', new SpeciesFormChangeManualTrigger(), true), new SpeciesFormChange(Species.GRENINJA, 'battle-bond', 'ash', new SpeciesFormChangeManualTrigger(), true),
new SpeciesFormChange(Species.GRENINJA, 'ash', 'battle-bond', new SpeciesFormChangeManualTrigger(), true) new SpeciesFormChange(Species.GRENINJA, 'ash', 'battle-bond', new SpeciesFormChangeManualTrigger(), true)

View File

@ -30,7 +30,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 24, Moves.SWEET_SCENT ], [ 24, Moves.SWEET_SCENT ],
[ 27, Moves.SYNTHESIS ], [ 27, Moves.SYNTHESIS ],
[ 30, Moves.WORRY_SEED ], [ 30, Moves.WORRY_SEED ],
[ 33, Moves.DOUBLE_EDGE ], [ 33, Moves.POWER_WHIP ],
[ 36, Moves.SOLAR_BEAM ], [ 36, Moves.SOLAR_BEAM ],
], ],
[Species.IVYSAUR]: [ [Species.IVYSAUR]: [
@ -47,16 +47,16 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 30, Moves.SWEET_SCENT ], [ 30, Moves.SWEET_SCENT ],
[ 35, Moves.SYNTHESIS ], [ 35, Moves.SYNTHESIS ],
[ 40, Moves.WORRY_SEED ], [ 40, Moves.WORRY_SEED ],
[ 45, Moves.DOUBLE_EDGE ], [ 45, Moves.POWER_WHIP ],
[ 50, Moves.SOLAR_BEAM ], [ 50, Moves.SOLAR_BEAM ],
], ],
[Species.VENUSAUR]: [ [Species.VENUSAUR]: [
[ 0, Moves.PETAL_BLIZZARD ], [ 0, Moves.PETAL_BLIZZARD ],
[ 1, Moves.GROWTH ],
[ 1, Moves.PETAL_DANCE ],
[ 1, Moves.VINE_WHIP ], [ 1, Moves.VINE_WHIP ],
[ 1, Moves.TACKLE ], [ 1, Moves.TACKLE ],
[ 1, Moves.GROWL ], [ 1, Moves.GROWL ],
[ 1, Moves.GROWTH ],
[ 1, Moves.PETAL_DANCE ],
[ 9, Moves.LEECH_SEED ], [ 9, Moves.LEECH_SEED ],
[ 12, Moves.RAZOR_LEAF ], [ 12, Moves.RAZOR_LEAF ],
[ 15, Moves.POISON_POWDER ], [ 15, Moves.POISON_POWDER ],
@ -66,7 +66,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 30, Moves.SWEET_SCENT ], [ 30, Moves.SWEET_SCENT ],
[ 37, Moves.SYNTHESIS ], [ 37, Moves.SYNTHESIS ],
[ 44, Moves.WORRY_SEED ], [ 44, Moves.WORRY_SEED ],
[ 51, Moves.DOUBLE_EDGE ], [ 51, Moves.POWER_WHIP ],
[ 58, Moves.SOLAR_BEAM ], [ 58, Moves.SOLAR_BEAM ],
], ],
[Species.CHARMANDER]: [ [Species.CHARMANDER]: [
@ -127,7 +127,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 27, Moves.SHELL_SMASH ], [ 27, Moves.SHELL_SMASH ],
[ 30, Moves.IRON_DEFENSE ], [ 30, Moves.IRON_DEFENSE ],
[ 33, Moves.HYDRO_PUMP ], [ 33, Moves.HYDRO_PUMP ],
[ 36, Moves.SKULL_BASH ], [ 36, Moves.WAVE_CRASH ],
], ],
[Species.WARTORTLE]: [ [Species.WARTORTLE]: [
[ 1, Moves.TACKLE ], [ 1, Moves.TACKLE ],
@ -143,7 +143,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 35, Moves.SHELL_SMASH ], [ 35, Moves.SHELL_SMASH ],
[ 40, Moves.IRON_DEFENSE ], [ 40, Moves.IRON_DEFENSE ],
[ 45, Moves.HYDRO_PUMP ], [ 45, Moves.HYDRO_PUMP ],
[ 50, Moves.SKULL_BASH ], [ 50, Moves.WAVE_CRASH ],
], ],
[Species.BLASTOISE]: [ [Species.BLASTOISE]: [
[ 0, Moves.FLASH_CANNON ], [ 0, Moves.FLASH_CANNON ],
@ -160,7 +160,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 35, Moves.SHELL_SMASH ], [ 35, Moves.SHELL_SMASH ],
[ 42, Moves.IRON_DEFENSE ], [ 42, Moves.IRON_DEFENSE ],
[ 49, Moves.HYDRO_PUMP ], [ 49, Moves.HYDRO_PUMP ],
[ 56, Moves.SKULL_BASH ], [ 56, Moves.WAVE_CRASH ],
], ],
[Species.CATERPIE]: [ [Species.CATERPIE]: [
[ 1, Moves.TACKLE ], [ 1, Moves.TACKLE ],
@ -341,9 +341,9 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 12, Moves.GLARE ], [ 12, Moves.GLARE ],
[ 17, Moves.SCREECH ], [ 17, Moves.SCREECH ],
[ 20, Moves.ACID ], [ 20, Moves.ACID ],
[ 25, Moves.SWALLOW ],
[ 25, Moves.STOCKPILE ], [ 25, Moves.STOCKPILE ],
[ 25, Moves.SPIT_UP ], [ 25, Moves.SPIT_UP ],
[ 25, Moves.SWALLOW ],
[ 28, Moves.ACID_SPRAY ], [ 28, Moves.ACID_SPRAY ],
[ 33, Moves.SLUDGE_BOMB ], [ 33, Moves.SLUDGE_BOMB ],
[ 36, Moves.GASTRO_ACID ], [ 36, Moves.GASTRO_ACID ],
@ -1780,14 +1780,15 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 4, Moves.DOUBLE_KICK ], [ 4, Moves.DOUBLE_KICK ],
[ 8, Moves.LOW_KICK ], [ 8, Moves.LOW_KICK ],
[ 12, Moves.ENDURE ], [ 12, Moves.ENDURE ],
[ 16, Moves.REVENGE ], [ 16, Moves.SUCKER_PUNCH ],
[ 21, Moves.WIDE_GUARD ], [ 21, Moves.WIDE_GUARD ],
[ 24, Moves.BLAZE_KICK ], [ 24, Moves.BLAZE_KICK ],
[ 28, Moves.MIND_READER ], [ 28, Moves.FEINT ],
[ 32, Moves.MEGA_KICK ], [ 32, Moves.MEGA_KICK ],
[ 36, Moves.CLOSE_COMBAT ], [ 36, Moves.CLOSE_COMBAT ],
[ 40, Moves.REVERSAL ], [ 40, Moves.REVERSAL ],
[ 44, Moves.HIGH_JUMP_KICK ], [ 44, Moves.HIGH_JUMP_KICK ],
[ 50, Moves.AXE_KICK ],
], ],
[Species.HITMONCHAN]: [ [Species.HITMONCHAN]: [
[ 0, Moves.DRAIN_PUNCH ], [ 0, Moves.DRAIN_PUNCH ],
@ -1796,16 +1797,14 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 1, Moves.FAKE_OUT ], [ 1, Moves.FAKE_OUT ],
[ 1, Moves.HELPING_HAND ], [ 1, Moves.HELPING_HAND ],
[ 1, Moves.FEINT ], [ 1, Moves.FEINT ],
[ 1, Moves.VACUUM_WAVE ],
[ 1, Moves.BULLET_PUNCH ],
[ 4, Moves.MACH_PUNCH ], [ 4, Moves.MACH_PUNCH ],
[ 8, Moves.POWER_UP_PUNCH ], [ 8, Moves.VACUUM_WAVE ],
[ 12, Moves.DETECT ], [ 12, Moves.DETECT ],
[ 16, Moves.REVENGE ], [ 16, Moves.BULLET_PUNCH ],
[ 21, Moves.QUICK_GUARD ], [ 21, Moves.QUICK_GUARD ],
[ 24, Moves.FIRE_PUNCH ],
[ 24, Moves.ICE_PUNCH ],
[ 24, Moves.THUNDER_PUNCH ], [ 24, Moves.THUNDER_PUNCH ],
[ 24, Moves.ICE_PUNCH ],
[ 24, Moves.FIRE_PUNCH ],
[ 28, Moves.AGILITY ], [ 28, Moves.AGILITY ],
[ 32, Moves.MEGA_PUNCH ], [ 32, Moves.MEGA_PUNCH ],
[ 36, Moves.CLOSE_COMBAT ], [ 36, Moves.CLOSE_COMBAT ],
@ -2598,7 +2597,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 31, Moves.LIGHT_SCREEN ], [ 31, Moves.LIGHT_SCREEN ],
[ 34, Moves.BODY_SLAM ], [ 34, Moves.BODY_SLAM ],
[ 39, Moves.SAFEGUARD ], [ 39, Moves.SAFEGUARD ],
[ 42, Moves.AROMATHERAPY ], [ 42, Moves.GIGA_DRAIN ],
[ 45, Moves.SOLAR_BEAM ], [ 45, Moves.SOLAR_BEAM ],
], ],
[Species.BAYLEEF]: [ [Species.BAYLEEF]: [
@ -2614,7 +2613,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 36, Moves.LIGHT_SCREEN ], [ 36, Moves.LIGHT_SCREEN ],
[ 40, Moves.BODY_SLAM ], [ 40, Moves.BODY_SLAM ],
[ 46, Moves.SAFEGUARD ], [ 46, Moves.SAFEGUARD ],
[ 50, Moves.AROMATHERAPY ], [ 50, Moves.GIGA_DRAIN ],
[ 54, Moves.SOLAR_BEAM ], [ 54, Moves.SOLAR_BEAM ],
], ],
[Species.MEGANIUM]: [ [Species.MEGANIUM]: [
@ -2632,7 +2631,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 40, Moves.LIGHT_SCREEN ], [ 40, Moves.LIGHT_SCREEN ],
[ 46, Moves.BODY_SLAM ], [ 46, Moves.BODY_SLAM ],
[ 54, Moves.SAFEGUARD ], [ 54, Moves.SAFEGUARD ],
[ 60, Moves.AROMATHERAPY ], [ 60, Moves.GIGA_DRAIN ],
[ 65, Moves.SOLAR_BEAM ], [ 65, Moves.SOLAR_BEAM ],
], ],
[Species.CYNDAQUIL]: [ [Species.CYNDAQUIL]: [
@ -2796,6 +2795,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 1, Moves.ECHOED_VOICE ], [ 1, Moves.ECHOED_VOICE ],
[ 9, Moves.CONFUSION ], [ 9, Moves.CONFUSION ],
[ 12, Moves.REFLECT ], [ 12, Moves.REFLECT ],
[ 15, Moves.DEFOG ],
[ 18, Moves.AIR_SLASH ], [ 18, Moves.AIR_SLASH ],
[ 23, Moves.EXTRASENSORY ], [ 23, Moves.EXTRASENSORY ],
[ 28, Moves.TAKE_DOWN ], [ 28, Moves.TAKE_DOWN ],
@ -3574,7 +3574,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 13, Moves.LICK ], [ 13, Moves.LICK ],
[ 19, Moves.HEADBUTT ], [ 19, Moves.HEADBUTT ],
[ 25, Moves.ROAR ], [ 25, Moves.ROAR ],
[ 31, Moves.RAGE ], [ 31, Moves.LAST_RESORT ],
[ 37, Moves.PLAY_ROUGH ], [ 37, Moves.PLAY_ROUGH ],
[ 43, Moves.PAYBACK ], [ 43, Moves.PAYBACK ],
[ 49, Moves.CRUNCH ], [ 49, Moves.CRUNCH ],
@ -3592,7 +3592,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 13, Moves.LICK ], [ 13, Moves.LICK ],
[ 19, Moves.HEADBUTT ], [ 19, Moves.HEADBUTT ],
[ 27, Moves.ROAR ], [ 27, Moves.ROAR ],
[ 35, Moves.RAGE ], [ 35, Moves.LAST_RESORT ],
[ 43, Moves.PLAY_ROUGH ], [ 43, Moves.PLAY_ROUGH ],
[ 51, Moves.PAYBACK ], [ 51, Moves.PAYBACK ],
[ 59, Moves.CRUNCH ], [ 59, Moves.CRUNCH ],
@ -3868,7 +3868,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 20, Moves.WING_ATTACK ], [ 20, Moves.WING_ATTACK ],
[ 24, Moves.SLASH ], [ 24, Moves.SLASH ],
[ 28, Moves.STEEL_WING ], [ 28, Moves.STEEL_WING ],
[ 32, Moves.AUTOTOMIZE ], [ 32, Moves.PAYBACK ],
[ 36, Moves.DRILL_PECK ], [ 36, Moves.DRILL_PECK ],
[ 40, Moves.METAL_SOUND ], [ 40, Moves.METAL_SOUND ],
[ 44, Moves.SPIKES ], [ 44, Moves.SPIKES ],
@ -3923,11 +3923,12 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 20, Moves.DRAGON_BREATH ], [ 20, Moves.DRAGON_BREATH ],
[ 25, Moves.BUBBLE_BEAM ], [ 25, Moves.BUBBLE_BEAM ],
[ 30, Moves.AGILITY ], [ 30, Moves.AGILITY ],
[ 37, Moves.LASER_FOCUS ], [ 37, Moves.WATER_PULSE ],
[ 44, Moves.DRAGON_PULSE ], [ 44, Moves.DRAGON_PULSE ],
[ 51, Moves.HYDRO_PUMP ], [ 51, Moves.HYDRO_PUMP ],
[ 58, Moves.DRAGON_DANCE ], [ 58, Moves.DRAGON_DANCE ],
[ 65, Moves.RAIN_DANCE ], [ 65, Moves.RAIN_DANCE ],
[ 72, Moves.WAVE_CRASH ],
], ],
[Species.PHANPY]: [ [Species.PHANPY]: [
[ 1, Moves.TACKLE ], [ 1, Moves.TACKLE ],
@ -3974,10 +3975,9 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 35, Moves.RECOVER ], [ 35, Moves.RECOVER ],
[ 40, Moves.DISCHARGE ], [ 40, Moves.DISCHARGE ],
[ 45, Moves.TRI_ATTACK ], [ 45, Moves.TRI_ATTACK ],
[ 50, Moves.MAGIC_COAT ], [ 50, Moves.LOCK_ON ],
[ 55, Moves.LOCK_ON ], [ 55, Moves.ZAP_CANNON ],
[ 60, Moves.ZAP_CANNON ], [ 60, Moves.HYPER_BEAM ],
[ 65, Moves.HYPER_BEAM ],
], ],
[Species.STANTLER]: [ [Species.STANTLER]: [
[ 1, Moves.TACKLE ], [ 1, Moves.TACKLE ],
@ -4268,7 +4268,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 72, Moves.FIRE_BLAST ], [ 72, Moves.FIRE_BLAST ],
[ 81, Moves.FUTURE_SIGHT ], [ 81, Moves.FUTURE_SIGHT ],
[ 90, Moves.SKY_ATTACK ], [ 90, Moves.SKY_ATTACK ],
[ 99, Moves.BURN_UP ], [ 99, Moves.OVERHEAT ],
], ],
[Species.CELEBI]: [ [Species.CELEBI]: [
[ 1, Moves.CONFUSION ], [ 1, Moves.CONFUSION ],

View File

@ -134,4 +134,44 @@ export function getStatusEffectCatchRateMultiplier(statusEffect: StatusEffect):
} }
return 1; return 1;
}
/**
* Returns a random non-volatile StatusEffect
*/
export function generateRandomStatusEffect(): StatusEffect {
return Utils.randIntRange(1, 6);
}
/**
* Returns a random non-volatile StatusEffect between the two provided
* @param statusEffectA The first StatusEffect
* @param statusEffectA The second StatusEffect
*/
export function getRandomStatusEffect(statusEffectA: StatusEffect, statusEffectB: StatusEffect): StatusEffect {
if (statusEffectA === StatusEffect.NONE || statusEffectA === StatusEffect.FAINT) {
return statusEffectB;
}
if (statusEffectB === StatusEffect.NONE || statusEffectB === StatusEffect.FAINT) {
return statusEffectA;
}
return Utils.randIntRange(0, 2) ? statusEffectA : statusEffectB;
}
/**
* Returns a random non-volatile StatusEffect between the two provided
* @param statusA The first Status
* @param statusB The second Status
*/
export function getRandomStatus(statusA: Status, statusB: Status): Status {
if (statusA === undefined || statusA.effect === StatusEffect.NONE || statusA.effect === StatusEffect.FAINT) {
return statusB;
}
if (statusB === undefined || statusB.effect === StatusEffect.NONE || statusB.effect === StatusEffect.FAINT) {
return statusA;
}
return Utils.randIntRange(0, 2) ? statusA : statusB;
} }

View File

@ -14,7 +14,7 @@ import { AttackTypeBoosterModifier, DamageMoneyRewardModifier, EnemyDamageBooste
import { PokeballType } from '../data/pokeball'; import { PokeballType } from '../data/pokeball';
import { Gender } from '../data/gender'; import { Gender } from '../data/gender';
import { initMoveAnim, loadMoveAnimAssets } from '../data/battle-anims'; import { initMoveAnim, loadMoveAnimAssets } from '../data/battle-anims';
import { Status, StatusEffect } from '../data/status-effect'; import { Status, StatusEffect, getRandomStatus } from '../data/status-effect';
import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from '../data/pokemon-evolutions'; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from '../data/pokemon-evolutions';
import { reverseCompatibleTms, tmSpecies } from '../data/tms'; import { reverseCompatibleTms, tmSpecies } from '../data/tms';
import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase } from '../phases'; import { DamagePhase, FaintPhase, LearnMovePhase, ObtainStatusEffectPhase, StatChangePhase, SwitchSummonPhase } from '../phases';
@ -43,8 +43,8 @@ import { Nature, getNatureStatMultiplier } from '../data/nature';
import { SpeciesFormChange, SpeciesFormChangeActiveTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangeStatusEffectTrigger } from '../data/pokemon-forms'; import { SpeciesFormChange, SpeciesFormChangeActiveTrigger, SpeciesFormChangeMoveLearnedTrigger, SpeciesFormChangePostMoveTrigger, SpeciesFormChangeStatusEffectTrigger } from '../data/pokemon-forms';
import { TerrainType } from '../data/terrain'; import { TerrainType } from '../data/terrain';
import { TrainerSlot } from '../data/trainer-config'; import { TrainerSlot } from '../data/trainer-config';
import { ABILITY_OVERRIDE, MOVE_OVERRIDE, MOVE_OVERRIDE_2, OPP_ABILITY_OVERRIDE, OPP_MOVE_OVERRIDE, OPP_MOVE_OVERRIDE_2, OPP_SHINY_OVERRIDE, OPP_VARIANT_OVERRIDE } from '../overrides';
import { BerryType } from '../data/berry'; import { BerryType } from '../data/berry';
import { ABILITY_OVERRIDE, MOVE_OVERRIDE, OPP_ABILITY_OVERRIDE, OPP_MOVE_OVERRIDE, OPP_SHINY_OVERRIDE, OPP_VARIANT_OVERRIDE } from '../overrides';
import i18next from '../plugins/i18n'; import i18next from '../plugins/i18n';
export enum FieldPosition { export enum FieldPosition {
@ -725,6 +725,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.moveset[0] = new PokemonMove(MOVE_OVERRIDE, Math.min(this.moveset[0].ppUsed, allMoves[MOVE_OVERRIDE].pp)); this.moveset[0] = new PokemonMove(MOVE_OVERRIDE, Math.min(this.moveset[0].ppUsed, allMoves[MOVE_OVERRIDE].pp));
else if (OPP_MOVE_OVERRIDE && !this.isPlayer()) else if (OPP_MOVE_OVERRIDE && !this.isPlayer())
this.moveset[0] = new PokemonMove(OPP_MOVE_OVERRIDE, Math.min(this.moveset[0].ppUsed, allMoves[OPP_MOVE_OVERRIDE].pp)); this.moveset[0] = new PokemonMove(OPP_MOVE_OVERRIDE, Math.min(this.moveset[0].ppUsed, allMoves[OPP_MOVE_OVERRIDE].pp));
if (MOVE_OVERRIDE_2 && this.isPlayer())
this.moveset[1] = new PokemonMove(MOVE_OVERRIDE_2, Math.min(this.moveset[1].ppUsed, allMoves[MOVE_OVERRIDE_2].pp));
else if (OPP_MOVE_OVERRIDE_2 && !this.isPlayer())
this.moveset[1] = new PokemonMove(OPP_MOVE_OVERRIDE_2, Math.min(this.moveset[1].ppUsed, allMoves[OPP_MOVE_OVERRIDE_2].pp));
return ret; return ret;
} }
@ -2474,9 +2479,10 @@ export class PlayerPokemon extends Pokemon {
if (newEvolution.condition.predicate(this)) { if (newEvolution.condition.predicate(this)) {
const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, undefined, this.shiny, this.variant, this.ivs, this.nature); const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, undefined, this.shiny, this.variant, this.ivs, this.nature);
newPokemon.natureOverride = this.natureOverride; newPokemon.natureOverride = this.natureOverride;
newPokemon.passive = this.passive;
newPokemon.moveset = this.moveset.slice();
newPokemon.moveset = this.copyMoveset(); newPokemon.moveset = this.copyMoveset();
newPokemon.luck = this.luck; newPokemon.luck = this.luck;
newPokemon.fusionSpecies = this.fusionSpecies; newPokemon.fusionSpecies = this.fusionSpecies;
newPokemon.fusionFormIndex = this.fusionFormIndex; newPokemon.fusionFormIndex = this.fusionFormIndex;
newPokemon.fusionAbilityIndex = this.fusionAbilityIndex; newPokemon.fusionAbilityIndex = this.fusionAbilityIndex;
@ -2536,6 +2542,10 @@ export class PlayerPokemon extends Pokemon {
this.generateCompatibleTms(); this.generateCompatibleTms();
} }
/**
* Returns a Promise to fuse two PlayerPokemon together
* @param pokemon The PlayerPokemon to fuse to this one
*/
fuse(pokemon: PlayerPokemon): Promise<void> { fuse(pokemon: PlayerPokemon): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
this.fusionSpecies = pokemon.species; this.fusionSpecies = pokemon.species;
@ -2549,8 +2559,25 @@ export class PlayerPokemon extends Pokemon {
this.scene.validateAchv(achvs.SPLICE); this.scene.validateAchv(achvs.SPLICE);
this.scene.gameData.gameStats.pokemonFused++; this.scene.gameData.gameStats.pokemonFused++;
// Store the average HP% that each Pokemon has
const newHpPercent = ((pokemon.hp / pokemon.stats[Stat.HP]) + (this.hp / this.stats[Stat.HP])) / 2;
this.generateName(); this.generateName();
this.calculateStats(); this.calculateStats();
// Set this Pokemon's HP to the average % of both fusion components
this.hp = Math.round(this.stats[Stat.HP] * newHpPercent);
if (!this.isFainted()) {
// If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum
this.hp = Math.min(this.hp, this.stats[Stat.HP]);
this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two
}
else if (!pokemon.isFainted()) {
// If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero
this.hp = Math.max(this.hp, 1);
this.status = pokemon.status; // Inherit the other Pokemon's status
}
this.generateCompatibleTms(); this.generateCompatibleTms();
this.updateInfo(true); this.updateInfo(true);
const fusedPartyMemberIndex = this.scene.getParty().indexOf(pokemon); const fusedPartyMemberIndex = this.scene.getParty().indexOf(pokemon);
@ -2731,8 +2758,8 @@ export class EnemyPokemon extends Pokemon {
let targetScores: integer[] = []; let targetScores: integer[] = [];
for (let mt of moveTargets[move.id]) { for (let mt of moveTargets[move.id]) {
// Prevent a target score from being calculated when there isn't a target // Prevent a target score from being calculated when the target is whoever attacks the user
if (mt === -1) if (mt === BattlerIndex.ATTACKER)
break; break;
const target = this.scene.getField()[mt]; const target = this.scene.getField()[mt];
@ -2806,10 +2833,10 @@ export class EnemyPokemon extends Pokemon {
}); });
if (!sortedBenefitScores.length) { if (!sortedBenefitScores.length) {
// Set target to -1 when using a counter move // Set target to BattlerIndex.ATTACKER when using a counter move
// This is the same as when the player does so // This is the same as when the player does so
if (!!move.findAttr(attr => attr instanceof CounterDamageAttr)) if (!!move.findAttr(attr => attr instanceof CounterDamageAttr))
return [-1]; return [BattlerIndex.ATTACKER];
return []; return [];
} }

View File

@ -31,6 +31,8 @@ export class LoadingScene extends SceneBase {
this.loadAtlas('bg', 'ui'); this.loadAtlas('bg', 'ui');
this.loadImage('command_fight_labels', 'ui'); this.loadImage('command_fight_labels', 'ui');
this.loadAtlas('prompt', 'ui'); this.loadAtlas('prompt', 'ui');
this.loadImage('candy', 'ui');
this.loadImage('candy_overlay', 'ui');
this.loadImage('cursor', 'ui'); this.loadImage('cursor', 'ui');
this.loadImage('cursor_reverse', 'ui'); this.loadImage('cursor_reverse', 'ui');
for (let wv of Utils.getEnumValues(WindowVariant)) { for (let wv of Utils.getEnumValues(WindowVariant)) {

View File

@ -9,7 +9,7 @@ export const battle: SimpleTranslationEntries = {
"trainerComeBack": "{{trainerName}} retire {{pokemonName}} !", "trainerComeBack": "{{trainerName}} retire {{pokemonName}} !",
"playerGo": "{{pokemonName}} ! Go !", "playerGo": "{{pokemonName}} ! Go !",
"trainerGo": "{{pokemonName}} est envoyé par\n{{trainerName}} !", "trainerGo": "{{pokemonName}} est envoyé par\n{{trainerName}} !",
"switchQuestion": "Voulez-vous changer\n{{pokemonName}} ?", "switchQuestion": "Voulez-vous changer\nvotre {{pokemonName}} ?",
"trainerDefeated": `Vous avez battu\n{{trainerName}} !`, "trainerDefeated": `Vous avez battu\n{{trainerName}} !`,
"pokemonCaught": "Vous avez attrapé {{pokemonName}} !", "pokemonCaught": "Vous avez attrapé {{pokemonName}} !",
"pokemon": "Pokémon", "pokemon": "Pokémon",

View File

@ -1151,7 +1151,7 @@ const enemyBuffModifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.ENEMY_ATTACK_FREEZE_CHANCE, 2), new WeightedModifierType(modifierTypes.ENEMY_ATTACK_FREEZE_CHANCE, 2),
new WeightedModifierType(modifierTypes.ENEMY_ATTACK_BURN_CHANCE, 2), new WeightedModifierType(modifierTypes.ENEMY_ATTACK_BURN_CHANCE, 2),
new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 10), new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 10),
new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 10000), new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 5),
new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 1) new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 1)
].map(m => { m.setTier(ModifierTier.COMMON); return m; }), ].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
[ModifierTier.GREAT]: [ [ModifierTier.GREAT]: [
@ -1162,12 +1162,12 @@ const enemyBuffModifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 1) new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 1)
].map(m => { m.setTier(ModifierTier.GREAT); return m; }), ].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
[ModifierTier.ULTRA]: [ [ModifierTier.ULTRA]: [
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 5), new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 10),
new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 5), new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 10),
new WeightedModifierType(modifierTypes.ENEMY_HEAL, 5), new WeightedModifierType(modifierTypes.ENEMY_HEAL, 10),
new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 5), new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 10),
new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 5), new WeightedModifierType(modifierTypes.ENEMY_ENDURE_CHANCE, 10),
new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 300) new WeightedModifierType(modifierTypes.ENEMY_FUSED_CHANCE, 5)
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
[ModifierTier.ROGUE]: [ ].map(m => { m.setTier(ModifierTier.ROGUE); return m; }), [ModifierTier.ROGUE]: [ ].map(m => { m.setTier(ModifierTier.ROGUE); return m; }),
[ModifierTier.MASTER]: [ ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) [ModifierTier.MASTER]: [ ].map(m => { m.setTier(ModifierTier.MASTER); return m; })

View File

@ -15,9 +15,11 @@ export const WEATHER_OVERRIDE = WeatherType.NONE;
export const ABILITY_OVERRIDE = Abilities.NONE; export const ABILITY_OVERRIDE = Abilities.NONE;
export const MOVE_OVERRIDE = Moves.NONE; export const MOVE_OVERRIDE = Moves.NONE;
export const MOVE_OVERRIDE_2 = Moves.NONE;
export const OPP_SPECIES_OVERRIDE = 0; export const OPP_SPECIES_OVERRIDE = 0;
export const OPP_ABILITY_OVERRIDE = Abilities.NONE; export const OPP_ABILITY_OVERRIDE = Abilities.NONE;
export const OPP_MOVE_OVERRIDE = Moves.NONE; export const OPP_MOVE_OVERRIDE = Moves.NONE;
export const OPP_MOVE_OVERRIDE_2 = Moves.NONE;
export const OPP_SHINY_OVERRIDE = false; export const OPP_SHINY_OVERRIDE = false;
export const OPP_VARIANT_OVERRIDE = 0; export const OPP_VARIANT_OVERRIDE = 0;

View File

@ -2,7 +2,7 @@ import BattleScene, { bypassLogin, startingWave } from "./battle-scene";
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon"; import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult, TurnMove } from "./field/pokemon";
import * as Utils from './utils'; import * as Utils from './utils';
import { Moves } from "./data/enums/moves"; 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, OneHitKOAccuracyAttr, ForceSwitchOutAttr } 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, ForceSwitchOutAttr, VariableTargetAttr } from "./data/move";
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler"; import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat"; import { Stat } from "./data/pokemon-stat";
@ -30,7 +30,7 @@ import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, get
import { TempBattleStat } from "./data/temp-battle-stat"; import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag";
import { ArenaTagType } from "./data/enums/arena-tag-type"; import { ArenaTagType } from "./data/enums/arena-tag-type";
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, PreventBerryUseAbAttr } 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, PreventBerryUseAbAttr, StatChangeCopyAbAttr } from "./data/ability";
import { Unlockables, getUnlockableName } from "./system/unlockables"; import { Unlockables, getUnlockableName } from "./system/unlockables";
import { getBiomeKey } from "./field/arena"; import { getBiomeKey } from "./field/arena";
import { BattleType, BattlerIndex, TurnCommand } from "./battle"; import { BattleType, BattlerIndex, TurnCommand } from "./battle";
@ -57,6 +57,7 @@ import { fetchDailyRunSeed, getDailyRunStarters } from "./data/daily-run";
import { GameModes, gameModes } from "./game-mode"; import { GameModes, gameModes } from "./game-mode";
import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species"; import { getPokemonSpecies, speciesStarters } from "./data/pokemon-species";
import i18next from './plugins/i18n'; import i18next from './plugins/i18n';
import { Abilities } from "./data/enums/abilities";
import { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE } from './overrides'; import { STARTER_FORM_OVERRIDE, STARTER_SPECIES_OVERRIDE } from './overrides';
export class LoginPhase extends Phase { export class LoginPhase extends Phase {
@ -1178,14 +1179,25 @@ export class SummonPhase extends PartyMemberPokemonPhase {
this.preSummon(); this.preSummon();
} }
/**
* Sends out a Pokemon before the battle begins and shows the appropriate messages
*/
preSummon(): void { preSummon(): void {
const partyMember = this.getPokemon(); const partyMember = this.getPokemon();
// If the Pokemon about to be sent out is fainted, switch to the first non-fainted Pokemon
if (partyMember.isFainted()) { if (partyMember.isFainted()) {
console.warn("The Pokemon about to be sent out is fainted. Attempting to resolve...");
const party = this.getParty(); const party = this.getParty();
const nonFaintedIndex = party.slice(this.partyMemberIndex).findIndex(p => !p.isFainted()) + this.partyMemberIndex;
const nonFaintedPartyMember = party[nonFaintedIndex]; const nonFaintedIndex = party.findIndex(x => !x.isFainted()); // Find the first non-fainted Pokemon index
party[nonFaintedIndex] = partyMember; if (nonFaintedIndex === -1) {
party[this.partyMemberIndex] = nonFaintedPartyMember; console.error("Party Details:\n", party);
throw new Error("All available Pokemon were fainted!");
}
// Swaps the fainted Pokemon and the first non-fainted Pokemon in the party
[party[this.partyMemberIndex], party[nonFaintedIndex]] = [party[nonFaintedIndex], party[this.partyMemberIndex]];
console.warn("Swapped %s %O with %s %O", partyMember?.name, partyMember, party[0]?.name, party[0]);
} }
if (this.player) { if (this.player) {
@ -1729,7 +1741,7 @@ export class CommandPhase extends FieldPhase {
}, null, true); }, null, true);
} else if (cursor < 5) { } else if (cursor < 5) {
const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true)); const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true));
if (targetPokemon.isBoss() && targetPokemon.bossSegmentIndex >= 1 && cursor < PokeballType.MASTER_BALL) { if (targetPokemon.isBoss() && targetPokemon.bossSegmentIndex >= 1 && !targetPokemon.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) {
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(i18next.t('battle:noPokeballStrong'), null, () => { this.scene.ui.showText(i18next.t('battle:noPokeballStrong'), null, () => {
@ -2399,7 +2411,7 @@ export class MoveEffectPhase extends PokemonPhase {
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ])); const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ]));
const activeTargets = targets.map(t => t.isActive(true)); const activeTargets = targets.map(t => t.isActive(true));
if (!activeTargets.length || (!this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) { if (!activeTargets.length || (!this.move.getMove().getAttrs(VariableTargetAttr).length && !this.move.getMove().isMultiTarget() && !targetHitChecks[this.targets[0]])) {
user.turnData.hitCount = 1; user.turnData.hitCount = 1;
user.turnData.hitsLeft = 1; user.turnData.hitsLeft = 1;
if (activeTargets.length) { if (activeTargets.length) {
@ -2660,8 +2672,9 @@ export class StatChangePhase extends PokemonPhase {
private levels: integer; private levels: integer;
private showMessage: boolean; private showMessage: boolean;
private ignoreAbilities: boolean; private ignoreAbilities: boolean;
private canBeCopied: boolean;
constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false) { constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true) {
super(scene, battlerIndex); super(scene, battlerIndex);
this.selfTarget = selfTarget; this.selfTarget = selfTarget;
@ -2669,6 +2682,7 @@ export class StatChangePhase extends PokemonPhase {
this.levels = levels; this.levels = levels;
this.showMessage = showMessage; this.showMessage = showMessage;
this.ignoreAbilities = ignoreAbilities; this.ignoreAbilities = ignoreAbilities;
this.canBeCopied = canBeCopied;
} }
start() { start() {
@ -2716,8 +2730,12 @@ export class StatChangePhase extends PokemonPhase {
for (let stat of filteredStats) for (let stat of filteredStats)
pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6); pokemon.summonData.battleStats[stat] = Math.max(Math.min(pokemon.summonData.battleStats[stat] + levels.value, 6), -6);
if (levels.value > 0 && this.canBeCopied)
for (let opponent of pokemon.getOpponents())
applyAbAttrs(StatChangeCopyAbAttr, opponent, null, this.stats, levels.value);
applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget); applyPostStatChangeAbAttrs(PostStatChangeAbAttr, pokemon, filteredStats, this.levels, this.selfTarget);
pokemon.updateInfo(); pokemon.updateInfo();
handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end()); handleTutorial(this.scene, Tutorial.Stat_Change).then(() => super.end());
@ -3219,19 +3237,22 @@ export class VictoryPhase extends PokemonPhase {
const expShareModifier = this.scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier; const expShareModifier = this.scene.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
const expBalanceModifier = this.scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier; const expBalanceModifier = this.scene.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier;
const expPartyMembers = party.filter(p => p.hp && p.level < this.scene.getMaxExpLevel()); const nonFaintedPartyMembers = party.filter(p => p.hp);
const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.scene.getMaxExpLevel());
const partyMemberExp = []; const partyMemberExp = [];
if (participantIds.size) { if (participantIds.size) {
let expValue = this.getPokemon().getExpValue(); let expValue = this.getPokemon().getExpValue();
if (this.scene.currentBattle.battleType === BattleType.TRAINER) if (this.scene.currentBattle.battleType === BattleType.TRAINER)
expValue = Math.floor(expValue * 1.5); expValue = Math.floor(expValue * 1.5);
for (let partyMember of expPartyMembers) { for (let partyMember of nonFaintedPartyMembers) {
const pId = partyMember.id; const pId = partyMember.id;
const participated = participantIds.has(pId); const participated = participantIds.has(pId);
if (participated) if (participated)
partyMember.addFriendship(2); partyMember.addFriendship(2);
else if (!expShareModifier) { if (!expPartyMembers.includes(partyMember))
continue;
if (!participated && !expShareModifier) {
partyMemberExp.push(0); partyMemberExp.push(0);
continue; continue;
} }
@ -4340,7 +4361,7 @@ export class SelectModifierPhase extends BattlePhase {
const isMoveModifier = modifierType instanceof PokemonMoveModifierType; const isMoveModifier = modifierType instanceof PokemonMoveModifierType;
const isTmModifier = modifierType instanceof TmModifierType; const isTmModifier = modifierType instanceof TmModifierType;
const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType; const isRememberMoveModifier = modifierType instanceof RememberMoveModifierType;
const isPpRestoreModifier = modifierType instanceof PokemonPpRestoreModifierType; const isPpRestoreModifier = (modifierType instanceof PokemonPpRestoreModifierType || modifierType instanceof PokemonPpUpModifierType);
const partyUiMode = isMoveModifier ? PartyUiMode.MOVE_MODIFIER const partyUiMode = isMoveModifier ? PartyUiMode.MOVE_MODIFIER
: isTmModifier ? PartyUiMode.TM_MODIFIER : isTmModifier ? PartyUiMode.TM_MODIFIER
: isRememberMoveModifier ? PartyUiMode.REMEMBER_MOVE_MODIFIER : isRememberMoveModifier ? PartyUiMode.REMEMBER_MOVE_MODIFIER

View File

@ -128,6 +128,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
private starterSelectMessageBox: Phaser.GameObjects.NineSlice; private starterSelectMessageBox: Phaser.GameObjects.NineSlice;
private starterSelectMessageBoxContainer: Phaser.GameObjects.Container; private starterSelectMessageBoxContainer: Phaser.GameObjects.Container;
private statsContainer: StatsContainer; private statsContainer: StatsContainer;
private pokemonFormText: Phaser.GameObjects.Text;
private genMode: boolean; private genMode: boolean;
private statsMode: boolean; private statsMode: boolean;
@ -434,6 +435,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCandyIcon.setOrigin(0, 0); this.pokemonCandyIcon.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonCandyIcon); this.starterSelectContainer.add(this.pokemonCandyIcon);
this.pokemonFormText = addTextObject(this.scene, 6, 42, 'Form', TextStyle.WINDOW_ALT, { fontSize: '42px' });
this.pokemonFormText.setOrigin(0, 0);
this.starterSelectContainer.add(this.pokemonFormText);
this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay'); this.pokemonCandyOverlayIcon = this.scene.add.sprite(1, 12, 'items', 'candy_overlay');
this.pokemonCandyOverlayIcon.setScale(0.5); this.pokemonCandyOverlayIcon.setScale(0.5);
this.pokemonCandyOverlayIcon.setOrigin(0, 0); this.pokemonCandyOverlayIcon.setOrigin(0, 0);
@ -1288,6 +1293,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCandyOverlayIcon.setVisible(true); this.pokemonCandyOverlayIcon.setVisible(true);
this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`); this.pokemonCandyCountText.setText(`x${this.scene.gameData.starterData[species.speciesId].candyCount}`);
this.pokemonCandyCountText.setVisible(true); this.pokemonCandyCountText.setVisible(true);
this.pokemonFormText.setVisible(true);
} }
this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE); this.iconAnimHandler.addOrUpdate(this.starterSelectGenIconContainers[species.generation - 1].getAt(this.genSpecies[species.generation - 1].indexOf(species)) as Phaser.GameObjects.Sprite, PokemonIconAnimMode.PASSIVE);
@ -1337,6 +1343,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCandyIcon.setVisible(false); this.pokemonCandyIcon.setVisible(false);
this.pokemonCandyOverlayIcon.setVisible(false); this.pokemonCandyOverlayIcon.setVisible(false);
this.pokemonCandyCountText.setVisible(false); this.pokemonCandyCountText.setVisible(false);
this.pokemonFormText.setVisible(false);
const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, true, true); const defaultDexAttr = this.scene.gameData.getSpeciesDefaultDexAttr(species, true, true);
const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const defaultAbilityIndex = this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species);
@ -1363,6 +1370,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.pokemonCandyIcon.setVisible(false); this.pokemonCandyIcon.setVisible(false);
this.pokemonCandyOverlayIcon.setVisible(false); this.pokemonCandyOverlayIcon.setVisible(false);
this.pokemonCandyCountText.setVisible(false); this.pokemonCandyCountText.setVisible(false);
this.pokemonFormText.setVisible(false);
this.setSpeciesDetails(species, false, 0, false, 0, 0, 0); this.setSpeciesDetails(species, false, 0, false, 0, 0, 0);
this.pokemonSprite.clearTint(); this.pokemonSprite.clearTint();
@ -1522,6 +1530,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
this.starterMoveset.push(...availableStarterMoves.filter(sm => this.starterMoveset.indexOf(sm) === -1).slice(0, 4 - this.starterMoveset.length)); this.starterMoveset.push(...availableStarterMoves.filter(sm => this.starterMoveset.indexOf(sm) === -1).slice(0, 4 - this.starterMoveset.length));
const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex); const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex);
const formText = species?.forms[formIndex]?.formKey.split('-');
for (let i = 0; i < formText?.length; i++)
formText[i] = formText[i].charAt(0).toUpperCase() + formText[i].substring(1);
this.pokemonFormText.setText(formText?.join(' '));
this.setTypeIcons(speciesForm.type1, speciesForm.type2); this.setTypeIcons(speciesForm.type1, speciesForm.type2);
} else { } else {
this.pokemonAbilityText.setText(''); this.pokemonAbilityText.setText('');

View File

@ -1,8 +1,10 @@
import BattleScene, { Button } from "../battle-scene"; import BattleScene, { Button, starterColors } from "../battle-scene";
import { Mode } from "./ui"; import { Mode } from "./ui";
import UiHandler from "./ui-handler"; import UiHandler from "./ui-handler";
import * as Utils from "../utils"; import * as Utils from "../utils";
import { PlayerPokemon } from "../field/pokemon"; import { PlayerPokemon } from "../field/pokemon";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from '../data/pokemon-species';
import { argbFromRgba } from "@material/material-color-utilities";
import { Type, getTypeRgb } from "../data/type"; import { Type, getTypeRgb } from "../data/type";
import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "./text"; import { TextStyle, addBBCodeTextObject, addTextObject, getBBCodeFrag, getTextColor } from "./text";
import Move, { MoveCategory } from "../data/move"; import Move, { MoveCategory } from "../data/move";
@ -44,6 +46,9 @@ export default class SummaryUiHandler extends UiHandler {
private genderText: Phaser.GameObjects.Text; private genderText: Phaser.GameObjects.Text;
private shinyIcon: Phaser.GameObjects.Image; private shinyIcon: Phaser.GameObjects.Image;
private fusionShinyIcon: Phaser.GameObjects.Image; private fusionShinyIcon: Phaser.GameObjects.Image;
private candyShadow: Phaser.GameObjects.Sprite;
private candyIcon: Phaser.GameObjects.Sprite;
private candyOverlay: Phaser.GameObjects.Sprite;
private statusContainer: Phaser.GameObjects.Container; private statusContainer: Phaser.GameObjects.Container;
private status: Phaser.GameObjects.Image; private status: Phaser.GameObjects.Image;
private summaryPageContainer: Phaser.GameObjects.Container; private summaryPageContainer: Phaser.GameObjects.Container;
@ -136,6 +141,20 @@ export default class SummaryUiHandler extends UiHandler {
this.pokeball.setOrigin(0, 1); this.pokeball.setOrigin(0, 1);
this.summaryContainer.add(this.pokeball); this.summaryContainer.add(this.pokeball);
this.candyShadow = this.scene.add.sprite(13, -140, 'candy');
this.candyShadow.setTint(0x141414)
this.candyShadow.setScale(0.8);
this.candyShadow.setInteractive(new Phaser.Geom.Rectangle(0, 0, 16, 16), Phaser.Geom.Rectangle.Contains);
this.summaryContainer.add(this.candyShadow);
this.candyIcon = this.scene.add.sprite(13, -140, 'candy');
this.candyIcon.setScale(0.8);
this.summaryContainer.add(this.candyIcon);
this.candyOverlay = this.scene.add.sprite(13, -140, 'candy_overlay');
this.candyOverlay.setScale(0.8);
this.summaryContainer.add(this.candyOverlay);
this.levelText = addTextObject(this.scene, 36, -17, '', TextStyle.SUMMARY_ALT); this.levelText = addTextObject(this.scene, 36, -17, '', TextStyle.SUMMARY_ALT);
this.levelText.setOrigin(0, 1); this.levelText.setOrigin(0, 1);
this.summaryContainer.add(this.levelText); this.summaryContainer.add(this.levelText);
@ -222,6 +241,10 @@ export default class SummaryUiHandler extends UiHandler {
this.shinyOverlay.setVisible(this.pokemon.isShiny()); this.shinyOverlay.setVisible(this.pokemon.isShiny());
const colorScheme = starterColors[this.pokemon.species.getRootSpeciesId()];
this.candyIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[0])));
this.candyOverlay.setTint(argbFromRgba(Utils.rgbHexToRgba(colorScheme[1])));
this.numberText.setText(Utils.padInt(this.pokemon.species.speciesId, 4)); this.numberText.setText(Utils.padInt(this.pokemon.species.speciesId, 4));
this.numberText.setColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD)); this.numberText.setColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD));
this.numberText.setShadowColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true)); this.numberText.setShadowColor(this.getTextColor(!this.pokemon.isShiny() ? TextStyle.SUMMARY : TextStyle.SUMMARY_GOLD, true));
@ -251,6 +274,21 @@ export default class SummaryUiHandler extends UiHandler {
this.splicedIcon.on('pointerout', () => (this.scene as BattleScene).ui.hideTooltip()); this.splicedIcon.on('pointerout', () => (this.scene as BattleScene).ui.hideTooltip());
} }
var currentFriendship = this.scene.gameData.starterData[this.pokemon.species.getRootSpeciesId()].friendship;
if (!currentFriendship || currentFriendship === undefined)
currentFriendship = 0;
const friendshipCap = getStarterValueFriendshipCap(speciesStarters[this.pokemon.species.getRootSpeciesId()]);
const candyCropY = 16 - (16 * (currentFriendship / friendshipCap));
if (this.candyShadow.visible) {
this.candyShadow.on('pointerover', () => (this.scene as BattleScene).ui.showTooltip(null, `${currentFriendship}/${friendshipCap}`, true));
this.candyShadow.on('pointerout', () => (this.scene as BattleScene).ui.hideTooltip());
}
this.candyIcon.setCrop(0,candyCropY,16, 16);
this.candyOverlay.setCrop(0,candyCropY,16, 16);
const doubleShiny = isFusion && this.pokemon.shiny && this.pokemon.fusionShiny; const doubleShiny = isFusion && this.pokemon.shiny && this.pokemon.fusionShiny;
const baseVariant = !doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant; const baseVariant = !doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant;

View File

@ -62,6 +62,11 @@ export function padInt(value: integer, length: integer, padWith?: string): strin
return valueStr; return valueStr;
} }
/**
* Returns a random integer between min and min + range
* @param range The amount of possible numbers
* @param min The starting number
*/
export function randInt(range: integer, min: integer = 0): integer { export function randInt(range: integer, min: integer = 0): integer {
if (range === 1) if (range === 1)
return min; return min;
@ -74,6 +79,11 @@ export function randSeedInt(range: integer, min: integer = 0): integer {
return Phaser.Math.RND.integerInRange(min, (range - 1) + min); return Phaser.Math.RND.integerInRange(min, (range - 1) + min);
} }
/**
* Returns a random integer between min and max (non-inclusive)
* @param min The lowest number
* @param max The highest number
*/
export function randIntRange(min: integer, max: integer): integer { export function randIntRange(min: integer, max: integer): integer {
return randInt(max - min, min); return randInt(max - min, min);
} }