Add confusion and frenzy move effects
This commit is contained in:
parent
b11e391e7a
commit
656b6951b6
|
@ -332,7 +332,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
|||
moveAnim.bgSprite.setScale(1.25);
|
||||
moveAnim.bgSprite.setAlpha(0);
|
||||
scene.field.add(moveAnim.bgSprite);
|
||||
scene.field.moveAbove(moveAnim.bgSprite, scene.arenaEnemy);
|
||||
scene.field.moveBelow(moveAnim.bgSprite, scene.getEnemyPokemon());
|
||||
|
||||
scene.tweens.add({
|
||||
targets: moveAnim.bgSprite,
|
||||
|
@ -766,15 +766,15 @@ export class MoveAnim extends BattleAnim {
|
|||
getAnim(): Anim {
|
||||
return moveAnims.get(this.move) instanceof Anim
|
||||
? moveAnims.get(this.move) as Anim
|
||||
: moveAnims.get(this.move)[this.user instanceof PlayerPokemon ? 0 : 1] as Anim;
|
||||
: moveAnims.get(this.move)[this.user.isPlayer() ? 0 : 1] as Anim;
|
||||
}
|
||||
|
||||
isOppAnim(): boolean {
|
||||
return this.user instanceof EnemyPokemon && Array.isArray(moveAnims.get(this.move));
|
||||
return !this.user.isPlayer() && Array.isArray(moveAnims.get(this.move));
|
||||
}
|
||||
|
||||
isReverseCoords(): boolean {
|
||||
return this.user instanceof EnemyPokemon && !this.isOppAnim();
|
||||
return !this.user.isPlayer() && !this.isOppAnim();
|
||||
}
|
||||
|
||||
getGraphicScale(): number {
|
||||
|
@ -799,7 +799,7 @@ export class MoveChargeAnim extends MoveAnim {
|
|||
getAnim(): Anim {
|
||||
return chargeAnims.get(this.chargeAnim) instanceof Anim
|
||||
? chargeAnims.get(this.chargeAnim) as Anim
|
||||
: chargeAnims.get(this.chargeAnim)[this.user instanceof PlayerPokemon ? 0 : 1] as Anim;
|
||||
: chargeAnims.get(this.chargeAnim)[this.user.isPlayer() ? 0 : 1] as Anim;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import BattleScene from "./battle-scene";
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult } from "./pokemon";
|
||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
|
||||
import * as Utils from './utils';
|
||||
import { allMoves, applyMoveAttrs, ChargeAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move";
|
||||
import { Mode } from './ui/ui';
|
||||
|
@ -8,7 +8,7 @@ import { Stat } from "./pokemon-stat";
|
|||
import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HitHealModifier } from "./modifier";
|
||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
|
||||
import { ChargeAnim, CommonAnim, CommonBattleAnim, MoveAnim, chargeAnims, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||
import { StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
|
||||
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
||||
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
||||
|
@ -19,7 +19,8 @@ import { Biome, biomeLinks } from "./biome";
|
|||
import { ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier-type";
|
||||
import PokemonSpecies from "./pokemon-species";
|
||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { BattleTagLapseType } from "./battle-tag";
|
||||
import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag";
|
||||
import { getPokemonMessage } from "./messages";
|
||||
|
||||
export class SelectStarterPhase extends BattlePhase {
|
||||
constructor(scene: BattleScene) {
|
||||
|
@ -385,6 +386,11 @@ export class CheckSwitchPhase extends BattlePhase {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.scene.getPlayerPokemon().getTag(BattleTagType.FRENZY)) {
|
||||
super.end();
|
||||
return;
|
||||
}
|
||||
|
||||
this.scene.ui.showText('Will you switch\nPOKéMON?', null, () => {
|
||||
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, false, true));
|
||||
|
@ -551,7 +557,7 @@ export class CommonAnimPhase extends PokemonPhase {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class MovePhase extends BattlePhase {
|
||||
export abstract class MovePhase extends BattlePhase {
|
||||
protected pokemon: Pokemon;
|
||||
protected move: PokemonMove;
|
||||
protected cancelled: boolean;
|
||||
|
@ -570,20 +576,24 @@ abstract class MovePhase extends BattlePhase {
|
|||
return !!this.pokemon.hp;
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.cancelled = true;
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
this.pokemon.lapseTags(BattleTagLapseType.MOVE);
|
||||
|
||||
const doMove = () => {
|
||||
if (this.cancelled) {
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
if (!this.move)
|
||||
console.log(this.pokemon.moveset);
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500));
|
||||
this.scene.unshiftPhase(this.getEffectPhase());
|
||||
if (this.pokemon.summonData.moveQueue.length && !this.pokemon.summonData.moveQueue.shift().ignorePP)
|
||||
this.move.ppUsed++;
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `${this.pokemon instanceof EnemyPokemon ? 'Foe ' : ''}${this.pokemon.name} used\n${this.move.getName()}!`, 500));
|
||||
this.scene.unshiftPhase(this.getEffectPhase());
|
||||
this.end();
|
||||
};
|
||||
|
||||
|
@ -616,12 +626,13 @@ abstract class MovePhase extends BattlePhase {
|
|||
}
|
||||
if (activated) {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
||||
`${this.pokemon instanceof PlayerPokemon ? '' : 'Foe '}${this.pokemon.name}${getStatusEffectActivationText(this.pokemon.status.effect)}`));
|
||||
new CommonBattleAnim(CommonAnim.POISON + (this.pokemon.status.effect - 1), this.pokemon).play(this.scene, () => doMove());
|
||||
getPokemonMessage(this.pokemon, getStatusEffectActivationText(this.pokemon.status.effect))));
|
||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.isPlayer(), CommonAnim.POISON + (this.pokemon.status.effect - 1)));
|
||||
doMove();
|
||||
} else {
|
||||
if (healed) {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
||||
`${this.pokemon instanceof PlayerPokemon ? '' : 'Foe '}${this.pokemon.name}${getStatusEffectHealText(this.pokemon.status.effect)}`));
|
||||
getPokemonMessage(this.pokemon, getStatusEffectHealText(this.pokemon.status.effect))));
|
||||
this.pokemon.resetStatus();
|
||||
this.pokemon.updateInfo(true);
|
||||
}
|
||||
|
@ -678,7 +689,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
return;
|
||||
}
|
||||
|
||||
user.lapseTags(BattleTagLapseType.MOVE);
|
||||
user.lapseTags(BattleTagLapseType.MOVE_EFFECT);
|
||||
|
||||
if (user.turnData.hitsLeft === undefined) {
|
||||
const hitCount = new Utils.IntegerHolder(1);
|
||||
|
@ -688,7 +699,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
if (!this.hitCheck()) {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `${!this.player ? 'Foe ' : ''}${user.name}'s\nattack missed!`));
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(user, '\'s\nattack missed!')));
|
||||
user.summonData.moveHistory.push({ move: this.move.moveId, result: MoveResult.MISSED });
|
||||
applyMoveAttrs(MissEffectAttr, this.scene, user, target, this.move.getMove());
|
||||
this.end();
|
||||
|
@ -696,25 +707,22 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
new MoveAnim(this.move.getMove().id as Moves, user, target).play(this.scene, () => {
|
||||
target.apply(user, this.move).then(result => {
|
||||
++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();
|
||||
}
|
||||
if (target.hp) {
|
||||
applyMoveAttrs(MoveEffectAttr, this.scene, 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 (!this.move.getMove().getAttrs(ChargeAttr).filter(ca => (ca as ChargeAttr).chargeEffect).length)
|
||||
applyMoveAttrs(MoveHitEffectAttr, this.scene, user, target, this.move.getMove());
|
||||
}
|
||||
this.end();
|
||||
});
|
||||
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, this.scene, 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)
|
||||
applyMoveAttrs(MoveHitEffectAttr, this.scene, user, target, this.move.getMove());
|
||||
this.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -736,7 +744,7 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
|||
hitCheck(): boolean {
|
||||
// Check if not self targeting for this
|
||||
|
||||
const hiddenTag = this.getTargetPokemon().getTag(t => t.isHidden());
|
||||
const hiddenTag = this.getTargetPokemon().getTag(HiddenTag);
|
||||
if (hiddenTag) {
|
||||
if (!this.move.getMove().getAttrs(HitsTagAttr).filter(hta => (hta as HitsTagAttr).tagType === hiddenTag.tagType).length)
|
||||
return false;
|
||||
|
@ -847,7 +855,8 @@ export class StatChangePhase extends PokemonPhase {
|
|||
constructor(scene: BattleScene, player: boolean, stats: BattleStat[], levels: integer) {
|
||||
super(scene, player);
|
||||
|
||||
this.stats = stats;
|
||||
const allStats = Utils.getEnumValues(BattleStat);
|
||||
this.stats = stats.map(s => s !== BattleStat.RAND ? s : allStats[Utils.randInt(BattleStat.SPD + 1)]);
|
||||
this.levels = levels;
|
||||
}
|
||||
|
||||
|
@ -915,7 +924,7 @@ export class StatChangePhase extends PokemonPhase {
|
|||
const messages: string[] = [];
|
||||
|
||||
for (let s = 0; s < this.stats.length; s++)
|
||||
messages.push(`${this.player ? '' : 'Foe '}${this.getPokemon().name}'s ${getBattleStatName(this.stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`);
|
||||
messages.push(getPokemonMessage(this.getPokemon(), `'s ${getBattleStatName(this.stats[s])} ${getBattleStatLevelChangeDescription(Math.abs(relLevels[s]), this.levels >= 1)}!`));
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
@ -935,7 +944,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
|||
if (pokemon.trySetStatus(this.statusEffect)) {
|
||||
pokemon.updateInfo(true);
|
||||
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `${this.player ? '' : 'Foe '}${pokemon.name}${getStatusEffectObtainText(this.statusEffect)}`));
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect))));
|
||||
if (pokemon.status.isPostTurn())
|
||||
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player));
|
||||
this.end();
|
||||
|
@ -943,7 +952,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
|
|||
return;
|
||||
}
|
||||
} else if (pokemon.status.effect === this.statusEffect)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `${this.player ? '' : 'Foe '}${pokemon.name}${getStatusEffectOverlapText(this.statusEffect)}`));
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(pokemon, getStatusEffectOverlapText(this.statusEffect))));
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
@ -959,7 +968,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||
pokemon.status.incrementTurn();
|
||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, () => {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene,
|
||||
`${pokemon instanceof PlayerPokemon ? '' : 'Foe '}${pokemon.name}${getStatusEffectActivationText(pokemon.status.effect)}`));
|
||||
getPokemonMessage(pokemon, getStatusEffectActivationText(pokemon.status.effect))));
|
||||
switch (pokemon.status.effect) {
|
||||
case StatusEffect.POISON:
|
||||
case StatusEffect.BURN:
|
||||
|
@ -1000,6 +1009,44 @@ export class MessagePhase extends BattlePhase {
|
|||
}
|
||||
}
|
||||
|
||||
export class DamagePhase extends PokemonPhase {
|
||||
private damage: integer;
|
||||
private damageResult: DamageResult;
|
||||
|
||||
constructor(scene: BattleScene, player: boolean, damageResult?: DamageResult) {
|
||||
super(scene, player);
|
||||
|
||||
this.damageResult = damageResult || MoveResult.EFFECTIVE;
|
||||
}
|
||||
|
||||
start() {
|
||||
super.start();
|
||||
|
||||
switch (this.damageResult) {
|
||||
case MoveResult.EFFECTIVE:
|
||||
this.scene.sound.play('hit');
|
||||
break;
|
||||
case MoveResult.SUPER_EFFECTIVE:
|
||||
this.scene.sound.play('hit_strong');
|
||||
break;
|
||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
||||
this.scene.sound.play('hit_weak');
|
||||
break;
|
||||
}
|
||||
|
||||
const flashTimer = this.scene.time.addEvent({
|
||||
delay: 100,
|
||||
repeat: 5,
|
||||
startAt: 200,
|
||||
callback: () => {
|
||||
this.getPokemon().getSprite().setVisible(flashTimer.repeatCount % 2 === 0);
|
||||
if (!flashTimer.repeatCount)
|
||||
this.getPokemon().updateInfo().then(() => this.end());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class FaintPhase extends PokemonPhase {
|
||||
constructor(scene: BattleScene, player: boolean) {
|
||||
super(scene, player);
|
||||
|
@ -1008,13 +1055,12 @@ export class FaintPhase extends PokemonPhase {
|
|||
start() {
|
||||
super.start();
|
||||
|
||||
if (this.player) {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `${this.getPokemon().name} fainted!`, null, true));
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.getPokemon(), ' fainted!'), null, true));
|
||||
|
||||
if (this.player)
|
||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, true, false));
|
||||
} else {
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `Foe ${this.getPokemon().name} fainted!`, null, true));
|
||||
else
|
||||
this.scene.unshiftPhase(new VictoryPhase(this.scene));
|
||||
}
|
||||
|
||||
const pokemon = this.getPokemon();
|
||||
|
||||
|
@ -1031,7 +1077,7 @@ export class FaintPhase extends PokemonPhase {
|
|||
onComplete: () => {
|
||||
pokemon.setVisible(false);
|
||||
pokemon.y -= 150;
|
||||
if (pokemon instanceof PlayerPokemon)
|
||||
if (pokemon.isPlayer())
|
||||
this.scene.currentBattle.removeFaintedParticipant(pokemon as PlayerPokemon);
|
||||
this.scene.field.remove(pokemon);
|
||||
this.end();
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
import { CommonAnim } from "./battle-anims";
|
||||
import { CommonAnimPhase, DamagePhase, MessagePhase, MovePhase } from "./battle-phases";
|
||||
import { getPokemonMessage } from "./messages";
|
||||
import Pokemon from "./pokemon";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
import * as Utils from "./utils";
|
||||
|
||||
export enum BattleTagType {
|
||||
NONE,
|
||||
CONFUSED,
|
||||
FRENZY,
|
||||
FLYING,
|
||||
UNDERGROUND
|
||||
}
|
||||
|
@ -7,7 +16,9 @@ export enum BattleTagType {
|
|||
export enum BattleTagLapseType {
|
||||
FAINT,
|
||||
MOVE,
|
||||
TURN_END
|
||||
MOVE_EFFECT,
|
||||
TURN_END,
|
||||
CUSTOM
|
||||
}
|
||||
|
||||
export class BattleTag {
|
||||
|
@ -21,13 +32,98 @@ export class BattleTag {
|
|||
this.turnCount = turnCount;
|
||||
}
|
||||
|
||||
isHidden() {
|
||||
switch (this.tagType) {
|
||||
case BattleTagType.FLYING:
|
||||
case BattleTagType.UNDERGROUND:
|
||||
return true;
|
||||
}
|
||||
onAdd(pokemon: Pokemon): void { }
|
||||
|
||||
return false;
|
||||
onRemove(pokemon: Pokemon): void { }
|
||||
|
||||
onOverlap(pokemon: Pokemon): void { }
|
||||
|
||||
lapse(pokemon: Pokemon): boolean {
|
||||
return !!--this.turnCount;
|
||||
}
|
||||
}
|
||||
|
||||
export class PseudoStatusTag extends BattleTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, BattleTagLapseType.MOVE, turnCount);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfusedTag extends PseudoStatusTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, turnCount);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
|
||||
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION));
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' became\nconfused!')));
|
||||
}
|
||||
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
super.onRemove(pokemon);
|
||||
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' snapped\nout of confusion!')));
|
||||
}
|
||||
|
||||
onOverlap(pokemon: Pokemon): void {
|
||||
super.onOverlap(pokemon);
|
||||
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nalready confused!')));
|
||||
}
|
||||
|
||||
lapse(pokemon: Pokemon): boolean {
|
||||
const ret = super.lapse(pokemon);
|
||||
|
||||
if (ret) {
|
||||
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' is\nconfused!')));
|
||||
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.isPlayer(), CommonAnim.CONFUSION));
|
||||
|
||||
if (Utils.randInt(2)) {
|
||||
const atk = pokemon.getBattleStat(Stat.ATK);
|
||||
const def = pokemon.getBattleStat(Stat.DEF);
|
||||
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.getCurrentPhase() as MovePhase).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export class HideSpriteTag extends BattleTag {
|
||||
constructor(tagType: BattleTagType, turnCount: integer) {
|
||||
super(tagType, BattleTagLapseType.MOVE_EFFECT, turnCount);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
|
||||
pokemon.setVisible(false);
|
||||
}
|
||||
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
// Wait 2 frames before setting visible for battle animations that don't immediately show the sprite invisible
|
||||
pokemon.scene.tweens.addCounter({
|
||||
duration: 2,
|
||||
useFrames: true,
|
||||
onComplete: () => pokemon.setVisible(true)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getBattleTag(tagType: BattleTagType, turnCount: integer): BattleTag {
|
||||
switch (tagType) {
|
||||
case BattleTagType.CONFUSED:
|
||||
return new ConfusedTag(tagType, turnCount);
|
||||
case BattleTagType.FLYING:
|
||||
case BattleTagType.UNDERGROUND:
|
||||
return new HideSpriteTag(tagType, turnCount);
|
||||
default:
|
||||
return new BattleTag(tagType, BattleTagLapseType.CUSTOM, turnCount);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import Pokemon, { EnemyPokemon } from "./pokemon";
|
||||
|
||||
export function getPokemonMessage(pokemon: Pokemon, content: string): string {
|
||||
return `${!pokemon.isPlayer() ? 'Wild ' : ''}${pokemon.name}${content}`;
|
||||
}
|
|
@ -498,8 +498,8 @@ export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer
|
|||
function getNewModifierTypeOption(party: PlayerPokemon[], tier?: ModifierTier, upgrade?: boolean): ModifierTypeOption {
|
||||
const tierValue = Utils.randInt(256);
|
||||
if (tier === undefined) {
|
||||
tier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0);
|
||||
upgrade = Utils.randInt(32) === 0;
|
||||
tier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0);
|
||||
}
|
||||
const thresholds = Object.keys(modifierPoolThresholds[tier]);
|
||||
const totalWeight = parseInt(thresholds[thresholds.length - 1]);
|
||||
|
|
173
src/move.ts
173
src/move.ts
|
@ -3,10 +3,11 @@ import { EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase,
|
|||
import BattleScene from "./battle-scene";
|
||||
import { BattleStat } from "./battle-stat";
|
||||
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon";
|
||||
import { BattleTagLapseType, BattleTagType } from "./battle-tag";
|
||||
import { BattleTagType } from "./battle-tag";
|
||||
import { StatusEffect } from "./status-effect";
|
||||
import { Type } from "./type";
|
||||
import * as Utils from "./utils";
|
||||
import { getPokemonMessage } from "./messages";
|
||||
|
||||
export enum MoveCategory {
|
||||
PHYSICAL,
|
||||
|
@ -622,10 +623,6 @@ export enum Moves {
|
|||
FUSION_BOLT
|
||||
};
|
||||
|
||||
const enum MoveEffectText {
|
||||
BUT_IT_FAILED = 'But it failed!'
|
||||
}
|
||||
|
||||
type MoveAttrFunc = (scene: BattleScene, user: Pokemon, target: Pokemon, move: Move) => void;
|
||||
|
||||
export abstract class MoveAttr {
|
||||
|
@ -635,6 +632,21 @@ export abstract class MoveAttr {
|
|||
}
|
||||
|
||||
export class MoveEffectAttr extends MoveAttr {
|
||||
public selfTarget: boolean;
|
||||
|
||||
constructor(selfTarget?: boolean) {
|
||||
super();
|
||||
|
||||
this.selfTarget = !!selfTarget;
|
||||
}
|
||||
|
||||
canApply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]) {
|
||||
return !!(this.selfTarget ? user.hp && !user.getTag(BattleTagType.FRENZY) : target.hp);
|
||||
}
|
||||
|
||||
apply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]) {
|
||||
return this.canApply(scene, user, target, move, args);
|
||||
}
|
||||
}
|
||||
|
||||
export class MoveHitEffectAttr extends MoveAttr {
|
||||
|
@ -702,10 +714,12 @@ class StatusEffectAttr extends MoveHitEffectAttr {
|
|||
apply(scene: BattleScene, 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))
|
||||
scene.unshiftPhase(new ObtainStatusEffectPhase(scene, target instanceof PlayerPokemon, this.effect));
|
||||
if (!target.status || (target.status.effect === this.effect && move.chance < 0)) {
|
||||
scene.unshiftPhase(new ObtainStatusEffectPhase(scene, target.isPlayer(), this.effect));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,9 +754,9 @@ export class ChargeAttr extends OverrideMoveEffectAttr {
|
|||
if (!lastMove.length || lastMove[0].move !== move.id || lastMove[0].result !== MoveResult.OTHER) {
|
||||
(args[0] as Utils.BooleanHolder).value = true;
|
||||
new MoveChargeAnim(this.chargeAnim, move.id, user, target).play(scene, () => {
|
||||
scene.unshiftPhase(new MessagePhase(scene, `${user instanceof EnemyPokemon ? 'Foe ' : ''}${user.name} ${this.chargeText}`));
|
||||
scene.unshiftPhase(new MessagePhase(scene, getPokemonMessage(user, ` ${this.chargeText}`)));
|
||||
if (this.tagType)
|
||||
user.addTag(this.tagType, BattleTagLapseType.MOVE);
|
||||
user.addTag(this.tagType);
|
||||
if (this.chargeEffect)
|
||||
applyMoveAttrs(MoveEffectAttr, scene, user, target, move);
|
||||
user.summonData.moveHistory.push({ move: move.id, result: MoveResult.OTHER });
|
||||
|
@ -750,7 +764,7 @@ export class ChargeAttr extends OverrideMoveEffectAttr {
|
|||
resolve(true);
|
||||
});
|
||||
} else
|
||||
resolve(true);
|
||||
resolve(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -758,29 +772,36 @@ export class ChargeAttr extends OverrideMoveEffectAttr {
|
|||
export class StatChangeAttr extends MoveEffectAttr {
|
||||
public stats: BattleStat[];
|
||||
public levels: integer;
|
||||
public selfTarget: boolean;
|
||||
|
||||
constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean) {
|
||||
super();
|
||||
super(selfTarget);
|
||||
this.stats = typeof(stats) === 'number'
|
||||
? [ stats as BattleStat ]
|
||||
: stats as BattleStat[];
|
||||
this.levels = levels;
|
||||
this.selfTarget = !!selfTarget;
|
||||
}
|
||||
|
||||
apply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance)
|
||||
scene.unshiftPhase(new StatChangePhase(scene, user instanceof PlayerPokemon === this.selfTarget, this.stats, this.levels));
|
||||
return true;
|
||||
if (!super.apply(scene, user, target, move, args))
|
||||
return false;
|
||||
|
||||
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
|
||||
scene.unshiftPhase(new StatChangePhase(scene, user.isPlayer() === this.selfTarget, this.stats, this.levels));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class FlinchAttr extends MoveHitEffectAttr {
|
||||
apply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (Utils.randInt(100) < move.chance)
|
||||
if (Utils.randInt(100) < move.chance) {
|
||||
target.turnData.flinched = true;
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -799,6 +820,73 @@ export class MissEffectAttr extends MoveAttr {
|
|||
}
|
||||
}
|
||||
|
||||
export class FrenzyAttr extends MoveEffectAttr {
|
||||
constructor() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
canApply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]) {
|
||||
return !!(this.selfTarget ? user.hp : target.hp);
|
||||
}
|
||||
|
||||
apply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(scene, user, target, move, args))
|
||||
return false;
|
||||
|
||||
if (!user.summonData.moveQueue.length) {
|
||||
if (!user.getTag(BattleTagType.FRENZY)) {
|
||||
const turnCount = Utils.randInt(2) + 1;
|
||||
new Array(turnCount).fill(null).map(() => user.summonData.moveQueue.push({ move: move.id, ignorePP: true }));
|
||||
user.addTag(BattleTagType.FRENZY);
|
||||
console.log('add frenzy');
|
||||
} else {
|
||||
applyMoveAttrs(AddTagAttr, scene, user, target, move, args);
|
||||
user.lapseTag(BattleTagType.FRENZY);
|
||||
console.log('remove frenzy');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const frenzyMissFunc = (scene: BattleScene, user: Pokemon, target: Pokemon, move: Move) => {
|
||||
while (user.summonData.moveQueue.length && user.summonData.moveQueue[0].move === move.id)
|
||||
user.summonData.moveQueue.shift();
|
||||
user.lapseTag(BattleTagType.FRENZY)
|
||||
};
|
||||
|
||||
export class AddTagAttr extends MoveEffectAttr {
|
||||
public tagType: BattleTagType;
|
||||
public turnCount: integer;
|
||||
|
||||
constructor(tagType: BattleTagType, turnCount: integer, selfTarget?: boolean) {
|
||||
super(selfTarget);
|
||||
|
||||
this.tagType = tagType;
|
||||
this.turnCount = turnCount;
|
||||
}
|
||||
|
||||
apply(scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (!super.apply(scene, user, target, move, args))
|
||||
return false;
|
||||
|
||||
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
|
||||
(this.selfTarget ? user : target).addTag(this.tagType, this.turnCount);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfuseAttr extends AddTagAttr {
|
||||
constructor(selfTarget?: boolean) {
|
||||
super(BattleTagType.CONFUSED, Utils.randInt(4, 1), selfTarget);
|
||||
}
|
||||
}
|
||||
|
||||
export class HitsTagAttr extends MoveAttr {
|
||||
public tagType: BattleTagType;
|
||||
public doubleDamage: boolean;
|
||||
|
@ -811,6 +899,8 @@ export class HitsTagAttr extends MoveAttr {
|
|||
}
|
||||
}
|
||||
|
||||
type AttrPredicate = (moveAttr: MoveAttr) => boolean;
|
||||
|
||||
export function applyMoveAttrs(attrType: { new(...args: any[]): MoveAttr }, scene: BattleScene, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
const attrPromises: Promise<boolean>[] = [];
|
||||
|
@ -830,7 +920,7 @@ class RandomMoveAttr extends OverrideMoveEffectAttr {
|
|||
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 });
|
||||
scene.unshiftPhase(user instanceof PlayerPokemon ? new PlayerMovePhase(scene, user, new PokemonMove(moveId)) : new EnemyMovePhase(scene, user as EnemyPokemon, new PokemonMove(moveId)));
|
||||
scene.unshiftPhase(user.isPlayer() ? new PlayerMovePhase(scene, user as PlayerPokemon, new PokemonMove(moveId)) : new EnemyMovePhase(scene, user as EnemyPokemon, new PokemonMove(moveId)));
|
||||
initMoveAnim(moveId).then(() => {
|
||||
loadMoveAnimAssets(scene, [ moveId ], true)
|
||||
.then(() => resolve(true));
|
||||
|
@ -879,7 +969,8 @@ export const allMoves = [
|
|||
new AttackMove(Moves.BODY_SLAM, "Body Slam", Type.NORMAL, MoveCategory.PHYSICAL, 85, 100, 15, 66, "May paralyze opponent.", 30, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||
new AttackMove(Moves.WRAP, "Wrap", Type.NORMAL, MoveCategory.PHYSICAL, 15, 90, 20, -1, "Traps opponent, damaging them for 4-5 turns.", 100, 1),
|
||||
new AttackMove(Moves.TAKE_DOWN, "Take Down", Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, 1, "User receives recoil damage.", -1, 1),
|
||||
new AttackMove(Moves.THRASH, "Thrash", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 10, -1, "User attacks for 2-3 turns but then becomes confused.", -1, 1),
|
||||
new AttackMove(Moves.THRASH, "Thrash", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 10, -1, "User attacks for 2-3 turns but then becomes confused.", -1, 1,
|
||||
new FrenzyAttr(), frenzyMissFunc, new ConfuseAttr(true)), // TODO: Update to still confuse if last hit misses
|
||||
new AttackMove(Moves.DOUBLE_EDGE, "Double-Edge", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, "User receives recoil damage.", -1, 1),
|
||||
new StatusMove(Moves.TAIL_WHIP, "Tail Whip", Type.NORMAL, 100, 30, -1, "Lowers opponent's Defense.", -1, 1, new StatChangeAttr(BattleStat.DEF, -1)),
|
||||
new AttackMove(Moves.POISON_STING, "Poison Sting", Type.POISON, MoveCategory.PHYSICAL, 15, 100, 35, -1, "May poison the opponent.", 30, 1, new StatusEffectAttr(StatusEffect.POISON)),
|
||||
|
@ -890,7 +981,7 @@ export const allMoves = [
|
|||
new StatusMove(Moves.GROWL, "Growl", Type.NORMAL, 100, 40, -1, "Lowers opponent's Attack.", -1, 1, new StatChangeAttr(BattleStat.ATK, -1)),
|
||||
new StatusMove(Moves.ROAR, "Roar", Type.NORMAL, -1, 20, -1, "In battles, the opponent switches. In the wild, the Pokémon runs.", -1, 1),
|
||||
new StatusMove(Moves.SING, "Sing", Type.NORMAL, 55, 15, -1, "Puts opponent to sleep.", -1, 1, new StatusEffectAttr(StatusEffect.SLEEP)),
|
||||
new StatusMove(Moves.SUPERSONIC, "Supersonic", Type.NORMAL, 55, 20, -1, "Confuses opponent.", -1, 1),
|
||||
new StatusMove(Moves.SUPERSONIC, "Supersonic", Type.NORMAL, 55, 20, -1, "Confuses opponent.", -1, 1, new ConfuseAttr()),
|
||||
new AttackMove(Moves.SONIC_BOOM, "Sonic Boom", Type.NORMAL, MoveCategory.SPECIAL, -1, 90, 20, -1, "Always inflicts 20 HP.", -1, 1),
|
||||
new StatusMove(Moves.DISABLE, "Disable", Type.NORMAL, 100, 20, -1, "Opponent can't use its last attack for a few turns.", -1, 1),
|
||||
new AttackMove(Moves.ACID, "Acid", Type.POISON, MoveCategory.SPECIAL, 40, 100, 30, -1, "May lower opponent's Special Defense.", 10, 1, new StatChangeAttr(BattleStat.SPDEF, -1)),
|
||||
|
@ -902,7 +993,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.SURF, "Surf", Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 123, "Hits all adjacent Pokémon.", -1, 1),
|
||||
new AttackMove(Moves.ICE_BEAM, "Ice Beam", Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 135, "May freeze opponent.", 10, 1, new StatusEffectAttr(StatusEffect.FREEZE)),
|
||||
new AttackMove(Moves.BLIZZARD, "Blizzard", Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 143, "May freeze opponent.", 10, 1, new StatusEffectAttr(StatusEffect.FREEZE)),
|
||||
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 1),
|
||||
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 1, new ConfuseAttr()),
|
||||
new AttackMove(Moves.BUBBLE_BEAM, "Bubble Beam", Type.WATER, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Speed.", 10, 1, new StatChangeAttr(BattleStat.SPD, -1)),
|
||||
new AttackMove(Moves.AURORA_BEAM, "Aurora Beam", Type.ICE, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Attack.", 10, 1, new StatChangeAttr(BattleStat.ATK, -1)),
|
||||
new AttackMove(Moves.HYPER_BEAM, "Hyper Beam", Type.NORMAL, MoveCategory.SPECIAL, 150, 90, 5, 163, "User must recharge next turn.", -1, 1),
|
||||
|
@ -924,7 +1015,8 @@ export const allMoves = [
|
|||
new StatusMove(Moves.POISON_POWDER, "Poison Powder", Type.POISON, 75, 35, -1, "Poisons opponent.", -1, 1, new StatusEffectAttr(StatusEffect.POISON)),
|
||||
new StatusMove(Moves.STUN_SPORE, "Stun Spore", Type.GRASS, 75, 30, -1, "Paralyzes opponent.", -1, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||
new StatusMove(Moves.SLEEP_POWDER, "Sleep Powder", Type.GRASS, 75, 15, -1, "Puts opponent to sleep.", -1, 1, new StatusEffectAttr(StatusEffect.SLEEP)),
|
||||
new AttackMove(Moves.PETAL_DANCE, "Petal Dance", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, -1, "User attacks for 2-3 turns but then becomes confused.", -1, 1),
|
||||
new AttackMove(Moves.PETAL_DANCE, "Petal Dance", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, -1, "User attacks for 2-3 turns but then becomes confused.", -1, 1,
|
||||
new FrenzyAttr(), frenzyMissFunc, new ConfuseAttr(true)), // TODO: Update to still confuse if last hit misses
|
||||
new StatusMove(Moves.STRING_SHOT, "String Shot", Type.BUG, 95, 40, -1, "Sharply lowers opponent's Speed.", -1, 1, new StatChangeAttr(BattleStat.SPD, -2)),
|
||||
new AttackMove(Moves.DRAGON_RAGE, "Dragon Rage", Type.DRAGON, MoveCategory.SPECIAL, -1, 100, 10, -1, "Always inflicts 40 HP.", -1, 1),
|
||||
new AttackMove(Moves.FIRE_SPIN, "Fire Spin", Type.FIRE, MoveCategory.SPECIAL, 35, 85, 15, 24, "Traps opponent, damaging them for 4-5 turns.", 100, 1),
|
||||
|
@ -939,7 +1031,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.DIG, "Dig", Type.GROUND, MoveCategory.PHYSICAL, 80, 100, 10, 55, "Digs underground on first turn, attacks on second. Can also escape from caves.", -1, 1,
|
||||
new ChargeAttr(ChargeAnim.DIG_CHARGING, 'dug a hole!', BattleTagType.UNDERGROUND)),
|
||||
new StatusMove(Moves.TOXIC, "Toxic", Type.POISON, 90, 10, -1, "Badly poisons opponent.", -1, 1, new StatusEffectAttr(StatusEffect.TOXIC)),
|
||||
new AttackMove(Moves.CONFUSION, "Confusion", Type.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, -1, "May confuse opponent.", 10, 1), // TODO
|
||||
new AttackMove(Moves.CONFUSION, "Confusion", Type.PSYCHIC, MoveCategory.SPECIAL, 50, 100, 25, -1, "May confuse opponent.", 10, 1, new ConfuseAttr()),
|
||||
new AttackMove(Moves.PSYCHIC, "Psychic", Type.PSYCHIC, MoveCategory.SPECIAL, 90, 100, 10, 120, "May lower opponent's Special Defense.", 10, 1, new StatChangeAttr(BattleStat.SPDEF, -1)),
|
||||
new StatusMove(Moves.HYPNOSIS, "Hypnosis", Type.PSYCHIC, 60, 20, -1, "Puts opponent to sleep.", -1, 1, new StatusEffectAttr(StatusEffect.SLEEP)),
|
||||
new StatusMove(Moves.MEDITATE, "Meditate", Type.PSYCHIC, -1, 40, -1, "Raises user's Attack.", -1, 1, new StatChangeAttr(BattleStat.ATK, 1, true)),
|
||||
|
@ -955,7 +1047,7 @@ export const allMoves = [
|
|||
new StatusMove(Moves.HARDEN, "Harden", Type.NORMAL, -1, 30, -1, "Raises user's Defense.", -1, 1, new StatChangeAttr(BattleStat.DEF, 1, true)),
|
||||
new StatusMove(Moves.MINIMIZE, "Minimize", Type.NORMAL, -1, 10, -1, "Sharply raises user's Evasiveness.", -1, 1, new StatChangeAttr(BattleStat.EVA, 1, true)),
|
||||
new StatusMove(Moves.SMOKESCREEN, "Smokescreen", Type.NORMAL, 100, 20, -1, "Lowers opponent's Accuracy.", -1, 1, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||
new StatusMove(Moves.CONFUSE_RAY, "Confuse Ray", Type.GHOST, 100, 10, 17, "Confuses opponent.", -1, 1), // TODO
|
||||
new StatusMove(Moves.CONFUSE_RAY, "Confuse Ray", Type.GHOST, 100, 10, 17, "Confuses opponent.", -1, 1, new ConfuseAttr()),
|
||||
new StatusMove(Moves.WITHDRAW, "Withdraw", Type.WATER, -1, 40, -1, "Raises user's Defense.", -1, 1, new StatChangeAttr(BattleStat.DEF, 1, true)),
|
||||
new StatusMove(Moves.DEFENSE_CURL, "Defense Curl", Type.NORMAL, -1, 40, -1, "Raises user's Defense.", -1, 1, new StatChangeAttr(BattleStat.DEF, 1, true)),
|
||||
new StatusMove(Moves.BARRIER, "Barrier", Type.PSYCHIC, -1, 20, -1, "Sharply raises user's Defense.", -1, 1, new StatChangeAttr(BattleStat.DEF, 2, true)),
|
||||
|
@ -995,7 +1087,7 @@ export const allMoves = [
|
|||
new ChargeAttr(ChargeAnim.SKY_ATTACK_CHARGING, 'is glowing!'), new HighCritAttr(), new FlinchAttr()),
|
||||
new StatusMove(Moves.TRANSFORM, "Transform", Type.NORMAL, -1, 10, -1, "User takes on the form and attacks of the opponent.", -1, 1),
|
||||
new AttackMove(Moves.BUBBLE, "Bubble", Type.WATER, MoveCategory.SPECIAL, 40, 100, 30, -1, "May lower opponent's Speed.", 10, 1, new StatChangeAttr(BattleStat.SPD, -1)),
|
||||
new AttackMove(Moves.DIZZY_PUNCH, "Dizzy Punch", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, "May confuse opponent.", 20, 1), // TODO
|
||||
new AttackMove(Moves.DIZZY_PUNCH, "Dizzy Punch", Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 10, -1, "May confuse opponent.", 20, 1, new ConfuseAttr()),
|
||||
new StatusMove(Moves.SPORE, "Spore", Type.GRASS, 100, 15, -1, "Puts opponent to sleep.", -1, 1, new StatusEffectAttr(StatusEffect.SLEEP)),
|
||||
new StatusMove(Moves.FLASH, "Flash", Type.NORMAL, 100, 20, -1, "Lowers opponent's Accuracy.", -1, 1, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||
new AttackMove(Moves.PSYWAVE, "Psywave", Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 15, -1, "Inflicts damage 50-150% of user's level.", -1, 1),
|
||||
|
@ -1036,7 +1128,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.MACH_PUNCH, "Mach Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 30, -1, "User attacks first.", -1, 2),
|
||||
new StatusMove(Moves.SCARY_FACE, "Scary Face", Type.NORMAL, 100, 10, 6, "Sharply lowers opponent's Speed.", -1, 2, new StatChangeAttr(BattleStat.SPD, -2)),
|
||||
new AttackMove(Moves.FEINT_ATTACK, "Feint Attack", Type.DARK, MoveCategory.PHYSICAL, 60, 999, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 2),
|
||||
new StatusMove(Moves.SWEET_KISS, "Sweet Kiss", Type.FAIRY, 75, 10, -1, "Confuses opponent.", -1, 2), // TODO
|
||||
new StatusMove(Moves.SWEET_KISS, "Sweet Kiss", Type.FAIRY, 75, 10, -1, "Confuses opponent.", -1, 2, new ConfuseAttr()),
|
||||
new StatusMove(Moves.BELLY_DRUM, "Belly Drum", Type.NORMAL, -1, 10, -1, "User loses 50% of its max HP, but Attack raises to maximum.", -1, 2),
|
||||
new AttackMove(Moves.SLUDGE_BOMB, "Sludge Bomb", Type.POISON, MoveCategory.SPECIAL, 90, 100, 10, 148, "May poison opponent.", 30, 2, new StatusEffectAttr(StatusEffect.POISON)),
|
||||
new AttackMove(Moves.MUD_SLAP, "Mud-Slap", Type.GROUND, MoveCategory.SPECIAL, 20, 100, 10, 5, "Lowers opponent's Accuracy.", 100, 2, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||
|
@ -1050,14 +1142,15 @@ export const allMoves = [
|
|||
new StatusMove(Moves.DETECT, "Detect", Type.FIGHTING, -1, 5, -1, "Protects the user, but may fail if used consecutively.", -1, 2),
|
||||
new AttackMove(Moves.BONE_RUSH, "Bone Rush", Type.GROUND, MoveCategory.PHYSICAL, 25, 90, 10, -1, "Hits 2-5 times in one turn.", -1, 2, new MultiHitAttr()),
|
||||
new StatusMove(Moves.LOCK_ON, "Lock-On", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 2),
|
||||
new AttackMove(Moves.OUTRAGE, "Outrage", Type.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, 156, "User attacks for 2-3 turns but then becomes confused.", -1, 2),
|
||||
new AttackMove(Moves.OUTRAGE, "Outrage", Type.DRAGON, MoveCategory.PHYSICAL, 120, 100, 10, 156, "User attacks for 2-3 turns but then becomes confused.", -1, 2,
|
||||
new FrenzyAttr(), frenzyMissFunc, new ConfuseAttr(true)), // TODO: Update to still confuse if last hit misses
|
||||
new StatusMove(Moves.SANDSTORM, "Sandstorm", Type.ROCK, -1, 10, 51, "Creates a sandstorm for 5 turns.", -1, 2),
|
||||
new AttackMove(Moves.GIGA_DRAIN, "Giga Drain", Type.GRASS, MoveCategory.SPECIAL, 75, 100, 10, 111, "User recovers half the HP inflicted on opponent.", -1, 2),
|
||||
new StatusMove(Moves.ENDURE, "Endure", Type.NORMAL, -1, 10, 47, "Always left with at least 1 HP, but may fail if used consecutively.", -1, 2),
|
||||
new StatusMove(Moves.CHARM, "Charm", Type.FAIRY, 100, 20, 2, "Sharply lowers opponent's Attack.", -1, 2, new StatChangeAttr(BattleStat.ATK, -2)),
|
||||
new AttackMove(Moves.ROLLOUT, "Rollout", Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, "Doubles in power each turn for 5 turns.", -1, 2),
|
||||
new AttackMove(Moves.FALSE_SWIPE, "False Swipe", Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, 57, "Always leaves opponent with at least 1 HP.", -1, 2),
|
||||
new StatusMove(Moves.SWAGGER, "Swagger", Type.NORMAL, 85, 15, -1, "Confuses opponent, but sharply raises its Attack.", -1, 2, new StatChangeAttr(BattleStat.ATK, 2)), // todo
|
||||
new StatusMove(Moves.SWAGGER, "Swagger", Type.NORMAL, 85, 15, -1, "Confuses opponent, but sharply raises its Attack.", -1, 2, new StatChangeAttr(BattleStat.ATK, 2), new ConfuseAttr()),
|
||||
new StatusMove(Moves.MILK_DRINK, "Milk Drink", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 2),
|
||||
new AttackMove(Moves.SPARK, "Spark", Type.ELECTRIC, MoveCategory.PHYSICAL, 65, 100, 20, -1, "May paralyze opponent.", 30, 2, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||
new AttackMove(Moves.FURY_CUTTER, "Fury Cutter", Type.BUG, MoveCategory.PHYSICAL, 40, 95, 20, -1, "Power increases each turn.", -1, 2),
|
||||
|
@ -1073,7 +1166,7 @@ export const allMoves = [
|
|||
new StatusMove(Moves.PAIN_SPLIT, "Pain Split", Type.NORMAL, -1, 20, -1, "The user's and opponent's HP becomes the average of both.", -1, 2),
|
||||
new AttackMove(Moves.SACRED_FIRE, "Sacred Fire", Type.FIRE, MoveCategory.PHYSICAL, 100, 95, 5, -1, "May burn opponent.", 50, 2, new StatusEffectAttr(StatusEffect.BURN)),
|
||||
new AttackMove(Moves.MAGNITUDE, "Magnitude", Type.GROUND, MoveCategory.PHYSICAL, -1, 100, 30, -1, "Hits with random power.", -1, 2),
|
||||
new AttackMove(Moves.DYNAMIC_PUNCH, "Dynamic Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 50, 5, -1, "Confuses opponent.", 100, 2), // TODO
|
||||
new AttackMove(Moves.DYNAMIC_PUNCH, "Dynamic Punch", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 50, 5, -1, "Confuses opponent.", 100, 2, new ConfuseAttr()),
|
||||
new AttackMove(Moves.MEGAHORN, "Megahorn", Type.BUG, MoveCategory.PHYSICAL, 120, 85, 10, -1, "", -1, 2),
|
||||
new AttackMove(Moves.DRAGON_BREATH, "Dragon Breath", Type.DRAGON, MoveCategory.SPECIAL, 60, 100, 20, -1, "May paralyze opponent.", 30, 2, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||
new StatusMove(Moves.BATON_PASS, "Baton Pass", Type.NORMAL, -1, 40, 132, "User switches out and gives stat changes to the incoming Pokémon.", -1, 2),
|
||||
|
@ -1105,14 +1198,14 @@ 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, 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, 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, new FlinchAttr()), // TODO
|
||||
new AttackMove(Moves.UPROAR, "Uproar", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, "User attacks for 3 turns and prevents sleep.", -1, 3),
|
||||
new AttackMove(Moves.UPROAR, "Uproar", Type.NORMAL, MoveCategory.SPECIAL, 90, 100, 10, -1, "User attacks for 3 turns and prevents sleep.", -1, 3), // TODO
|
||||
new StatusMove(Moves.STOCKPILE, "Stockpile", Type.NORMAL, -1, 20, -1, "Stores energy for use with Spit Up and Swallow.", -1, 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, 3),
|
||||
new StatusMove(Moves.SWALLOW, "Swallow", Type.NORMAL, -1, 10, -1, "The more times the user has performed Stockpile, the more HP is recovered.", -1, 3),
|
||||
new AttackMove(Moves.HEAT_WAVE, "Heat Wave", Type.FIRE, MoveCategory.SPECIAL, 95, 90, 10, 118, "May burn opponent.", 10, 3, new StatusEffectAttr(StatusEffect.BURN)),
|
||||
new StatusMove(Moves.HAIL, "Hail", Type.ICE, -1, 10, -1, "Non-Ice types are damaged for 5 turns.", -1, 3),
|
||||
new StatusMove(Moves.TORMENT, "Torment", Type.DARK, 100, 15, -1, "Opponent cannot use the same move in a row.", -1, 3),
|
||||
new StatusMove(Moves.FLATTER, "Flatter", Type.DARK, 100, 15, -1, "Confuses opponent, but raises its Special Attack.", -1, 3, new StatChangeAttr(BattleStat.SPATK, 1)), // TODO
|
||||
new StatusMove(Moves.FLATTER, "Flatter", Type.DARK, 100, 15, -1, "Confuses opponent, but raises its Special Attack.", -1, 3, new StatChangeAttr(BattleStat.SPATK, 1), new ConfuseAttr()),
|
||||
new StatusMove(Moves.WILL_O_WISP, "Will-O-Wisp", Type.FIRE, 85, 15, 107, "Burns opponent.", -1, 3, new StatusEffectAttr(StatusEffect.BURN)),
|
||||
new StatusMove(Moves.MEMENTO, "Memento", Type.DARK, 100, 10, -1, "User faints, sharply lowers opponent's Attack and Special Attack.", -1, 3,
|
||||
new StatChangeAttr([ BattleStat.ATK, BattleStat.SPATK ], -2)), // TODO
|
||||
|
@ -1153,7 +1246,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.LUSTER_PURGE, "Luster Purge", Type.PSYCHIC, MoveCategory.SPECIAL, 70, 100, 5, -1, "May lower opponent's Special Defense.", 50, 3, new StatChangeAttr(BattleStat.SPDEF, -1)),
|
||||
new AttackMove(Moves.MIST_BALL, "Mist Ball", Type.PSYCHIC, MoveCategory.SPECIAL, 70, 100, 5, -1, "May lower opponent's Special Attack.", 50, 3, new StatChangeAttr(BattleStat.SPATK, -1)),
|
||||
new StatusMove(Moves.FEATHER_DANCE, "Feather Dance", Type.FLYING, 100, 15, -1, "Sharply lowers opponent's Attack.", -1, 3, new StatChangeAttr(BattleStat.ATK, -2)),
|
||||
new StatusMove(Moves.TEETER_DANCE, "Teeter Dance", Type.NORMAL, 100, 20, -1, "Confuses all Pokémon.", -1, 3), // TODO
|
||||
new StatusMove(Moves.TEETER_DANCE, "Teeter Dance", Type.NORMAL, 100, 20, -1, "Confuses all Pokémon.", -1, 3, new ConfuseAttr(true), new ConfuseAttr()),
|
||||
new AttackMove(Moves.BLAZE_KICK, "Blaze Kick", Type.FIRE, MoveCategory.PHYSICAL, 85, 90, 10, -1, "High critical hit ratio. May burn opponent.", 10, 3, new HighCritAttr(), new StatusEffectAttr(StatusEffect.BURN)),
|
||||
new StatusMove(Moves.MUD_SPORT, "Mud Sport", Type.GROUND, -1, 15, -1, "Weakens the power of Electric-type moves.", -1, 3),
|
||||
new AttackMove(Moves.ICE_BALL, "Ice Ball", Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, "Doubles in power each turn for 5 turns.", -1, 3),
|
||||
|
@ -1182,7 +1275,7 @@ export const allMoves = [
|
|||
new StatusMove(Moves.COSMIC_POWER, "Cosmic Power", Type.PSYCHIC, -1, 20, -1, "Raises user's Defense and Special Defense.", -1, 3,
|
||||
new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], 1, true)),
|
||||
new AttackMove(Moves.WATER_SPOUT, "Water Spout", Type.WATER, MoveCategory.SPECIAL, 150, 100, 5, -1, "The higher the user's HP, the higher the damage caused.", -1, 3),
|
||||
new AttackMove(Moves.SIGNAL_BEAM, "Signal Beam", Type.BUG, MoveCategory.SPECIAL, 75, 100, 15, -1, "May confuse opponent.", 10, 3), // TODO
|
||||
new AttackMove(Moves.SIGNAL_BEAM, "Signal Beam", Type.BUG, MoveCategory.SPECIAL, 75, 100, 15, -1, "May confuse opponent.", 10, 3, new ConfuseAttr()),
|
||||
new AttackMove(Moves.SHADOW_PUNCH, "Shadow Punch", Type.GHOST, MoveCategory.PHYSICAL, 60, 999, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 3),
|
||||
new AttackMove(Moves.EXTRASENSORY, "Extrasensory", Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 20, -1, "May cause flinching.", 10, 3, new FlinchAttr()),
|
||||
new AttackMove(Moves.SKY_UPPERCUT, "Sky Uppercut", Type.FIGHTING, MoveCategory.PHYSICAL, 85, 90, 15, -1, "Hits the opponent, even during Fly.", -1, 3, new HitsTagAttr(BattleTagType.FLYING)),
|
||||
|
@ -1215,7 +1308,7 @@ export const allMoves = [
|
|||
new StatChangeAttr([ BattleStat.ATK, BattleStat.SPD ], 1, true)),
|
||||
new AttackMove(Moves.ROCK_BLAST, "Rock Blast", Type.ROCK, MoveCategory.PHYSICAL, 25, 90, 10, 76, "Hits 2-5 times in one turn.", -1, 3, new MultiHitAttr()),
|
||||
new AttackMove(Moves.SHOCK_WAVE, "Shock Wave", Type.ELECTRIC, MoveCategory.SPECIAL, 60, 999, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 3),
|
||||
new AttackMove(Moves.WATER_PULSE, "Water Pulse", Type.WATER, MoveCategory.SPECIAL, 60, 100, 20, 11, "May confuse opponent.", 20, 3),
|
||||
new AttackMove(Moves.WATER_PULSE, "Water Pulse", Type.WATER, MoveCategory.SPECIAL, 60, 100, 20, 11, "May confuse opponent.", 20, 3, new ConfuseAttr()),
|
||||
new AttackMove(Moves.DOOM_DESIRE, "Doom Desire", Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, "Damage occurs 2 turns later.", -1, 3,
|
||||
new ChargeAttr(ChargeAnim.DOOM_DESIRE_CHARGING, 'chose\nDOOM DESIRE as its destiny!')),
|
||||
new AttackMove(Moves.PSYCHO_BOOST, "Psycho Boost", Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, "Sharply lowers user's Special Attack.", 100, 3, new StatChangeAttr(BattleStat.SPATK, -2, true)),
|
||||
|
@ -1231,7 +1324,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.FEINT, "Feint", Type.NORMAL, MoveCategory.PHYSICAL, 30, 100, 10, -1, "Only hits if opponent uses Protect or Detect in the same turn.", -1, 4),
|
||||
new AttackMove(Moves.PLUCK, "Pluck", Type.FLYING, MoveCategory.PHYSICAL, 60, 100, 20, -1, "If the opponent is holding a berry, its effect is stolen by user.", -1, 4),
|
||||
new StatusMove(Moves.TAILWIND, "Tailwind", Type.FLYING, -1, 15, 113, "Doubles Speed for 4 turns.", -1, 4),
|
||||
new StatusMove(Moves.ACUPRESSURE, "Acupressure", Type.NORMAL, -1, 30, -1, "Sharply raises a random stat.", -1, 4, new StatChangeAttr(BattleStat.RAND, 2, true)), // TODO
|
||||
new StatusMove(Moves.ACUPRESSURE, "Acupressure", Type.NORMAL, -1, 30, -1, "Sharply raises a random stat.", -1, 4, new StatChangeAttr(BattleStat.RAND, 2, true)),
|
||||
new AttackMove(Moves.METAL_BURST, "Metal Burst", Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, "Deals damage equal to 1.5x opponent's attack.", -1, 4),
|
||||
new AttackMove(Moves.U_TURN, "U-turn", Type.BUG, MoveCategory.PHYSICAL, 70, 100, 20, 60, "User switches out immediately after attacking.", -1, 4),
|
||||
new AttackMove(Moves.CLOSE_COMBAT, "Close Combat", Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, 167, "Lowers user's Defense and Special Defense.", 100, 4,
|
||||
|
@ -1296,7 +1389,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.ZEN_HEADBUTT, "Zen Headbutt", Type.PSYCHIC, MoveCategory.PHYSICAL, 80, 90, 15, 59, "May cause flinching.", 20, 4, new FlinchAttr()),
|
||||
new AttackMove(Moves.MIRROR_SHOT, "Mirror Shot", Type.STEEL, MoveCategory.SPECIAL, 65, 85, 10, -1, "May lower opponent's Accuracy.", 30, 4, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||
new AttackMove(Moves.FLASH_CANNON, "Flash Cannon", Type.STEEL, MoveCategory.SPECIAL, 80, 100, 10, 93, "May lower opponent's Special Defense.", 10, 4, new StatChangeAttr(BattleStat.SPDEF, -1)),
|
||||
new AttackMove(Moves.ROCK_CLIMB, "Rock Climb", Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, -1, "May confuse opponent.", 20, 4), // TODO
|
||||
new AttackMove(Moves.ROCK_CLIMB, "Rock Climb", Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, -1, "May confuse opponent.", 20, 4, new ConfuseAttr()),
|
||||
new StatusMove(Moves.DEFOG, "Defog", Type.FLYING, -1, 15, -1, "Lowers opponent's Evasiveness and clears fog.", -1, 4, new StatChangeAttr(BattleStat.EVA, -1)), // TODO
|
||||
new StatusMove(Moves.TRICK_ROOM, "Trick Room", Type.PSYCHIC, -1, 5, 161, "Slower Pokémon move first in the turn for 5 turns.", -1, 4),
|
||||
new AttackMove(Moves.DRACO_METEOR, "Draco Meteor", Type.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, 169, "Sharply lowers user's Special Attack.", 100, 4, new StatChangeAttr(BattleStat.SPATK, -2, true)),
|
||||
|
@ -1314,7 +1407,7 @@ export const allMoves = [
|
|||
new StatusMove(Moves.CAPTIVATE, "Captivate", Type.NORMAL, 100, 20, -1, "Sharply lowers opponent's Special Attack if opposite gender.", -1, 4), // TODO XX
|
||||
new StatusMove(Moves.STEALTH_ROCK, "Stealth Rock", Type.ROCK, -1, 20, 116, "Damages opponent switching into battle.", -1, 4),
|
||||
new AttackMove(Moves.GRASS_KNOT, "Grass Knot", Type.GRASS, MoveCategory.SPECIAL, -1, 100, 20, 81, "The heavier the opponent, the stronger the attack.", -1, 4),
|
||||
new AttackMove(Moves.CHATTER, "Chatter", Type.FLYING, MoveCategory.SPECIAL, 65, 100, 20, -1, "Confuses opponent.", 100, 4),
|
||||
new AttackMove(Moves.CHATTER, "Chatter", Type.FLYING, MoveCategory.SPECIAL, 65, 100, 20, -1, "Confuses opponent.", 100, 4, new ConfuseAttr()),
|
||||
new AttackMove(Moves.JUDGMENT, "Judgment", Type.NORMAL, MoveCategory.SPECIAL, 100, 100, 10, -1, "Type depends on the Arceus Plate being held.", -1, 4),
|
||||
new AttackMove(Moves.BUG_BITE, "Bug Bite", Type.BUG, MoveCategory.PHYSICAL, 60, 100, 20, -1, "Receives the effect from the opponent's held berry.", -1, 4),
|
||||
new AttackMove(Moves.CHARGE_BEAM, "Charge Beam", Type.ELECTRIC, MoveCategory.SPECIAL, 50, 90, 10, 23, "May raise user's Special Attack.", 70, 4, new StatChangeAttr(BattleStat.SPATK, 1, true)),
|
||||
|
@ -1418,7 +1511,7 @@ export const allMoves = [
|
|||
new AttackMove(Moves.NIGHT_DAZE, "Night Daze", Type.DARK, MoveCategory.SPECIAL, 85, 95, 10, -1, "May lower opponent's Accuracy.", 40, 5, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||
new AttackMove(Moves.PSYSTRIKE, "Psystrike", Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 10, -1, "Inflicts damage based on the target's Defense, not Special Defense.", -1, 5),
|
||||
new AttackMove(Moves.TAIL_SLAP, "Tail Slap", Type.NORMAL, MoveCategory.PHYSICAL, 25, 85, 10, -1, "Hits 2-5 times in one turn.", -1, 5, new MultiHitAttr()),
|
||||
new AttackMove(Moves.HURRICANE, "Hurricane", Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 160, "May confuse opponent.", 30, 5),
|
||||
new AttackMove(Moves.HURRICANE, "Hurricane", Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 160, "May confuse opponent.", 30, 5, new ConfuseAttr()),
|
||||
new AttackMove(Moves.HEAD_CHARGE, "Head Charge", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, "User receives recoil damage.", -1, 5),
|
||||
new AttackMove(Moves.GEAR_GRIND, "Gear Grind", Type.STEEL, MoveCategory.PHYSICAL, 50, 85, 15, -1, "Hits twice in one turn.", -1, 5, new MultiHitAttr(MultiHitType._2)),
|
||||
new AttackMove(Moves.SEARING_SHOT, "Searing Shot", Type.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, "May burn opponent.", 30, 5, new StatusEffectAttr(StatusEffect.BURN)),
|
||||
|
|
231
src/pokemon.ts
231
src/pokemon.ts
|
@ -1,7 +1,7 @@
|
|||
import Phaser from 'phaser';
|
||||
import BattleScene from './battle-scene';
|
||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './battle-info';
|
||||
import { default as Move, allMoves, MoveCategory, Moves, StatChangeAttr, applyMoveAttrs, HighCritAttr, HitsTagAttr } from './move';
|
||||
import { default as Move, allMoves, MoveCategory, Moves, StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs } from './move';
|
||||
import { pokemonLevelMoves } from './pokemon-level-moves';
|
||||
import { default as PokemonSpecies, getPokemonSpecies } from './pokemon-species';
|
||||
import * as Utils from './utils';
|
||||
|
@ -15,9 +15,9 @@ import { initMoveAnim, loadMoveAnimAssets } from './battle-anims';
|
|||
import { Status, StatusEffect } from './status-effect';
|
||||
import { tmSpecies } from './tms';
|
||||
import { pokemonEvolutions, SpeciesEvolution, SpeciesEvolutionCondition } from './pokemon-evolutions';
|
||||
import { MessagePhase } from './battle-phases';
|
||||
import { DamagePhase, MessagePhase } from './battle-phases';
|
||||
import { BattleStat } from './battle-stat';
|
||||
import { BattleTag, BattleTagLapseType, BattleTagType } from './battle-tag';
|
||||
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag';
|
||||
import { Species } from './species';
|
||||
|
||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
|
@ -411,135 +411,128 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
this.levelExp = this.exp - getLevelTotalExp(this.level, this.species.growthRate);
|
||||
}
|
||||
|
||||
apply(source: Pokemon, battlerMove: PokemonMove): Promise<MoveResult> {
|
||||
return new Promise(resolve => {
|
||||
let result: MoveResult = MoveResult.STATUS;
|
||||
let success = false;
|
||||
const move = battlerMove.getMove();
|
||||
const moveCategory = move.category;
|
||||
let damage = 0;
|
||||
switch (moveCategory) {
|
||||
case MoveCategory.PHYSICAL:
|
||||
case MoveCategory.SPECIAL:
|
||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||
const power = new Utils.NumberHolder(move.power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
|
||||
const critChance = new Utils.IntegerHolder(16);
|
||||
applyMoveAttrs(HighCritAttr, this.scene, source, this, move, critChance);
|
||||
const isCritical = Utils.randInt(critChance.value) === 0;
|
||||
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
|
||||
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
|
||||
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 > -1 && source.species.type2 === move.type) ? 1.5 : 1;
|
||||
const typeMultiplier = getTypeDamageMultiplier(move.type, this.species.type1) * (this.species.type2 > -1 ? getTypeDamageMultiplier(move.type, this.species.type2) : 1);
|
||||
const criticalMultiplier = isCritical ? 2 : 1;
|
||||
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
||||
damage = Math.floor(damage / 2);
|
||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||
if (this.getTag(hta.tagType)) {
|
||||
console.log('ye');
|
||||
damage *= 2;
|
||||
}
|
||||
});
|
||||
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
|
||||
if (damage) {
|
||||
this.hp = Math.max(this.hp - damage, 0);
|
||||
source.turnData.damageDealt += damage;
|
||||
if (isCritical)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'A critical hit!'));
|
||||
}
|
||||
if (typeMultiplier >= 2)
|
||||
result = MoveResult.SUPER_EFFECTIVE;
|
||||
else if (typeMultiplier >= 1)
|
||||
result = MoveResult.EFFECTIVE;
|
||||
else if (typeMultiplier > 0)
|
||||
result = MoveResult.NOT_VERY_EFFECTIVE;
|
||||
else
|
||||
result = MoveResult.NO_EFFECT;
|
||||
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
|
||||
let result: MoveResult = MoveResult.STATUS;
|
||||
let success = false;
|
||||
const move = battlerMove.getMove();
|
||||
const moveCategory = move.category;
|
||||
let damage = 0;
|
||||
switch (moveCategory) {
|
||||
case MoveCategory.PHYSICAL:
|
||||
case MoveCategory.SPECIAL:
|
||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||
const power = new Utils.NumberHolder(move.power);
|
||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
|
||||
const critChance = new Utils.IntegerHolder(16);
|
||||
applyMoveAttrs(HighCritAttr, this.scene, source, this, move, critChance);
|
||||
const isCritical = Utils.randInt(critChance.value) === 0;
|
||||
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
|
||||
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
|
||||
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 > -1 && source.species.type2 === move.type) ? 1.5 : 1;
|
||||
const typeMultiplier = getTypeDamageMultiplier(move.type, this.species.type1) * (this.species.type2 > -1 ? getTypeDamageMultiplier(move.type, this.species.type2) : 1);
|
||||
const criticalMultiplier = isCritical ? 2 : 1;
|
||||
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
||||
damage = Math.floor(damage / 2);
|
||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||
if (this.getTag(hta.tagType))
|
||||
damage *= 2;
|
||||
});
|
||||
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
|
||||
|
||||
if (typeMultiplier >= 2)
|
||||
result = MoveResult.SUPER_EFFECTIVE;
|
||||
else if (typeMultiplier >= 1)
|
||||
result = MoveResult.EFFECTIVE;
|
||||
else if (typeMultiplier > 0)
|
||||
result = MoveResult.NOT_VERY_EFFECTIVE;
|
||||
else
|
||||
result = MoveResult.NO_EFFECT;
|
||||
|
||||
switch (result) {
|
||||
case MoveResult.EFFECTIVE:
|
||||
this.scene.sound.play('hit');
|
||||
success = true;
|
||||
break;
|
||||
case MoveResult.SUPER_EFFECTIVE:
|
||||
this.scene.sound.play('hit_strong');
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s super effective!'));
|
||||
success = true;
|
||||
break;
|
||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
||||
this.scene.sound.play('hit_weak');
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s not very effective!'))
|
||||
success = true;
|
||||
break;
|
||||
case MoveResult.NO_EFFECT:
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `It doesn\'t affect ${this.name}!`))
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MoveCategory.STATUS:
|
||||
result = MoveResult.STATUS;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (success) {
|
||||
if (result <= MoveResult.NOT_VERY_EFFECTIVE) {
|
||||
const flashTimer = this.scene.time.addEvent({
|
||||
delay: 100,
|
||||
repeat: 5,
|
||||
startAt: 200,
|
||||
callback: () => {
|
||||
this.getSprite().setVisible(flashTimer.repeatCount % 2 === 0);
|
||||
if (!flashTimer.repeatCount) {
|
||||
this.battleInfo.updateInfo(this).then(() => resolve(result));
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.battleInfo.updateInfo(this).then(() => resolve(result));
|
||||
if (damage) {
|
||||
this.hp = Math.max(this.hp - damage, 0);
|
||||
source.turnData.damageDealt += damage;
|
||||
this.scene.unshiftPhase(new DamagePhase(this.scene, this.isPlayer(), result as DamageResult))
|
||||
if (isCritical)
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'A critical hit!'));
|
||||
}
|
||||
} else
|
||||
resolve(result);
|
||||
});
|
||||
|
||||
switch (result) {
|
||||
case MoveResult.EFFECTIVE:
|
||||
success = true;
|
||||
break;
|
||||
case MoveResult.SUPER_EFFECTIVE:
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s super effective!'));
|
||||
success = true;
|
||||
break;
|
||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s not very effective!'))
|
||||
success = true;
|
||||
break;
|
||||
case MoveResult.NO_EFFECT:
|
||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `It doesn\'t affect ${this.name}!`))
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MoveCategory.STATUS:
|
||||
result = MoveResult.STATUS;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
addTag(tagType: BattleTagType, lapseType: BattleTagLapseType, turnCount?: integer): boolean {
|
||||
if (this.getTag(tagType))
|
||||
addTag(tagType: BattleTagType, turnCount?: integer): boolean {
|
||||
const existingTag = this.getTag(tagType);
|
||||
if (existingTag) {
|
||||
existingTag.onOverlap(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
const newTag = new BattleTag(tagType, lapseType || BattleTagLapseType.FAINT, turnCount || 1);
|
||||
const newTag = getBattleTag(tagType, turnCount || 1);
|
||||
this.summonData.tags.push(newTag);
|
||||
if (newTag.isHidden())
|
||||
this.setVisible(false);
|
||||
newTag.onAdd(this);
|
||||
}
|
||||
|
||||
getTag(tagFilter: BattleTagType | ((tag: BattleTag) => boolean)): BattleTag {
|
||||
return typeof(tagFilter) === 'number'
|
||||
? this.summonData.tags.find(t => t.tagType === tagFilter)
|
||||
: this.summonData.tags.find(t => tagFilter(t));
|
||||
getTag(tagType: BattleTagType | { new(...args: any[]): BattleTag }): BattleTag {
|
||||
return typeof(tagType) === 'number'
|
||||
? this.summonData.tags.find(t => t.tagType === tagType)
|
||||
: this.summonData.tags.find(t => t instanceof tagType);
|
||||
}
|
||||
|
||||
getTags(tagFilter: BattleTagType | ((tag: BattleTag) => boolean)): BattleTag[] {
|
||||
return typeof(tagFilter) === 'number'
|
||||
? this.summonData.tags.filter(t => t.tagType === tagFilter)
|
||||
: this.summonData.tags.filter(t => tagFilter(t));
|
||||
findTag(tagFilter: ((tag: BattleTag) => boolean)) {
|
||||
return this.summonData.tags.find(t => tagFilter(t));
|
||||
}
|
||||
|
||||
lapseTags(lapseType: BattleTagLapseType) {
|
||||
getTags(tagType: BattleTagType | { new(...args: any[]): BattleTag }): BattleTag[] {
|
||||
return typeof(tagType) === 'number'
|
||||
? this.summonData.tags.filter(t => t.tagType === tagType)
|
||||
: this.summonData.tags.filter(t => t instanceof tagType);
|
||||
}
|
||||
|
||||
findTags(tagFilter: ((tag: BattleTag) => boolean)) {
|
||||
return this.summonData.tags.filter(t => tagFilter(t));
|
||||
}
|
||||
|
||||
lapseTag(tagType: BattleTagType): void {
|
||||
const tags = this.summonData.tags;
|
||||
tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(--t.turnCount))).forEach(t => tags.splice(tags.indexOf(t), 1));
|
||||
const visible = !this.getTag(t => t.isHidden());
|
||||
if (visible && !this.visible) {
|
||||
// Wait 2 frames before setting visible for battle animations that don't immediately show the sprite invisible
|
||||
this.scene.tweens.addCounter({
|
||||
duration: 2,
|
||||
useFrames: true,
|
||||
onComplete: () => this.setVisible(true)
|
||||
});
|
||||
} else
|
||||
this.setVisible(visible);
|
||||
const tag = tags.find(t => t.tagType === tagType);
|
||||
if (tag && !(tag.lapse(this))) {
|
||||
tag.onRemove(this);
|
||||
tags.splice(tags.indexOf(tag), 1);
|
||||
}
|
||||
}
|
||||
|
||||
lapseTags(lapseType: BattleTagLapseType): void {
|
||||
const tags = this.summonData.tags;
|
||||
tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this)))).forEach(t => {
|
||||
t.onRemove(this);
|
||||
tags.splice(tags.indexOf(t), 1);
|
||||
});
|
||||
}
|
||||
|
||||
getLastXMoves(turnCount?: integer): TurnMove[] {
|
||||
|
@ -914,8 +907,6 @@ export class PokemonSummonData {
|
|||
public moveHistory: TurnMove[] = [];
|
||||
public moveQueue: QueuedMove[] = [];
|
||||
public tags: BattleTag[] = [];
|
||||
public charging: boolean;
|
||||
public confusionTurns: integer;
|
||||
}
|
||||
|
||||
export class PokemonBattleSummonData {
|
||||
|
@ -947,6 +938,8 @@ export enum MoveResult {
|
|||
OTHER
|
||||
};
|
||||
|
||||
export type DamageResult = MoveResult.EFFECTIVE | MoveResult.SUPER_EFFECTIVE | MoveResult.NOT_VERY_EFFECTIVE;
|
||||
|
||||
export class PokemonMove {
|
||||
public moveId: Moves;
|
||||
public ppUsed: integer;
|
||||
|
|
Loading…
Reference in New Issue