Add color change ability, some moves, and fix mimic

pull/1/head
Flashfyre 2023-05-06 00:42:01 -04:00
parent 45f73cc6ed
commit 15cfd3bad4
9 changed files with 152 additions and 52 deletions

View File

@ -413,12 +413,16 @@ export class SummonPhase extends BattlePhase {
} }
end() { end() {
if (this.scene.getPlayerPokemon().shiny) const pokemon = this.scene.getPlayerPokemon();
if (pokemon.shiny)
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, true)); this.scene.unshiftPhase(new ShinySparklePhase(this.scene, true));
this.scene.arena.applyTags(ArenaTrapTag, this.scene.getPlayerPokemon()); pokemon.resetTurnData();
applyPostSummonAbAttrs(PostSummonAbAttr, this.scene.getPlayerPokemon()); this.scene.arena.applyTags(ArenaTrapTag, pokemon);
applyPostSummonAbAttrs(PostSummonAbAttr, pokemon);
super.end(); super.end();
} }
@ -594,8 +598,8 @@ export class CommandPhase extends FieldPhase {
const moveQueue = playerPokemon.getMoveQueue(); const moveQueue = playerPokemon.getMoveQueue();
while (moveQueue.length && moveQueue[0] while (moveQueue.length && moveQueue[0]
&& moveQueue[0].move && (!playerPokemon.moveset.find(m => m.moveId === moveQueue[0].move) && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move)
|| !playerPokemon.moveset[playerPokemon.moveset.findIndex(m => m.moveId === moveQueue[0].move)].isUsable(moveQueue[0].ignorePP))) || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m.moveId === moveQueue[0].move)].isUsable(moveQueue[0].ignorePP)))
moveQueue.shift(); moveQueue.shift();
if (moveQueue.length) { if (moveQueue.length) {
@ -603,8 +607,8 @@ export class CommandPhase extends FieldPhase {
if (!queuedMove.move) if (!queuedMove.move)
this.handleCommand(Command.FIGHT, -1, false); this.handleCommand(Command.FIGHT, -1, false);
else { else {
const moveIndex = playerPokemon.moveset.findIndex(m => m.moveId === queuedMove.move); const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move);
if (moveIndex > -1 && playerPokemon.moveset[moveIndex].isUsable(queuedMove.ignorePP)) if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex].isUsable(queuedMove.ignorePP))
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP); this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP);
} }
} else } else
@ -647,12 +651,12 @@ export class CommandPhase extends FieldPhase {
} }
if (playerPokemon.trySelectMove(cursor, args[0] as boolean)) { if (playerPokemon.trySelectMove(cursor, args[0] as boolean)) {
playerMove = playerPokemon.moveset[cursor]; playerMove = playerPokemon.getMoveset()[cursor];
const playerPhase = new PlayerMovePhase(this.scene, playerPokemon, playerMove, false, args[0] as boolean); const playerPhase = new PlayerMovePhase(this.scene, playerPokemon, playerMove, false, args[0] as boolean);
this.scene.pushPhase(playerPhase); this.scene.pushPhase(playerPhase);
success = true; success = true;
} else if (cursor < playerPokemon.moveset.length) { } else if (cursor < playerPokemon.getMoveset().length) {
const move = playerPokemon.moveset[cursor]; const move = playerPokemon.getMoveset()[cursor];
if (move.isDisabled()) { if (move.isDisabled()) {
this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.setMode(Mode.MESSAGE);
this.scene.ui.showText(`${move.getName()} is disabled!`, null, () => { this.scene.ui.showText(`${move.getName()} is disabled!`, null, () => {
@ -703,7 +707,7 @@ export class CommandPhase extends FieldPhase {
const enemyNextMove = enemyPokemon.getNextMove(); const enemyNextMove = enemyPokemon.getNextMove();
let enemyMove: PokemonMove; let enemyMove: PokemonMove;
if (enemyNextMove.move) { if (enemyNextMove.move) {
enemyMove = enemyPokemon.moveset.find(m => m.moveId === enemyNextMove.move) || new PokemonMove(enemyNextMove.move, 0, 0); enemyMove = enemyPokemon.getMoveset().find(m => m.moveId === enemyNextMove.move) || new PokemonMove(enemyNextMove.move, 0, 0);
const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove, false, enemyNextMove.ignorePP); const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove, false, enemyNextMove.ignorePP);
if (isDelayed(command, playerMove, enemyMove)) if (isDelayed(command, playerMove, enemyMove))
this.scene.unshiftPhase(enemyPhase); this.scene.unshiftPhase(enemyPhase);
@ -753,7 +757,7 @@ export class TurnEndPhase extends FieldPhase {
pokemon.lapseTags(BattlerTagLapseType.TURN_END); pokemon.lapseTags(BattlerTagLapseType.TURN_END);
const disabledMoves = pokemon.moveset.filter(m => m.isDisabled()); const disabledMoves = pokemon.getMoveset().filter(m => m.isDisabled());
for (let dm of disabledMoves) { for (let dm of disabledMoves) {
if (!--dm.disableTurns) if (!--dm.disableTurns)
this.scene.pushPhase(new MessagePhase(this.scene, `${dm.getName()} is disabled\nno more!`)); this.scene.pushPhase(new MessagePhase(this.scene, `${dm.getName()} is disabled\nno more!`));
@ -1048,7 +1052,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
if (!isProtected && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length) { if (!isProtected && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length) {
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveHitEffectAttr && (!!target.hp || (attr as MoveHitEffectAttr).selfTarget), user, target, this.move.getMove()); applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveHitEffectAttr && (!!target.hp || (attr as MoveHitEffectAttr).selfTarget), user, target, this.move.getMove());
if (target.hp) if (target.hp)
applyPostDefendAbAttrs(PostDefendAbAttr, user, target, this.move, result); applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, result);
if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT))
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user); this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user);
} }
@ -1763,23 +1767,23 @@ export class LearnMovePhase extends PartyMemberPokemonPhase {
const pokemon = this.getPokemon(); const pokemon = this.getPokemon();
const move = allMoves[this.moveId]; const move = allMoves[this.moveId];
const existingMoveIndex = pokemon.moveset.findIndex(m => m?.moveId === move.id); const existingMoveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === move.id);
if (existingMoveIndex > -1) { if (existingMoveIndex > -1) {
this.end(); this.end();
return; return;
} }
const emptyMoveIndex = pokemon.moveset.length < 4 const emptyMoveIndex = pokemon.getMoveset().length < 4
? pokemon.moveset.length ? pokemon.getMoveset().length
: pokemon.moveset.findIndex(m => m === null); : pokemon.getMoveset().findIndex(m => m === null);
const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler const messageMode = this.scene.ui.getHandler() instanceof EvolutionSceneHandler
? Mode.EVOLUTION_SCENE ? Mode.EVOLUTION_SCENE
: Mode.MESSAGE; : Mode.MESSAGE;
if (emptyMoveIndex > -1) { if (emptyMoveIndex > -1) {
pokemon.moveset[emptyMoveIndex] = new PokemonMove(this.moveId, 0, 0); pokemon.setMove(emptyMoveIndex, this.moveId);
initMoveAnim(this.moveId).then(() => { initMoveAnim(this.moveId).then(() => {
loadMoveAnimAssets(this.scene, [ this.moveId ], true) loadMoveAnimAssets(this.scene, [ this.moveId ], true)
.then(() => { .then(() => {
@ -1820,7 +1824,7 @@ export class LearnMovePhase extends PartyMemberPokemonPhase {
this.scene.ui.showText('@d{32}1, @d{15}2, and@d{15}… @d{15}… @d{15}… @d{15}@s{pb_bounce_1}Poof!', null, () => { this.scene.ui.showText('@d{32}1, @d{15}2, and@d{15}… @d{15}… @d{15}… @d{15}@s{pb_bounce_1}Poof!', null, () => {
this.scene.ui.showText(`${pokemon.name} forgot how to\nuse ${pokemon.moveset[moveIndex].getName()}.`, null, () => { this.scene.ui.showText(`${pokemon.name} forgot how to\nuse ${pokemon.moveset[moveIndex].getName()}.`, null, () => {
this.scene.ui.showText('And…', null, () => { this.scene.ui.showText('And…', null, () => {
pokemon.moveset[moveIndex] = null; pokemon.setMove(moveIndex, Moves.NONE);
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId));
this.end(); this.end();
}, null, true); }, null, true);

View File

@ -7,7 +7,7 @@ import { getPokemonMessage } from "../messages";
import { Weather, WeatherType } from "./weather"; import { Weather, WeatherType } from "./weather";
import { BattlerTag, BattlerTagType, TrappedTag } from "./battler-tag"; import { BattlerTag, BattlerTagType, TrappedTag } from "./battler-tag";
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect"; import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
import { MoveFlags, Moves, RecoilAttr } from "./move"; import { MoveFlags, Moves, RecoilAttr, allMoves } from "./move";
export class Ability { export class Ability {
public id: Abilities; public id: Abilities;
@ -230,6 +230,25 @@ export class PostDefendAbAttr extends AbAttr {
} }
} }
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
if (moveResult < MoveResult.NO_EFFECT) {
const type = move.getMove().type;
const type2 = pokemon.species.type2;
if (type !== pokemon.getTypes()[0] && type !== type2) {
pokemon.summonData.types = [ type ].concat(type2 !== null ? [ type2 ] : []);
return true;
}
}
return false;
}
getTriggerMessage(pokemon: Pokemon, ...args: any[]): string {
return getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nmade it the ${Type[pokemon.getTypes()[0]]} type!`);
}
}
export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
private chance: integer; private chance: integer;
private effects: StatusEffect[]; private effects: StatusEffect[];
@ -1104,7 +1123,8 @@ export function initAbilities() {
.attr(ProtectStatAttr), .attr(ProtectStatAttr),
new Ability(Abilities.CLOUD_NINE, "Cloud Nine", "Eliminates the effects of non-severe weather.", 3) new Ability(Abilities.CLOUD_NINE, "Cloud Nine", "Eliminates the effects of non-severe weather.", 3)
.attr(SuppressWeatherEffectAbAttr), .attr(SuppressWeatherEffectAbAttr),
new Ability(Abilities.COLOR_CHANGE, "Color Change (N)", "Changes the POKéMON's type to the foe's move.", 3), new Ability(Abilities.COLOR_CHANGE, "Color Change", "Changes the POKéMON's type to the foe's move.", 3)
.attr(PostDefendTypeChangeAbAttr),
new Ability(Abilities.COMPOUND_EYES, "Compound Eyes", "The POKéMON's accuracy is boosted.", 3) new Ability(Abilities.COMPOUND_EYES, "Compound Eyes", "The POKéMON's accuracy is boosted.", 3)
.attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3), .attr(BattleStatMultiplierAbAttr, BattleStat.ACC, 1.3),
new Ability(Abilities.CUTE_CHARM, "Cute Charm", "Contact with the POKéMON may cause infatuation.", 3) new Ability(Abilities.CUTE_CHARM, "Cute Charm", "Contact with the POKéMON may cause infatuation.", 3)

View File

@ -3,7 +3,7 @@ import { DamagePhase, EnemyMovePhase, ObtainStatusEffectPhase, PlayerMovePhase,
import { BattleStat } from "./battle-stat"; import { BattleStat } from "./battle-stat";
import { BattlerTagType } from "./battler-tag"; import { BattlerTagType } from "./battler-tag";
import { getPokemonMessage } from "../messages"; import { getPokemonMessage } from "../messages";
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon"; import Pokemon, { AttackMoveResult, EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon";
import { StatusEffect, getStatusEffectDescriptor } from "./status-effect"; import { StatusEffect, getStatusEffectDescriptor } from "./status-effect";
import { Type } from "./type"; import { Type } from "./type";
import * as Utils from "../utils"; import * as Utils from "../utils";
@ -847,6 +847,29 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr {
} }
} }
type MoveFilter = (move: Move) => boolean;
export class CounterDamageAttr extends FixedDamageAttr {
private moveFilter: MoveFilter;
constructor(moveFilter: MoveFilter) {
super(0);
this.moveFilter = moveFilter;
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const damage = user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).reduce((total: integer, ar: AttackMoveResult) => total + ar.damage, 0);
(args[0] as Utils.IntegerHolder).value = Math.max(damage * 2, 1);
return true;
}
getCondition(): MoveCondition {
return (user: Pokemon, target: Pokemon, move: Move) => !!user.turnData.attacksReceived.filter(ar => this.moveFilter(allMoves[ar.move])).length;
}
}
export class RecoilAttr extends MoveEffectAttr { export class RecoilAttr extends MoveEffectAttr {
private useHp: boolean; private useHp: boolean;
@ -1460,11 +1483,11 @@ export class DisableMoveAttr extends MoveEffectAttr {
if (turnMove.virtual) if (turnMove.virtual)
continue; continue;
const moveIndex = target.moveset.findIndex(m => m.moveId === turnMove.move); const moveIndex = target.getMoveset().findIndex(m => m.moveId === turnMove.move);
if (moveIndex === -1) if (moveIndex === -1)
return false; return false;
const disabledMove = target.moveset[moveIndex]; const disabledMove = target.getMoveset()[moveIndex];
disabledMove.disableTurns = 4; disabledMove.disableTurns = 4;
user.scene.queueMessage(getPokemonMessage(target, `'s ${disabledMove.getName()}\nwas disabled!`)); user.scene.queueMessage(getPokemonMessage(target, `'s ${disabledMove.getName()}\nwas disabled!`));
@ -1484,7 +1507,7 @@ export class DisableMoveAttr extends MoveEffectAttr {
if (turnMove.virtual) if (turnMove.virtual)
continue; continue;
const move = target.moveset.find(m => m.moveId === turnMove.move); const move = target.getMoveset().find(m => m.moveId === turnMove.move);
if (!move) if (!move)
continue; continue;
@ -1733,7 +1756,7 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
} }
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const moveset = (!this.enemyMoveset ? user : target).moveset; const moveset = (!this.enemyMoveset ? user : target).getMoveset();
const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL));
if (moves.length) { if (moves.length) {
const move = moves[Utils.randInt(moves.length)]; const move = moves[Utils.randInt(moves.length)];
@ -1805,6 +1828,32 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr {
} }
} }
export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const targetMoves = target.getMoveHistory().filter(m => !m.virtual);
if (!targetMoves.length)
return false;
const copiedMove = allMoves[targetMoves[0].move];
const thisMoveIndex = user.getMoveset().findIndex(m => m.moveId === move.id);
if (thisMoveIndex === -1)
return false;
user.summonData.moveset = user.getMoveset().slice(0);
user.summonData.moveset[thisMoveIndex] = new PokemonMove(copiedMove.id, 0, 0);
user.scene.queueMessage(getPokemonMessage(user, ` copied\n${copiedMove.name}!`));
return true;
}
getCondition(): MoveCondition {
return targetMoveCopiableCondition;
}
}
export class SketchAttr extends MoveEffectAttr { export class SketchAttr extends MoveEffectAttr {
constructor() { constructor() {
super(true); super(true);
@ -1820,12 +1869,12 @@ export class SketchAttr extends MoveEffectAttr {
const sketchedMove = allMoves[targetMoves[0].move]; const sketchedMove = allMoves[targetMoves[0].move];
const sketchIndex = user.moveset.findIndex(m => m.moveId === move.id); const sketchIndex = user.getMoveset().findIndex(m => m.moveId === move.id);
if (sketchIndex === -1) if (sketchIndex === -1)
return false; return false;
user.moveset[sketchIndex] = new PokemonMove(sketchedMove.id, 0, 0); user.setMove(sketchIndex, sketchedMove.id);
user.scene.queueMessage(getPokemonMessage(user, ` sketched\n${sketchedMove.name}!`)); user.scene.queueMessage(getPokemonMessage(user, ` sketched\n${sketchedMove.name}!`));
@ -1843,7 +1892,7 @@ export class SketchAttr extends MoveEffectAttr {
const sketchableMove = targetMoves[0]; const sketchableMove = targetMoves[0];
if (user.moveset.find(m => m.moveId === sketchableMove.move)) if (user.getMoveset().find(m => m.moveId === sketchableMove.move))
return false; return false;
return true; return true;
@ -2034,7 +2083,8 @@ export function initMoves() {
.attr(RecoilAttr), .attr(RecoilAttr),
new AttackMove(Moves.LOW_KICK, "Low Kick", Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, 12, "The heavier the opponent, the stronger the attack.", -1, 0, 1) new AttackMove(Moves.LOW_KICK, "Low Kick", Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, 12, "The heavier the opponent, the stronger the attack.", -1, 0, 1)
.attr(WeightPowerAttr), .attr(WeightPowerAttr),
new AttackMove(Moves.COUNTER, "Counter (N)", Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, "When hit by a Physical Attack, user strikes back with 2x power.", -1, -5, 1) new AttackMove(Moves.COUNTER, "Counter", Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, "When hit by a Physical Attack, user strikes back with 2x power.", -1, -5, 1)
.attr(CounterDamageAttr, (move: Move) => move.category === MoveCategory.PHYSICAL)
.target(MoveTarget.ATTACKER), .target(MoveTarget.ATTACKER),
new AttackMove(Moves.SEISMIC_TOSS, "Seismic Toss", Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, "Inflicts damage equal to user's level (maximum 150).", -1, 0, 1) new AttackMove(Moves.SEISMIC_TOSS, "Seismic Toss", Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, "Inflicts damage equal to user's level (maximum 150).", -1, 0, 1)
.attr(LevelPowerAttr), .attr(LevelPowerAttr),
@ -2114,7 +2164,7 @@ export function initMoves() {
new AttackMove(Moves.NIGHT_SHADE, "Night Shade", Type.GHOST, MoveCategory.SPECIAL, -1, 100, 15, 42, "Inflicts damage equal to user's level (maximum 150).", -1, 0, 1) new AttackMove(Moves.NIGHT_SHADE, "Night Shade", Type.GHOST, MoveCategory.SPECIAL, -1, 100, 15, 42, "Inflicts damage equal to user's level (maximum 150).", -1, 0, 1)
.attr(LevelPowerAttr), .attr(LevelPowerAttr),
new StatusMove(Moves.MIMIC, "Mimic", Type.NORMAL, -1, 10, -1, "Copies the opponent's last move.", -1, 0, 1) new StatusMove(Moves.MIMIC, "Mimic", Type.NORMAL, -1, 10, -1, "Copies the opponent's last move.", -1, 0, 1)
.attr(CopyMoveAttr) .attr(MovesetCopyMoveAttr)
.ignoresVirtual(), .ignoresVirtual(),
new StatusMove(Moves.SCREECH, "Screech", Type.NORMAL, 85, 40, -1, "Sharply lowers opponent's Defense.", -1, 0, 1) new StatusMove(Moves.SCREECH, "Screech", Type.NORMAL, 85, 40, -1, "Sharply lowers opponent's Defense.", -1, 0, 1)
.attr(StatChangeAttr, BattleStat.DEF, -2), .attr(StatChangeAttr, BattleStat.DEF, -2),
@ -2150,7 +2200,8 @@ export function initMoves() {
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)
.ignoresVirtual(), .ignoresVirtual(),
new SelfStatusMove(Moves.MIRROR_MOVE, "Mirror Move (N)", Type.FLYING, -1, 20, -1, "User performs the opponent's last move.", -1, 0, 1) new SelfStatusMove(Moves.MIRROR_MOVE, "Mirror Move", Type.FLYING, -1, 20, -1, "User performs the opponent's last move.", -1, 0, 1)
.attr(CopyMoveAttr)
.ignoresVirtual(), .ignoresVirtual(),
new AttackMove(Moves.SELF_DESTRUCT, "Self-Destruct", Type.NORMAL, MoveCategory.PHYSICAL, 200, 100, 5, -1, "User faints.", -1, 0, 1) new AttackMove(Moves.SELF_DESTRUCT, "Self-Destruct", Type.NORMAL, MoveCategory.PHYSICAL, 200, 100, 5, -1, "User faints.", -1, 0, 1)
.attr(SacrificialAttr) .attr(SacrificialAttr)
@ -2434,7 +2485,8 @@ export function initMoves() {
.target(MoveTarget.BOTH_SIDES), .target(MoveTarget.BOTH_SIDES),
new AttackMove(Moves.CRUNCH, "Crunch", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 108, "May lower opponent's Defense.", 20, 0, 2) new AttackMove(Moves.CRUNCH, "Crunch", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 108, "May lower opponent's Defense.", 20, 0, 2)
.attr(StatChangeAttr, BattleStat.DEF, -1), .attr(StatChangeAttr, BattleStat.DEF, -1),
new AttackMove(Moves.MIRROR_COAT, "Mirror Coat (N)", Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 20, -1, "When hit by a Special Attack, user strikes back with 2x power.", -1, -5, 2) new AttackMove(Moves.MIRROR_COAT, "Mirror Coat", Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 20, -1, "When hit by a Special Attack, user strikes back with 2x power.", -1, -5, 2)
.attr(CounterDamageAttr, (move: Move) => move.category === MoveCategory.SPECIAL)
.target(MoveTarget.ATTACKER), .target(MoveTarget.ATTACKER),
new SelfStatusMove(Moves.PSYCH_UP, "Psych Up (N)", Type.NORMAL, -1, 10, -1, "Copies the opponent's stat changes.", -1, 0, 2), new SelfStatusMove(Moves.PSYCH_UP, "Psych Up (N)", Type.NORMAL, -1, 10, -1, "Copies the opponent's stat changes.", -1, 0, 2),
new AttackMove(Moves.EXTREME_SPEED, "Extreme Speed", Type.NORMAL, MoveCategory.PHYSICAL, 80, 100, 5, -1, "User attacks first.", -1, 2, 2), new AttackMove(Moves.EXTREME_SPEED, "Extreme Speed", Type.NORMAL, MoveCategory.PHYSICAL, 80, 100, 5, -1, "User attacks first.", -1, 2, 2),
@ -2721,7 +2773,7 @@ export function initMoves() {
new StatusMove(Moves.GUARD_SWAP, "Guard Swap (N)", Type.PSYCHIC, -1, 10, -1, "User and opponent swap Defense and Special Defense.", -1, 0, 4), new StatusMove(Moves.GUARD_SWAP, "Guard Swap (N)", Type.PSYCHIC, -1, 10, -1, "User and opponent swap Defense and Special Defense.", -1, 0, 4),
new AttackMove(Moves.PUNISHMENT, "Punishment (N)", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, "Power increases when opponent's stats have been raised.", -1, 0, 4), new AttackMove(Moves.PUNISHMENT, "Punishment (N)", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 5, -1, "Power increases when opponent's stats have been raised.", -1, 0, 4),
new AttackMove(Moves.LAST_RESORT, "Last Resort", Type.NORMAL, MoveCategory.PHYSICAL, 140, 100, 5, -1, "Can only be used after all other moves are used.", -1, 0, 4) new AttackMove(Moves.LAST_RESORT, "Last Resort", Type.NORMAL, MoveCategory.PHYSICAL, 140, 100, 5, -1, "Can only be used after all other moves are used.", -1, 0, 4)
.condition((user: Pokemon, target: Pokemon, move: Move) => !user.moveset.filter(m => m.moveId !== move.id && m.getPpRatio() > 0).length), .condition((user: Pokemon, target: Pokemon, move: Move) => !user.getMoveset().filter(m => m.moveId !== move.id && m.getPpRatio() > 0).length),
new StatusMove(Moves.WORRY_SEED, "Worry Seed (N)", Type.GRASS, 100, 10, -1, "Changes the opponent's Ability to Insomnia.", -1, 0, 4), new StatusMove(Moves.WORRY_SEED, "Worry Seed (N)", Type.GRASS, 100, 10, -1, "Changes the opponent's Ability to Insomnia.", -1, 0, 4),
new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch (N)", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, "User attacks first, but only works if opponent is readying an attack.", -1, 0, 4), new AttackMove(Moves.SUCKER_PUNCH, "Sucker Punch (N)", Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, "User attacks first, but only works if opponent is readying an attack.", -1, 0, 4),
new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, 91, "Poisons opponents when they switch into battle.", -1, 0, 4) new StatusMove(Moves.TOXIC_SPIKES, "Toxic Spikes", Type.POISON, -1, 20, 91, "Poisons opponents when they switch into battle.", -1, 0, 4)

View File

@ -185,7 +185,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
constructor(name: string, restorePoints: integer, iconImage?: string) { constructor(name: string, restorePoints: integer, iconImage?: string) {
super(name, `Restore ${restorePoints > -1 ? restorePoints : 'all'} PP for all of one POKéMON's moves`, (_type, args) => new Modifiers.PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints), super(name, `Restore ${restorePoints > -1 ? restorePoints : 'all'} PP for all of one POKéMON's moves`, (_type, args) => new Modifiers.PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints),
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if (!pokemon.moveset.filter(m => m.ppUsed).length) if (!pokemon.getMoveset().filter(m => m.ppUsed).length)
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}, iconImage, 'elixir'); }, iconImage, 'elixir');
@ -361,7 +361,7 @@ export class TmModifierType extends PokemonModifierType {
constructor(moveId: Moves) { constructor(moveId: Moves) {
super(`TM${Utils.padInt(Object.keys(tmSpecies).indexOf(moveId.toString()) + 1, 3)} - ${allMoves[moveId].name}`, `Teach ${allMoves[moveId].name} to a POKéMON`, (_type, args) => new Modifiers.TmModifier(this, (args[0] as PlayerPokemon).id), super(`TM${Utils.padInt(Object.keys(tmSpecies).indexOf(moveId.toString()) + 1, 3)} - ${allMoves[moveId].name}`, `Teach ${allMoves[moveId].name} to a POKéMON`, (_type, args) => new Modifiers.TmModifier(this, (args[0] as PlayerPokemon).id),
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.moveset.filter(m => m?.moveId === moveId).length) if (pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.getMoveset().filter(m => m?.moveId === moveId).length)
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
return null; return null;
}, `tm_${Type[allMoves[moveId].type].toLowerCase()}`, 'tm'); }, `tm_${Type[allMoves[moveId].type].toLowerCase()}`, 'tm');
@ -424,7 +424,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
if (pregenArgs) if (pregenArgs)
return new AttackTypeBoosterModifierType(pregenArgs[0] as Type, 20); return new AttackTypeBoosterModifierType(pregenArgs[0] as Type, 20);
const attackMoveTypes = party.map(p => p.moveset.map(m => m.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat(); const attackMoveTypes = party.map(p => p.getMoveset().map(m => m.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat();
const attackMoveTypeWeights = new Map<Type, integer>(); const attackMoveTypeWeights = new Map<Type, integer>();
let totalWeight = 0; let totalWeight = 0;
for (let t of attackMoveTypes) { for (let t of attackMoveTypes) {
@ -648,11 +648,11 @@ const modifierPool = {
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}), }),
new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount * 3; return thresholdPartyMemberCount * 3;
}), }),
new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}), }),
new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4), new WeightedModifierType(modifierTypes.TEMP_STAT_BOOSTER, 4),
@ -685,11 +685,11 @@ const modifierPool = {
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}), }),
new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount * 3; return thresholdPartyMemberCount * 3;
}), }),
new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => {
const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.moveset.filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3); const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => (m.getMove().pp - m.ppUsed) <= 5).length).length, 3);
return thresholdPartyMemberCount; return thresholdPartyMemberCount;
}), }),
new WeightedModifierType(modifierTypes.MAP, (party: Pokemon[]) => { new WeightedModifierType(modifierTypes.MAP, (party: Pokemon[]) => {

View File

@ -699,7 +699,7 @@ export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier {
apply(args: any[]): boolean { apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon; const pokemon = args[0] as Pokemon;
const move = pokemon.moveset[this.moveIndex]; const move = pokemon.getMoveset()[this.moveIndex];
move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
return true; return true;
@ -717,7 +717,7 @@ export class PokemonAllMovePpRestoreModifier extends ConsumablePokemonModifier {
apply(args: any[]): boolean { apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon; const pokemon = args[0] as Pokemon;
for (let move of pokemon.moveset) for (let move of pokemon.getMoveset())
move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; move.ppUsed = this.restorePoints >= -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
return true; return true;

View File

@ -181,7 +181,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
loadAssets(): Promise<void> { loadAssets(): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const moveIds = this.moveset.map(m => m.getMove().id); const moveIds = this.getMoveset().map(m => m.getMove().id);
Promise.allSettled(moveIds.map(m => initMoveAnim(m))) Promise.allSettled(moveIds.map(m => initMoveAnim(m)))
.then(() => { .then(() => {
loadMoveAnimAssets(this.scene, moveIds); loadMoveAnimAssets(this.scene, moveIds);
@ -320,6 +320,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return Math.floor((this.hp / this.getMaxHp()) * 100) / 100; return Math.floor((this.hp / this.getMaxHp()) * 100) / 100;
} }
getMoveset(): PokemonMove[] {
if (this.summonData?.moveset)
return this.summonData.moveset;
return this.moveset;
}
getTypes(): Type[] { getTypes(): Type[] {
const types = []; const types = [];
@ -396,6 +402,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return ret; return ret;
} }
setMove(moveIndex: integer, moveId: Moves): void {
const move = moveId ? new PokemonMove(moveId) : null;
this.moveset[moveId] = move;
if (this.summonData.moveset)
this.summonData.moveset[moveIndex] = move;
}
generateAndPopulateMoveset(): void { generateAndPopulateMoveset(): void {
this.moveset = []; this.moveset = [];
const movePool = []; const movePool = [];
@ -434,8 +447,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean { trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
const move = this.moveset.length > moveIndex const move = this.getMoveset().length > moveIndex
? this.moveset[moveIndex] ? this.getMoveset()[moveIndex]
: null; : null;
return move?.isUsable(ignorePp); return move?.isUsable(ignorePp);
} }
@ -576,6 +589,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.queueMessage('A critical hit!'); this.scene.queueMessage('A critical hit!');
this.damage(damage); this.damage(damage);
source.turnData.damageDealt += damage; source.turnData.damageDealt += damage;
this.turnData.attacksReceived.unshift({ move: move.id, result: result as DamageResult, damage: damage, sourceId: source.id });
} }
switch (result) { switch (result) {
@ -1023,7 +1037,7 @@ export class EnemyPokemon extends Pokemon {
getNextMove(): QueuedMove { getNextMove(): QueuedMove {
const queuedMove = this.getMoveQueue().length const queuedMove = this.getMoveQueue().length
? this.moveset.find(m => m.moveId === this.getMoveQueue()[0].move) ? this.getMoveset().find(m => m.moveId === this.getMoveQueue()[0].move)
: null; : null;
if (queuedMove) { if (queuedMove) {
if (queuedMove.isUsable(this.getMoveQueue()[0].ignorePP)) if (queuedMove.isUsable(this.getMoveQueue()[0].ignorePP))
@ -1034,7 +1048,7 @@ export class EnemyPokemon extends Pokemon {
} }
} }
const movePool = this.moveset.filter(m => m.isUsable()); const movePool = this.getMoveset().filter(m => m.isUsable());
if (movePool.length) { if (movePool.length) {
if (movePool.length === 1) if (movePool.length === 1)
return { move: movePool[0].moveId }; return { move: movePool[0].moveId };
@ -1139,10 +1153,18 @@ export interface QueuedMove {
ignorePP?: boolean; ignorePP?: boolean;
} }
export interface AttackMoveResult {
move: Moves;
result: DamageResult;
damage: integer;
sourceId: integer;
}
export class PokemonSummonData { export class PokemonSummonData {
public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ]; public battleStats: integer[] = [ 0, 0, 0, 0, 0, 0, 0 ];
public moveQueue: QueuedMove[] = []; public moveQueue: QueuedMove[] = [];
public tags: BattlerTag[] = []; public tags: BattlerTag[] = [];
public moveset: PokemonMove[];
public types: Type[]; public types: Type[];
} }
@ -1156,6 +1178,7 @@ export class PokemonTurnData {
public hitCount: integer; public hitCount: integer;
public hitsLeft: integer; public hitsLeft: integer;
public damageDealt: integer = 0; public damageDealt: integer = 0;
public attacksReceived: AttackMoveResult[] = [];
} }
export enum AiType { export enum AiType {

View File

@ -63,7 +63,7 @@ export function initAutoPlay() {
const getMaxMoveEffectiveness = (attacker: Pokemon, defender: Pokemon) => { const getMaxMoveEffectiveness = (attacker: Pokemon, defender: Pokemon) => {
let maxEffectiveness = 0.5; let maxEffectiveness = 0.5;
for (let m of attacker.moveset) { for (let m of attacker.getMoveset()) {
const moveType = m.getMove().type; const moveType = m.getMove().type;
const effectiveness = defender.getAttackMoveEffectiveness(moveType); const effectiveness = defender.getAttackMoveEffectiveness(moveType);
if (effectiveness > maxEffectiveness) if (effectiveness > maxEffectiveness)
@ -129,7 +129,7 @@ export function initAutoPlay() {
playerPokemon.aiType = AiType.SMART; playerPokemon.aiType = AiType.SMART;
thisArg.time.delayedCall(20, () => { thisArg.time.delayedCall(20, () => {
const nextMove = playerPokemon.getNextMove() as PokemonMove; const nextMove = playerPokemon.getNextMove() as PokemonMove;
fightUiHandler.setCursor(playerPokemon.moveset.indexOf(nextMove)); fightUiHandler.setCursor(playerPokemon.getMoveset().indexOf(nextMove));
thisArg.time.delayedCall(20, () => this.processInput(Button.ACTION)); thisArg.time.delayedCall(20, () => this.processInput(Button.ACTION));
}); });
} }

View File

@ -65,6 +65,7 @@ export default class PokemonData {
this.summonData.battleStats = source.summonData.battleStats; this.summonData.battleStats = source.summonData.battleStats;
this.summonData.moveQueue = source.summonData.moveQueue; this.summonData.moveQueue = source.summonData.moveQueue;
this.summonData.tags = []; // TODO this.summonData.tags = []; // TODO
this.summonData.moveset = source.summonData.moveset;
this.summonData.types = source.summonData.types; this.summonData.types = source.summonData.types;
} }
} }

View File

@ -91,7 +91,7 @@ export default class FightUiHandler extends UiHandler {
ui.add(this.cursorObj); ui.add(this.cursorObj);
} }
const moveset = this.scene.getPlayerPokemon().moveset; const moveset = this.scene.getPlayerPokemon().getMoveset();
const hasMove = cursor < moveset.length; const hasMove = cursor < moveset.length;
@ -114,7 +114,7 @@ export default class FightUiHandler extends UiHandler {
} }
displayMoves() { displayMoves() {
const moveset = this.scene.getPlayerPokemon().moveset; const moveset = this.scene.getPlayerPokemon().getMoveset();
for (let m = 0; m < 4; m++) { for (let m = 0; m < 4; m++) {
const moveText = addTextObject(this.scene, m % 2 === 0 ? 0 : 100, m < 2 ? 0 : 16, '-', TextStyle.WINDOW); const moveText = addTextObject(this.scene, m % 2 === 0 ? 0 : 100, m < 2 ? 0 : 16, '-', TextStyle.WINDOW);
if (m < moveset.length) if (m < moveset.length)