mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-03 23:12:20 +02:00
Improvements to double battles
This commit is contained in:
parent
afc37ab45d
commit
38005e9cba
@ -1,11 +1,11 @@
|
|||||||
import BattleScene, { maxExpLevel, startingLevel, startingWave } from "./battle-scene";
|
import BattleScene, { maxExpLevel, startingLevel, startingWave } from "./battle-scene";
|
||||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition } from "./pokemon";
|
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult, FieldPosition, HitResult } from "./pokemon";
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveCategory, MoveEffectAttr, MoveFlags, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr } from "./data/move";
|
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMoveAttrs, HitsTagAttr, MissEffectAttr, MoveAttr, MoveCategory, MoveEffectAttr, MoveFlags, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr, MoveTarget, OneHitKOAttr, getMoveTargets } from "./data/move";
|
||||||
import { Mode } from './ui/ui';
|
import { Mode } from './ui/ui';
|
||||||
import { Command } from "./ui/command-ui-handler";
|
import { Command } from "./ui/command-ui-handler";
|
||||||
import { Stat } from "./data/pokemon-stat";
|
import { Stat } from "./data/pokemon-stat";
|
||||||
import { BerryModifier, ContactHeldItemTransferChanceModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HeldItemTransferModifier, HitHealModifier, MapModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
|
import { BerryModifier, ContactHeldItemTransferChanceModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, MapModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier } 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, getPokeballName, getPokeballTintColor, PokeballType } from "./data/pokeball";
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballName, 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";
|
||||||
@ -28,7 +28,7 @@ import { ArenaTagType, ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
|
|||||||
import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, SuppressWeatherEffectAbAttr, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
|
import { CheckTrappedAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, SuppressWeatherEffectAbAttr, applyCheckTrappedAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreWeatherEffectAbAttrs } from "./data/ability";
|
||||||
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
import { Unlockables, getUnlockableName } from "./system/unlockables";
|
||||||
import { getBiomeKey } from "./arena";
|
import { getBiomeKey } from "./arena";
|
||||||
import { BattleTarget } from "./battle";
|
import { BattleTarget, TurnCommand } from "./battle";
|
||||||
|
|
||||||
export class CheckLoadPhase extends BattlePhase {
|
export class CheckLoadPhase extends BattlePhase {
|
||||||
private loaded: boolean;
|
private loaded: boolean;
|
||||||
@ -472,21 +472,28 @@ export class SummonPhase extends PartyMemberPokemonPhase {
|
|||||||
} else
|
} else
|
||||||
playerPokemon.setFieldPosition(!this.scene.currentBattle.double ? FieldPosition.CENTER : FieldPosition.LEFT);
|
playerPokemon.setFieldPosition(!this.scene.currentBattle.double ? FieldPosition.CENTER : FieldPosition.LEFT);
|
||||||
|
|
||||||
|
const xOffset = playerPokemon.getFieldPositionOffset()[0];
|
||||||
|
|
||||||
pokeball.setVisible(true);
|
pokeball.setVisible(true);
|
||||||
|
|
||||||
|
this.scene.tweens.add({
|
||||||
|
targets: pokeball,
|
||||||
|
duration: 650,
|
||||||
|
x: 100 + xOffset
|
||||||
|
});
|
||||||
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: pokeball,
|
targets: pokeball,
|
||||||
ease: 'Cubic.easeOut',
|
|
||||||
duration: 150,
|
duration: 150,
|
||||||
x: 54,
|
ease: 'Cubic.easeOut',
|
||||||
y: 70,
|
y: 70,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: pokeball,
|
targets: pokeball,
|
||||||
duration: 500,
|
duration: 500,
|
||||||
angle: 1440,
|
|
||||||
x: 100,
|
|
||||||
y: 132,
|
|
||||||
ease: 'Cubic.easeIn',
|
ease: 'Cubic.easeIn',
|
||||||
|
angle: 1440,
|
||||||
|
y: 132,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this.scene.sound.play('pb_rel');
|
this.scene.sound.play('pb_rel');
|
||||||
pokeball.destroy();
|
pokeball.destroy();
|
||||||
@ -734,11 +741,15 @@ export class CommandPhase extends FieldPhase {
|
|||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case Command.FIGHT:
|
case Command.FIGHT:
|
||||||
const targetIndex = Utils.randInt(enemyField.length); // TODO: Let user select this
|
|
||||||
|
|
||||||
if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean)) {
|
if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean)) {
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.FIGHT, cursor: cursor,
|
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor,
|
||||||
move: cursor > -1 ? { move: playerPokemon.moveset[cursor].moveId } : null, targetIndex: targetIndex, args: args }; // TODO: Struggle logic
|
move: cursor > -1 ? { move: playerPokemon.moveset[cursor].moveId } : null, args: args }; // TODO: Struggle logic
|
||||||
|
const moveTargets = getMoveTargets(playerPokemon, playerPokemon.moveset[cursor].moveId);
|
||||||
|
if (moveTargets.targets.length <= 1 || moveTargets.multiple)
|
||||||
|
turnCommand.targets = moveTargets.targets;
|
||||||
|
else
|
||||||
|
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex));
|
||||||
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
|
||||||
success = true;
|
success = true;
|
||||||
} else if (cursor < playerPokemon.getMoveset().length) {
|
} else if (cursor < playerPokemon.getMoveset().length) {
|
||||||
const move = playerPokemon.getMoveset()[cursor];
|
const move = playerPokemon.getMoveset()[cursor];
|
||||||
@ -761,8 +772,8 @@ export class CommandPhase extends FieldPhase {
|
|||||||
this.scene.ui.setMode(Mode.COMMAND);
|
this.scene.ui.setMode(Mode.COMMAND);
|
||||||
}, null, true);
|
}, null, true);
|
||||||
} else if (cursor < 4) {
|
} else if (cursor < 4) {
|
||||||
const targetIndex = Utils.randInt(enemyField.length); // TODO: Let user select this
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor };
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor, targetIndex: targetIndex };
|
this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex))
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -814,14 +825,39 @@ export class EnemyCommandPhase extends FieldPhase {
|
|||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
const enemyPokemon = this.scene.getEnemyField()[this.fieldIndex];
|
const enemyPokemon = this.scene.getEnemyField()[this.fieldIndex];
|
||||||
const playerField = this.scene.getPlayerField();
|
|
||||||
|
|
||||||
this.scene.currentBattle.turnCommands[this.fieldIndex + BattleTarget.ENEMY] = { command: Command.FIGHT, move: enemyPokemon.getNextMove(), targetIndex: Utils.randInt(playerField.length) }
|
const nextMove = enemyPokemon.getNextMove();
|
||||||
|
const moveTargets = getMoveTargets(enemyPokemon, nextMove.move);
|
||||||
|
|
||||||
|
this.scene.currentBattle.turnCommands[this.fieldIndex + BattleTarget.ENEMY] =
|
||||||
|
{ command: Command.FIGHT, move: nextMove, targets: !moveTargets.multiple ? [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ] : moveTargets.targets };
|
||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SelectTargetPhase extends PokemonPhase {
|
||||||
|
constructor(scene: BattleScene, fieldIndex: integer) {
|
||||||
|
super(scene, true, fieldIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
const move = this.scene.currentBattle.turnCommands[this.fieldIndex].move?.move || Moves.NONE;
|
||||||
|
this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (cursor: integer) => {
|
||||||
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
|
if (cursor === -1) {
|
||||||
|
this.scene.currentBattle.turnCommands[this.fieldIndex] = null;
|
||||||
|
this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex));
|
||||||
|
} else
|
||||||
|
this.scene.currentBattle.turnCommands[this.fieldIndex].targets = [ cursor ];
|
||||||
|
console.log(cursor, this.fieldIndex, this.scene.currentBattle.turnCommands[this.fieldIndex].targets)
|
||||||
|
this.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class TurnStartPhase extends FieldPhase {
|
export class TurnStartPhase extends FieldPhase {
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
super(scene);
|
super(scene);
|
||||||
@ -874,16 +910,16 @@ export class TurnStartPhase extends FieldPhase {
|
|||||||
const move = pokemon.getMoveset().find(m => m.moveId === queuedMove.move) || new PokemonMove(queuedMove.move);
|
const move = pokemon.getMoveset().find(m => m.moveId === queuedMove.move) || new PokemonMove(queuedMove.move);
|
||||||
if (pokemon.isPlayer()) {
|
if (pokemon.isPlayer()) {
|
||||||
if (turnCommand.cursor === -1)
|
if (turnCommand.cursor === -1)
|
||||||
this.scene.pushPhase(new PlayerMovePhase(this.scene, pokemon as PlayerPokemon, turnCommand.targetIndex, move));
|
this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets, move));
|
||||||
else {
|
else {
|
||||||
const playerPhase = new PlayerMovePhase(this.scene, pokemon as PlayerPokemon, turnCommand.targetIndex, move, false, queuedMove.ignorePP);
|
const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets, move, false, queuedMove.ignorePP);
|
||||||
this.scene.pushPhase(playerPhase);
|
this.scene.pushPhase(playerPhase);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
this.scene.pushPhase(new EnemyMovePhase(this.scene, pokemon as EnemyPokemon, turnCommand.targetIndex, move, false, queuedMove.ignorePP));
|
this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets, move, false, queuedMove.ignorePP));
|
||||||
break;
|
break;
|
||||||
case Command.BALL:
|
case Command.BALL:
|
||||||
this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targetIndex, turnCommand.cursor));
|
this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets[0] % 2, turnCommand.cursor));
|
||||||
break;
|
break;
|
||||||
case Command.POKEMON:
|
case Command.POKEMON:
|
||||||
case Command.RUN:
|
case Command.RUN:
|
||||||
@ -996,29 +1032,27 @@ export class CommonAnimPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class MovePhase extends BattlePhase {
|
export class MovePhase extends BattlePhase {
|
||||||
protected pokemon: Pokemon;
|
protected pokemon: Pokemon;
|
||||||
protected targetIndex: integer;
|
protected targets: BattleTarget[];
|
||||||
protected move: PokemonMove;
|
protected move: PokemonMove;
|
||||||
protected followUp: boolean;
|
protected followUp: boolean;
|
||||||
protected ignorePp: boolean;
|
protected ignorePp: boolean;
|
||||||
protected cancelled: boolean;
|
protected cancelled: boolean;
|
||||||
|
|
||||||
constructor(scene: BattleScene, pokemon: Pokemon, targetIndex: integer, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) {
|
constructor(scene: BattleScene, pokemon: Pokemon, targets: BattleTarget[], move: PokemonMove, followUp?: boolean, ignorePp?: boolean) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
|
||||||
this.pokemon = pokemon;
|
this.pokemon = pokemon;
|
||||||
this.targetIndex = targetIndex;
|
this.targets = targets;
|
||||||
this.move = move;
|
this.move = move;
|
||||||
this.followUp = !!followUp;
|
this.followUp = !!followUp;
|
||||||
this.ignorePp = !!ignorePp;
|
this.ignorePp = !!ignorePp;
|
||||||
this.cancelled = false;
|
this.cancelled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getEffectPhase(): MoveEffectPhase;
|
|
||||||
|
|
||||||
canMove(): boolean {
|
canMove(): boolean {
|
||||||
return !!this.pokemon.hp && this.move.isUsable(this.ignorePp);
|
return !!this.pokemon.hp && this.move.isUsable(this.ignorePp) && !!this.targets.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel(): void {
|
cancel(): void {
|
||||||
@ -1030,6 +1064,8 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
|
|
||||||
console.log(Moves[this.move.moveId]);
|
console.log(Moves[this.move.moveId]);
|
||||||
|
|
||||||
|
console.log(this.scene.currentBattle.turnCommands);
|
||||||
|
|
||||||
if (!this.canMove()) {
|
if (!this.canMove()) {
|
||||||
if (this.move.isDisabled())
|
if (this.move.isDisabled())
|
||||||
this.scene.queueMessage(`${this.move.getName()} is disabled!`);
|
this.scene.queueMessage(`${this.move.getName()} is disabled!`);
|
||||||
@ -1037,7 +1073,9 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = this.pokemon.getOpponent(this.targetIndex);
|
console.log(this.targets);
|
||||||
|
|
||||||
|
const targets = this.scene.getField().filter(p => p && p.hp && this.targets.indexOf(p.getBattleTarget()) > -1);
|
||||||
|
|
||||||
if (!this.followUp && this.canMove())
|
if (!this.followUp && this.canMove())
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
this.pokemon.lapseTags(BattlerTagLapseType.MOVE);
|
||||||
@ -1045,13 +1083,13 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
const doMove = () => {
|
const doMove = () => {
|
||||||
const moveQueue = this.pokemon.getMoveQueue();
|
const moveQueue = this.pokemon.getMoveQueue();
|
||||||
|
|
||||||
if (moveQueue.length && moveQueue[0].move === Moves.NONE) {
|
if ((moveQueue.length && moveQueue[0].move === Moves.NONE) || !targets.length) {
|
||||||
moveQueue.shift();
|
moveQueue.shift();
|
||||||
this.cancel();
|
this.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.cancelled) {
|
if (this.cancelled) {
|
||||||
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAILED });
|
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
|
||||||
this.end();
|
this.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1060,13 +1098,14 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
if (!moveQueue.length || !moveQueue.shift().ignorePP)
|
if (!moveQueue.length || !moveQueue.shift().ignorePP)
|
||||||
this.move.ppUsed++;
|
this.move.ppUsed++;
|
||||||
|
|
||||||
let success = this.move.getMove().applyConditions(this.pokemon, target, this.move.getMove());
|
// Assume conditions affecting targets only apply to moves with a single target
|
||||||
|
let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove());
|
||||||
if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
|
if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
|
||||||
success = false;
|
success = false;
|
||||||
if (success)
|
if (success)
|
||||||
this.scene.unshiftPhase(this.getEffectPhase());
|
this.scene.unshiftPhase(this.getEffectPhase());
|
||||||
else {
|
else {
|
||||||
this.pokemon.pushMoveHistory({ move: this.move.moveId, result: MoveResult.FAILED, virtual: this.move.virtual });
|
this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual });
|
||||||
this.scene.queueMessage('But it failed!');
|
this.scene.queueMessage('But it failed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1086,7 +1125,7 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StatusEffect.SLEEP:
|
case StatusEffect.SLEEP:
|
||||||
applyMoveAttrs(BypassSleepAttr, this.pokemon, target, this.move.getMove());
|
applyMoveAttrs(BypassSleepAttr, this.pokemon, null, this.move.getMove());
|
||||||
healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn;
|
healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn;
|
||||||
activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP);
|
activated = !healed && !this.pokemon.getTag(BattlerTagType.BYPASS_SLEEP);
|
||||||
this.cancelled = activated;
|
this.cancelled = activated;
|
||||||
@ -1099,7 +1138,7 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
if (activated) {
|
if (activated) {
|
||||||
this.scene.queueMessage(getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect)));
|
this.scene.queueMessage(getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect)));
|
||||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex(), this.targetIndex, CommonAnim.POISON + (this.pokemon.status.effect - 1)));
|
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex(), this.pokemon.getBattleTarget(), CommonAnim.POISON + (this.pokemon.status.effect - 1)));
|
||||||
doMove();
|
doMove();
|
||||||
} else {
|
} else {
|
||||||
if (healed) {
|
if (healed) {
|
||||||
@ -1113,6 +1152,10 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
doMove();
|
doMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEffectPhase(): MoveEffectPhase {
|
||||||
|
return new MoveEffectPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex(), this.targets, this.move);
|
||||||
|
}
|
||||||
|
|
||||||
end() {
|
end() {
|
||||||
if (!this.followUp && this.canMove())
|
if (!this.followUp && this.canMove())
|
||||||
this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex()));
|
this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.isPlayer(), this.pokemon.getFieldIndex()));
|
||||||
@ -1121,46 +1164,27 @@ export abstract class MovePhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PlayerMovePhase extends MovePhase {
|
class MoveEffectPhase extends PokemonPhase {
|
||||||
constructor(scene: BattleScene, pokemon: PlayerPokemon, targetIndex: integer, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) {
|
|
||||||
super(scene, pokemon, targetIndex, move, followUp, ignorePp);
|
|
||||||
}
|
|
||||||
|
|
||||||
getEffectPhase(): MoveEffectPhase {
|
|
||||||
return new PlayerMoveEffectPhase(this.scene, this.pokemon.getFieldIndex(), this.targetIndex, this.move);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EnemyMovePhase extends MovePhase {
|
|
||||||
constructor(scene: BattleScene, pokemon: EnemyPokemon, targetIndex: integer, move: PokemonMove, followUp?: boolean, ignorePp?: boolean) {
|
|
||||||
super(scene, pokemon, targetIndex, move, followUp, ignorePp);
|
|
||||||
}
|
|
||||||
|
|
||||||
getEffectPhase(): MoveEffectPhase {
|
|
||||||
return new EnemyMoveEffectPhase(this.scene, this.pokemon.getFieldIndex(), this.targetIndex, this.move);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class MoveEffectPhase extends PokemonPhase {
|
|
||||||
protected move: PokemonMove;
|
protected move: PokemonMove;
|
||||||
protected targetIndex: integer;
|
protected targets: BattleTarget[];
|
||||||
|
|
||||||
constructor(scene: BattleScene, player: boolean, fieldIndex: integer, targetIndex: integer, move: PokemonMove) {
|
constructor(scene: BattleScene, player: boolean, fieldIndex: integer, targets: BattleTarget[], move: PokemonMove) {
|
||||||
super(scene, player, fieldIndex);
|
super(scene, player, fieldIndex);
|
||||||
|
|
||||||
this.move = move;
|
this.move = move;
|
||||||
this.targetIndex = targetIndex;
|
this.targets = targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
const user = this.getUserPokemon();
|
const user = this.getUserPokemon();
|
||||||
const target = this.getTargetPokemon();
|
const targets = this.getTargets();
|
||||||
|
|
||||||
const overridden = new Utils.BooleanHolder(false);
|
const overridden = new Utils.BooleanHolder(false);
|
||||||
|
|
||||||
applyMoveAttrs(OverrideMoveEffectAttr, user, target, this.move.getMove(), overridden).then(() => {
|
// Assume single target for override
|
||||||
|
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget(), this.move.getMove(), overridden).then(() => {
|
||||||
|
|
||||||
if (overridden.value) {
|
if (overridden.value) {
|
||||||
this.end();
|
this.end();
|
||||||
@ -1171,39 +1195,57 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
if (user.turnData.hitsLeft === undefined) {
|
if (user.turnData.hitsLeft === undefined) {
|
||||||
const hitCount = new Utils.IntegerHolder(1);
|
const hitCount = new Utils.IntegerHolder(1);
|
||||||
applyMoveAttrs(MultiHitAttr, user, target, this.move.getMove(), hitCount);
|
// Assume single target for multi hit
|
||||||
|
applyMoveAttrs(MultiHitAttr, user, this.getTarget(), this.move.getMove(), hitCount);
|
||||||
user.turnData.hitCount = 0;
|
user.turnData.hitCount = 0;
|
||||||
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
user.turnData.hitsLeft = user.turnData.hitCount = hitCount.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.hitCheck()) {
|
const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual };
|
||||||
|
user.pushMoveHistory(moveHistoryEntry);
|
||||||
|
|
||||||
|
const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattleTarget(), this.hitCheck(p) ]));
|
||||||
|
if (targets.length === 1 && !targetHitChecks[this.targets[0]]) {
|
||||||
this.scene.queueMessage(getPokemonMessage(user, '\'s\nattack missed!'));
|
this.scene.queueMessage(getPokemonMessage(user, '\'s\nattack missed!'));
|
||||||
user.pushMoveHistory({ move: this.move.moveId, result: MoveResult.MISSED, virtual: this.move.virtual });
|
moveHistoryEntry.result = MoveResult.MISS;
|
||||||
applyMoveAttrs(MissEffectAttr, user, target, this.move.getMove());
|
applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove());
|
||||||
this.end();
|
this.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.lapseTag(BattlerTagType.PROTECTED);
|
// Move animation only needs one target
|
||||||
|
new MoveAnim(this.move.getMove().id as Moves, user, this.getTarget()?.getBattleTarget()).play(this.scene, () => {
|
||||||
new MoveAnim(this.move.getMove().id as Moves, user, this.targetIndex).play(this.scene, () => {
|
for (let target of targets) {
|
||||||
const result = !isProtected ? target.apply(user, this.move) : MoveResult.NO_EFFECT;
|
if (!targetHitChecks[target.getBattleTarget()]) {
|
||||||
user.pushMoveHistory({ move: this.move.moveId, result: result, virtual: this.move.virtual });
|
this.scene.queueMessage(getPokemonMessage(user, '\'s\nattack missed!'));
|
||||||
if (result !== MoveResult.NO_EFFECT && result !== MoveResult.FAILED) {
|
if (moveHistoryEntry.result === MoveResult.PENDING)
|
||||||
applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove());
|
moveHistoryEntry.result = MoveResult.MISS;
|
||||||
if (result < MoveResult.NO_EFFECT) {
|
applyMoveAttrs(MissEffectAttr, user, null, this.move.getMove());
|
||||||
const flinched = new Utils.BooleanHolder(false);
|
continue;
|
||||||
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
|
|
||||||
if (!isProtected && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length) {
|
const isProtected = !this.move.getMove().hasFlag(MoveFlags.IGNORE_PROTECT) && target.lapseTag(BattlerTagType.PROTECTED);
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveHitEffectAttr && (!!target.hp || (attr as MoveHitEffectAttr).selfTarget), user, target, this.move.getMove());
|
|
||||||
if (target.hp)
|
moveHistoryEntry.result = MoveResult.SUCCESS;
|
||||||
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, result);
|
|
||||||
if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT))
|
const hitResult = !isProtected ? target.apply(user, this.move) : HitResult.NO_EFFECT;
|
||||||
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex());
|
|
||||||
|
if (hitResult !== HitResult.NO_EFFECT && hitResult !== HitResult.FAIL) {
|
||||||
|
applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove());
|
||||||
|
if (hitResult < HitResult.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
|
||||||
|
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());
|
||||||
|
if (target.hp)
|
||||||
|
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult);
|
||||||
|
if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT))
|
||||||
|
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.end();
|
this.end();
|
||||||
@ -1213,7 +1255,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
end() {
|
end() {
|
||||||
const user = this.getUserPokemon();
|
const user = this.getUserPokemon();
|
||||||
if (--user.turnData.hitsLeft >= 1 && this.getTargetPokemon().hp)
|
if (--user.turnData.hitsLeft >= 1 && this.getTarget()?.hp)
|
||||||
this.scene.unshiftPhase(this.getNewHitPhase());
|
this.scene.unshiftPhase(this.getNewHitPhase());
|
||||||
else {
|
else {
|
||||||
if (user.turnData.hitCount > 1)
|
if (user.turnData.hitCount > 1)
|
||||||
@ -1224,11 +1266,11 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||||||
super.end();
|
super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
hitCheck(): boolean {
|
hitCheck(target: Pokemon): boolean {
|
||||||
if (this.move.getMove().moveTarget === MoveTarget.USER)
|
if (this.move.getMove().moveTarget === MoveTarget.USER)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const hiddenTag = this.getTargetPokemon().getTag(HiddenTag);
|
const hiddenTag = target.getTag(HiddenTag);
|
||||||
if (hiddenTag) {
|
if (hiddenTag) {
|
||||||
if (!this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
|
if (!this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
|
||||||
return false;
|
return false;
|
||||||
@ -1242,14 +1284,14 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (moveAccuracy.value === -1)
|
if (moveAccuracy.value === -1)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
applyMoveAttrs(VariableAccuracyAttr, this.getUserPokemon(), this.getTargetPokemon(), this.move.getMove(), moveAccuracy);
|
applyMoveAttrs(VariableAccuracyAttr, this.getUserPokemon(), target, this.move.getMove(), moveAccuracy);
|
||||||
|
|
||||||
if (!this.move.getMove().getAttrs(OneHitKOAttr).length && this.scene.arena.getTag(ArenaTagType.GRAVITY))
|
if (!this.move.getMove().getAttrs(OneHitKOAttr).length && this.scene.arena.getTag(ArenaTagType.GRAVITY))
|
||||||
moveAccuracy.value = Math.floor(moveAccuracy.value * 1.67);
|
moveAccuracy.value = Math.floor(moveAccuracy.value * 1.67);
|
||||||
|
|
||||||
if (this.move.getMove().category !== MoveCategory.STATUS) {
|
if (this.move.getMove().category !== MoveCategory.STATUS) {
|
||||||
const userAccuracyLevel = new Utils.IntegerHolder(this.getUserPokemon().summonData.battleStats[BattleStat.ACC]);
|
const userAccuracyLevel = new Utils.IntegerHolder(this.getUserPokemon().summonData.battleStats[BattleStat.ACC]);
|
||||||
const targetEvasionLevel = new Utils.IntegerHolder(this.getTargetPokemon().summonData.battleStats[BattleStat.EVA]);
|
const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]);
|
||||||
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.player, TempBattleStat.ACC, userAccuracyLevel);
|
this.scene.applyModifiers(TempBattleStatBoosterModifier, this.player, TempBattleStat.ACC, userAccuracyLevel);
|
||||||
const rand = Utils.randInt(100, 1);
|
const rand = Utils.randInt(100, 1);
|
||||||
let accuracyMultiplier = 1;
|
let accuracyMultiplier = 1;
|
||||||
@ -1264,40 +1306,20 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getUserPokemon(): Pokemon;
|
|
||||||
|
|
||||||
getTargetPokemon(): Pokemon {
|
|
||||||
return this.getUserPokemon().getOpponent(this.targetIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract getNewHitPhase(): MoveEffectPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PlayerMoveEffectPhase extends MoveEffectPhase {
|
|
||||||
constructor(scene: BattleScene, fieldIndex: integer, targetIndex: integer, move: PokemonMove) {
|
|
||||||
super(scene, true, fieldIndex, targetIndex, move);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserPokemon(): Pokemon {
|
getUserPokemon(): Pokemon {
|
||||||
return this.scene.getPlayerField()[this.fieldIndex];
|
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
getTargets(): Pokemon[] {
|
||||||
|
return this.scene.getField().filter(p => this.targets.indexOf(p.getBattleTarget()) > -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTarget(): Pokemon {
|
||||||
|
return this.getTargets().find(() => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNewHitPhase() {
|
getNewHitPhase() {
|
||||||
return new PlayerMoveEffectPhase(this.scene, this.fieldIndex, this.targetIndex, this.move);
|
return new MoveEffectPhase(this.scene, this.player, this.fieldIndex, this.targets, this.move);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EnemyMoveEffectPhase extends MoveEffectPhase {
|
|
||||||
constructor(scene: BattleScene, fieldIndex: integer, targetIndex: integer, move: PokemonMove) {
|
|
||||||
super(scene, false, fieldIndex, targetIndex, move);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserPokemon(): Pokemon {
|
|
||||||
return this.scene.getEnemyField()[this.fieldIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
getNewHitPhase() {
|
|
||||||
return new EnemyMoveEffectPhase(this.scene, this.fieldIndex, this.targetIndex, this.move);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1371,6 +1393,8 @@ export class StatChangePhase extends PokemonPhase {
|
|||||||
constructor(scene: BattleScene, player: boolean, fieldIndex: integer, selfTarget: boolean, stats: BattleStat[], levels: integer) {
|
constructor(scene: BattleScene, player: boolean, fieldIndex: integer, selfTarget: boolean, stats: BattleStat[], levels: integer) {
|
||||||
super(scene, player, fieldIndex);
|
super(scene, player, fieldIndex);
|
||||||
|
|
||||||
|
console.log(this.player, this.fieldIndex);
|
||||||
|
|
||||||
const allStats = Utils.getEnumValues(BattleStat);
|
const allStats = Utils.getEnumValues(BattleStat);
|
||||||
this.selfTarget = selfTarget;
|
this.selfTarget = selfTarget;
|
||||||
this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]);
|
this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]);
|
||||||
@ -1594,25 +1618,25 @@ export class DamagePhase extends PokemonPhase {
|
|||||||
constructor(scene: BattleScene, player: boolean, fieldIndex: integer, damageResult?: DamageResult) {
|
constructor(scene: BattleScene, player: boolean, fieldIndex: integer, damageResult?: DamageResult) {
|
||||||
super(scene, player, fieldIndex);
|
super(scene, player, fieldIndex);
|
||||||
|
|
||||||
this.damageResult = damageResult || MoveResult.EFFECTIVE;
|
this.damageResult = damageResult || HitResult.EFFECTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
switch (this.damageResult) {
|
switch (this.damageResult) {
|
||||||
case MoveResult.EFFECTIVE:
|
case HitResult.EFFECTIVE:
|
||||||
this.scene.sound.play('hit');
|
this.scene.sound.play('hit');
|
||||||
break;
|
break;
|
||||||
case MoveResult.SUPER_EFFECTIVE:
|
case HitResult.SUPER_EFFECTIVE:
|
||||||
this.scene.sound.play('hit_strong');
|
this.scene.sound.play('hit_strong');
|
||||||
break;
|
break;
|
||||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
case HitResult.NOT_VERY_EFFECTIVE:
|
||||||
this.scene.sound.play('hit_weak');
|
this.scene.sound.play('hit_weak');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.damageResult !== MoveResult.OTHER) {
|
if (this.damageResult !== HitResult.OTHER) {
|
||||||
const flashTimer = this.scene.time.addEvent({
|
const flashTimer = this.scene.time.addEvent({
|
||||||
delay: 100,
|
delay: 100,
|
||||||
repeat: 5,
|
repeat: 5,
|
||||||
|
@ -847,7 +847,7 @@ export default class BattleScene extends Phaser.Scene {
|
|||||||
count = Math.max(count, Math.floor(chances / 2));
|
count = Math.max(count, Math.floor(chances / 2));
|
||||||
const enemyField = this.getEnemyField();
|
const enemyField = this.getEnemyField();
|
||||||
getEnemyModifierTypesForWave(waveIndex, count, this.getEnemyField())
|
getEnemyModifierTypesForWave(waveIndex, count, this.getEnemyField())
|
||||||
.map(mt => mt.newModifier(enemyField[enemyField.length === 1 ? 0 : Utils.randInt(enemyField.length)]).add(this.enemyModifiers, false));
|
.map(mt => mt.newModifier(enemyField[Utils.randInt(enemyField.length)]).add(this.enemyModifiers, false));
|
||||||
|
|
||||||
this.updateModifiers(false).then(() => resolve());
|
this.updateModifiers(false).then(() => resolve());
|
||||||
});
|
});
|
||||||
|
@ -9,11 +9,11 @@ export enum BattleTarget {
|
|||||||
ENEMY_2
|
ENEMY_2
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TurnCommand {
|
export interface TurnCommand {
|
||||||
command: Command;
|
command: Command;
|
||||||
cursor?: integer;
|
cursor?: integer;
|
||||||
move?: QueuedMove;
|
move?: QueuedMove;
|
||||||
targetIndex?: integer;
|
targets?: BattleTarget[];
|
||||||
args?: any[];
|
args?: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Pokemon, { MoveResult, PokemonMove } from "../pokemon";
|
import Pokemon, { HitResult, MoveResult, PokemonMove } from "../pokemon";
|
||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { BattleStat, getBattleStatName } from "./battle-stat";
|
import { BattleStat, getBattleStatName } from "./battle-stat";
|
||||||
@ -232,14 +232,14 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PostDefendAbAttr extends AbAttr {
|
export class PostDefendAbAttr extends AbAttr {
|
||||||
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
|
export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr {
|
||||||
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||||
if (moveResult < MoveResult.NO_EFFECT) {
|
if (hitResult < HitResult.NO_EFFECT) {
|
||||||
const type = move.getMove().type;
|
const type = move.getMove().type;
|
||||||
const pokemonTypes = pokemon.getTypes();
|
const pokemonTypes = pokemon.getTypes();
|
||||||
if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) {
|
if (pokemonTypes.length !== 1 || pokemonTypes[0] !== type) {
|
||||||
@ -267,7 +267,7 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr {
|
|||||||
this.effects = effects;
|
this.effects = effects;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||||
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance) {
|
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance) {
|
||||||
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)];
|
const effect = this.effects.length === 1 ? this.effects[0] : this.effects[Utils.randInt(this.effects.length)];
|
||||||
return attacker.trySetStatus(effect);
|
return attacker.trySetStatus(effect);
|
||||||
@ -290,7 +290,7 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
|
|||||||
this.turnCount = turnCount;
|
this.turnCount = turnCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, args: any[]): boolean {
|
applyPostDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
|
||||||
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance)
|
if (move.getMove().hasFlag(MoveFlags.MAKES_CONTACT) && Utils.randInt(100) < this.chance)
|
||||||
return attacker.addTag(this.tagType, this.turnCount, move.moveId, pokemon.id);
|
return attacker.addTag(this.tagType, this.turnCount, move.moveId, pokemon.id);
|
||||||
|
|
||||||
@ -654,7 +654,7 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
|||||||
if (pokemon.getHpRatio() < 1) {
|
if (pokemon.getHpRatio() < 1) {
|
||||||
const scene = pokemon.scene;
|
const scene = pokemon.scene;
|
||||||
scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby its ${pokemon.getAbility()}!`));
|
scene.queueMessage(getPokemonMessage(pokemon, ` is hurt\nby its ${pokemon.getAbility()}!`));
|
||||||
scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), MoveResult.OTHER));
|
scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), HitResult.OTHER));
|
||||||
pokemon.damage(Math.ceil(pokemon.getMaxHp() * (16 / this.damageFactor)));
|
pokemon.damage(Math.ceil(pokemon.getMaxHp() * (16 / this.damageFactor)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -727,7 +727,7 @@ export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefend
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
|
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
|
||||||
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, moveResult: MoveResult, ...args: any[]): void {
|
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, ...args: any[]): void {
|
||||||
if (!pokemon.canApplyAbility())
|
if (!pokemon.canApplyAbility())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -737,7 +737,7 @@ export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefe
|
|||||||
if (!canApplyAttr(pokemon, attr))
|
if (!canApplyAttr(pokemon, attr))
|
||||||
continue;
|
continue;
|
||||||
pokemon.scene.setPhaseQueueSplice();
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
if (attr.applyPostDefend(pokemon, attacker, move, moveResult, args)) {
|
if (attr.applyPostDefend(pokemon, attacker, move, hitResult, args)) {
|
||||||
if (attr.showAbility)
|
if (attr.showAbility)
|
||||||
queueShowAbility(pokemon);
|
queueShowAbility(pokemon);
|
||||||
const message = attr.getTriggerMessage(pokemon, attacker, move);
|
const message = attr.getTriggerMessage(pokemon, attacker, move);
|
||||||
|
@ -3,7 +3,7 @@ import { Type } from "./type";
|
|||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { Moves, allMoves } from "./move";
|
import { Moves, allMoves } from "./move";
|
||||||
import { getPokemonMessage } from "../messages";
|
import { getPokemonMessage } from "../messages";
|
||||||
import Pokemon, { DamageResult, MoveResult } from "../pokemon";
|
import Pokemon, { DamageResult, HitResult, MoveResult } from "../pokemon";
|
||||||
import { DamagePhase, ObtainStatusEffectPhase } from "../battle-phases";
|
import { DamagePhase, ObtainStatusEffectPhase } from "../battle-phases";
|
||||||
import { StatusEffect } from "./status-effect";
|
import { StatusEffect } from "./status-effect";
|
||||||
import { BattlerTagType } from "./battler-tag";
|
import { BattlerTagType } from "./battler-tag";
|
||||||
@ -151,7 +151,7 @@ class SpikesTag extends ArenaTrapTag {
|
|||||||
const damageHpRatio = 1 / (10 - 2 * this.layers);
|
const damageHpRatio = 1 / (10 - 2 * this.layers);
|
||||||
|
|
||||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!'));
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!'));
|
||||||
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), MoveResult.OTHER));
|
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), HitResult.OTHER));
|
||||||
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
|
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -225,7 +225,7 @@ class StealthRockTag extends ArenaTrapTag {
|
|||||||
|
|
||||||
if (damageHpRatio) {
|
if (damageHpRatio) {
|
||||||
pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`);
|
pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`);
|
||||||
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), MoveResult.OTHER));
|
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), pokemon.getFieldIndex(), HitResult.OTHER));
|
||||||
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
|
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
//import { battleAnimRawData } from "./battle-anim-raw-data";
|
//import { battleAnimRawData } from "./battle-anim-raw-data";
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
import { ChargeAttr, Moves, allMoves, getMoveTarget } from "./move";
|
import { ChargeAttr, Moves, allMoves } from "./move";
|
||||||
import Pokemon from "../pokemon";
|
import Pokemon from "../pokemon";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
|
import { BattleTarget } from "../battle";
|
||||||
//import fs from 'vite-plugin-fs/browser';
|
//import fs from 'vite-plugin-fs/browser';
|
||||||
|
|
||||||
export enum AnimFrameTarget {
|
export enum AnimFrameTarget {
|
||||||
@ -831,8 +832,8 @@ export class CommonBattleAnim extends BattleAnim {
|
|||||||
export class MoveAnim extends BattleAnim {
|
export class MoveAnim extends BattleAnim {
|
||||||
public move: Moves;
|
public move: Moves;
|
||||||
|
|
||||||
constructor(move: Moves, user: Pokemon, targetIndex: integer) {
|
constructor(move: Moves, user: Pokemon, target: BattleTarget) {
|
||||||
super(user, getMoveTarget(user, targetIndex, move));
|
super(user, user.scene.getField()[target]);
|
||||||
|
|
||||||
this.move = move;
|
this.move = move;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { PokemonHealPhase, StatChangePhase } from "../battle-phases";
|
import { PokemonHealPhase, StatChangePhase } from "../battle-phases";
|
||||||
import { getPokemonMessage } from "../messages";
|
import { getPokemonMessage } from "../messages";
|
||||||
import Pokemon, { MoveResult } from "../pokemon";
|
import Pokemon, { HitResult, MoveResult } from "../pokemon";
|
||||||
import { getBattleStatName } from "./battle-stat";
|
import { getBattleStatName } from "./battle-stat";
|
||||||
import { BattleStat } from "./battle-stat";
|
import { BattleStat } from "./battle-stat";
|
||||||
import { BattlerTagType } from "./battler-tag";
|
import { BattlerTagType } from "./battler-tag";
|
||||||
@ -54,7 +54,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
|
|||||||
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:
|
case BerryType.ENIGMA:
|
||||||
return (pokemon: Pokemon) => !!pokemon.turnData.attacksReceived.filter(a => a.result === MoveResult.SUPER_EFFECTIVE).length;
|
return (pokemon: Pokemon) => !!pokemon.turnData.attacksReceived.filter(a => a.result === HitResult.SUPER_EFFECTIVE).length;
|
||||||
case BerryType.LIECHI:
|
case BerryType.LIECHI:
|
||||||
case BerryType.GANLON:
|
case BerryType.GANLON:
|
||||||
case BerryType.SALAC:
|
case BerryType.SALAC:
|
||||||
|
124
src/data/move.ts
124
src/data/move.ts
@ -1,9 +1,9 @@
|
|||||||
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||||
import { DamagePhase, EnemyMovePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "../battle-phases";
|
import { DamagePhase, MovePhase, ObtainStatusEffectPhase, PokemonHealPhase, StatChangePhase } from "../battle-phases";
|
||||||
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, { AttackMoveResult, EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../pokemon";
|
import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, 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";
|
||||||
@ -11,6 +11,7 @@ import { WeatherType } from "./weather";
|
|||||||
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
import { ArenaTagType, ArenaTrapTag } from "./arena-tag";
|
||||||
import { BlockRecoilDamageAttr, applyAbAttrs } from "./ability";
|
import { BlockRecoilDamageAttr, applyAbAttrs } from "./ability";
|
||||||
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
||||||
|
import { BattleTarget } from "../battle";
|
||||||
|
|
||||||
export enum MoveCategory {
|
export enum MoveCategory {
|
||||||
PHYSICAL,
|
PHYSICAL,
|
||||||
@ -24,14 +25,13 @@ export enum MoveTarget {
|
|||||||
ALL_OTHERS,
|
ALL_OTHERS,
|
||||||
NEAR_OTHER,
|
NEAR_OTHER,
|
||||||
ALL_NEAR_OTHERS,
|
ALL_NEAR_OTHERS,
|
||||||
ENEMY,
|
|
||||||
NEAR_ENEMY,
|
NEAR_ENEMY,
|
||||||
ALL_NEAR_ENEMIES,
|
ALL_NEAR_ENEMIES,
|
||||||
RANDOM_NEAR_ENEMY,
|
RANDOM_NEAR_ENEMY,
|
||||||
ALL_ENEMIES,
|
ALL_ENEMIES,
|
||||||
ATTACKER,
|
ATTACKER,
|
||||||
ALLY,
|
|
||||||
NEAR_ALLY,
|
NEAR_ALLY,
|
||||||
|
ALLY,
|
||||||
USER_OR_NEAR_ALLY,
|
USER_OR_NEAR_ALLY,
|
||||||
USER_AND_ALLIES,
|
USER_AND_ALLIES,
|
||||||
ALL,
|
ALL,
|
||||||
@ -47,6 +47,7 @@ export enum MoveFlags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MoveCondition = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
type MoveCondition = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||||
|
type UserMoveCondition = (user: Pokemon, move: Move) => boolean;
|
||||||
|
|
||||||
export default class Move {
|
export default class Move {
|
||||||
public id: Moves;
|
public id: Moves;
|
||||||
@ -889,7 +890,7 @@ export class RecoilAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) / 4), 1);
|
const recoilDamage = Math.max(Math.floor((!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) / 4), 1);
|
||||||
user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), user.getFieldIndex(), MoveResult.OTHER));
|
user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), user.getFieldIndex(), HitResult.OTHER));
|
||||||
user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!'));
|
user.scene.queueMessage(getPokemonMessage(user, ' is hit\nwith recoil!'));
|
||||||
user.damage(recoilDamage);
|
user.damage(recoilDamage);
|
||||||
|
|
||||||
@ -906,7 +907,7 @@ export class SacrificialAttr extends MoveEffectAttr {
|
|||||||
if (!super.apply(user, target, move, args))
|
if (!super.apply(user, target, move, args))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), user.getFieldIndex(), MoveResult.OTHER));
|
user.scene.unshiftPhase(new DamagePhase(user.scene, user.isPlayer(), user.getFieldIndex(), HitResult.OTHER));
|
||||||
user.damage(user.getMaxHp());
|
user.damage(user.getMaxHp());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1172,7 +1173,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.pushMoveHistory({ move: move.id, result: MoveResult.OTHER });
|
user.pushMoveHistory({ move: move.id, targets: [ target.getBattleTarget() ], result: MoveResult.OTHER });
|
||||||
user.getMoveQueue().push({ move: move.id, ignorePP: true });
|
user.getMoveQueue().push({ move: move.id, ignorePP: true });
|
||||||
resolve(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
@ -1275,7 +1276,7 @@ export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultipl
|
|||||||
let count = 0;
|
let count = 0;
|
||||||
let turnMove: TurnMove;
|
let turnMove: TurnMove;
|
||||||
|
|
||||||
while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.indexOf(turnMove?.move) > -1)) && (!resetOnFail || turnMove.result < MoveResult.NO_EFFECT)) {
|
while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.indexOf(turnMove?.move) > -1)) && (!resetOnFail || turnMove.result === MoveResult.SUCCESS)) {
|
||||||
if (count < (limit - 1))
|
if (count < (limit - 1))
|
||||||
count++;
|
count++;
|
||||||
else if (resetOnLimit)
|
else if (resetOnLimit)
|
||||||
@ -1460,16 +1461,16 @@ export class BlizzardAccuracyAttr extends VariableAccuracyAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MissEffectAttr extends MoveAttr {
|
export class MissEffectAttr extends MoveAttr {
|
||||||
private missEffectFunc: MoveCondition;
|
private missEffectFunc: UserMoveCondition;
|
||||||
|
|
||||||
constructor(missEffectFunc: MoveCondition) {
|
constructor(missEffectFunc: UserMoveCondition) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.missEffectFunc = missEffectFunc;
|
this.missEffectFunc = missEffectFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
this.missEffectFunc(user, target, move);
|
this.missEffectFunc(user, move);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1555,7 +1556,7 @@ export class FrenzyAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const frenzyMissFunc: MoveCondition = (user: Pokemon, target: Pokemon, move: Move) => {
|
export const frenzyMissFunc: UserMoveCondition = (user: Pokemon, move: Move) => {
|
||||||
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id)
|
while (user.getMoveQueue().length && user.getMoveQueue()[0].move === move.id)
|
||||||
user.getMoveQueue().shift();
|
user.getMoveQueue().shift();
|
||||||
user.lapseTag(BattlerTagType.FRENZY);
|
user.lapseTag(BattlerTagType.FRENZY);
|
||||||
@ -1648,7 +1649,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
|
|||||||
let timesUsed = 0;
|
let timesUsed = 0;
|
||||||
const moveHistory = user.getLastXMoves(-1);
|
const moveHistory = user.getLastXMoves(-1);
|
||||||
let turnMove: TurnMove;
|
let turnMove: TurnMove;
|
||||||
while (moveHistory.length && (turnMove = moveHistory.shift()).move === move.id && turnMove.result === MoveResult.STATUS)
|
while (moveHistory.length && (turnMove = moveHistory.shift()).move === move.id && turnMove.result === MoveResult.SUCCESS)
|
||||||
timesUsed++;
|
timesUsed++;
|
||||||
if (timesUsed)
|
if (timesUsed)
|
||||||
return !Utils.randInt(Math.pow(2, timesUsed));
|
return !Utils.randInt(Math.pow(2, timesUsed));
|
||||||
@ -1771,9 +1772,11 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
|||||||
const move = moves[Utils.randInt(moves.length)];
|
const move = moves[Utils.randInt(moves.length)];
|
||||||
const moveIndex = moveset.findIndex(m => m.moveId === move.moveId);
|
const moveIndex = moveset.findIndex(m => m.moveId === move.moveId);
|
||||||
user.getMoveQueue().push({ move: move.moveId, ignorePP: this.enemyMoveset });
|
user.getMoveQueue().push({ move: move.moveId, ignorePP: this.enemyMoveset });
|
||||||
user.scene.unshiftPhase(user.isPlayer()
|
const moveTargets = getMoveTargets(user, move.moveId);
|
||||||
? new PlayerMovePhase(user.scene, user as PlayerPokemon, target.getFieldIndex(), moveset[moveIndex], true)
|
if (!moveTargets.targets.length)
|
||||||
: new EnemyMovePhase(user.scene, user as EnemyPokemon, target.getFieldIndex(), moveset[moveIndex], true));
|
return false;
|
||||||
|
const targets = moveTargets.multiple ? moveTargets.targets : [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ];
|
||||||
|
user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex], true));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1787,9 +1790,13 @@ export class RandomMoveAttr extends OverrideMoveEffectAttr {
|
|||||||
const moveIds = Utils.getEnumValues(Moves).filter(m => !allMoves[m].hasFlag(MoveFlags.IGNORE_VIRTUAL));
|
const moveIds = Utils.getEnumValues(Moves).filter(m => !allMoves[m].hasFlag(MoveFlags.IGNORE_VIRTUAL));
|
||||||
const moveId = moveIds[Utils.randInt(moveIds.length)];
|
const moveId = moveIds[Utils.randInt(moveIds.length)];
|
||||||
user.getMoveQueue().push({ move: moveId, ignorePP: true });
|
user.getMoveQueue().push({ move: moveId, ignorePP: true });
|
||||||
user.scene.unshiftPhase(user.isPlayer()
|
const moveTargets = getMoveTargets(user, moveId);
|
||||||
? new PlayerMovePhase(user.scene, user as PlayerPokemon, target.getFieldIndex(), new PokemonMove(moveId, 0, 0, true), true)
|
if (!moveTargets.targets.length) {
|
||||||
: new EnemyMovePhase(user.scene, user as EnemyPokemon, target.getFieldIndex(), new PokemonMove(moveId, 0, 0, true), true));
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const targets = moveTargets.multiple ? moveTargets.targets : [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ];
|
||||||
|
user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, new PokemonMove(moveId, 0, 0, true), true));
|
||||||
initMoveAnim(moveId).then(() => {
|
initMoveAnim(moveId).then(() => {
|
||||||
loadMoveAnimAssets(user.scene, [ moveId ], true)
|
loadMoveAnimAssets(user.scene, [ moveId ], true)
|
||||||
.then(() => resolve(true));
|
.then(() => resolve(true));
|
||||||
@ -1825,9 +1832,14 @@ export class CopyMoveAttr extends OverrideMoveEffectAttr {
|
|||||||
const copiedMove = targetMoves[0];
|
const copiedMove = targetMoves[0];
|
||||||
|
|
||||||
user.getMoveQueue().push({ move: copiedMove.move, ignorePP: true });
|
user.getMoveQueue().push({ move: copiedMove.move, ignorePP: true });
|
||||||
user.scene.unshiftPhase(user.isPlayer()
|
|
||||||
? new PlayerMovePhase(user.scene, user as PlayerPokemon, target.getFieldIndex(), new PokemonMove(copiedMove.move, 0, 0, true), true)
|
const moveTargets = getMoveTargets(user, copiedMove.move);
|
||||||
: new EnemyMovePhase(user.scene, user as EnemyPokemon, target.getFieldIndex(), new PokemonMove(copiedMove.move, 0, 0, true), true));
|
if (!moveTargets.targets.length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const targets = moveTargets.multiple ? moveTargets.targets : [ moveTargets.targets[Utils.randInt(moveTargets.targets.length)] ];
|
||||||
|
|
||||||
|
user.scene.unshiftPhase(new MovePhase(user.scene, user as PlayerPokemon, targets, new PokemonMove(copiedMove.move, 0, 0, true), true));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1934,20 +1946,64 @@ export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon
|
|||||||
return applyMoveAttrsInternal(attrFilter, user, target, move, args);
|
return applyMoveAttrsInternal(attrFilter, user, target, move, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMoveTarget(user: Pokemon, targetIndex: integer, move: Moves): Pokemon {
|
export type MoveTargetSet = {
|
||||||
const moveTarget = allMoves[move].moveTarget;
|
targets: BattleTarget[];
|
||||||
|
multiple: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const other = user.getOpponent(targetIndex);
|
export function getMoveTargets(user: Pokemon, move: Moves): MoveTargetSet {
|
||||||
|
const moveTarget = move ? allMoves[move].moveTarget : MoveTarget.NEAR_ENEMY;
|
||||||
|
const opponents = user.getOpponents();
|
||||||
|
|
||||||
|
let set: BattleTarget[];
|
||||||
|
let multiple = false;
|
||||||
|
|
||||||
switch (moveTarget) {
|
switch (moveTarget) {
|
||||||
case MoveTarget.USER:
|
case MoveTarget.USER:
|
||||||
|
set = [ user.getBattleTarget() ];
|
||||||
|
break;
|
||||||
|
case MoveTarget.OTHER:
|
||||||
|
set = user.getLastXMoves().find(() => true)?.targets || [];
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_OTHER:
|
||||||
|
case MoveTarget.ALL_NEAR_OTHERS:
|
||||||
|
case MoveTarget.ALL_OTHERS:
|
||||||
|
set = (opponents.concat([ user.getAlly() ])).map(p => p?.getBattleTarget());
|
||||||
|
multiple = moveTarget !== MoveTarget.NEAR_OTHER;
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_ENEMY:
|
||||||
|
case MoveTarget.ALL_NEAR_ENEMIES:
|
||||||
|
case MoveTarget.ALL_ENEMIES:
|
||||||
|
case MoveTarget.ENEMY_SIDE:
|
||||||
|
set = opponents.map(p => p.getBattleTarget());
|
||||||
|
multiple = moveTarget !== MoveTarget.NEAR_ENEMY;
|
||||||
|
break;
|
||||||
|
case MoveTarget.RANDOM_NEAR_ENEMY:
|
||||||
|
set = [ opponents[Utils.randInt(opponents.length)].getBattleTarget() ];
|
||||||
|
break;
|
||||||
|
case MoveTarget.ATTACKER:
|
||||||
|
set = [ user.scene.getPokemonById(user.turnData.attacksReceived[0].sourceId).getBattleTarget() ];
|
||||||
|
break;
|
||||||
|
case MoveTarget.NEAR_ALLY:
|
||||||
|
case MoveTarget.ALLY:
|
||||||
|
set = [ user.getAlly()?.getBattleTarget() ];
|
||||||
|
break;
|
||||||
case MoveTarget.USER_OR_NEAR_ALLY:
|
case MoveTarget.USER_OR_NEAR_ALLY:
|
||||||
case MoveTarget.USER_AND_ALLIES:
|
case MoveTarget.USER_AND_ALLIES:
|
||||||
case MoveTarget.USER_SIDE:
|
case MoveTarget.USER_SIDE:
|
||||||
return user;
|
set = [ user, user.getAlly() ].map(p => p?.getBattleTarget());
|
||||||
default:
|
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
|
||||||
return other;
|
break;
|
||||||
|
case MoveTarget.ALL:
|
||||||
|
case MoveTarget.BOTH_SIDES:
|
||||||
|
set = [ user, user.getAlly() ].concat(user.getOpponents()).map(p => p?.getBattleTarget());
|
||||||
|
multiple = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(set, multiple, MoveTarget[moveTarget]);
|
||||||
|
|
||||||
|
return { targets: set.filter(t => t !== undefined), multiple };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allMoves = [
|
export const allMoves = [
|
||||||
@ -2004,7 +2060,7 @@ export function initMoves() {
|
|||||||
.attr(MultiHitAttr, MultiHitType._2),
|
.attr(MultiHitAttr, MultiHitType._2),
|
||||||
new AttackMove(Moves.MEGA_KICK, "Mega Kick", Type.NORMAL, MoveCategory.PHYSICAL, 120, 75, 5, -1, "", -1, 0, 1),
|
new AttackMove(Moves.MEGA_KICK, "Mega Kick", Type.NORMAL, MoveCategory.PHYSICAL, 120, 75, 5, -1, "", -1, 0, 1),
|
||||||
new AttackMove(Moves.JUMP_KICK, "Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, "If it misses, the user loses half their HP.", -1, 0, 1)
|
new AttackMove(Moves.JUMP_KICK, "Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 95, 10, -1, "If it misses, the user loses half their HP.", -1, 0, 1)
|
||||||
.attr(MissEffectAttr, (user: Pokemon, target: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; })
|
.attr(MissEffectAttr, (user: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; })
|
||||||
.condition(failOnGravityCondition),
|
.condition(failOnGravityCondition),
|
||||||
new AttackMove(Moves.ROLLING_KICK, "Rolling Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, -1, "May cause flinching.", 30, 0, 1)
|
new AttackMove(Moves.ROLLING_KICK, "Rolling Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 60, 85, 15, -1, "May cause flinching.", 30, 0, 1)
|
||||||
.attr(FlinchAttr),
|
.attr(FlinchAttr),
|
||||||
@ -2072,13 +2128,15 @@ export function initMoves() {
|
|||||||
.target(MoveTarget.USER_SIDE),
|
.target(MoveTarget.USER_SIDE),
|
||||||
new AttackMove(Moves.WATER_GUN, "Water Gun", Type.WATER, MoveCategory.SPECIAL, 40, 100, 25, -1, "", -1, 0, 1),
|
new AttackMove(Moves.WATER_GUN, "Water Gun", Type.WATER, MoveCategory.SPECIAL, 40, 100, 25, -1, "", -1, 0, 1),
|
||||||
new AttackMove(Moves.HYDRO_PUMP, "Hydro Pump", Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, 142, "", -1, 0, 1),
|
new AttackMove(Moves.HYDRO_PUMP, "Hydro Pump", Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, 142, "", -1, 0, 1),
|
||||||
new AttackMove(Moves.SURF, "Surf", Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 123, "Hits all adjacent Pokémon.", -1, 0, 1), // TODO
|
new AttackMove(Moves.SURF, "Surf", Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 123, "Hits all adjacent Pokémon.", -1, 0, 1)
|
||||||
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new AttackMove(Moves.ICE_BEAM, "Ice Beam", Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 135, "May freeze opponent.", 10, 0, 1)
|
new AttackMove(Moves.ICE_BEAM, "Ice Beam", Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 135, "May freeze opponent.", 10, 0, 1)
|
||||||
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
.attr(StatusEffectAttr, StatusEffect.FREEZE)
|
||||||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new AttackMove(Moves.BLIZZARD, "Blizzard", Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 143, "May freeze opponent.", 10, 0, 1)
|
new AttackMove(Moves.BLIZZARD, "Blizzard", Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 143, "May freeze opponent.", 10, 0, 1)
|
||||||
.attr(BlizzardAccuracyAttr)
|
.attr(BlizzardAccuracyAttr)
|
||||||
.attr(StatusEffectAttr, StatusEffect.FREEZE), // TODO: 30% chance to hit protect/detect in hail
|
.attr(StatusEffectAttr, StatusEffect.FREEZE) // TODO: 30% chance to hit protect/detect in hail
|
||||||
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 0, 1)
|
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 0, 1)
|
||||||
.attr(ConfuseAttr)
|
.attr(ConfuseAttr)
|
||||||
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
.target(MoveTarget.ALL_NEAR_ENEMIES),
|
||||||
@ -2255,7 +2313,7 @@ export function initMoves() {
|
|||||||
new SelfStatusMove(Moves.SOFT_BOILED, "Soft-Boiled", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1)
|
new SelfStatusMove(Moves.SOFT_BOILED, "Soft-Boiled", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1)
|
||||||
.attr(HealAttr, 0.5),
|
.attr(HealAttr, 0.5),
|
||||||
new AttackMove(Moves.HIGH_JUMP_KICK, "High Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, -1, "If it misses, the user loses half their HP.", -1, 0, 1)
|
new AttackMove(Moves.HIGH_JUMP_KICK, "High Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, -1, "If it misses, the user loses half their HP.", -1, 0, 1)
|
||||||
.attr(MissEffectAttr, (user: Pokemon, target: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; })
|
.attr(MissEffectAttr, (user: Pokemon, move: Move) => { user.damage(Math.floor(user.getMaxHp() / 2)); return true; })
|
||||||
.condition(failOnGravityCondition),
|
.condition(failOnGravityCondition),
|
||||||
new StatusMove(Moves.GLARE, "Glare", Type.NORMAL, 100, 30, -1, "Paralyzes opponent.", -1, 0, 1)
|
new StatusMove(Moves.GLARE, "Glare", Type.NORMAL, 100, 30, -1, "Paralyzes opponent.", -1, 0, 1)
|
||||||
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
|
.attr(StatusEffectAttr, StatusEffect.PARALYSIS),
|
||||||
@ -2339,7 +2397,7 @@ export function initMoves() {
|
|||||||
.ignoresVirtual(),
|
.ignoresVirtual(),
|
||||||
new AttackMove(Moves.TRIPLE_KICK, "Triple Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 10, 90, 10, -1, "Hits thrice in one turn at increasing power.", -1, 0, 2)
|
new AttackMove(Moves.TRIPLE_KICK, "Triple Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 10, 90, 10, -1, "Hits thrice in one turn at increasing power.", -1, 0, 2)
|
||||||
.attr(MultiHitAttr, MultiHitType._3_INCR)
|
.attr(MultiHitAttr, MultiHitType._3_INCR)
|
||||||
.attr(MissEffectAttr, (user: Pokemon, target: Pokemon, move: Move) => {
|
.attr(MissEffectAttr, (user: Pokemon, move: Move) => {
|
||||||
user.turnData.hitsLeft = 0;
|
user.turnData.hitsLeft = 0;
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
|
@ -25,6 +25,7 @@ import { ArenaTagType, WeakenMoveTypeTag } from './data/arena-tag';
|
|||||||
import { Biome } from './data/biome';
|
import { Biome } from './data/biome';
|
||||||
import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, PreApplyBattlerTagAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, abilities, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability';
|
import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, PreApplyBattlerTagAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, abilities, applyBattleStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from './data/ability';
|
||||||
import PokemonData from './system/pokemon-data';
|
import PokemonData from './system/pokemon-data';
|
||||||
|
import { BattleTarget } from './battle';
|
||||||
|
|
||||||
export enum FieldPosition {
|
export enum FieldPosition {
|
||||||
CENTER,
|
CENTER,
|
||||||
@ -191,6 +192,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
abstract getFieldIndex(): integer;
|
abstract getFieldIndex(): integer;
|
||||||
|
|
||||||
|
abstract getBattleTarget(): BattleTarget;
|
||||||
|
|
||||||
loadAssets(): Promise<void> {
|
loadAssets(): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const moveIds = this.getMoveset().map(m => m.getMove().id);
|
const moveIds = this.getMoveset().map(m => m.getMove().id);
|
||||||
@ -572,8 +575,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.isPlayer() ? 'the opposing team' : 'your team';
|
return this.isPlayer() ? 'the opposing team' : 'your team';
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
|
getAlly(): Pokemon {
|
||||||
let result: MoveResult;
|
return (this.isPlayer() ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.getFieldIndex() ? 0 : 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(source: Pokemon, battlerMove: PokemonMove): HitResult {
|
||||||
|
let result: HitResult;
|
||||||
const move = battlerMove.getMove();
|
const move = battlerMove.getMove();
|
||||||
const moveCategory = move.category;
|
const moveCategory = move.category;
|
||||||
let damage = 0;
|
let damage = 0;
|
||||||
@ -595,7 +602,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
|
||||||
|
|
||||||
if (cancelled.value)
|
if (cancelled.value)
|
||||||
result = MoveResult.NO_EFFECT;
|
result = HitResult.NO_EFFECT;
|
||||||
else {
|
else {
|
||||||
if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === move.type))
|
if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === move.type))
|
||||||
power.value *= 1.5;
|
power.value *= 1.5;
|
||||||
@ -636,20 +643,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (damage && fixedDamage.value) {
|
if (damage && fixedDamage.value) {
|
||||||
damage = fixedDamage.value;
|
damage = fixedDamage.value;
|
||||||
isCritical = false;
|
isCritical = false;
|
||||||
result = MoveResult.EFFECTIVE;
|
result = HitResult.EFFECTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
|
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
if (typeMultiplier.value >= 2)
|
if (typeMultiplier.value >= 2)
|
||||||
result = MoveResult.SUPER_EFFECTIVE;
|
result = HitResult.SUPER_EFFECTIVE;
|
||||||
else if (typeMultiplier.value >= 1)
|
else if (typeMultiplier.value >= 1)
|
||||||
result = MoveResult.EFFECTIVE;
|
result = HitResult.EFFECTIVE;
|
||||||
else if (typeMultiplier.value > 0)
|
else if (typeMultiplier.value > 0)
|
||||||
result = MoveResult.NOT_VERY_EFFECTIVE;
|
result = HitResult.NOT_VERY_EFFECTIVE;
|
||||||
else
|
else
|
||||||
result = MoveResult.NO_EFFECT;
|
result = HitResult.NO_EFFECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (damage) {
|
if (damage) {
|
||||||
@ -662,20 +669,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case MoveResult.SUPER_EFFECTIVE:
|
case HitResult.SUPER_EFFECTIVE:
|
||||||
this.scene.queueMessage('It\'s super effective!');
|
this.scene.queueMessage('It\'s super effective!');
|
||||||
break;
|
break;
|
||||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
case HitResult.NOT_VERY_EFFECTIVE:
|
||||||
this.scene.queueMessage('It\'s not very effective!');
|
this.scene.queueMessage('It\'s not very effective!');
|
||||||
break;
|
break;
|
||||||
case MoveResult.NO_EFFECT:
|
case HitResult.NO_EFFECT:
|
||||||
this.scene.queueMessage(`It doesn\'t affect ${this.name}!`);
|
this.scene.queueMessage(`It doesn\'t affect ${this.name}!`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MoveCategory.STATUS:
|
case MoveCategory.STATUS:
|
||||||
result = MoveResult.STATUS;
|
result = HitResult.STATUS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1018,6 +1025,10 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
return this.scene.getPlayerField().indexOf(this);
|
return this.scene.getPlayerField().indexOf(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBattleTarget(): BattleTarget {
|
||||||
|
return this.getFieldIndex();
|
||||||
|
}
|
||||||
|
|
||||||
generateCompatibleTms(): void {
|
generateCompatibleTms(): void {
|
||||||
this.compatibleTms = [];
|
this.compatibleTms = [];
|
||||||
|
|
||||||
@ -1203,6 +1214,10 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
return this.scene.getEnemyField().indexOf(this);
|
return this.scene.getEnemyField().indexOf(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBattleTarget(): BattleTarget {
|
||||||
|
return BattleTarget.ENEMY + this.getFieldIndex();
|
||||||
|
}
|
||||||
|
|
||||||
addToParty() {
|
addToParty() {
|
||||||
const party = this.scene.getParty();
|
const party = this.scene.getParty();
|
||||||
let ret: PlayerPokemon = null;
|
let ret: PlayerPokemon = null;
|
||||||
@ -1219,6 +1234,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
|
|
||||||
export interface TurnMove {
|
export interface TurnMove {
|
||||||
move: Moves;
|
move: Moves;
|
||||||
|
targets?: BattleTarget[];
|
||||||
result: MoveResult;
|
result: MoveResult;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
turn?: integer;
|
turn?: integer;
|
||||||
@ -1264,17 +1280,25 @@ export enum AiType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum MoveResult {
|
export enum MoveResult {
|
||||||
|
PENDING,
|
||||||
|
SUCCESS,
|
||||||
|
FAIL,
|
||||||
|
MISS,
|
||||||
|
OTHER
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HitResult {
|
||||||
EFFECTIVE = 1,
|
EFFECTIVE = 1,
|
||||||
SUPER_EFFECTIVE,
|
SUPER_EFFECTIVE,
|
||||||
NOT_VERY_EFFECTIVE,
|
NOT_VERY_EFFECTIVE,
|
||||||
NO_EFFECT,
|
NO_EFFECT,
|
||||||
STATUS,
|
STATUS,
|
||||||
FAILED,
|
FAIL,
|
||||||
MISSED,
|
MISS,
|
||||||
OTHER
|
OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DamageResult = MoveResult.EFFECTIVE | MoveResult.SUPER_EFFECTIVE | MoveResult.NOT_VERY_EFFECTIVE | MoveResult.OTHER;
|
export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | HitResult.NOT_VERY_EFFECTIVE | HitResult.OTHER;
|
||||||
|
|
||||||
export class PokemonMove {
|
export class PokemonMove {
|
||||||
public moveId: Moves;
|
public moveId: Moves;
|
||||||
|
@ -24,7 +24,7 @@ export function initAutoPlay() {
|
|||||||
const commandUiHandler = this.ui.handlers[Mode.COMMAND] as CommandUiHandler;
|
const commandUiHandler = this.ui.handlers[Mode.COMMAND] as CommandUiHandler;
|
||||||
const fightUiHandler = this.ui.handlers[Mode.FIGHT] as FightUiHandler;
|
const fightUiHandler = this.ui.handlers[Mode.FIGHT] as FightUiHandler;
|
||||||
const partyUiHandler = this.ui.handlers[Mode.PARTY] as PartyUiHandler;
|
const partyUiHandler = this.ui.handlers[Mode.PARTY] as PartyUiHandler;
|
||||||
const switchCheckUiHandler = this.ui.handlers[Mode.CONFIRM] as ConfirmUiHandler;
|
const confirmUiHandler = this.ui.handlers[Mode.CONFIRM] as ConfirmUiHandler;
|
||||||
const modifierSelectUiHandler = this.ui.handlers[Mode.MODIFIER_SELECT] as ModifierSelectUiHandler;
|
const modifierSelectUiHandler = this.ui.handlers[Mode.MODIFIER_SELECT] as ModifierSelectUiHandler;
|
||||||
|
|
||||||
const getBestPartyMemberIndex = () => {
|
const getBestPartyMemberIndex = () => {
|
||||||
@ -153,15 +153,15 @@ export function initAutoPlay() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const originalSwitchCheckUiHandlerShow = switchCheckUiHandler.show;
|
const originalSwitchCheckUiHandlerShow = confirmUiHandler.show;
|
||||||
switchCheckUiHandler.show = function (args: any[]) {
|
confirmUiHandler.show = function (args: any[]) {
|
||||||
originalSwitchCheckUiHandlerShow.apply(this, [ args ]);
|
originalSwitchCheckUiHandlerShow.apply(this, [ args ]);
|
||||||
if (thisArg.auto) {
|
if (thisArg.auto) {
|
||||||
const bestPartyMemberIndex = getBestPartyMemberIndex();
|
const bestPartyMemberIndex = getBestPartyMemberIndex();
|
||||||
thisArg.time.delayedCall(20, () => {
|
thisArg.time.delayedCall(20, () => {
|
||||||
if (bestPartyMemberIndex)
|
if (bestPartyMemberIndex)
|
||||||
nextPartyMemberIndex = bestPartyMemberIndex;
|
nextPartyMemberIndex = bestPartyMemberIndex;
|
||||||
switchCheckUiHandler.setCursor(bestPartyMemberIndex ? 1 : 0);
|
confirmUiHandler.setCursor(bestPartyMemberIndex ? 1 : 0);
|
||||||
thisArg.time.delayedCall(20, () => this.processInput(Button.ACTION));
|
thisArg.time.delayedCall(20, () => this.processInput(Button.ACTION));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
121
src/ui/target-select-ui-handler.ts
Normal file
121
src/ui/target-select-ui-handler.ts
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { BattleTarget } from "../battle";
|
||||||
|
import BattleScene, { Button } from "../battle-scene";
|
||||||
|
import { Moves, getMoveTargets } from "../data/move";
|
||||||
|
import { Mode } from "./ui";
|
||||||
|
import UiHandler from "./uiHandler";
|
||||||
|
import * as Utils from "../utils";
|
||||||
|
|
||||||
|
export type TargetSelectCallback = (cursor: integer) => void;
|
||||||
|
|
||||||
|
export default class TargetSelectUiHandler extends UiHandler {
|
||||||
|
private fieldIndex: integer;
|
||||||
|
private move: Moves;
|
||||||
|
private targetSelectCallback: TargetSelectCallback;
|
||||||
|
|
||||||
|
private targets: BattleTarget[];
|
||||||
|
private targetFlashTween: Phaser.Tweens.Tween;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene) {
|
||||||
|
super(scene, Mode.TARGET_SELECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup(): void { }
|
||||||
|
|
||||||
|
show(args: any[]) {
|
||||||
|
if (args.length < 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
super.show(args);
|
||||||
|
|
||||||
|
this.fieldIndex = args[0] as integer;
|
||||||
|
this.move = args[1] as Moves;
|
||||||
|
this.targetSelectCallback = args[2] as TargetSelectCallback;
|
||||||
|
|
||||||
|
this.targets = getMoveTargets(this.scene.getPlayerField()[this.fieldIndex], this.move).targets;
|
||||||
|
|
||||||
|
if (!this.targets.length)
|
||||||
|
return;
|
||||||
|
|
||||||
|
console.log(this.targets);
|
||||||
|
|
||||||
|
this.setCursor(this.targets.indexOf(this.cursor) > -1 ? this.cursor : this.targets[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
processInput(button: Button) {
|
||||||
|
const ui = this.getUi();
|
||||||
|
|
||||||
|
let success = false;
|
||||||
|
|
||||||
|
if (button === Button.ACTION || button === Button.CANCEL) {
|
||||||
|
this.targetSelectCallback(button === Button.ACTION ? this.cursor : -1);
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
switch (button) {
|
||||||
|
case Button.UP:
|
||||||
|
if (this.cursor < BattleTarget.ENEMY && this.targets.find(t => t >= BattleTarget.ENEMY))
|
||||||
|
success = this.setCursor(this.targets.find(t => t >= BattleTarget.ENEMY));
|
||||||
|
break;
|
||||||
|
case Button.DOWN:
|
||||||
|
if (this.cursor >= BattleTarget.ENEMY && this.targets.find(t => t < BattleTarget.ENEMY))
|
||||||
|
success = this.setCursor(this.targets.find(t => t < BattleTarget.ENEMY));
|
||||||
|
break;
|
||||||
|
case Button.LEFT:
|
||||||
|
if (this.cursor % 2 && this.targets.find(t => t === this.cursor - 1))
|
||||||
|
success = this.setCursor(this.cursor - 1);
|
||||||
|
break;
|
||||||
|
case Button.RIGHT:
|
||||||
|
if (!(this.cursor % 2) && this.targets.find(t => t === this.cursor + 1))
|
||||||
|
success = this.setCursor(this.cursor + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
ui.playSelect();
|
||||||
|
}
|
||||||
|
|
||||||
|
setCursor(cursor: integer): boolean {
|
||||||
|
const lastCursor = this.cursor;
|
||||||
|
|
||||||
|
const ret = super.setCursor(cursor);
|
||||||
|
|
||||||
|
if (this.targetFlashTween) {
|
||||||
|
this.targetFlashTween.stop();
|
||||||
|
const lastTarget = this.scene.getField()[lastCursor];
|
||||||
|
if (lastTarget)
|
||||||
|
lastTarget.setAlpha(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = this.scene.getField()[cursor];
|
||||||
|
|
||||||
|
this.targetFlashTween = this.scene.tweens.add({
|
||||||
|
targets: [ target ],
|
||||||
|
alpha: 0,
|
||||||
|
loop: -1,
|
||||||
|
duration: new Utils.FixedInt(250) as unknown as integer,
|
||||||
|
ease: 'Sine.easeIn',
|
||||||
|
yoyo: true,
|
||||||
|
onUpdate: t => {
|
||||||
|
if (target)
|
||||||
|
target.setAlpha(t.getValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
eraseCursor() {
|
||||||
|
const target = this.scene.getField()[this.cursor];
|
||||||
|
if (this.targetFlashTween) {
|
||||||
|
this.targetFlashTween.stop();
|
||||||
|
this.targetFlashTween = null;
|
||||||
|
}
|
||||||
|
if (target)
|
||||||
|
target.setAlpha(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
super.clear();
|
||||||
|
this.eraseCursor();
|
||||||
|
}
|
||||||
|
}
|
@ -12,12 +12,14 @@ import SummaryUiHandler from './summary-ui-handler';
|
|||||||
import StarterSelectUiHandler from './starter-select-ui-handler';
|
import StarterSelectUiHandler from './starter-select-ui-handler';
|
||||||
import EvolutionSceneHandler from './evolution-scene-handler';
|
import EvolutionSceneHandler from './evolution-scene-handler';
|
||||||
import BiomeSelectUiHandler from './biome-select-ui-handler';
|
import BiomeSelectUiHandler from './biome-select-ui-handler';
|
||||||
|
import TargetSelectUiHandler from './target-select-ui-handler';
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
MESSAGE,
|
MESSAGE,
|
||||||
COMMAND,
|
COMMAND,
|
||||||
FIGHT,
|
FIGHT,
|
||||||
BALL,
|
BALL,
|
||||||
|
TARGET_SELECT,
|
||||||
MODIFIER_SELECT,
|
MODIFIER_SELECT,
|
||||||
PARTY,
|
PARTY,
|
||||||
SUMMARY,
|
SUMMARY,
|
||||||
@ -54,6 +56,7 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||||||
new CommandUiHandler(scene),
|
new CommandUiHandler(scene),
|
||||||
new FightUiHandler(scene),
|
new FightUiHandler(scene),
|
||||||
new BallUiHandler(scene),
|
new BallUiHandler(scene),
|
||||||
|
new TargetSelectUiHandler(scene),
|
||||||
new ModifierSelectUiHandler(scene),
|
new ModifierSelectUiHandler(scene),
|
||||||
new PartyUiHandler(scene),
|
new PartyUiHandler(scene),
|
||||||
new SummaryUiHandler(scene),
|
new SummaryUiHandler(scene),
|
||||||
|
@ -25,6 +25,8 @@ export function padInt(value: integer, length: integer, padWith?: string): strin
|
|||||||
export function randInt(range: integer, min?: integer): integer {
|
export function randInt(range: integer, min?: integer): integer {
|
||||||
if (!min)
|
if (!min)
|
||||||
min = 0;
|
min = 0;
|
||||||
|
if (range === 1)
|
||||||
|
return min;
|
||||||
return Math.floor(Math.random() * range) + min;
|
return Math.floor(Math.random() * range) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user