Add King's Rock and more berry types

pull/1/head
Flashfyre 2023-04-25 01:32:48 -04:00
parent 3753ea73be
commit a148ecab80
19 changed files with 1417 additions and 1104 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

View File

@ -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());

View File

@ -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);

View File

@ -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:

View File

@ -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));
} }
} }

View File

@ -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),

View File

@ -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)

View File

@ -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; })
}; };

View File

@ -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);

View File

@ -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 {