Add sleep moves
This commit is contained in:
parent
d030327336
commit
a66d2a8d17
|
@ -1,7 +1,7 @@
|
|||
import BattleScene from "./battle-scene";
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
|
||||
import * as Utils from './utils';
|
||||
import { allMoves, applyMoveAttrs, ChargeAttr, ConditionalFailMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move";
|
||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move";
|
||||
import { Mode } from './ui/ui';
|
||||
import { Command } from "./ui/command-ui-handler";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
|
@ -9,7 +9,7 @@ import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HitHealMod
|
|||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
|
||||
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||
import { StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
|
||||
import { Status, StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
|
||||
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
||||
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
||||
import { EvolutionPhase } from "./evolution-phase";
|
||||
|
@ -585,13 +585,16 @@ export class CommonAnimPhase extends PokemonPhase {
|
|||
export abstract class MovePhase extends BattlePhase {
|
||||
protected pokemon: Pokemon;
|
||||
protected move: PokemonMove;
|
||||
protected followUp: boolean;
|
||||
protected hasFollowUp: boolean;
|
||||
protected cancelled: boolean;
|
||||
|
||||
constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove) {
|
||||
constructor(scene: BattleScene, pokemon: Pokemon, move: PokemonMove, followUp?: boolean) {
|
||||
super(scene);
|
||||
|
||||
this.pokemon = pokemon;
|
||||
this.move = move;
|
||||
this.followUp = !!followUp;
|
||||
this.cancelled = false;
|
||||
}
|
||||
|
||||
|
@ -608,19 +611,23 @@ export abstract class MovePhase extends BattlePhase {
|
|||
start() {
|
||||
super.start();
|
||||
|
||||
this.pokemon.lapseTags(BattleTagLapseType.MOVE);
|
||||
const target = this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon();
|
||||
|
||||
if (!this.followUp)
|
||||
this.pokemon.lapseTags(BattleTagLapseType.MOVE);
|
||||
|
||||
const doMove = () => {
|
||||
if (this.cancelled) {
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500));
|
||||
if (this.pokemon.summonData.moveQueue.length && !this.pokemon.summonData.moveQueue.shift().ignorePP)
|
||||
if (!this.pokemon.summonData.moveQueue.length || !this.pokemon.summonData.moveQueue.shift().ignorePP)
|
||||
this.move.ppUsed++;
|
||||
|
||||
const failed = new Utils.BooleanHolder(false);
|
||||
applyMoveAttrs(ConditionalFailMoveAttr, this.pokemon, this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon(), this.move.getMove(), failed);
|
||||
applyMoveAttrs(ConditionalMoveAttr, this.pokemon, target, this.move.getMove(), failed);
|
||||
if (failed.value)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'But it failed!'));
|
||||
else
|
||||
|
@ -634,10 +641,11 @@ export abstract class MovePhase extends BattlePhase {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.pokemon.status && !this.pokemon.status.isPostTurn()) {
|
||||
if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) {
|
||||
this.pokemon.status.incrementTurn();
|
||||
let activated = false;
|
||||
let healed = false;
|
||||
|
||||
switch (this.pokemon.status.effect) {
|
||||
case StatusEffect.PARALYSIS:
|
||||
if (Utils.randInt(4) === 0) {
|
||||
|
@ -646,8 +654,9 @@ export abstract class MovePhase extends BattlePhase {
|
|||
}
|
||||
break;
|
||||
case StatusEffect.SLEEP:
|
||||
applyMoveAttrs(BypassSleepAttr, this.pokemon, target, this.move.getMove());
|
||||
healed = this.pokemon.status.turnCount === this.pokemon.status.cureTurn;
|
||||
activated = !healed;
|
||||
activated = !healed && !this.pokemon.getTag(BattleTagType.BYPASS_SLEEP);
|
||||
this.cancelled = activated;
|
||||
break;
|
||||
case StatusEffect.FREEZE:
|
||||
|
@ -673,11 +682,18 @@ export abstract class MovePhase extends BattlePhase {
|
|||
} else
|
||||
doMove();
|
||||
}
|
||||
|
||||
end() {
|
||||
if (!this.followUp)
|
||||
this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.isPlayer()));
|
||||
|
||||
super.end();
|
||||
}
|
||||
}
|
||||
|
||||
export class PlayerMovePhase extends MovePhase {
|
||||
constructor(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove) {
|
||||
super(scene, pokemon, move);
|
||||
constructor(scene: BattleScene, pokemon: PlayerPokemon, move: PokemonMove, followUp?: boolean) {
|
||||
super(scene, pokemon, move, followUp);
|
||||
}
|
||||
|
||||
getEffectPhase(): MoveEffectPhase {
|
||||
|
@ -686,8 +702,8 @@ export class PlayerMovePhase extends MovePhase {
|
|||
}
|
||||
|
||||
export class EnemyMovePhase extends MovePhase {
|
||||
constructor(scene: BattleScene, pokemon: EnemyPokemon, move: PokemonMove) {
|
||||
super(scene, pokemon, move);
|
||||
constructor(scene: BattleScene, pokemon: EnemyPokemon, move: PokemonMove, followUp?: boolean) {
|
||||
super(scene, pokemon, move, followUp);
|
||||
}
|
||||
|
||||
getEffectPhase(): MoveEffectPhase {
|
||||
|
@ -742,14 +758,6 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
const result = target.apply(user, this.move);
|
||||
++user.turnData.hitCount;
|
||||
user.summonData.moveHistory.push({ move: this.move.moveId, result: result });
|
||||
if (user.hp <= 0) {
|
||||
this.scene.pushPhase(new FaintPhase(this.scene, this.player));
|
||||
target.resetBattleSummonData();
|
||||
}
|
||||
if (target.hp <= 0) {
|
||||
this.scene.pushPhase(new FaintPhase(this.scene, !this.player));
|
||||
this.getUserPokemon().resetBattleSummonData();
|
||||
}
|
||||
applyMoveAttrs(MoveEffectAttr, user, target, this.move.getMove());
|
||||
// Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present
|
||||
if (target.hp && !this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length)
|
||||
|
@ -844,6 +852,20 @@ export class EnemyMoveEffectPhase extends MoveEffectPhase {
|
|||
}
|
||||
}
|
||||
|
||||
export class MoveEndPhase extends PokemonPhase {
|
||||
constructor(scene: BattleScene, player: boolean) {
|
||||
super(scene, player);
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
this.getPokemon().lapseTags(BattleTagLapseType.AFTER_MOVE);
|
||||
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveAnimTestPhase extends BattlePhase {
|
||||
private moveQueue: Moves[];
|
||||
|
||||
|
@ -963,17 +985,21 @@ export class StatChangePhase extends PokemonPhase {
|
|||
|
||||
export class ObtainStatusEffectPhase extends PokemonPhase {
|
||||
private statusEffect: StatusEffect;
|
||||
private cureTurn: integer;
|
||||
|
||||
constructor(scene: BattleScene, player: boolean, statusEffect: StatusEffect) {
|
||||
constructor(scene: BattleScene, player: boolean, statusEffect: StatusEffect, cureTurn?: integer) {
|
||||
super(scene, player);
|
||||
|
||||
this.statusEffect = statusEffect;
|
||||
this.cureTurn = cureTurn;
|
||||
}
|
||||
|
||||
start() {
|
||||
const pokemon = this.getPokemon();
|
||||
if (!pokemon.status) {
|
||||
if (pokemon.trySetStatus(this.statusEffect)) {
|
||||
if (this.cureTurn)
|
||||
pokemon.status.cureTurn = this.cureTurn;
|
||||
pokemon.updateInfo(true);
|
||||
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect))));
|
||||
|
@ -1004,16 +1030,12 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||
switch (pokemon.status.effect) {
|
||||
case StatusEffect.POISON:
|
||||
case StatusEffect.BURN:
|
||||
pokemon.hp = Math.max(pokemon.hp - Math.max(pokemon.getMaxHp() >> 3, 1), 0);
|
||||
pokemon.damage(Math.max(pokemon.getMaxHp() >> 3, 1));
|
||||
break;
|
||||
case StatusEffect.TOXIC:
|
||||
pokemon.hp = Math.max(pokemon.hp - Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1), 0);
|
||||
pokemon.damage(Math.max(Math.floor((pokemon.getMaxHp() / 16) * pokemon.status.turnCount), 1));
|
||||
break;
|
||||
}
|
||||
if (pokemon.hp <= 0) {
|
||||
this.scene.pushPhase(new FaintPhase(this.scene, this.player));
|
||||
(this.player ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).resetBattleSummonData();
|
||||
}
|
||||
pokemon.updateInfo().then(() => this.end());
|
||||
});
|
||||
} else
|
||||
|
@ -1109,6 +1131,7 @@ export class FaintPhase extends PokemonPhase {
|
|||
onComplete: () => {
|
||||
pokemon.setVisible(false);
|
||||
pokemon.y -= 150;
|
||||
pokemon.trySetStatus(StatusEffect.FAINT);
|
||||
if (pokemon.isPlayer())
|
||||
this.scene.currentBattle.removeFaintedParticipant(pokemon as PlayerPokemon);
|
||||
this.scene.field.remove(pokemon);
|
||||
|
@ -1347,7 +1370,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
|
|||
}
|
||||
|
||||
start() {
|
||||
if (!this.skipAnim)
|
||||
if (!this.skipAnim && this.getPokemon().getHpRatio() < 1)
|
||||
super.start();
|
||||
else
|
||||
this.end();
|
||||
|
|
|
@ -9,15 +9,18 @@ export enum BattleTagType {
|
|||
NONE,
|
||||
FLINCHED,
|
||||
CONFUSED,
|
||||
NIGHTMARE,
|
||||
FRENZY,
|
||||
FLYING,
|
||||
UNDERGROUND,
|
||||
BYPASS_SLEEP,
|
||||
IGNORE_FLYING
|
||||
}
|
||||
|
||||
export enum BattleTagLapseType {
|
||||
FAINT,
|
||||
MOVE,
|
||||
AFTER_MOVE,
|
||||
MOVE_EFFECT,
|
||||
TURN_END,
|
||||
CUSTOM
|
||||
|
@ -40,7 +43,7 @@ export class BattleTag {
|
|||
|
||||
onOverlap(pokemon: Pokemon): void { }
|
||||
|
||||
lapse(pokemon: Pokemon): boolean {
|
||||
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean {
|
||||
return --this.turnCount > 0;
|
||||
}
|
||||
}
|
||||
|
@ -50,8 +53,8 @@ export class FlinchedTag extends BattleTag {
|
|||
super(BattleTagType.FLINCHED, BattleTagLapseType.MOVE, 1);
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon): boolean {
|
||||
super.lapse(pokemon);
|
||||
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean {
|
||||
super.lapse(pokemon, lapseType);
|
||||
|
||||
(pokemon.scene.getCurrentPhase() as MovePhase).cancel();
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' flinched!')));
|
||||
|
@ -61,14 +64,14 @@ export class FlinchedTag extends BattleTag {
|
|||
}
|
||||
|
||||
export class PseudoStatusTag extends BattleTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, BattleTagLapseType.MOVE, turnCount);
|
||||
constructor(tagType: BattleTagType, lapseType: BattleTagLapseType, turnCount: integer) {
|
||||
super(tagType, lapseType, turnCount);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfusedTag extends PseudoStatusTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, turnCount);
|
||||
constructor(turnCount: integer) {
|
||||
super(BattleTagType.CONFUSED, BattleTagLapseType.MOVE, turnCount);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
|
@ -90,8 +93,8 @@ export class ConfusedTag extends PseudoStatusTag {
|
|||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nalready confused!')));
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon): boolean {
|
||||
const ret = super.lapse(pokemon);
|
||||
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean {
|
||||
const ret = super.lapse(pokemon, lapseType);
|
||||
|
||||
if (ret) {
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nconfused!')));
|
||||
|
@ -103,7 +106,7 @@ export class ConfusedTag extends PseudoStatusTag {
|
|||
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * ((Utils.randInt(15) + 85) / 100));
|
||||
pokemon.hp = Math.max(pokemon.hp - damage, 0);
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, 'It hurt itself in its\nconfusion!'));
|
||||
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), damage));
|
||||
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer()));
|
||||
(pokemon.scene.getCurrentPhase() as MovePhase).cancel();
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +115,40 @@ export class ConfusedTag extends PseudoStatusTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class NightmareTag extends PseudoStatusTag {
|
||||
constructor() {
|
||||
super(BattleTagType.NIGHTMARE, BattleTagLapseType.AFTER_MOVE, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' began\nhaving a nightmare!')));
|
||||
}
|
||||
|
||||
onOverlap(pokemon: Pokemon): void {
|
||||
super.onOverlap(pokemon);
|
||||
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nalready locked in a nightmare!')));
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon, lapseType: BattleTagLapseType): boolean {
|
||||
console.trace(lapseType);
|
||||
const ret = lapseType !== BattleTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
|
||||
|
||||
if (ret) {
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is locked\nin a nightmare!')));
|
||||
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CURSE)); // TODO: Update animation type
|
||||
|
||||
const damage = Math.ceil(pokemon.getMaxHp() / 4);
|
||||
pokemon.hp = Math.max(pokemon.hp - damage, 0);
|
||||
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export class HideSpriteTag extends BattleTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, BattleTagLapseType.MOVE_EFFECT, turnCount);
|
||||
|
@ -139,10 +176,14 @@ export function getBattleTag(tagType: BattleTagType, turnCount: integer): Battle
|
|||
return new FlinchedTag();
|
||||
break;
|
||||
case BattleTagType.CONFUSED:
|
||||
return new ConfusedTag(tagType, turnCount);
|
||||
return new ConfusedTag(turnCount);
|
||||
case BattleTagType.NIGHTMARE:
|
||||
return new NightmareTag();
|
||||
case BattleTagType.FLYING:
|
||||
case BattleTagType.UNDERGROUND:
|
||||
return new HideSpriteTag(tagType, turnCount);
|
||||
case BattleTagType.BYPASS_SLEEP:
|
||||
return new BattleTag(BattleTagType.BYPASS_SLEEP, BattleTagLapseType.TURN_END, turnCount);
|
||||
case BattleTagType.IGNORE_FLYING:
|
||||
return new BattleTag(tagType, BattleTagLapseType.TURN_END, turnCount);
|
||||
default:
|
||||
|
|
|
@ -356,6 +356,8 @@ export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
|
|||
let restorePoints = this.restorePoints;
|
||||
if (!this.fainted)
|
||||
restorePoints = Math.floor(restorePoints * (args[1] as number));
|
||||
else
|
||||
pokemon.resetStatus();
|
||||
pokemon.hp = Math.min(pokemon.hp + (this.percent ? (restorePoints * 0.01) * pokemon.getMaxHp() : restorePoints), pokemon.getMaxHp());
|
||||
}
|
||||
|
||||
|
|
76
src/move.ts
76
src/move.ts
|
@ -1,6 +1,5 @@
|
|||
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||
import { EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases";
|
||||
import BattleScene from "./battle-scene";
|
||||
import { EnemyMovePhase, MessagePhase, MovePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon";
|
||||
import { BattleTagType } from "./battle-tag";
|
||||
|
@ -686,8 +685,18 @@ enum MultiHitType {
|
|||
}
|
||||
|
||||
class HealAttr extends MoveEffectAttr {
|
||||
private fullHeal: boolean;
|
||||
private showAnim: boolean;
|
||||
|
||||
constructor(fullHeal?: boolean, showAnim?: boolean) {
|
||||
super(true);
|
||||
|
||||
this.fullHeal = !!fullHeal;
|
||||
this.showAnim = !!showAnim;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.getMaxHp() / 2), 1), getPokemonMessage(user, ' regained\nhealth!'), true, true));
|
||||
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.getMaxHp() / (this.fullHeal ? 1 : 2)), 1), getPokemonMessage(user, ' regained\nhealth!'), true, !this.showAnim));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -737,17 +746,23 @@ export class MultiHitAttr extends MoveAttr {
|
|||
|
||||
class StatusEffectAttr extends MoveHitEffectAttr {
|
||||
public effect: StatusEffect;
|
||||
public selfTarget: boolean;
|
||||
public cureTurn: integer;
|
||||
|
||||
constructor(effect: StatusEffect) {
|
||||
constructor(effect: StatusEffect, selfTarget?: boolean, cureTurn?: integer) {
|
||||
super();
|
||||
|
||||
this.effect = effect;
|
||||
this.selfTarget = !!selfTarget;
|
||||
this.cureTurn = cureTurn;
|
||||
}
|
||||
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const statusCheck = move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance;
|
||||
if (statusCheck) {
|
||||
if (!target.status || (target.status.effect === this.effect && move.chance < 0)) {
|
||||
user.scene.unshiftPhase(new ObtainStatusEffectPhase(user.scene, target.isPlayer(), this.effect));
|
||||
const pokemon = this.selfTarget ? user : target;
|
||||
if (!pokemon.status || (pokemon.status.effect === this.effect && move.chance < 0)) {
|
||||
user.scene.unshiftPhase(new ObtainStatusEffectPhase(user.scene, pokemon.isPlayer(), this.effect, this.cureTurn));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -755,6 +770,19 @@ class StatusEffectAttr extends MoveHitEffectAttr {
|
|||
}
|
||||
}
|
||||
|
||||
export class BypassSleepAttr extends MoveAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (user.status?.effect === StatusEffect.SLEEP) {
|
||||
console.log('add bypass sleep');
|
||||
user.addTag(BattleTagType.BYPASS_SLEEP, 1);
|
||||
console.log(user.getTag(BattleTagType.BYPASS_SLEEP));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class OneHitKOAttr extends MoveHitEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
target.hp = 0;
|
||||
|
@ -826,7 +854,7 @@ export class StatChangeAttr extends MoveEffectAttr {
|
|||
}
|
||||
}
|
||||
|
||||
export class ConditionalFailMoveAttr extends MoveAttr {
|
||||
export class ConditionalMoveAttr extends MoveAttr {
|
||||
private successFunc: MoveAttrFunc;
|
||||
|
||||
constructor(successFunc: MoveAttrFunc) {
|
||||
|
@ -956,13 +984,28 @@ export function applyMoveAttrs(attrType: { new(...args: any[]): MoveAttr }, user
|
|||
});
|
||||
}
|
||||
|
||||
class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const moves = user.moveset.filter(m => m.moveId !== move.id);
|
||||
if (moves.length) {
|
||||
const move = moves[Utils.randInt(moves.length)];
|
||||
const moveIndex = user.moveset.findIndex(m => m.moveId === move.moveId);
|
||||
user.summonData.moveQueue.push({ move: move.moveId });
|
||||
user.scene.unshiftPhase(user.isPlayer() ? new PlayerMovePhase(user.scene, user as PlayerPokemon, user.moveset[moveIndex], true) : new EnemyMovePhase(user.scene, user as EnemyPokemon, user.moveset[moveIndex], true));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class RandomMoveAttr extends OverrideMoveEffectAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
const moveIds = Utils.getEnumValues(Moves).filter(m => m !== move.id);
|
||||
const moveId = moveIds[Utils.randInt(moveIds.length)];
|
||||
user.summonData.moveQueue.push({ move: moveId, ignorePP: true });
|
||||
user.scene.unshiftPhase(user.isPlayer() ? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(moveId)) : new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(moveId)));
|
||||
user.scene.unshiftPhase(user.isPlayer() ? new PlayerMovePhase(user.scene, user as PlayerPokemon, new PokemonMove(moveId), true) : new EnemyMovePhase(user.scene, user as EnemyPokemon, new PokemonMove(moveId), true));
|
||||
initMoveAnim(moveId).then(() => {
|
||||
loadMoveAnimAssets(user.scene, [ moveId ], true)
|
||||
.then(() => resolve(true));
|
||||
|
@ -1123,7 +1166,8 @@ export const allMoves = [
|
|||
return true;
|
||||
})),
|
||||
new StatusMove(Moves.GLARE, "Glare", Type.NORMAL, 100, 30, -1, "Paralyzes opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||
new AttackMove(Moves.DREAM_EATER, "Dream Eater", Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 15, -1, "User recovers half the HP inflicted on a sleeping opponent.", -1, 0, 1, new HitHealAttr()),
|
||||
new AttackMove(Moves.DREAM_EATER, "Dream Eater", Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 15, -1, "User recovers half the HP inflicted on a sleeping opponent.", -1, 0, 1,
|
||||
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP), new HitHealAttr()),
|
||||
new StatusMove(Moves.POISON_GAS, "Poison Gas", Type.POISON, 90, 40, -1, "Poisons opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.POISON)),
|
||||
new AttackMove(Moves.BARRAGE, "Barrage", Type.NORMAL, MoveCategory.PHYSICAL, 15, 85, 20, -1, "Hits 2-5 times in one turn.", -1, 0, 1, new MultiHitAttr()),
|
||||
new AttackMove(Moves.LEECH_LIFE, "Leech Life", Type.BUG, MoveCategory.PHYSICAL, 80, 100, 10, 95, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
|
||||
|
@ -1142,7 +1186,8 @@ export const allMoves = [
|
|||
new AttackMove(Moves.EXPLOSION, "Explosion", Type.NORMAL, MoveCategory.PHYSICAL, 250, 100, 5, -1, "User faints.", -1, 0, 1),
|
||||
new AttackMove(Moves.FURY_SWIPES, "Fury Swipes", Type.NORMAL, MoveCategory.PHYSICAL, 18, 80, 15, -1, "Hits 2-5 times in one turn.", -1, 0, 1, new MultiHitAttr()),
|
||||
new AttackMove(Moves.BONEMERANG, "Bonemerang", Type.GROUND, MoveCategory.PHYSICAL, 50, 90, 10, -1, "Hits twice in one turn.", -1, 0, 1, new MultiHitAttr(MultiHitType._2)),
|
||||
new StatusMove(Moves.REST, "Rest", Type.PSYCHIC, -1, 5, 85, "User sleeps for 2 turns, but user is fully healed.", -1, 0, 1),
|
||||
new StatusMove(Moves.REST, "Rest", Type.PSYCHIC, -1, 5, 85, "User sleeps for 2 turns, but user is fully healed.", -1, 0, 1,
|
||||
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect !== StatusEffect.SLEEP), new StatusEffectAttr(StatusEffect.SLEEP, true, 3), new HealAttr(true, true)),
|
||||
new AttackMove(Moves.ROCK_SLIDE, "Rock Slide", Type.ROCK, MoveCategory.PHYSICAL, 75, 90, 10, 86, "May cause flinching.", 30, 0, 1, new FlinchAttr()),
|
||||
new AttackMove(Moves.HYPER_FANG, "Hyper Fang", Type.NORMAL, MoveCategory.PHYSICAL, 80, 90, 15, -1, "May cause flinching.", 10, 0, 1, new FlinchAttr()),
|
||||
new StatusMove(Moves.SHARPEN, "Sharpen", Type.NORMAL, -1, 30, -1, "Raises user's Attack.", -1, 0, 1, new StatChangeAttr(BattleStat.ATK, 1, true)),
|
||||
|
@ -1161,9 +1206,11 @@ export const allMoves = [
|
|||
new AttackMove(Moves.THIEF, "Thief", Type.DARK, MoveCategory.PHYSICAL, 60, 100, 25, 18, "Also steals opponent's held item.", -1, 0, 2),
|
||||
new StatusMove(Moves.SPIDER_WEB, "Spider Web", Type.BUG, -1, 10, -1, "Opponent cannot escape/switch.", -1, 0, 2),
|
||||
new StatusMove(Moves.MIND_READER, "Mind Reader", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 2),
|
||||
new StatusMove(Moves.NIGHTMARE, "Nightmare", Type.GHOST, 100, 15, -1, "The sleeping opponent loses 25% of its max HP each turn.", -1, 0, 2),
|
||||
new StatusMove(Moves.NIGHTMARE, "Nightmare", Type.GHOST, 100, 15, -1, "The sleeping opponent loses 25% of its max HP each turn.", -1, 0, 2,
|
||||
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => target.status?.effect === StatusEffect.SLEEP), new AddTagAttr(BattleTagType.NIGHTMARE, 1)),
|
||||
new AttackMove(Moves.FLAME_WHEEL, "Flame Wheel", Type.FIRE, MoveCategory.PHYSICAL, 60, 100, 25, -1, "May burn opponent.", 10, 0, 2, new StatusEffectAttr(StatusEffect.BURN)),
|
||||
new AttackMove(Moves.SNORE, "Snore", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 15, -1, "Can only be used if asleep. May cause flinching.", 30, 0, 2, new FlinchAttr()), // TODO
|
||||
new AttackMove(Moves.SNORE, "Snore", Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 15, -1, "Can only be used if asleep. May cause flinching.", 30, 0, 2,
|
||||
new BypassSleepAttr(), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect === StatusEffect.SLEEP), new FlinchAttr()),
|
||||
new StatusMove(Moves.CURSE, "Curse", Type.GHOST, -1, 10, -1, "Ghosts lose 50% of max HP and curse the opponent; Non-Ghosts raise Attack, Defense and lower Speed.", -1, 0, 2),
|
||||
new AttackMove(Moves.FLAIL, "Flail", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 15, -1, "The lower the user's HP, the higher the power.", -1, 0, 2),
|
||||
new StatusMove(Moves.CONVERSION_2, "Conversion 2", Type.NORMAL, -1, 30, -1, "User changes type to become resistant to opponent's last move.", -1, 0, 2),
|
||||
|
@ -1205,7 +1252,8 @@ export const allMoves = [
|
|||
new AttackMove(Moves.STEEL_WING, "Steel Wing", Type.STEEL, MoveCategory.PHYSICAL, 70, 90, 25, -1, "May raise user's Defense.", 10, 0, 2, new StatChangeAttr(BattleStat.DEF, 1, true)),
|
||||
new StatusMove(Moves.MEAN_LOOK, "Mean Look", Type.NORMAL, -1, 5, -1, "Opponent cannot flee or switch.", -1, 0, 2),
|
||||
new StatusMove(Moves.ATTRACT, "Attract", Type.NORMAL, 100, 15, -1, "If opponent is the opposite gender, it's less likely to attack.", -1, 0, 2),
|
||||
new StatusMove(Moves.SLEEP_TALK, "Sleep Talk", Type.NORMAL, -1, 10, 70, "User performs one of its own moves while sleeping.", -1, 0, 2),
|
||||
new StatusMove(Moves.SLEEP_TALK, "Sleep Talk", Type.NORMAL, -1, 10, 70, "User performs one of its own moves while sleeping.", -1, 0, 2,
|
||||
new BypassSleepAttr(), new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect === StatusEffect.SLEEP), new RandomMovesetMoveAttr()),
|
||||
new StatusMove(Moves.HEAL_BELL, "Heal Bell", Type.NORMAL, -1, 5, -1, "Heals the user's party's status conditions.", -1, 0, 2),
|
||||
new AttackMove(Moves.RETURN, "Return", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 20, -1, "Power increases with higher Friendship.", -1, 0, 2),
|
||||
new AttackMove(Moves.PRESENT, "Present", Type.NORMAL, MoveCategory.PHYSICAL, -1, 90, 15, -1, "Either deals damage or heals.", -1, 0, 2),
|
||||
|
@ -1246,7 +1294,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.WHIRLPOOL, "Whirlpool", Type.WATER, MoveCategory.SPECIAL, 35, 85, 15, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 2),
|
||||
new AttackMove(Moves.BEAT_UP, "Beat Up", Type.DARK, MoveCategory.PHYSICAL, -1, 100, 10, -1, "Each Pokémon in user's party attacks.", -1, 0, 2),
|
||||
new AttackMove(Moves.FAKE_OUT, "Fake Out", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 10, -1, "User attacks first, foe flinches. Only usable on first turn.", 100, 3, 3,
|
||||
new ConditionalFailMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.battleSummonData.turnCount === 1), new FlinchAttr()),
|
||||
new ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.battleSummonData.turnCount === 1), new FlinchAttr()),
|
||||
new AttackMove(Moves.UPROAR, "Uproar", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, "User attacks for 3 turns and prevents sleep.", -1, 0, 3), // TODO
|
||||
new StatusMove(Moves.STOCKPILE, "Stockpile", Type.NORMAL, -1, 20, -1, "Stores energy for use with Spit Up and Swallow.", -1, 0, 3),
|
||||
new AttackMove(Moves.SPIT_UP, "Spit Up", Type.NORMAL, MoveCategory.SPECIAL, -1, 100, 10, -1, "Power depends on how many times the user performed Stockpile.", -1, 0, 3),
|
||||
|
|
|
@ -15,7 +15,7 @@ import { initMoveAnim, loadMoveAnimAssets } from './battle-anims';
|
|||
import { Status, StatusEffect } from './status-effect';
|
||||
import { tmSpecies } from './tms';
|
||||
import { pokemonEvolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './pokemon-evolutions';
|
||||
import { DamagePhase, MessagePhase } from './battle-phases';
|
||||
import { DamagePhase, FaintPhase, MessagePhase } from './battle-phases';
|
||||
import { BattleStat } from './battle-stat';
|
||||
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag';
|
||||
import { Species } from './species';
|
||||
|
@ -476,7 +476,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
if (damage) {
|
||||
this.hp = Math.max(this.hp - damage, 0);
|
||||
this.damage(damage);
|
||||
source.turnData.damageDealt += damage;
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult))
|
||||
if (isCritical)
|
||||
|
@ -510,6 +510,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return result;
|
||||
}
|
||||
|
||||
damage(damage: integer): void {
|
||||
if (!this.hp)
|
||||
return;
|
||||
|
||||
this.hp = Math.max(this.hp - damage, 0);
|
||||
if (!this.hp) {
|
||||
this.scene.pushPhase(new FaintPhase(this.scene, this.isPlayer()));
|
||||
(this.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).resetBattleSummonData();
|
||||
}
|
||||
}
|
||||
|
||||
addTag(tagType: BattleTagType, turnCount?: integer): boolean {
|
||||
const existingTag = this.getTag(tagType);
|
||||
if (existingTag) {
|
||||
|
@ -545,7 +556,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
lapseTag(tagType: BattleTagType): void {
|
||||
const tags = this.summonData.tags;
|
||||
const tag = tags.find(t => t.tagType === tagType);
|
||||
if (tag && !(tag.lapse(this))) {
|
||||
if (tag && !(tag.lapse(this, BattleTagLapseType.CUSTOM))) {
|
||||
tag.onRemove(this);
|
||||
tags.splice(tags.indexOf(tag), 1);
|
||||
}
|
||||
|
@ -553,7 +564,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
lapseTags(lapseType: BattleTagLapseType): void {
|
||||
const tags = this.summonData.tags;
|
||||
tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this))) || (lapseType === BattleTagLapseType.TURN_END && t.turnCount < 1)).forEach(t => {
|
||||
tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this, lapseType))) || (lapseType === BattleTagLapseType.TURN_END && t.turnCount < 1)).forEach(t => {
|
||||
t.onRemove(this);
|
||||
tags.splice(tags.indexOf(t), 1);
|
||||
});
|
||||
|
@ -651,7 +662,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
resetStatus(): void {
|
||||
const lastStatus = this.status.effect;
|
||||
this.status = undefined;
|
||||
if (lastStatus === StatusEffect.SLEEP) {
|
||||
if (this.getTag(BattleTagType.NIGHTMARE))
|
||||
this.lapseTag(BattleTagType.NIGHTMARE);
|
||||
}
|
||||
}
|
||||
|
||||
resetSummonData(): void {
|
||||
|
|
|
@ -7,7 +7,8 @@ export enum StatusEffect {
|
|||
PARALYSIS,
|
||||
SLEEP,
|
||||
FREEZE,
|
||||
BURN
|
||||
BURN,
|
||||
FAINT
|
||||
}
|
||||
|
||||
export class Status {
|
||||
|
|
Loading…
Reference in New Issue