Add King's Rock and more berry types
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 290 B |
After Width: | Height: | Size: 295 B |
After Width: | Height: | Size: 336 B |
After Width: | Height: | Size: 322 B |
After Width: | Height: | Size: 347 B |
After Width: | Height: | Size: 375 B |
After Width: | Height: | Size: 345 B |
After Width: | Height: | Size: 320 B |
|
@ -5,7 +5,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, HitsTagAttr, Mis
|
||||||
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";
|
||||||
import { BerryModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HealingBoosterModifier, HeldItemTransferModifier, HitHealModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, TempBattleStatBoosterModifier, TurnHealModifier } from "./modifier/modifier";
|
import { BerryModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HeldItemTransferModifier, HitHealModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, TempBattleStatBoosterModifier, TurnHealModifier } from "./modifier/modifier";
|
||||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
||||||
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
|
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims";
|
||||||
|
@ -768,7 +768,7 @@ export abstract class MovePhase extends BattlePhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.cancelled) {
|
if (this.cancelled) {
|
||||||
this.pokemon.getMoveHistory().push({ move: Moves.NONE, result: MoveResult.FAILED });
|
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAILED });
|
||||||
this.end();
|
this.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -783,7 +783,7 @@ export abstract class MovePhase extends BattlePhase {
|
||||||
if (success)
|
if (success)
|
||||||
this.scene.unshiftPhase(this.getEffectPhase());
|
this.scene.unshiftPhase(this.getEffectPhase());
|
||||||
else {
|
else {
|
||||||
this.pokemon.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.FAILED, virtual: this.move.virtual });
|
this.pokemon.pushMoveHistory({ move: this.move.moveId, result: MoveResult.FAILED, virtual: this.move.virtual });
|
||||||
this.scene.queueMessage('But it failed!');
|
this.scene.queueMessage('But it failed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,7 +900,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
||||||
|
|
||||||
if (!this.hitCheck()) {
|
if (!this.hitCheck()) {
|
||||||
this.scene.queueMessage(getPokemonMessage(user, '\'s\nattack missed!'));
|
this.scene.queueMessage(getPokemonMessage(user, '\'s\nattack missed!'));
|
||||||
user.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.MISSED, virtual: this.move.virtual });
|
user.pushMoveHistory({ move: this.move.moveId, result: MoveResult.MISSED, virtual: this.move.virtual });
|
||||||
applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove());
|
applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove());
|
||||||
this.end();
|
this.end();
|
||||||
return;
|
return;
|
||||||
|
@ -911,9 +911,15 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
||||||
new MoveAnim(this.move.getMove().id as Moves, user, target).play(this.scene, () => {
|
new MoveAnim(this.move.getMove().id as Moves, user, target).play(this.scene, () => {
|
||||||
const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT;
|
const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT;
|
||||||
++user.turnData.hitCount;
|
++user.turnData.hitCount;
|
||||||
user.getMoveHistory().push({ move: this.move.moveId, result: result, virtual: this.move.virtual });
|
user.pushMoveHistory({ move: this.move.moveId, result: result, virtual: this.move.virtual });
|
||||||
if (result !== MoveResult.NO_EFFECT && result !== MoveResult.FAILED) {
|
if (result !== MoveResult.NO_EFFECT && result !== MoveResult.FAILED) {
|
||||||
applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove());
|
applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove());
|
||||||
|
if (result < MoveResult.NO_EFFECT) {
|
||||||
|
const flinched = new Utils.BooleanHolder(false);
|
||||||
|
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
||||||
|
if (flinched.value)
|
||||||
|
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
||||||
|
}
|
||||||
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
||||||
if (!isProtected && target.hp && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length)
|
if (!isProtected && target.hp && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length)
|
||||||
applyMoveAttrs(MoveHitEffectAttr, user, target, this.move.getMove());
|
applyMoveAttrs(MoveHitEffectAttr, user, target, this.move.getMove());
|
||||||
|
|
|
@ -470,7 +470,7 @@ export default class BattleScene extends Phaser.Scene {
|
||||||
|
|
||||||
this.arenaBgTransition.setPosition(0, 0);
|
this.arenaBgTransition.setPosition(0, 0);
|
||||||
this.arenaPlayer.setPosition(340, 20);
|
this.arenaPlayer.setPosition(340, 20);
|
||||||
this.arenaPlayerTransition.setPosition(40, 2);
|
this.arenaPlayerTransition.setPosition(40, 20);
|
||||||
this.arenaEnemy.setPosition(-240, 13);
|
this.arenaEnemy.setPosition(-240, 13);
|
||||||
this.arenaNextEnemy.setPosition(-240, 13);
|
this.arenaNextEnemy.setPosition(-240, 13);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Pokemon from "../pokemon";
|
||||||
import { Stat } from "./pokemon-stat";
|
import { Stat } from "./pokemon-stat";
|
||||||
import { StatusEffect } from "./status-effect";
|
import { StatusEffect } from "./status-effect";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { Moves, allMoves } from "./move";
|
import { LapseBattlerTagAttr, Moves, allMoves } from "./move";
|
||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
|
|
||||||
export enum BattlerTagType {
|
export enum BattlerTagType {
|
||||||
|
@ -30,6 +30,7 @@ export enum BattlerTagType {
|
||||||
PROTECTED,
|
PROTECTED,
|
||||||
FLYING,
|
FLYING,
|
||||||
UNDERGROUND,
|
UNDERGROUND,
|
||||||
|
CRIT_BOOST,
|
||||||
NO_CRIT,
|
NO_CRIT,
|
||||||
BYPASS_SLEEP,
|
BYPASS_SLEEP,
|
||||||
IGNORE_FLYING
|
IGNORE_FLYING
|
||||||
|
@ -462,6 +463,28 @@ export class HideSpriteTag extends BattlerTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class CritBoostTag extends BattlerTag {
|
||||||
|
constructor(tagType: BattlerTagType, sourceMove: Moves) {
|
||||||
|
super(tagType, BattlerTagLapseType.TURN_END, 1, sourceMove);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd(pokemon: Pokemon): void {
|
||||||
|
super.onAdd(pokemon);
|
||||||
|
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is getting\npumped!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||||
|
return lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(pokemon: Pokemon): void {
|
||||||
|
super.onRemove(pokemon);
|
||||||
|
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' relaxed.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves, sourceId: integer): BattlerTag {
|
export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves, sourceId: integer): BattlerTag {
|
||||||
switch (tagType) {
|
switch (tagType) {
|
||||||
case BattlerTagType.RECHARGING:
|
case BattlerTagType.RECHARGING:
|
||||||
|
@ -501,6 +524,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
|
||||||
case BattlerTagType.FLYING:
|
case BattlerTagType.FLYING:
|
||||||
case BattlerTagType.UNDERGROUND:
|
case BattlerTagType.UNDERGROUND:
|
||||||
return new HideSpriteTag(tagType, turnCount, sourceMove);
|
return new HideSpriteTag(tagType, turnCount, sourceMove);
|
||||||
|
case BattlerTagType.CRIT_BOOST:
|
||||||
|
return new CritBoostTag(tagType, sourceMove);
|
||||||
case BattlerTagType.NO_CRIT:
|
case BattlerTagType.NO_CRIT:
|
||||||
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount, sourceMove);
|
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount, sourceMove);
|
||||||
case BattlerTagType.BYPASS_SLEEP:
|
case BattlerTagType.BYPASS_SLEEP:
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
import { MessagePhase, PokemonHealPhase } from "../battle-phases";
|
import { PokemonHealPhase, StatChangePhase } from "../battle-phases";
|
||||||
import { getPokemonMessage } from "../messages";
|
import { getPokemonMessage } from "../messages";
|
||||||
import Pokemon from "../pokemon";
|
import Pokemon, { MoveResult } from "../pokemon";
|
||||||
|
import { getBattleStatName } from "./battle-stat";
|
||||||
|
import { BattleStat } from "./battle-stat";
|
||||||
import { BattlerTagType } from "./battler-tag";
|
import { BattlerTagType } from "./battler-tag";
|
||||||
import { getStatusEffectHealText } from "./status-effect";
|
import { getStatusEffectHealText } from "./status-effect";
|
||||||
|
|
||||||
export enum BerryType {
|
export enum BerryType {
|
||||||
SITRUS,
|
SITRUS,
|
||||||
LUM
|
LUM,
|
||||||
|
ENIGMA,
|
||||||
|
LIECHI,
|
||||||
|
GANLON,
|
||||||
|
SALAC,
|
||||||
|
PETAYA,
|
||||||
|
APICOT,
|
||||||
|
LANSAT,
|
||||||
|
STARF
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBerryName(berryType: BerryType) {
|
export function getBerryName(berryType: BerryType) {
|
||||||
switch (berryType) {
|
return `${BerryType[berryType]} BERRY`;
|
||||||
case BerryType.SITRUS:
|
|
||||||
return 'SITRUS BERRY';
|
|
||||||
case BerryType.LUM:
|
|
||||||
return 'LUM BERRY';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBerryEffectDescription(berryType: BerryType) {
|
export function getBerryEffectDescription(berryType: BerryType) {
|
||||||
|
@ -24,6 +29,19 @@ export function getBerryEffectDescription(berryType: BerryType) {
|
||||||
return 'Restores 25% HP if HP is below 50%';
|
return 'Restores 25% HP if HP is below 50%';
|
||||||
case BerryType.LUM:
|
case BerryType.LUM:
|
||||||
return 'Cures any non-volatile status condition and confusion';
|
return 'Cures any non-volatile status condition and confusion';
|
||||||
|
case BerryType.ENIGMA:
|
||||||
|
return 'Restores 25% HP if hit by a super effective move';
|
||||||
|
case BerryType.LIECHI:
|
||||||
|
case BerryType.GANLON:
|
||||||
|
case BerryType.SALAC:
|
||||||
|
case BerryType.PETAYA:
|
||||||
|
case BerryType.APICOT:
|
||||||
|
const stat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||||
|
return `Raises ${getBattleStatName(stat)} if HP is below 25%`;
|
||||||
|
case BerryType.LANSAT:
|
||||||
|
return 'Raises critical hit ratio if HP is below 25%';
|
||||||
|
case BerryType.STARF:
|
||||||
|
return 'Sharply raises a random stat if HP is below 25%';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +53,26 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
||||||
return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.5;
|
return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.5;
|
||||||
case BerryType.LUM:
|
case BerryType.LUM:
|
||||||
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
|
return (pokemon: Pokemon) => !!pokemon.status || !!pokemon.getTag(BattlerTagType.CONFUSED);
|
||||||
|
case BerryType.ENIGMA:
|
||||||
|
return (pokemon: Pokemon) => {
|
||||||
|
const opponent = pokemon.isPlayer() ? pokemon.scene.getEnemyPokemon() : pokemon.scene.getPlayerPokemon();
|
||||||
|
const opponentLastMove = opponent ? opponent.getLastXMoves(1).find(() => true) : null; // TODO: Update so this works even if opponent has fainted
|
||||||
|
|
||||||
|
return opponentLastMove && opponentLastMove.turn === pokemon.scene.currentBattle?.turn - 1 && opponentLastMove.result === MoveResult.SUPER_EFFECTIVE;
|
||||||
|
};
|
||||||
|
case BerryType.LIECHI:
|
||||||
|
case BerryType.GANLON:
|
||||||
|
case BerryType.SALAC:
|
||||||
|
case BerryType.PETAYA:
|
||||||
|
case BerryType.APICOT:
|
||||||
|
return (pokemon: Pokemon) => {
|
||||||
|
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||||
|
return pokemon.getHpRatio() < 0.25 && pokemon.summonData.battleStats[battleStat] < 6;
|
||||||
|
};
|
||||||
|
case BerryType.LANSAT:
|
||||||
|
return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.25 && !pokemon.getTag(BattlerTagType.CRIT_BOOST);
|
||||||
|
case BerryType.STARF:
|
||||||
|
return (pokemon: Pokemon) => pokemon.getHpRatio() < 0.25;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,6 +81,7 @@ export type BerryEffectFunc = (pokemon: Pokemon) => void;
|
||||||
export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||||
switch (berryType) {
|
switch (berryType) {
|
||||||
case BerryType.SITRUS:
|
case BerryType.SITRUS:
|
||||||
|
case BerryType.ENIGMA:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 4), getPokemonMessage(pokemon, `'s ${getBerryName(berryType)}\nrestored its HP!`), true));
|
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.isPlayer(), Math.floor(pokemon.getMaxHp() / 4), getPokemonMessage(pokemon, `'s ${getBerryName(berryType)}\nrestored its HP!`), true));
|
||||||
};
|
};
|
||||||
|
@ -55,5 +94,20 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
||||||
} else if (pokemon.getTag(BattlerTagType.CONFUSED))
|
} else if (pokemon.getTag(BattlerTagType.CONFUSED))
|
||||||
pokemon.lapseTag(BattlerTagType.CONFUSED);
|
pokemon.lapseTag(BattlerTagType.CONFUSED);
|
||||||
};
|
};
|
||||||
|
case BerryType.LIECHI:
|
||||||
|
case BerryType.GANLON:
|
||||||
|
case BerryType.SALAC:
|
||||||
|
case BerryType.PETAYA:
|
||||||
|
case BerryType.APICOT:
|
||||||
|
return (pokemon: Pokemon) => {
|
||||||
|
const battleStat = (berryType - BerryType.LIECHI) as BattleStat;
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), [ battleStat ], 1));
|
||||||
|
};
|
||||||
|
case BerryType.LANSAT:
|
||||||
|
return (pokemon: Pokemon) => {
|
||||||
|
pokemon.addTag(BattlerTagType.CRIT_BOOST);
|
||||||
|
};
|
||||||
|
case BerryType.STARF:
|
||||||
|
return (pokemon: Pokemon) => pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.isPlayer(), [ BattleStat.RAND ], 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ import { Type } from "./type";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { WeatherType } from "./weather";
|
import { WeatherType } from "./weather";
|
||||||
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
||||||
|
import { FlinchChanceModifier } from "../modifier/modifier";
|
||||||
|
|
||||||
export enum MoveCategory {
|
export enum MoveCategory {
|
||||||
PHYSICAL,
|
PHYSICAL,
|
||||||
|
@ -1037,7 +1038,7 @@ export class ChargeAttr extends OverrideMoveEffectAttr {
|
||||||
user.addTag(this.tagType, 1, move.id, user.id);
|
user.addTag(this.tagType, 1, move.id, user.id);
|
||||||
if (this.chargeEffect)
|
if (this.chargeEffect)
|
||||||
applyMoveAttrs(MoveEffectAttr, user, target, move);
|
applyMoveAttrs(MoveEffectAttr, user, target, move);
|
||||||
user.getMoveHistory().push({ move: move.id, result: MoveResult.OTHER });
|
user.pushMoveHistory({ move: move.id, result: MoveResult.OTHER });
|
||||||
user.getMoveQueue().push({ move: move.id, ignorePP: true });
|
user.getMoveQueue().push({ move: move.id, ignorePP: true });
|
||||||
resolve(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
|
@ -1427,7 +1428,8 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
||||||
if (!super.apply(user, target, move, args))
|
if (!super.apply(user, target, move, args))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
|
const chance = this.getTagChance(user, target, move);
|
||||||
|
if (chance < 0 || chance === 100 || Utils.randInt(100) < chance) {
|
||||||
(this.selfTarget ? user : target).addTag(this.tagType, this.turnCount, move.id, user.id);
|
(this.selfTarget ? user : target).addTag(this.tagType, this.turnCount, move.id, user.id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1435,6 +1437,10 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTagChance(user: Pokemon, target: Pokemon, move: Move): integer {
|
||||||
|
return move.chance;
|
||||||
|
}
|
||||||
|
|
||||||
getCondition(): MoveCondition {
|
getCondition(): MoveCondition {
|
||||||
return this.failOnOverlap
|
return this.failOnOverlap
|
||||||
? (user: Pokemon, target: Pokemon, move: Move) => !(this.selfTarget ? user : target).getTag(this.tagType)
|
? (user: Pokemon, target: Pokemon, move: Move) => !(this.selfTarget ? user : target).getTag(this.tagType)
|
||||||
|
@ -1925,7 +1931,8 @@ export const allMoves = [
|
||||||
new SelfStatusMove(Moves.LIGHT_SCREEN, "Light Screen (N)", Type.PSYCHIC, -1, 30, 75, "Halves damage from Special attacks for 5 turns.", -1, 0, 1),
|
new SelfStatusMove(Moves.LIGHT_SCREEN, "Light Screen (N)", Type.PSYCHIC, -1, 30, 75, "Halves damage from Special attacks for 5 turns.", -1, 0, 1),
|
||||||
new SelfStatusMove(Moves.HAZE, "Haze (N)", Type.ICE, -1, 30, -1, "Resets all stat changes.", -1, 0, 1),
|
new SelfStatusMove(Moves.HAZE, "Haze (N)", Type.ICE, -1, 30, -1, "Resets all stat changes.", -1, 0, 1),
|
||||||
new SelfStatusMove(Moves.REFLECT, "Reflect (N)", Type.PSYCHIC, -1, 20, 74, "Halves damage from Physical attacks for 5 turns.", -1, 0, 1),
|
new SelfStatusMove(Moves.REFLECT, "Reflect (N)", Type.PSYCHIC, -1, 20, 74, "Halves damage from Physical attacks for 5 turns.", -1, 0, 1),
|
||||||
new SelfStatusMove(Moves.FOCUS_ENERGY, "Focus Energy (N)", Type.NORMAL, -1, 30, -1, "Increases critical hit ratio.", -1, 0, 1),
|
new SelfStatusMove(Moves.FOCUS_ENERGY, "Focus Energy", Type.NORMAL, -1, 30, -1, "Increases critical hit ratio.", -1, 0, 1)
|
||||||
|
.attr(AddBattlerTagAttr, BattlerTagType.CRIT_BOOST, true, undefined, true),
|
||||||
new AttackMove(Moves.BIDE, "Bide (N)", Type.NORMAL, MoveCategory.PHYSICAL, -1, -1, 10, -1, "User takes damage for two turns then strikes back double.", -1, 0, 1),
|
new AttackMove(Moves.BIDE, "Bide (N)", Type.NORMAL, MoveCategory.PHYSICAL, -1, -1, 10, -1, "User takes damage for two turns then strikes back double.", -1, 0, 1),
|
||||||
new SelfStatusMove(Moves.METRONOME, "Metronome", Type.NORMAL, -1, 10, 80, "User performs almost any move in the game at random.", -1, 0, 1)
|
new SelfStatusMove(Moves.METRONOME, "Metronome", Type.NORMAL, -1, 10, 80, "User performs almost any move in the game at random.", -1, 0, 1)
|
||||||
.attr(RandomMoveAttr),
|
.attr(RandomMoveAttr),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Gender } from "./gender";
|
import { Gender } from "./gender";
|
||||||
import { AttackTypeBoosterModifier } from "../modifier/modifier";
|
import { AttackTypeBoosterModifier, FlinchChanceModifier } from "../modifier/modifier";
|
||||||
import { AttackTypeBoosterModifierType } from "../modifier/modifier-type";
|
import { AttackTypeBoosterModifierType } from "../modifier/modifier-type";
|
||||||
import { Moves } from "./move";
|
import { Moves } from "./move";
|
||||||
import { PokeballType } from "./pokeball";
|
import { PokeballType } from "./pokeball";
|
||||||
|
@ -167,7 +167,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||||
],
|
],
|
||||||
[Species.SLOWPOKE]: [
|
[Species.SLOWPOKE]: [
|
||||||
new SpeciesEvolution(Species.SLOWBRO, 37, null, null),
|
new SpeciesEvolution(Species.SLOWBRO, 37, null, null),
|
||||||
new SpeciesEvolution(Species.SLOWKING, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition((p: Pokemon) => true /* King's rock*/), SpeciesWildEvolutionDelay.VERY_LONG)
|
new SpeciesEvolution(Species.SLOWKING, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition((p: Pokemon) => !!p.scene.findModifier(m => (m instanceof FlinchChanceModifier) && (m as FlinchChanceModifier).pokemonId === p.id, true)), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||||
],
|
],
|
||||||
[Species.MAGNEMITE]: [
|
[Species.MAGNEMITE]: [
|
||||||
new SpeciesEvolution(Species.MAGNETON, 30, null, null)
|
new SpeciesEvolution(Species.MAGNETON, 30, null, null)
|
||||||
|
@ -779,7 +779,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
||||||
],
|
],
|
||||||
[Species.POLIWHIRL]: [
|
[Species.POLIWHIRL]: [
|
||||||
new SpeciesEvolution(Species.POLIWRATH, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
|
new SpeciesEvolution(Species.POLIWRATH, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
|
||||||
new SpeciesEvolution(Species.POLITOED, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition((p: Pokemon) => true /* King's rock*/), SpeciesWildEvolutionDelay.VERY_LONG)
|
new SpeciesEvolution(Species.POLITOED, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition((p: Pokemon) => !!p.scene.findModifier(m => (m instanceof FlinchChanceModifier) && (m as FlinchChanceModifier).pokemonId === p.id, true)), SpeciesWildEvolutionDelay.VERY_LONG)
|
||||||
],
|
],
|
||||||
[Species.WEEPINBELL]: [
|
[Species.WEEPINBELL]: [
|
||||||
new SpeciesEvolution(Species.VICTREEBEL, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
|
new SpeciesEvolution(Species.VICTREEBEL, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
|
||||||
|
|
|
@ -35,7 +35,7 @@ export class ModifierType {
|
||||||
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, iconImage?: string, group?: string, soundName?: string) {
|
constructor(name: string, description: string, newModifierFunc: NewModifierFunc, iconImage?: string, group?: string, soundName?: string) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.iconImage = iconImage || name?.replace(/[ \-]/g, '_')?.toLowerCase();
|
this.iconImage = iconImage || name?.replace(/[ \-]/g, '_')?.replace(/'/g, '')?.toLowerCase();
|
||||||
this.group = group || '';
|
this.group = group || '';
|
||||||
this.soundName = soundName || 'restore';
|
this.soundName = soundName || 'restore';
|
||||||
this.newModifierFunc = newModifierFunc;
|
this.newModifierFunc = newModifierFunc;
|
||||||
|
@ -497,7 +497,14 @@ const modifierTypes = {
|
||||||
|
|
||||||
BERRY: () => new ModifierTypeGenerator((party: Pokemon[]) => {
|
BERRY: () => new ModifierTypeGenerator((party: Pokemon[]) => {
|
||||||
const berryTypes = Utils.getEnumValues(BerryType);
|
const berryTypes = Utils.getEnumValues(BerryType);
|
||||||
const randBerryType = berryTypes[Utils.randInt(berryTypes.length)];
|
let randBerryType;
|
||||||
|
let rand = Utils.randInt(10);
|
||||||
|
if (rand < 2)
|
||||||
|
randBerryType = BerryType.SITRUS;
|
||||||
|
else if (rand < 4)
|
||||||
|
randBerryType = BerryType.LUM;
|
||||||
|
else
|
||||||
|
randBerryType = berryTypes[Utils.randInt(berryTypes.length - 2) + 2];
|
||||||
return new PokemonHeldItemModifierType(getBerryName(randBerryType), getBerryEffectDescription(randBerryType),
|
return new PokemonHeldItemModifierType(getBerryName(randBerryType), getBerryEffectDescription(randBerryType),
|
||||||
(type, args) => new Modifiers.BerryModifier(type, (args[0] as Pokemon).id, randBerryType),
|
(type, args) => new Modifiers.BerryModifier(type, (args[0] as Pokemon).id, randBerryType),
|
||||||
null, 'berry');
|
null, 'berry');
|
||||||
|
@ -521,6 +528,7 @@ const modifierTypes = {
|
||||||
GOLDEN_EXP_CHARM: () => new ExpBoosterModifierType('GOLDEN EXP CHARM', 100),
|
GOLDEN_EXP_CHARM: () => new ExpBoosterModifierType('GOLDEN EXP CHARM', 100),
|
||||||
|
|
||||||
LUCKY_EGG: () => new PokemonExpBoosterModifierType('LUCKY EGG', 50),
|
LUCKY_EGG: () => new PokemonExpBoosterModifierType('LUCKY EGG', 50),
|
||||||
|
GOLDEN_EGG: () => new PokemonExpBoosterModifierType('GOLDEN EGG', 200),
|
||||||
|
|
||||||
HEALING_CHARM: () => new ModifierType('HEALING CHARM', 'Doubles the effectiveness of HP restoring moves and items (excludes revives)',
|
HEALING_CHARM: () => new ModifierType('HEALING CHARM', 'Doubles the effectiveness of HP restoring moves and items (excludes revives)',
|
||||||
(type, _args) => new Modifiers.HealingBoosterModifier(type, 2), 'healing_charm'),
|
(type, _args) => new Modifiers.HealingBoosterModifier(type, 2), 'healing_charm'),
|
||||||
|
@ -536,6 +544,9 @@ const modifierTypes = {
|
||||||
FOCUS_BAND: () => new PokemonHeldItemModifierType('FOCUS BAND', 'Adds a 10% chance to survive with 1 HP after being damaged enough to faint',
|
FOCUS_BAND: () => new PokemonHeldItemModifierType('FOCUS BAND', 'Adds a 10% chance to survive with 1 HP after being damaged enough to faint',
|
||||||
(type, args) => new Modifiers.SurviveDamageModifier(type, (args[0] as Pokemon).id)),
|
(type, args) => new Modifiers.SurviveDamageModifier(type, (args[0] as Pokemon).id)),
|
||||||
|
|
||||||
|
KINGS_ROCK: () => new PokemonHeldItemModifierType('KING\'S ROCK', 'Adds a 10% chance an attack move will cause the opponent to flinch',
|
||||||
|
(type, args) => new Modifiers.FlinchChanceModifier(type, (args[0] as Pokemon).id)),
|
||||||
|
|
||||||
LEFTOVERS: () => new PokemonHeldItemModifierType('LEFTOVERS', 'Heals 1/16 of a POKéMON\'s maximum HP every turn',
|
LEFTOVERS: () => new PokemonHeldItemModifierType('LEFTOVERS', 'Heals 1/16 of a POKéMON\'s maximum HP every turn',
|
||||||
(type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)),
|
(type, args) => new Modifiers.TurnHealModifier(type, (args[0] as Pokemon).id)),
|
||||||
SHELL_BELL: () => new PokemonHeldItemModifierType('SHELL BELL', 'Heals 1/8 of a POKéMON\'s dealt damage',
|
SHELL_BELL: () => new PokemonHeldItemModifierType('SHELL BELL', 'Heals 1/8 of a POKéMON\'s dealt damage',
|
||||||
|
@ -570,7 +581,7 @@ const modifierPool = {
|
||||||
return thresholdPartyMemberCount;
|
return thresholdPartyMemberCount;
|
||||||
}),
|
}),
|
||||||
new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4),
|
new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4),
|
||||||
new WeightedModifierType(modifierTypes.BERRY, 2)
|
new WeightedModifierType(modifierTypes.BERRY, 20)
|
||||||
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
|
].map(m => { m.setTier(ModifierTier.COMMON); return m; }),
|
||||||
[ModifierTier.GREAT]: [
|
[ModifierTier.GREAT]: [
|
||||||
new WeightedModifierType(modifierTypes.GREAT_BALL, 6),
|
new WeightedModifierType(modifierTypes.GREAT_BALL, 6),
|
||||||
|
@ -617,6 +628,7 @@ const modifierPool = {
|
||||||
//new WeightedModifierType(modifierTypes.OVAL_CHARM, 1),
|
//new WeightedModifierType(modifierTypes.OVAL_CHARM, 1),
|
||||||
new WeightedModifierType(modifierTypes.HEALING_CHARM, 1),
|
new WeightedModifierType(modifierTypes.HEALING_CHARM, 1),
|
||||||
new WeightedModifierType(modifierTypes.FOCUS_BAND, 3),
|
new WeightedModifierType(modifierTypes.FOCUS_BAND, 3),
|
||||||
|
new WeightedModifierType(modifierTypes.KINGS_ROCK, 2),
|
||||||
new WeightedModifierType(modifierTypes.LEFTOVERS, 2),
|
new WeightedModifierType(modifierTypes.LEFTOVERS, 2),
|
||||||
new WeightedModifierType(modifierTypes.SHELL_BELL, 2),
|
new WeightedModifierType(modifierTypes.SHELL_BELL, 2),
|
||||||
new WeightedModifierType(modifierTypes.BERRY_POUCH, 3),
|
new WeightedModifierType(modifierTypes.BERRY_POUCH, 3),
|
||||||
|
@ -644,13 +656,15 @@ const enemyModifierPool = {
|
||||||
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1)
|
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 1)
|
||||||
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
|
].map(m => { m.setTier(ModifierTier.GREAT); return m; }),
|
||||||
[ModifierTier.ULTRA]: [
|
[ModifierTier.ULTRA]: [
|
||||||
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 5),
|
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),
|
||||||
new WeightedModifierType(modifierTypes.FOCUS_BAND, 2),
|
new WeightedModifierType(modifierTypes.FOCUS_BAND, 2),
|
||||||
new WeightedModifierType(modifierTypes.LUCKY_EGG, 2),
|
new WeightedModifierType(modifierTypes.LUCKY_EGG, 4),
|
||||||
|
new WeightedModifierType(modifierTypes.KINGS_ROCK, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.LEFTOVERS, 1),
|
||||||
|
new WeightedModifierType(modifierTypes.SHELL_BELL, 1),
|
||||||
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
|
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),
|
||||||
[ModifierTier.MASTER]: [
|
[ModifierTier.MASTER]: [
|
||||||
new WeightedModifierType(modifierTypes.LEFTOVERS, 4),
|
new WeightedModifierType(modifierTypes.GOLDEN_EGG, 1),
|
||||||
new WeightedModifierType(modifierTypes.SHELL_BELL, 4),
|
|
||||||
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, 1)
|
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, 1)
|
||||||
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
|
].map(m => { m.setTier(ModifierTier.MASTER); return m; })
|
||||||
};
|
};
|
||||||
|
|
|
@ -370,7 +370,7 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
|
||||||
const pokemon = args[0] as Pokemon;
|
const pokemon = args[0] as Pokemon;
|
||||||
const surviveDamage = args[1] as Utils.BooleanHolder;
|
const surviveDamage = args[1] as Utils.BooleanHolder;
|
||||||
|
|
||||||
if (!surviveDamage.value && (this.getStackCount() === this.getMaxStackCount() || Utils.randInt(10) < this.getStackCount())) {
|
if (!surviveDamage.value && Utils.randInt(10) < this.getStackCount()) {
|
||||||
surviveDamage.value = true;
|
surviveDamage.value = true;
|
||||||
|
|
||||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`));
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` hung on\nusing its ${this.type.name}!`));
|
||||||
|
@ -384,6 +384,37 @@ export class SurviveDamageModifier extends PokemonHeldItemModifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FlinchChanceModifier extends PokemonHeldItemModifier {
|
||||||
|
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
|
super(type, pokemonId, stackCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
matchType(modifier: Modifier) {
|
||||||
|
return modifier instanceof FlinchChanceModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return new FlinchChanceModifier(this.type, this.pokemonId, this.stackCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldApply(args: any[]): boolean {
|
||||||
|
return super.shouldApply(args) && args.length === 2 && args[1] instanceof Utils.BooleanHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(args: any[]): boolean {
|
||||||
|
const flinched = args[1] as Utils.BooleanHolder;
|
||||||
|
|
||||||
|
if (!flinched.value && Utils.randInt(10) < this.getStackCount())
|
||||||
|
flinched.value = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMaxStackCount(): number {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class TurnHealModifier extends PokemonHeldItemModifier {
|
export class TurnHealModifier extends PokemonHeldItemModifier {
|
||||||
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) {
|
||||||
super(type, pokemonId, stackCount);
|
super(type, pokemonId, stackCount);
|
||||||
|
|
|
@ -491,6 +491,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const critLevel = new Utils.IntegerHolder(0);
|
const critLevel = new Utils.IntegerHolder(0);
|
||||||
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
applyMoveAttrs(HighCritAttr, source, this, move, critLevel);
|
||||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel);
|
||||||
|
if (source.getTag(BattlerTagType.CRIT_BOOST))
|
||||||
|
critLevel.value += 2;
|
||||||
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
|
const critChance = Math.ceil(16 / Math.pow(2, critLevel.value));
|
||||||
let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance));
|
let isCritical = !source.getTag(BattlerTagType.NO_CRIT) && (critChance === 1 || !Utils.randInt(critChance));
|
||||||
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
|
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
|
||||||
|
@ -642,6 +644,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return this.summonData.moveHistory;
|
return this.summonData.moveHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pushMoveHistory(turnMove: TurnMove) {
|
||||||
|
turnMove.turn = this.scene.currentBattle?.turn;
|
||||||
|
this.getMoveHistory().push(turnMove);
|
||||||
|
}
|
||||||
|
|
||||||
getLastXMoves(turnCount?: integer): TurnMove[] {
|
getLastXMoves(turnCount?: integer): TurnMove[] {
|
||||||
const moveHistory = this.getMoveHistory();
|
const moveHistory = this.getMoveHistory();
|
||||||
return moveHistory.slice(turnCount >= 0 ? Math.max(moveHistory.length - (turnCount || 1), 0) : 0, moveHistory.length).reverse();
|
return moveHistory.slice(turnCount >= 0 ? Math.max(moveHistory.length - (turnCount || 1), 0) : 0, moveHistory.length).reverse();
|
||||||
|
@ -1035,6 +1042,7 @@ export interface TurnMove {
|
||||||
move: Moves;
|
move: Moves;
|
||||||
result: MoveResult;
|
result: MoveResult;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
|
turn?: integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QueuedMove {
|
export interface QueuedMove {
|
||||||
|
|