Add weather and weather-related move effects
parent
ef93aec804
commit
32d6bea725
30
src/arena.ts
30
src/arena.ts
|
@ -4,7 +4,11 @@ import { Biome, BiomePoolTier, BiomeTierPools, biomePools } from "./biome";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
import PokemonSpecies, { getPokemonSpecies } from "./pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies } from "./pokemon-species";
|
||||||
import { Species } from "./species";
|
import { Species } from "./species";
|
||||||
import { Weather, WeatherType } from "./weather";
|
import { Weather, WeatherType, getWeatherClearMessage, getWeatherStartMessage } from "./weather";
|
||||||
|
import { CommonAnimPhase, MessagePhase } from "./battle-phases";
|
||||||
|
import { CommonAnim } from "./battle-anims";
|
||||||
|
import { Type } from "./type";
|
||||||
|
import Move from "./move";
|
||||||
|
|
||||||
export class Arena {
|
export class Arena {
|
||||||
private scene: BattleScene;
|
private scene: BattleScene;
|
||||||
|
@ -87,14 +91,32 @@ export class Arena {
|
||||||
return Biome[this.biomeType].toLowerCase();
|
return Biome[this.biomeType].toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
setWeather(weather: WeatherType, turnCount?: integer): boolean {
|
trySetWeather(weather: WeatherType, viaMove: boolean): boolean {
|
||||||
if (this.weather?.weatherType === weather)
|
if (this.weather?.weatherType === (weather || null))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this.weather = new Weather(weather, turnCount || 0);
|
const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
|
||||||
|
this.weather = weather ? new Weather(weather, viaMove ? 5 : 0) : null;
|
||||||
|
|
||||||
|
if (this.weather) {
|
||||||
|
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, true, CommonAnim.SUNNY + (weather - 1)));
|
||||||
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherStartMessage(weather)));
|
||||||
|
} else
|
||||||
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherClearMessage(oldWeatherType)));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAttackTypeMultiplier(attackType: Type): number {
|
||||||
|
if (!this.weather)
|
||||||
|
return 1;
|
||||||
|
return this.weather.getAttackTypeMultiplier(attackType);
|
||||||
|
}
|
||||||
|
|
||||||
|
isMoveWeatherCancelled(move: Move) {
|
||||||
|
return this.weather && this.weather.isMoveWeatherCancelled(move);
|
||||||
|
}
|
||||||
|
|
||||||
isDaytime(): boolean {
|
isDaytime(): boolean {
|
||||||
switch (this.biomeType) {
|
switch (this.biomeType) {
|
||||||
case Biome.PLAINS:
|
case Biome.PLAINS:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//import { battleAnimRawData } from "./battle-anim-raw-data";
|
//import { battleAnimRawData } from "./battle-anim-raw-data";
|
||||||
import BattleScene from "./battle-scene";
|
import BattleScene from "./battle-scene";
|
||||||
import { ChargeAttr, Moves, allMoves } from "./move";
|
import { ChargeAttr, Moves, allMoves } from "./move";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./pokemon";
|
import Pokemon from "./pokemon";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
//import fs from 'vite-plugin-fs/browser';
|
//import fs from 'vite-plugin-fs/browser';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import BattleScene from "./battle-scene";
|
import BattleScene from "./battle-scene";
|
||||||
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
|
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
|
||||||
import * as Utils from './utils';
|
import { applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr } from "./move";
|
||||||
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move";
|
|
||||||
import { Mode } from './ui/ui';
|
import { Mode } from './ui/ui';
|
||||||
import { Command } from "./ui/command-ui-handler";
|
import { Command } from "./ui/command-ui-handler";
|
||||||
import { Stat } from "./pokemon-stat";
|
import { Stat } from "./pokemon-stat";
|
||||||
|
@ -9,7 +8,7 @@ import { ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, HitHealMod
|
||||||
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
|
||||||
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./pokeball";
|
||||||
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||||
import { Status, StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
|
import { StatusEffect, getStatusEffectActivationText, getStatusEffectHealText, getStatusEffectObtainText, getStatusEffectOverlapText } from "./status-effect";
|
||||||
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
import { SummaryUiMode } from "./ui/summary-ui-handler";
|
||||||
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
import EvolutionSceneHandler from "./ui/evolution-scene-handler";
|
||||||
import { EvolutionPhase } from "./evolution-phase";
|
import { EvolutionPhase } from "./evolution-phase";
|
||||||
|
@ -21,6 +20,9 @@ import PokemonSpecies from "./pokemon-species";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag";
|
import { BattleTagLapseType, BattleTagType, HideSpriteTag as HiddenTag } from "./battle-tag";
|
||||||
import { getPokemonMessage } from "./messages";
|
import { getPokemonMessage } from "./messages";
|
||||||
|
import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage as getWeatherEffectMessage } from "./weather";
|
||||||
|
import { Moves, allMoves } from "./move";
|
||||||
|
import * as Utils from './utils';
|
||||||
|
|
||||||
export class SelectStarterPhase extends BattlePhase {
|
export class SelectStarterPhase extends BattlePhase {
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
|
@ -203,7 +205,8 @@ export class SwitchBiomePhase extends BattlePhase {
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this.scene.arenaEnemy.setX(this.scene.arenaEnemy.x - 600);
|
this.scene.arenaEnemy.setX(this.scene.arenaEnemy.x - 600);
|
||||||
|
|
||||||
this.scene.newBiome(this.nextBiome);
|
this.scene.newArena(this.nextBiome)
|
||||||
|
.trySetWeather(getRandomWeatherType(this.nextBiome), false);
|
||||||
|
|
||||||
const biomeKey = this.scene.arena.getBiomeKey();
|
const biomeKey = this.scene.arena.getBiomeKey();
|
||||||
const bgTexture = `${biomeKey}_bg`;
|
const bgTexture = `${biomeKey}_bg`;
|
||||||
|
@ -393,9 +396,13 @@ export class CheckSwitchPhase extends BattlePhase {
|
||||||
|
|
||||||
this.scene.ui.showText('Will you switch\nPOKéMON?', null, () => {
|
this.scene.ui.showText('Will you switch\nPOKéMON?', null, () => {
|
||||||
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
this.scene.ui.setMode(Mode.CONFIRM, () => {
|
||||||
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
this.scene.unshiftPhase(new SwitchPhase(this.scene, false, true));
|
this.scene.unshiftPhase(new SwitchPhase(this.scene, false, true));
|
||||||
this.end();
|
this.end();
|
||||||
}, () => this.end());
|
}, () => {
|
||||||
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
|
this.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,10 +450,12 @@ export class CommandPhase extends BattlePhase {
|
||||||
let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => {
|
let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case Command.FIGHT:
|
case Command.FIGHT:
|
||||||
|
if (playerMove && enemyMove) {
|
||||||
const playerMovePriority = playerMove.getMove().priority;
|
const playerMovePriority = playerMove.getMove().priority;
|
||||||
const enemyMovePriority = enemyMove.getMove().priority;
|
const enemyMovePriority = enemyMove.getMove().priority;
|
||||||
if (playerMovePriority !== enemyMovePriority)
|
if (playerMovePriority !== enemyMovePriority)
|
||||||
return playerMovePriority < enemyMovePriority;
|
return playerMovePriority < enemyMovePriority;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Command.BALL:
|
case Command.BALL:
|
||||||
case Command.POKEMON:
|
case Command.POKEMON:
|
||||||
|
@ -486,6 +495,9 @@ export class CommandPhase extends BattlePhase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
if (this.scene.arena.weather)
|
||||||
|
this.scene.unshiftPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather, isDelayed(command, null, null)));
|
||||||
|
|
||||||
const enemyMove = enemyPokemon.getNextMove();
|
const enemyMove = enemyPokemon.getNextMove();
|
||||||
const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove);
|
const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove);
|
||||||
if (isDelayed(command, playerMove, enemyMove))
|
if (isDelayed(command, playerMove, enemyMove))
|
||||||
|
@ -534,6 +546,9 @@ export class TurnEndPhase extends BattlePhase {
|
||||||
playerPokemon.battleSummonData.turnCount++;
|
playerPokemon.battleSummonData.turnCount++;
|
||||||
enemyPokemon.battleSummonData.turnCount++;
|
enemyPokemon.battleSummonData.turnCount++;
|
||||||
|
|
||||||
|
if (this.scene.arena.weather && !this.scene.arena.weather.lapse())
|
||||||
|
this.scene.arena.trySetWeather(WeatherType.NONE, false);
|
||||||
|
|
||||||
this.end();
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -628,6 +643,8 @@ export abstract class MovePhase extends BattlePhase {
|
||||||
|
|
||||||
const failed = new Utils.BooleanHolder(false);
|
const failed = new Utils.BooleanHolder(false);
|
||||||
applyMoveAttrs(ConditionalMoveAttr, this.pokemon, target, this.move.getMove(), failed);
|
applyMoveAttrs(ConditionalMoveAttr, this.pokemon, target, this.move.getMove(), failed);
|
||||||
|
if (!failed.value && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
|
||||||
|
failed.value = true;
|
||||||
if (failed.value)
|
if (failed.value)
|
||||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'But it failed!'));
|
this.scene.unshiftPhase(new MessagePhase(this.scene, 'But it failed!'));
|
||||||
else
|
else
|
||||||
|
@ -790,6 +807,12 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const moveAccuracy = new Utils.NumberHolder(this.move.getMove().accuracy);
|
||||||
|
applyMoveAttrs(VariableAccuracyAttr, this.getUserPokemon(), this.getTargetPokemon(), this.move.getMove(), moveAccuracy);
|
||||||
|
|
||||||
|
if (moveAccuracy.value === -1)
|
||||||
|
return true;
|
||||||
|
|
||||||
if (this.move.getMove().category !== MoveCategory.STATUS) {
|
if (this.move.getMove().category !== MoveCategory.STATUS) {
|
||||||
const userAccuracyLevel = this.getUserPokemon().summonData.battleStats[BattleStat.ACC];
|
const userAccuracyLevel = this.getUserPokemon().summonData.battleStats[BattleStat.ACC];
|
||||||
const targetEvasionLevel = this.getTargetPokemon().summonData.battleStats[BattleStat.EVA];
|
const targetEvasionLevel = this.getTargetPokemon().summonData.battleStats[BattleStat.EVA];
|
||||||
|
@ -800,8 +823,9 @@ abstract class MoveEffectPhase extends PokemonPhase {
|
||||||
? (3 + Math.min(userAccuracyLevel - targetEvasionLevel, 6)) / 3
|
? (3 + Math.min(userAccuracyLevel - targetEvasionLevel, 6)) / 3
|
||||||
: 3 / (3 + Math.min(targetEvasionLevel - userAccuracyLevel, 6));
|
: 3 / (3 + Math.min(targetEvasionLevel - userAccuracyLevel, 6));
|
||||||
}
|
}
|
||||||
return rand <= this.move.getMove().accuracy * accuracyMultiplier;
|
return rand <= moveAccuracy.value * accuracyMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1043,6 +1067,43 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class WeatherEffectPhase extends CommonAnimPhase {
|
||||||
|
private weather: Weather;
|
||||||
|
private playerDelayed: boolean;
|
||||||
|
|
||||||
|
constructor(scene: BattleScene, weather: Weather, playerDelayed: boolean) {
|
||||||
|
super(scene, true, CommonAnim.SUNNY + (weather.weatherType - 1));
|
||||||
|
|
||||||
|
this.weather = weather;
|
||||||
|
this.playerDelayed = playerDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (this.weather.isDamaging()) {
|
||||||
|
const inflictDamage = (pokemon: Pokemon) => {
|
||||||
|
this.scene.unshiftPhase(new MessagePhase(this.scene, getWeatherDamageMessage(this.weather.weatherType, pokemon)));
|
||||||
|
pokemon.damage(Math.ceil(pokemon.getMaxHp() / 16));
|
||||||
|
this.scene.unshiftPhase(new DamagePhase(this.scene, pokemon.isPlayer()));
|
||||||
|
};
|
||||||
|
|
||||||
|
const playerPokemon = this.scene.getPlayerPokemon();
|
||||||
|
const enemyPokemon = this.scene.getEnemyPokemon();
|
||||||
|
|
||||||
|
const playerImmune = !!playerPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length;
|
||||||
|
const enemyImmune = !!playerPokemon.getTypes().filter(t => this.weather.isTypeDamageImmune(t)).length;
|
||||||
|
|
||||||
|
if (!this.playerDelayed && !playerImmune)
|
||||||
|
inflictDamage(playerPokemon);
|
||||||
|
if (!enemyImmune)
|
||||||
|
inflictDamage(enemyPokemon);
|
||||||
|
if (this.playerDelayed && !playerImmune)
|
||||||
|
inflictDamage(playerPokemon);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene.ui.showText(getWeatherEffectMessage(this.weather.weatherType), null, () => super.start());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class MessagePhase extends BattlePhase {
|
export class MessagePhase extends BattlePhase {
|
||||||
private text: string;
|
private text: string;
|
||||||
private callbackDelay: integer;
|
private callbackDelay: integer;
|
||||||
|
@ -1059,7 +1120,10 @@ export class MessagePhase extends BattlePhase {
|
||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
|
if (this.text)
|
||||||
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt);
|
this.scene.ui.showText(this.text, null, () => this.end(), this.callbackDelay || (this.prompt ? 0 : 1500), this.prompt);
|
||||||
|
else
|
||||||
|
this.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import { Biome, BiomeArena } from './biome';
|
import { Biome } from './biome';
|
||||||
import UI from './ui/ui';
|
import UI from './ui/ui';
|
||||||
import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, SwitchBiomePhase, NewBiomeEncounterPhase, SelectBiomePhase, SelectStarterPhase } from './battle-phases';
|
import { EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, SelectStarterPhase } from './battle-phases';
|
||||||
import { PlayerPokemon, EnemyPokemon } from './pokemon';
|
import { PlayerPokemon, EnemyPokemon } from './pokemon';
|
||||||
import PokemonSpecies, { allSpecies, getPokemonSpecies } from './pokemon-species';
|
import PokemonSpecies, { allSpecies, getPokemonSpecies } from './pokemon-species';
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
|
@ -13,6 +13,7 @@ import { Battle } from './battle';
|
||||||
import { initCommonAnims, loadCommonAnimAssets, populateAnims } from './battle-anims';
|
import { initCommonAnims, loadCommonAnimAssets, populateAnims } from './battle-anims';
|
||||||
import { BattlePhase } from './battle-phase';
|
import { BattlePhase } from './battle-phase';
|
||||||
import { initGameSpeed } from './game-speed';
|
import { initGameSpeed } from './game-speed';
|
||||||
|
import { Arena } from './arena';
|
||||||
|
|
||||||
const enableAuto = true;
|
const enableAuto = true;
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ export default class BattleScene extends Phaser.Scene {
|
||||||
public arenaEnemy: Phaser.GameObjects.Image;
|
public arenaEnemy: Phaser.GameObjects.Image;
|
||||||
public arenaEnemyTransition: Phaser.GameObjects.Image;
|
public arenaEnemyTransition: Phaser.GameObjects.Image;
|
||||||
public arenaNextEnemy: Phaser.GameObjects.Image;
|
public arenaNextEnemy: Phaser.GameObjects.Image;
|
||||||
public arena: BiomeArena;
|
public arena: Arena;
|
||||||
public trainer: Phaser.GameObjects.Sprite;
|
public trainer: Phaser.GameObjects.Sprite;
|
||||||
public currentBattle: Battle;
|
public currentBattle: Battle;
|
||||||
public pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ]));
|
public pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ]));
|
||||||
|
@ -283,9 +284,9 @@ export default class BattleScene extends Phaser.Scene {
|
||||||
|
|
||||||
if (isRandom) {
|
if (isRandom) {
|
||||||
const biomes = Utils.getEnumValues(Biome);
|
const biomes = Utils.getEnumValues(Biome);
|
||||||
this.newBiome(biomes[Utils.randInt(biomes.length)]);
|
this.newArena(biomes[Utils.randInt(biomes.length)]);
|
||||||
} else
|
} else
|
||||||
this.newBiome(Biome.PLAINS);
|
this.newArena(Biome.PLAINS);
|
||||||
|
|
||||||
const biomeKey = this.arena.getBiomeKey();
|
const biomeKey = this.arena.getBiomeKey();
|
||||||
this.arenaBg = this.add.sprite(0, 0, `${biomeKey}_bg`);
|
this.arenaBg = this.add.sprite(0, 0, `${biomeKey}_bg`);
|
||||||
|
@ -416,8 +417,8 @@ export default class BattleScene extends Phaser.Scene {
|
||||||
return this.currentBattle;
|
return this.currentBattle;
|
||||||
}
|
}
|
||||||
|
|
||||||
newBiome(biome: Biome): BiomeArena {
|
newArena(biome: Biome): Arena {
|
||||||
this.arena = new BiomeArena(this, biome, Biome[biome].toLowerCase());
|
this.arena = new Arena(this, biome, Biome[biome].toLowerCase());
|
||||||
return this.arena;
|
return this.arena;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import * as ModifierTypes from './modifier-type';
|
import * as ModifierTypes from './modifier-type';
|
||||||
import { LearnMovePhase, LevelUpPhase, MessagePhase, PokemonHealPhase } from "./battle-phases";
|
import { LearnMovePhase, LevelUpPhase, PokemonHealPhase } from "./battle-phases";
|
||||||
import BattleScene from "./battle-scene";
|
import BattleScene from "./battle-scene";
|
||||||
import { getLevelTotalExp } from "./exp";
|
import { getLevelTotalExp } from "./exp";
|
||||||
import { PokeballType } from "./pokeball";
|
import { PokeballType } from "./pokeball";
|
||||||
import Pokemon, { PlayerPokemon } from "./pokemon";
|
import Pokemon, { PlayerPokemon } from "./pokemon";
|
||||||
import { Stat } from "./pokemon-stat";
|
import { Stat } from "./pokemon-stat";
|
||||||
import { addTextObject, TextStyle } from "./text";
|
import { addTextObject, TextStyle } from "./text";
|
||||||
import * as Utils from "./utils";
|
|
||||||
import { Type } from './type';
|
import { Type } from './type';
|
||||||
import { EvolutionPhase } from './evolution-phase';
|
import { EvolutionPhase } from './evolution-phase';
|
||||||
import { pokemonEvolutions } from './pokemon-evolutions';
|
import { pokemonEvolutions } from './pokemon-evolutions';
|
||||||
import { getPokemonMessage } from './messages';
|
import { getPokemonMessage } from './messages';
|
||||||
|
import * as Utils from "./utils";
|
||||||
|
|
||||||
type ModifierType = ModifierTypes.ModifierType;
|
type ModifierType = ModifierTypes.ModifierType;
|
||||||
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||||
|
|
248
src/move.ts
248
src/move.ts
|
@ -1,12 +1,13 @@
|
||||||
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims";
|
||||||
import { EnemyMovePhase, MessagePhase, MovePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases";
|
import { EnemyMovePhase, MessagePhase, ObtainStatusEffectPhase, PlayerMovePhase, PokemonHealPhase, StatChangePhase } from "./battle-phases";
|
||||||
import { BattleStat } from "./battle-stat";
|
import { BattleStat } from "./battle-stat";
|
||||||
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon";
|
|
||||||
import { BattleTagType } from "./battle-tag";
|
import { BattleTagType } from "./battle-tag";
|
||||||
|
import { getPokemonMessage } from "./messages";
|
||||||
|
import Pokemon, { EnemyPokemon, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "./pokemon";
|
||||||
import { StatusEffect } from "./status-effect";
|
import { StatusEffect } from "./status-effect";
|
||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
import { getPokemonMessage } from "./messages";
|
import { WeatherType } from "./weather";
|
||||||
|
|
||||||
export enum MoveCategory {
|
export enum MoveCategory {
|
||||||
PHYSICAL,
|
PHYSICAL,
|
||||||
|
@ -622,7 +623,7 @@ export enum Moves {
|
||||||
V_CREATE,
|
V_CREATE,
|
||||||
FUSION_FLARE,
|
FUSION_FLARE,
|
||||||
FUSION_BOLT
|
FUSION_BOLT
|
||||||
};
|
}
|
||||||
|
|
||||||
type MoveAttrFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
type MoveAttrFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
|
||||||
|
|
||||||
|
@ -678,30 +679,59 @@ export class FixedDamageAttr extends MoveAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MultiHitType {
|
export enum MultiHitType {
|
||||||
_2,
|
_2,
|
||||||
_2_TO_5,
|
_2_TO_5,
|
||||||
_3_INCR
|
_3_INCR
|
||||||
}
|
}
|
||||||
|
|
||||||
class HealAttr extends MoveEffectAttr {
|
export class HealAttr extends MoveEffectAttr {
|
||||||
private fullHeal: boolean;
|
private healRatio: number;
|
||||||
private showAnim: boolean;
|
private showAnim: boolean;
|
||||||
|
|
||||||
constructor(fullHeal?: boolean, showAnim?: boolean) {
|
constructor(healRatio?: number, showAnim?: boolean) {
|
||||||
super(true);
|
super(true);
|
||||||
|
|
||||||
this.fullHeal = !!fullHeal;
|
this.healRatio = healRatio || 1;
|
||||||
this.showAnim = !!showAnim;
|
this.showAnim = !!showAnim;
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
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() / (this.fullHeal ? 1 : 2)), 1), getPokemonMessage(user, ' regained\nhealth!'), true, !this.showAnim));
|
this.addHealPhase(user, this.healRatio);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHealPhase(user: Pokemon, healRatio: number) {
|
||||||
|
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.getMaxHp() * healRatio), 1), getPokemonMessage(user, ' regained\nhealth!'), true, !this.showAnim));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WeatherHealAttr extends HealAttr {
|
||||||
|
constructor() {
|
||||||
|
super(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
let healRatio = 0.5;
|
||||||
|
const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE;
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.SUNNY:
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
healRatio = 2 / 3;
|
||||||
|
break;
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
healRatio = 0.25;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.addHealPhase(user, healRatio);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HitHealAttr extends MoveHitEffectAttr {
|
export class HitHealAttr extends MoveHitEffectAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.turnData.damageDealt / 2), 1), getPokemonMessage(target, ` had its\nenergy drained!`), false, true));
|
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.isPlayer(), Math.max(Math.floor(user.turnData.damageDealt / 2), 1), getPokemonMessage(target, ` had its\nenergy drained!`), false, true));
|
||||||
return true;
|
return true;
|
||||||
|
@ -744,7 +774,7 @@ export class MultiHitAttr extends MoveAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatusEffectAttr extends MoveHitEffectAttr {
|
export class StatusEffectAttr extends MoveHitEffectAttr {
|
||||||
public effect: StatusEffect;
|
public effect: StatusEffect;
|
||||||
public selfTarget: boolean;
|
public selfTarget: boolean;
|
||||||
public cureTurn: integer;
|
public cureTurn: integer;
|
||||||
|
@ -773,9 +803,7 @@ class StatusEffectAttr extends MoveHitEffectAttr {
|
||||||
export class BypassSleepAttr extends MoveAttr {
|
export class BypassSleepAttr extends MoveAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
if (user.status?.effect === StatusEffect.SLEEP) {
|
if (user.status?.effect === StatusEffect.SLEEP) {
|
||||||
console.log('add bypass sleep');
|
|
||||||
user.addTag(BattleTagType.BYPASS_SLEEP, 1);
|
user.addTag(BattleTagType.BYPASS_SLEEP, 1);
|
||||||
console.log(user.getTag(BattleTagType.BYPASS_SLEEP));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -783,7 +811,38 @@ export class BypassSleepAttr extends MoveAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OneHitKOAttr extends MoveHitEffectAttr {
|
export class WeatherChangeAttr extends MoveEffectAttr {
|
||||||
|
private weatherType: WeatherType;
|
||||||
|
|
||||||
|
constructor(weatherType: WeatherType) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.weatherType = weatherType;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
return user.scene.arena.trySetWeather(this.weatherType, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ClearWeatherAttr extends MoveEffectAttr {
|
||||||
|
private weatherType: WeatherType;
|
||||||
|
|
||||||
|
constructor(weatherType: WeatherType) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.weatherType = weatherType;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
if (user.scene.arena.weather?.weatherType === this.weatherType)
|
||||||
|
return user.scene.arena.trySetWeather(WeatherType.NONE, true);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OneHitKOAttr extends MoveHitEffectAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
target.hp = 0;
|
target.hp = 0;
|
||||||
user.scene.unshiftPhase(new MessagePhase(user.scene, 'It\'s a one-hit KO!'));
|
user.scene.unshiftPhase(new MessagePhase(user.scene, 'It\'s a one-hit KO!'));
|
||||||
|
@ -829,6 +888,22 @@ export class ChargeAttr extends OverrideMoveEffectAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SolarBeamChargeAttr extends ChargeAttr {
|
||||||
|
constructor() {
|
||||||
|
super(ChargeAnim.SOLAR_BEAM_CHARGING, 'took\nin sunlight!');
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const weatherType = user.scene.arena.weather?.weatherType;
|
||||||
|
if (weatherType === WeatherType.SUNNY || weatherType === WeatherType.HARSH_SUN)
|
||||||
|
resolve(false);
|
||||||
|
else
|
||||||
|
super.apply(user, target, move, args).then(result => resolve(result));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class StatChangeAttr extends MoveEffectAttr {
|
export class StatChangeAttr extends MoveEffectAttr {
|
||||||
public stats: BattleStat[];
|
public stats: BattleStat[];
|
||||||
public levels: integer;
|
public levels: integer;
|
||||||
|
@ -846,7 +921,89 @@ export class StatChangeAttr extends MoveEffectAttr {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
|
if (move.chance < 0 || move.chance === 100 || Utils.randInt(100) < move.chance) {
|
||||||
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.stats, this.levels));
|
const levels = this.getLevels(user);
|
||||||
|
user.scene.unshiftPhase(new StatChangePhase(user.scene, user.isPlayer() === this.selfTarget, this.stats, levels));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLevels(_user: Pokemon): integer {
|
||||||
|
return this.levels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GrowthStatChangeAttr extends StatChangeAttr {
|
||||||
|
constructor() {
|
||||||
|
super([ BattleStat.ATK, BattleStat.SPATK ], 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLevels(user: Pokemon): number {
|
||||||
|
const weatherType = user.scene.arena.weather?.weatherType;
|
||||||
|
if (weatherType === WeatherType.SUNNY || weatherType === WeatherType.HARSH_SUN)
|
||||||
|
return this.levels + 1;
|
||||||
|
return this.levels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VariablePowerAttr extends MoveAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
//const power = args[0] as Utils.NumberHolder;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SolarBeamPowerAttr extends VariablePowerAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
const power = args[0] as Utils.NumberHolder;
|
||||||
|
const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE;
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
power.value *= 0.5;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VariableAccuracyAttr extends MoveAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
//const accuracy = args[0] as Utils.NumberHolder;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ThunderAccuracyAttr extends VariableAccuracyAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
const accuracy = args[0] as Utils.NumberHolder;
|
||||||
|
const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE;
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.SUNNY:
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
accuracy.value = 50;
|
||||||
|
return true;
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
accuracy.value = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlizzardAccuracyAttr extends VariableAccuracyAttr {
|
||||||
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
const accuracy = args[0] as Utils.NumberHolder;
|
||||||
|
const weatherType = user.scene.arena.weather?.weatherType || WeatherType.NONE;
|
||||||
|
if (weatherType === WeatherType.HAIL) {
|
||||||
|
accuracy.value = -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,6 +1030,12 @@ export class ConditionalMoveAttr extends MoveAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class WeatherConditionalMoveAttr extends ConditionalMoveAttr {
|
||||||
|
constructor(weatherType: WeatherType) {
|
||||||
|
super((user: Pokemon, target: Pokemon, move: Move) => !user.scene.arena.weather || (user.scene.arena.weather.weatherType !== weatherType && !user.scene.arena.weather.isImmutable()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class MissEffectAttr extends MoveAttr {
|
export class MissEffectAttr extends MoveAttr {
|
||||||
private missEffectFunc: MoveAttrFunc;
|
private missEffectFunc: MoveAttrFunc;
|
||||||
|
|
||||||
|
@ -917,7 +1080,7 @@ export class FrenzyAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const frenzyMissFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
export const frenzyMissFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
||||||
while (user.summonData.moveQueue.length && user.summonData.moveQueue[0].move === move.id)
|
while (user.summonData.moveQueue.length && user.summonData.moveQueue[0].move === move.id)
|
||||||
user.summonData.moveQueue.shift();
|
user.summonData.moveQueue.shift();
|
||||||
user.lapseTag(BattleTagType.FRENZY)
|
user.lapseTag(BattleTagType.FRENZY)
|
||||||
|
@ -947,13 +1110,13 @@ export class AddTagAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlinchAttr extends AddTagAttr {
|
export class FlinchAttr extends AddTagAttr {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(BattleTagType.FLINCHED, 1, false);
|
super(BattleTagType.FLINCHED, 1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfuseAttr extends AddTagAttr {
|
export class ConfuseAttr extends AddTagAttr {
|
||||||
constructor(selfTarget?: boolean) {
|
constructor(selfTarget?: boolean) {
|
||||||
super(BattleTagType.CONFUSED, Utils.randInt(4, 1), selfTarget);
|
super(BattleTagType.CONFUSED, Utils.randInt(4, 1), selfTarget);
|
||||||
}
|
}
|
||||||
|
@ -984,7 +1147,7 @@ export function applyMoveAttrs(attrType: { new(...args: any[]): MoveAttr }, user
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const moves = user.moveset.filter(m => m.moveId !== move.id);
|
const moves = user.moveset.filter(m => m.moveId !== move.id);
|
||||||
if (moves.length) {
|
if (moves.length) {
|
||||||
|
@ -999,7 +1162,11 @@ class RandomMovesetMoveAttr extends OverrideMoveEffectAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RandomMoveAttr extends OverrideMoveEffectAttr {
|
export class RandomMoveAttr extends OverrideMoveEffectAttr {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const moveIds = Utils.getEnumValues(Moves).filter(m => m !== move.id);
|
const moveIds = Utils.getEnumValues(Moves).filter(m => m !== move.id);
|
||||||
|
@ -1077,7 +1244,7 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.HYDRO_PUMP, "Hydro Pump", Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, 142, "", -1, 0, 1),
|
new AttackMove(Moves.HYDRO_PUMP, "Hydro Pump", Type.WATER, MoveCategory.SPECIAL, 110, 80, 5, 142, "", -1, 0, 1),
|
||||||
new AttackMove(Moves.SURF, "Surf", Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 123, "Hits all adjacent Pokémon.", -1, 0, 1),
|
new AttackMove(Moves.SURF, "Surf", Type.WATER, MoveCategory.SPECIAL, 90, 100, 15, 123, "Hits all adjacent Pokémon.", -1, 0, 1),
|
||||||
new AttackMove(Moves.ICE_BEAM, "Ice Beam", Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 135, "May freeze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.FREEZE)),
|
new AttackMove(Moves.ICE_BEAM, "Ice Beam", Type.ICE, MoveCategory.SPECIAL, 90, 100, 10, 135, "May freeze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.FREEZE)),
|
||||||
new AttackMove(Moves.BLIZZARD, "Blizzard", Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 143, "May freeze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.FREEZE)),
|
new AttackMove(Moves.BLIZZARD, "Blizzard", Type.ICE, MoveCategory.SPECIAL, 110, 70, 5, 143, "May freeze opponent.", 10, 0, 1, new BlizzardAccuracyAttr(), new StatusEffectAttr(StatusEffect.FREEZE)), // TODO: 30% chance to hit protect/detect in hail
|
||||||
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 0, 1, new ConfuseAttr()),
|
new AttackMove(Moves.PSYBEAM, "Psybeam", Type.PSYCHIC, MoveCategory.SPECIAL, 65, 100, 20, 16, "May confuse opponent.", 10, 0, 1, new ConfuseAttr()),
|
||||||
new AttackMove(Moves.BUBBLE_BEAM, "Bubble Beam", Type.WATER, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Speed.", 10, 0, 1, new StatChangeAttr(BattleStat.SPD, -1)),
|
new AttackMove(Moves.BUBBLE_BEAM, "Bubble Beam", Type.WATER, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Speed.", 10, 0, 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, 0, 1, new StatChangeAttr(BattleStat.ATK, -1)),
|
new AttackMove(Moves.AURORA_BEAM, "Aurora Beam", Type.ICE, MoveCategory.SPECIAL, 65, 100, 20, -1, "May lower opponent's Attack.", 10, 0, 1, new StatChangeAttr(BattleStat.ATK, -1)),
|
||||||
|
@ -1092,11 +1259,10 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.ABSORB, "Absorb", Type.GRASS, MoveCategory.SPECIAL, 20, 100, 25, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
|
new AttackMove(Moves.ABSORB, "Absorb", Type.GRASS, MoveCategory.SPECIAL, 20, 100, 25, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
|
||||||
new AttackMove(Moves.MEGA_DRAIN, "Mega Drain", Type.GRASS, MoveCategory.SPECIAL, 40, 100, 15, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
|
new AttackMove(Moves.MEGA_DRAIN, "Mega Drain", Type.GRASS, MoveCategory.SPECIAL, 40, 100, 15, -1, "User recovers half the HP inflicted on opponent.", -1, 0, 1, new HitHealAttr()),
|
||||||
new StatusMove(Moves.LEECH_SEED, "Leech Seed", Type.GRASS, 90, 10, -1, "Drains HP from opponent each turn.", -1, 0, 1),
|
new StatusMove(Moves.LEECH_SEED, "Leech Seed", Type.GRASS, 90, 10, -1, "Drains HP from opponent each turn.", -1, 0, 1),
|
||||||
new StatusMove(Moves.GROWTH, "Growth", Type.NORMAL, -1, 20, -1, "Raises user's Attack and Special Attack.", -1, 0, 1,
|
new StatusMove(Moves.GROWTH, "Growth", Type.NORMAL, -1, 20, -1, "Raises user's Attack and Special Attack.", -1, 0, 1, new GrowthStatChangeAttr()),
|
||||||
new StatChangeAttr([ BattleStat.ATK, BattleStat.SPATK ], 1, true)),
|
|
||||||
new AttackMove(Moves.RAZOR_LEAF, "Razor Leaf", Type.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()),
|
new AttackMove(Moves.RAZOR_LEAF, "Razor Leaf", Type.GRASS, MoveCategory.PHYSICAL, 55, 95, 25, -1, "High critical hit ratio.", -1, 0, 1, new HighCritAttr()),
|
||||||
new AttackMove(Moves.SOLAR_BEAM, "Solar Beam", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, 168, "Charges on first turn, attacks on second.", -1, 0, 1,
|
new AttackMove(Moves.SOLAR_BEAM, "Solar Beam", Type.GRASS, MoveCategory.SPECIAL, 120, 100, 10, 168, "Charges on first turn, attacks on second.", -1, 0, 1,
|
||||||
new ChargeAttr(ChargeAnim.SOLAR_BEAM_CHARGING, 'took\nin sunlight!')),
|
new SolarBeamChargeAttr(), new SolarBeamPowerAttr()),
|
||||||
new StatusMove(Moves.POISON_POWDER, "Poison Powder", Type.POISON, 75, 35, -1, "Poisons opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.POISON)),
|
new StatusMove(Moves.POISON_POWDER, "Poison Powder", Type.POISON, 75, 35, -1, "Poisons opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.POISON)),
|
||||||
new StatusMove(Moves.STUN_SPORE, "Stun Spore", Type.GRASS, 75, 30, -1, "Paralyzes opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new StatusMove(Moves.STUN_SPORE, "Stun Spore", Type.GRASS, 75, 30, -1, "Paralyzes opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||||
new StatusMove(Moves.SLEEP_POWDER, "Sleep Powder", Type.GRASS, 75, 15, -1, "Puts opponent to sleep.", -1, 0, 1, new StatusEffectAttr(StatusEffect.SLEEP)),
|
new StatusMove(Moves.SLEEP_POWDER, "Sleep Powder", Type.GRASS, 75, 15, -1, "Puts opponent to sleep.", -1, 0, 1, new StatusEffectAttr(StatusEffect.SLEEP)),
|
||||||
|
@ -1107,7 +1273,7 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.FIRE_SPIN, "Fire Spin", Type.FIRE, MoveCategory.SPECIAL, 35, 85, 15, 24, "Traps opponent, damaging them for 4-5 turns.", 100, 0, 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, 0, 1),
|
||||||
new AttackMove(Moves.THUNDER_SHOCK, "Thunder Shock", Type.ELECTRIC, MoveCategory.SPECIAL, 40, 100, 30, -1, "May paralyze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new AttackMove(Moves.THUNDER_SHOCK, "Thunder Shock", Type.ELECTRIC, MoveCategory.SPECIAL, 40, 100, 30, -1, "May paralyze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||||
new AttackMove(Moves.THUNDERBOLT, "Thunderbolt", Type.ELECTRIC, MoveCategory.SPECIAL, 90, 100, 15, 126, "May paralyze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new AttackMove(Moves.THUNDERBOLT, "Thunderbolt", Type.ELECTRIC, MoveCategory.SPECIAL, 90, 100, 15, 126, "May paralyze opponent.", 10, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||||
new StatusMove(Moves.THUNDER_WAVE, "Thunder Wave", Type.ELECTRIC, 90, 20, 82, "Paralyzes opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new StatusMove(Moves.THUNDER_WAVE, "Thunder Wave", Type.ELECTRIC, 90, 20, 82, "Paralyzes opponent.", -1, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS), new ThunderAccuracyAttr()),
|
||||||
new AttackMove(Moves.THUNDER, "Thunder", Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 166, "May paralyze opponent.", 30, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new AttackMove(Moves.THUNDER, "Thunder", Type.ELECTRIC, MoveCategory.SPECIAL, 110, 70, 10, 166, "May paralyze opponent.", 30, 0, 1, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||||
new AttackMove(Moves.ROCK_THROW, "Rock Throw", Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, "", -1, 0, 1),
|
new AttackMove(Moves.ROCK_THROW, "Rock Throw", Type.ROCK, MoveCategory.PHYSICAL, 50, 90, 15, -1, "", -1, 0, 1),
|
||||||
new AttackMove(Moves.EARTHQUAKE, "Earthquake", Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, 149, "Power is doubled if opponent is underground from using Dig.", -1, 0, 1,
|
new AttackMove(Moves.EARTHQUAKE, "Earthquake", Type.GROUND, MoveCategory.PHYSICAL, 100, 100, 10, 149, "Power is doubled if opponent is underground from using Dig.", -1, 0, 1,
|
||||||
|
@ -1128,7 +1294,7 @@ export const allMoves = [
|
||||||
new StatusMove(Moves.MIMIC, "Mimic", Type.NORMAL, -1, 10, -1, "Copies the opponent's last move.", -1, 0, 1),
|
new StatusMove(Moves.MIMIC, "Mimic", Type.NORMAL, -1, 10, -1, "Copies the opponent's last move.", -1, 0, 1),
|
||||||
new StatusMove(Moves.SCREECH, "Screech", Type.NORMAL, 85, 40, -1, "Sharply lowers opponent's Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.DEF, -2)),
|
new StatusMove(Moves.SCREECH, "Screech", Type.NORMAL, 85, 40, -1, "Sharply lowers opponent's Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.DEF, -2)),
|
||||||
new StatusMove(Moves.DOUBLE_TEAM, "Double Team", Type.NORMAL, -1, 15, -1, "Raises user's Evasiveness.", -1, 0, 1, new StatChangeAttr(BattleStat.EVA, 1, true)),
|
new StatusMove(Moves.DOUBLE_TEAM, "Double Team", Type.NORMAL, -1, 15, -1, "Raises user's Evasiveness.", -1, 0, 1, new StatChangeAttr(BattleStat.EVA, 1, true)),
|
||||||
new StatusMove(Moves.RECOVER, "Recover", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1, new HealAttr()),
|
new StatusMove(Moves.RECOVER, "Recover", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1, new HealAttr(0.5)),
|
||||||
new StatusMove(Moves.HARDEN, "Harden", Type.NORMAL, -1, 30, -1, "Raises user's Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.DEF, 1, true)),
|
new StatusMove(Moves.HARDEN, "Harden", Type.NORMAL, -1, 30, -1, "Raises user's Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.DEF, 1, true)),
|
||||||
new StatusMove(Moves.MINIMIZE, "Minimize", Type.NORMAL, -1, 10, -1, "Sharply raises user's Evasiveness.", -1, 0, 1, new StatChangeAttr(BattleStat.EVA, 1, true)),
|
new StatusMove(Moves.MINIMIZE, "Minimize", Type.NORMAL, -1, 10, -1, "Sharply raises user's Evasiveness.", -1, 0, 1, new StatChangeAttr(BattleStat.EVA, 1, true)),
|
||||||
new StatusMove(Moves.SMOKESCREEN, "Smokescreen", Type.NORMAL, 100, 20, -1, "Lowers opponent's Accuracy.", -1, 0, 1, new StatChangeAttr(BattleStat.ACC, -1)),
|
new StatusMove(Moves.SMOKESCREEN, "Smokescreen", Type.NORMAL, 100, 20, -1, "Lowers opponent's Accuracy.", -1, 0, 1, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||||
|
@ -1159,7 +1325,7 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.CONSTRICT, "Constrict", Type.NORMAL, MoveCategory.PHYSICAL, 10, 100, 35, -1, "May lower opponent's Speed by one stage.", 10, 0, 1, new StatChangeAttr(BattleStat.SPD, -1)),
|
new AttackMove(Moves.CONSTRICT, "Constrict", Type.NORMAL, MoveCategory.PHYSICAL, 10, 100, 35, -1, "May lower opponent's Speed by one stage.", 10, 0, 1, new StatChangeAttr(BattleStat.SPD, -1)),
|
||||||
new StatusMove(Moves.AMNESIA, "Amnesia", Type.PSYCHIC, -1, 20, 128, "Sharply raises user's Special Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.SPDEF, 2, true)),
|
new StatusMove(Moves.AMNESIA, "Amnesia", Type.PSYCHIC, -1, 20, 128, "Sharply raises user's Special Defense.", -1, 0, 1, new StatChangeAttr(BattleStat.SPDEF, 2, true)),
|
||||||
new StatusMove(Moves.KINESIS, "Kinesis", Type.PSYCHIC, 80, 15, -1, "Lowers opponent's Accuracy.", -1, 0, 1, new StatChangeAttr(BattleStat.ACC, -1)),
|
new StatusMove(Moves.KINESIS, "Kinesis", Type.PSYCHIC, 80, 15, -1, "Lowers opponent's Accuracy.", -1, 0, 1, new StatChangeAttr(BattleStat.ACC, -1)),
|
||||||
new StatusMove(Moves.SOFT_BOILED, "Soft-Boiled", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1, new HealAttr()),
|
new StatusMove(Moves.SOFT_BOILED, "Soft-Boiled", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 1, new HealAttr(0.5)),
|
||||||
new AttackMove(Moves.HIGH_JUMP_KICK, "High Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, -1, "If it misses, the user loses half their HP.", -1, 0, 1,
|
new AttackMove(Moves.HIGH_JUMP_KICK, "High Jump Kick", Type.FIGHTING, MoveCategory.PHYSICAL, 130, 90, 10, -1, "If it misses, the user loses half their HP.", -1, 0, 1,
|
||||||
new MissEffectAttr((user: Pokemon, target: Pokemon, move: Move) => {
|
new MissEffectAttr((user: Pokemon, target: Pokemon, move: Move) => {
|
||||||
user.hp = Math.floor(user.hp / 2);
|
user.hp = Math.floor(user.hp / 2);
|
||||||
|
@ -1187,7 +1353,7 @@ export const allMoves = [
|
||||||
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.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 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 ConditionalMoveAttr((user: Pokemon, target: Pokemon, move: Move) => user.status?.effect !== StatusEffect.SLEEP), new StatusEffectAttr(StatusEffect.SLEEP, true, 3), new HealAttr(1, 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.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 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)),
|
new StatusMove(Moves.SHARPEN, "Sharpen", Type.NORMAL, -1, 30, -1, "Raises user's Attack.", -1, 0, 1, new StatChangeAttr(BattleStat.ATK, 1, true)),
|
||||||
|
@ -1239,14 +1405,14 @@ export const allMoves = [
|
||||||
new StatusMove(Moves.LOCK_ON, "Lock-On", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 2),
|
new StatusMove(Moves.LOCK_ON, "Lock-On", Type.NORMAL, -1, 5, -1, "User's next attack is guaranteed to hit.", -1, 0, 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, 0, 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, 0, 2,
|
||||||
new FrenzyAttr(), frenzyMissFunc, new ConfuseAttr(true)), // TODO: Update to still confuse if last hit misses
|
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, 0, 2),
|
new StatusMove(Moves.SANDSTORM, "Sandstorm", Type.ROCK, -1, 10, 51, "Creates a sandstorm for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.SANDSTORM), new WeatherChangeAttr(WeatherType.SANDSTORM)),
|
||||||
new AttackMove(Moves.GIGA_DRAIN, "Giga Drain", Type.GRASS, MoveCategory.SPECIAL, 75, 100, 10, 111, "User recovers half the HP inflicted on opponent.", -1, 4, 2, new HitHealAttr()),
|
new AttackMove(Moves.GIGA_DRAIN, "Giga Drain", Type.GRASS, MoveCategory.SPECIAL, 75, 100, 10, 111, "User recovers half the HP inflicted on opponent.", -1, 4, 2, new HitHealAttr()),
|
||||||
new StatusMove(Moves.ENDURE, "Endure", Type.NORMAL, -1, 10, 47, "Always left with at least 1 HP, but may fail if used consecutively.", -1, 0, 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, 0, 2),
|
||||||
new StatusMove(Moves.CHARM, "Charm", Type.FAIRY, 100, 20, 2, "Sharply lowers opponent's Attack.", -1, 0, 2, new StatChangeAttr(BattleStat.ATK, -2)),
|
new StatusMove(Moves.CHARM, "Charm", Type.FAIRY, 100, 20, 2, "Sharply lowers opponent's Attack.", -1, 0, 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, 0, 2),
|
new AttackMove(Moves.ROLLOUT, "Rollout", Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, "Doubles in power each turn for 5 turns.", -1, 0, 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, 0, 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, 0, 2),
|
||||||
new StatusMove(Moves.SWAGGER, "Swagger", Type.NORMAL, 85, 15, -1, "Confuses opponent, but sharply raises its Attack.", -1, 0, 2, new StatChangeAttr(BattleStat.ATK, 2), new ConfuseAttr()),
|
new StatusMove(Moves.SWAGGER, "Swagger", Type.NORMAL, 85, 15, -1, "Confuses opponent, but sharply raises its Attack.", -1, 0, 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, 0, 2, new HealAttr()),
|
new StatusMove(Moves.MILK_DRINK, "Milk Drink", Type.NORMAL, -1, 5, -1, "User recovers half its max HP.", -1, 0, 2, new HealAttr(0.5)),
|
||||||
new AttackMove(Moves.SPARK, "Spark", Type.ELECTRIC, MoveCategory.PHYSICAL, 65, 100, 20, -1, "May paralyze opponent.", 30, 0, 2, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new AttackMove(Moves.SPARK, "Spark", Type.ELECTRIC, MoveCategory.PHYSICAL, 65, 100, 20, -1, "May paralyze opponent.", 30, 0, 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, 0, 2),
|
new AttackMove(Moves.FURY_CUTTER, "Fury Cutter", Type.BUG, MoveCategory.PHYSICAL, 40, 95, 20, -1, "Power increases each turn.", -1, 0, 2),
|
||||||
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 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)),
|
||||||
|
@ -1273,15 +1439,15 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.IRON_TAIL, "Iron Tail", Type.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, -1, "May lower opponent's Defense.", 30, 0, 2, new StatChangeAttr(BattleStat.DEF, -1)),
|
new AttackMove(Moves.IRON_TAIL, "Iron Tail", Type.STEEL, MoveCategory.PHYSICAL, 100, 75, 15, -1, "May lower opponent's Defense.", 30, 0, 2, new StatChangeAttr(BattleStat.DEF, -1)),
|
||||||
new AttackMove(Moves.METAL_CLAW, "Metal Claw", Type.STEEL, MoveCategory.PHYSICAL, 50, 95, 35, 31, "May raise user's Attack.", 10, 0, 2, new StatChangeAttr(BattleStat.ATK, 1, true)),
|
new AttackMove(Moves.METAL_CLAW, "Metal Claw", Type.STEEL, MoveCategory.PHYSICAL, 50, 95, 35, 31, "May raise user's Attack.", 10, 0, 2, new StatChangeAttr(BattleStat.ATK, 1, true)),
|
||||||
new AttackMove(Moves.VITAL_THROW, "Vital Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 70, 999, 10, -1, "User attacks last, but ignores Accuracy and Evasiveness.", -1, -1, 2),
|
new AttackMove(Moves.VITAL_THROW, "Vital Throw", Type.FIGHTING, MoveCategory.PHYSICAL, 70, 999, 10, -1, "User attacks last, but ignores Accuracy and Evasiveness.", -1, -1, 2),
|
||||||
new StatusMove(Moves.MORNING_SUN, "Morning Sun", Type.NORMAL, -1, 5, -1, "User recovers HP. Amount varies with the weather.", -1, 0, 2), // TODO
|
new StatusMove(Moves.MORNING_SUN, "Morning Sun", Type.NORMAL, -1, 5, -1, "User recovers HP. Amount varies with the weather.", -1, 0, 2, new WeatherHealAttr()),
|
||||||
new StatusMove(Moves.SYNTHESIS, "Synthesis", Type.GRASS, -1, 5, -1, "User recovers HP. Amount varies with the weather.", -1, 0, 2), // TODO
|
new StatusMove(Moves.SYNTHESIS, "Synthesis", Type.GRASS, -1, 5, -1, "User recovers HP. Amount varies with the weather.", -1, 0, 2, new WeatherHealAttr()),
|
||||||
new StatusMove(Moves.MOONLIGHT, "Moonlight", Type.FAIRY, -1, 5, -1, "User recovers HP. Amount varies with the weather.", -1, 0, 2), // TODO
|
new StatusMove(Moves.MOONLIGHT, "Moonlight", Type.FAIRY, -1, 5, -1, "User recovers HP. Amount varies with the weather.", -1, 0, 2, new WeatherHealAttr()),
|
||||||
new AttackMove(Moves.HIDDEN_POWER, "Hidden Power", Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, "Type and power depends on user's IVs.", -1, 0, 2),
|
new AttackMove(Moves.HIDDEN_POWER, "Hidden Power", Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, "Type and power depends on user's IVs.", -1, 0, 2),
|
||||||
new AttackMove(Moves.CROSS_CHOP, "Cross Chop", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, "High critical hit ratio.", -1, 0, 2, new HighCritAttr()),
|
new AttackMove(Moves.CROSS_CHOP, "Cross Chop", Type.FIGHTING, MoveCategory.PHYSICAL, 100, 80, 5, -1, "High critical hit ratio.", -1, 0, 2, new HighCritAttr()),
|
||||||
new AttackMove(Moves.TWISTER, "Twister", Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, -1, "May cause flinching. Hits Pokémon using Fly/Bounce with double power.", 20, 0, 2,
|
new AttackMove(Moves.TWISTER, "Twister", Type.DRAGON, MoveCategory.SPECIAL, 40, 100, 20, -1, "May cause flinching. Hits Pokémon using Fly/Bounce with double power.", 20, 0, 2,
|
||||||
new HitsTagAttr(BattleTagType.FLYING, true), new FlinchAttr()), // TODO
|
new HitsTagAttr(BattleTagType.FLYING, true), new FlinchAttr()), // TODO
|
||||||
new StatusMove(Moves.RAIN_DANCE, "Rain Dance", Type.WATER, -1, 5, 50, "Makes it rain for 5 turns.", -1, 0, 2),
|
new StatusMove(Moves.RAIN_DANCE, "Rain Dance", Type.WATER, -1, 5, 50, "Makes it rain for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.RAIN), new WeatherChangeAttr(WeatherType.RAIN)),
|
||||||
new StatusMove(Moves.SUNNY_DAY, "Sunny Day", Type.FIRE, -1, 5, 49, "Makes it sunny for 5 turns.", -1, 0, 2),
|
new StatusMove(Moves.SUNNY_DAY, "Sunny Day", Type.FIRE, -1, 5, 49, "Makes it sunny for 5 turns.", -1, 0, 2, new WeatherConditionalMoveAttr(WeatherType.SUNNY), new WeatherChangeAttr(WeatherType.SUNNY)),
|
||||||
new AttackMove(Moves.CRUNCH, "Crunch", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 108, "May lower opponent's Defense.", 20, 0, 2, new StatChangeAttr(BattleStat.DEF, -1)),
|
new AttackMove(Moves.CRUNCH, "Crunch", Type.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 108, "May lower opponent's Defense.", 20, 0, 2, new StatChangeAttr(BattleStat.DEF, -1)),
|
||||||
new AttackMove(Moves.MIRROR_COAT, "Mirror Coat", Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 20, -1, "When hit by a Special Attack, user strikes back with 2x power.", -1, -5, 2),
|
new AttackMove(Moves.MIRROR_COAT, "Mirror Coat", Type.PSYCHIC, MoveCategory.SPECIAL, -1, 100, 20, -1, "When hit by a Special Attack, user strikes back with 2x power.", -1, -5, 2),
|
||||||
new StatusMove(Moves.PSYCH_UP, "Psych Up", Type.NORMAL, -1, 10, -1, "Copies the opponent's stat changes.", -1, 0, 2),
|
new StatusMove(Moves.PSYCH_UP, "Psych Up", Type.NORMAL, -1, 10, -1, "Copies the opponent's stat changes.", -1, 0, 2),
|
||||||
|
@ -1300,7 +1466,7 @@ export const allMoves = [
|
||||||
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),
|
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),
|
||||||
new StatusMove(Moves.SWALLOW, "Swallow", Type.NORMAL, -1, 10, -1, "The more times the user has performed Stockpile, the more HP is recovered.", -1, 0, 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, 0, 3),
|
||||||
new AttackMove(Moves.HEAT_WAVE, "Heat Wave", Type.FIRE, MoveCategory.SPECIAL, 95, 90, 10, 118, "May burn opponent.", 10, 0, 3, new StatusEffectAttr(StatusEffect.BURN)),
|
new AttackMove(Moves.HEAT_WAVE, "Heat Wave", Type.FIRE, MoveCategory.SPECIAL, 95, 90, 10, 118, "May burn opponent.", 10, 0, 3, new StatusEffectAttr(StatusEffect.BURN)),
|
||||||
new StatusMove(Moves.HAIL, "Hail", Type.ICE, -1, 10, -1, "Non-Ice types are damaged for 5 turns.", -1, 0, 3),
|
new StatusMove(Moves.HAIL, "Hail", Type.ICE, -1, 10, -1, "Non-Ice types are damaged for 5 turns.", -1, 0, 3, new WeatherConditionalMoveAttr(WeatherType.HAIL), new WeatherChangeAttr(WeatherType.HAIL)),
|
||||||
new StatusMove(Moves.TORMENT, "Torment", Type.DARK, 100, 15, -1, "Opponent cannot use the same move in a row.", -1, 0, 3),
|
new StatusMove(Moves.TORMENT, "Torment", Type.DARK, 100, 15, -1, "Opponent cannot use the same move in a row.", -1, 0, 3),
|
||||||
new StatusMove(Moves.FLATTER, "Flatter", Type.DARK, 100, 15, -1, "Confuses opponent, but raises its Special Attack.", -1, 0, 3, new StatChangeAttr(BattleStat.SPATK, 1), new ConfuseAttr()),
|
new StatusMove(Moves.FLATTER, "Flatter", Type.DARK, 100, 15, -1, "Confuses opponent, but raises its Special Attack.", -1, 0, 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, 0, 3, new StatusEffectAttr(StatusEffect.BURN)),
|
new StatusMove(Moves.WILL_O_WISP, "Will-O-Wisp", Type.FIRE, 85, 15, 107, "Burns opponent.", -1, 0, 3, new StatusEffectAttr(StatusEffect.BURN)),
|
||||||
|
@ -1487,7 +1653,7 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.MIRROR_SHOT, "Mirror Shot", Type.STEEL, MoveCategory.SPECIAL, 65, 85, 10, -1, "May lower opponent's Accuracy.", 30, 0, 4, new StatChangeAttr(BattleStat.ACC, -1)),
|
new AttackMove(Moves.MIRROR_SHOT, "Mirror Shot", Type.STEEL, MoveCategory.SPECIAL, 65, 85, 10, -1, "May lower opponent's Accuracy.", 30, 0, 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, 0, 4, new StatChangeAttr(BattleStat.SPDEF, -1)),
|
new AttackMove(Moves.FLASH_CANNON, "Flash Cannon", Type.STEEL, MoveCategory.SPECIAL, 80, 100, 10, 93, "May lower opponent's Special Defense.", 10, 0, 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, 0, 4, new ConfuseAttr()),
|
new AttackMove(Moves.ROCK_CLIMB, "Rock Climb", Type.NORMAL, MoveCategory.PHYSICAL, 90, 85, 20, -1, "May confuse opponent.", 20, 0, 4, new ConfuseAttr()),
|
||||||
new StatusMove(Moves.DEFOG, "Defog", Type.FLYING, -1, 15, -1, "Lowers opponent's Evasiveness and clears fog.", -1, 0, 4, new StatChangeAttr(BattleStat.EVA, -1)), // TODO
|
new StatusMove(Moves.DEFOG, "Defog", Type.FLYING, -1, 15, -1, "Lowers opponent's Evasiveness and clears fog.", -1, 0, 4, new StatChangeAttr(BattleStat.EVA, -1), new ClearWeatherAttr(WeatherType.FOG)),
|
||||||
new StatusMove(Moves.TRICK_ROOM, "Trick Room", Type.PSYCHIC, -1, 5, 161, "Slower Pokémon move first in the turn for 5 turns.", -1, 0, 4),
|
new StatusMove(Moves.TRICK_ROOM, "Trick Room", Type.PSYCHIC, -1, 5, 161, "Slower Pokémon move first in the turn for 5 turns.", -1, 0, 4),
|
||||||
new AttackMove(Moves.DRACO_METEOR, "Draco Meteor", Type.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, 169, "Sharply lowers user's Special Attack.", 100, -7, 4, new StatChangeAttr(BattleStat.SPATK, -2, true)),
|
new AttackMove(Moves.DRACO_METEOR, "Draco Meteor", Type.DRAGON, MoveCategory.SPECIAL, 130, 90, 5, 169, "Sharply lowers user's Special Attack.", 100, -7, 4, new StatChangeAttr(BattleStat.SPATK, -2, true)),
|
||||||
new AttackMove(Moves.DISCHARGE, "Discharge", Type.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 15, -1, "May paralyze opponent.", 30, 0, 4, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
new AttackMove(Moves.DISCHARGE, "Discharge", Type.ELECTRIC, MoveCategory.SPECIAL, 80, 100, 15, -1, "May paralyze opponent.", 30, 0, 4, new StatusEffectAttr(StatusEffect.PARALYSIS)),
|
||||||
|
@ -1501,7 +1667,7 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.IRON_HEAD, "Iron Head", Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 15, 99, "May cause flinching.", 30, 0, 4, new FlinchAttr()),
|
new AttackMove(Moves.IRON_HEAD, "Iron Head", Type.STEEL, MoveCategory.PHYSICAL, 80, 100, 15, 99, "May cause flinching.", 30, 0, 4, new FlinchAttr()),
|
||||||
new AttackMove(Moves.MAGNET_BOMB, "Magnet Bomb", Type.STEEL, MoveCategory.PHYSICAL, 60, 999, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 0, 4),
|
new AttackMove(Moves.MAGNET_BOMB, "Magnet Bomb", Type.STEEL, MoveCategory.PHYSICAL, 60, 999, 20, -1, "Ignores Accuracy and Evasiveness.", -1, 0, 4),
|
||||||
new AttackMove(Moves.STONE_EDGE, "Stone Edge", Type.ROCK, MoveCategory.PHYSICAL, 100, 80, 5, 150, "High critical hit ratio.", -1, 0, 4, new HighCritAttr()),
|
new AttackMove(Moves.STONE_EDGE, "Stone Edge", Type.ROCK, MoveCategory.PHYSICAL, 100, 80, 5, 150, "High critical hit ratio.", -1, 0, 4, new HighCritAttr()),
|
||||||
new StatusMove(Moves.CAPTIVATE, "Captivate", Type.NORMAL, 100, 20, -1, "Sharply lowers opponent's Special Attack if opposite gender.", -1, 0, 4), // TODO XX
|
new StatusMove(Moves.CAPTIVATE, "Captivate", Type.NORMAL, 100, 20, -1, "Sharply lowers opponent's Special Attack if opposite gender.", -1, 0, 4), // TODO
|
||||||
new StatusMove(Moves.STEALTH_ROCK, "Stealth Rock", Type.ROCK, -1, 20, 116, "Damages opponent switching into battle.", -1, 0, 4),
|
new StatusMove(Moves.STEALTH_ROCK, "Stealth Rock", Type.ROCK, -1, 20, 116, "Damages opponent switching into battle.", -1, 0, 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, 0, 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, 0, 4),
|
||||||
new AttackMove(Moves.CHATTER, "Chatter", Type.FLYING, MoveCategory.SPECIAL, 65, 100, 20, -1, "Confuses opponent.", 100, 0, 4, new ConfuseAttr()),
|
new AttackMove(Moves.CHATTER, "Chatter", Type.FLYING, MoveCategory.SPECIAL, 65, 100, 20, -1, "Confuses opponent.", 100, 0, 4, new ConfuseAttr()),
|
||||||
|
@ -1513,7 +1679,7 @@ export const allMoves = [
|
||||||
new AttackMove(Moves.ATTACK_ORDER, "Attack Order", Type.BUG, MoveCategory.PHYSICAL, 90, 100, 15, -1, "High critical hit ratio.", -1, 0, 4, new HighCritAttr()),
|
new AttackMove(Moves.ATTACK_ORDER, "Attack Order", Type.BUG, MoveCategory.PHYSICAL, 90, 100, 15, -1, "High critical hit ratio.", -1, 0, 4, new HighCritAttr()),
|
||||||
new StatusMove(Moves.DEFEND_ORDER, "Defend Order", Type.BUG, -1, 10, -1, "Raises user's Defense and Special Defense.", -1, 0, 4,
|
new StatusMove(Moves.DEFEND_ORDER, "Defend Order", Type.BUG, -1, 10, -1, "Raises user's Defense and Special Defense.", -1, 0, 4,
|
||||||
new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], 1, true)),
|
new StatChangeAttr([ BattleStat.DEF, BattleStat.SPDEF ], 1, true)),
|
||||||
new StatusMove(Moves.HEAL_ORDER, "Heal Order", Type.BUG, -1, 10, -1, "User recovers half its max HP.", -1, 0, 4, new HealAttr()),
|
new StatusMove(Moves.HEAL_ORDER, "Heal Order", Type.BUG, -1, 10, -1, "User recovers half its max HP.", -1, 0, 4, new HealAttr(0.5)),
|
||||||
new AttackMove(Moves.HEAD_SMASH, "Head Smash", Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, "User receives recoil damage.", -1, 0, 4),
|
new AttackMove(Moves.HEAD_SMASH, "Head Smash", Type.ROCK, MoveCategory.PHYSICAL, 150, 80, 5, -1, "User receives recoil damage.", -1, 0, 4),
|
||||||
new AttackMove(Moves.DOUBLE_HIT, "Double Hit", Type.NORMAL, MoveCategory.PHYSICAL, 35, 90, 10, -1, "Hits twice in one turn.", -1, 0, 4, new MultiHitAttr(MultiHitType._2)),
|
new AttackMove(Moves.DOUBLE_HIT, "Double Hit", Type.NORMAL, MoveCategory.PHYSICAL, 35, 90, 10, -1, "Hits twice in one turn.", -1, 0, 4, new MultiHitAttr(MultiHitType._2)),
|
||||||
new AttackMove(Moves.ROAR_OF_TIME, "Roar of Time", Type.DRAGON, MoveCategory.SPECIAL, 150, 90, 5, -1, "User must recharge next turn.", -1, 0, 4),
|
new AttackMove(Moves.ROAR_OF_TIME, "Roar of Time", Type.DRAGON, MoveCategory.SPECIAL, 150, 90, 5, -1, "User must recharge next turn.", -1, 0, 4),
|
||||||
|
@ -1608,7 +1774,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, 0, 5, new StatChangeAttr(BattleStat.ACC, -1)),
|
new AttackMove(Moves.NIGHT_DAZE, "Night Daze", Type.DARK, MoveCategory.SPECIAL, 85, 95, 10, -1, "May lower opponent's Accuracy.", 40, 0, 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, 0, 5),
|
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, 0, 5),
|
||||||
new AttackMove(Moves.TAIL_SLAP, "Tail Slap", Type.NORMAL, MoveCategory.PHYSICAL, 25, 85, 10, -1, "Hits 2-5 times in one turn.", -1, 0, 5, new MultiHitAttr()),
|
new AttackMove(Moves.TAIL_SLAP, "Tail Slap", Type.NORMAL, MoveCategory.PHYSICAL, 25, 85, 10, -1, "Hits 2-5 times in one turn.", -1, 0, 5, new MultiHitAttr()),
|
||||||
new AttackMove(Moves.HURRICANE, "Hurricane", Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 160, "May confuse opponent.", 30, 0, 5, new ConfuseAttr()),
|
new AttackMove(Moves.HURRICANE, "Hurricane", Type.FLYING, MoveCategory.SPECIAL, 110, 70, 10, 160, "May confuse opponent.", 30, 0, 5, new ThunderAccuracyAttr(), new ConfuseAttr()),
|
||||||
new AttackMove(Moves.HEAD_CHARGE, "Head Charge", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, "User receives recoil damage.", -1, 0, 5),
|
new AttackMove(Moves.HEAD_CHARGE, "Head Charge", Type.NORMAL, MoveCategory.PHYSICAL, 120, 100, 15, -1, "User receives recoil damage.", -1, 0, 5),
|
||||||
new AttackMove(Moves.GEAR_GRIND, "Gear Grind", Type.STEEL, MoveCategory.PHYSICAL, 50, 85, 15, -1, "Hits twice in one turn.", -1, 0, 5, new MultiHitAttr(MultiHitType._2)),
|
new AttackMove(Moves.GEAR_GRIND, "Gear Grind", Type.STEEL, MoveCategory.PHYSICAL, 50, 85, 15, -1, "Hits twice in one turn.", -1, 0, 5, new MultiHitAttr(MultiHitType._2)),
|
||||||
new AttackMove(Moves.SEARING_SHOT, "Searing Shot", Type.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, "May burn opponent.", 30, 0, 5, new StatusEffectAttr(StatusEffect.BURN)),
|
new AttackMove(Moves.SEARING_SHOT, "Searing Shot", Type.FIRE, MoveCategory.SPECIAL, 100, 100, 5, -1, "May burn opponent.", 30, 0, 5, new StatusEffectAttr(StatusEffect.BURN)),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Phaser from 'phaser';
|
import Phaser from 'phaser';
|
||||||
import BattleScene from './battle-scene';
|
import BattleScene from './battle-scene';
|
||||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './battle-info';
|
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './battle-info';
|
||||||
import { default as Move, allMoves, MoveCategory, Moves, StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr } from './move';
|
import Move, { StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory } from "./move";
|
||||||
import { pokemonLevelMoves } from './pokemon-level-moves';
|
import { pokemonLevelMoves } from './pokemon-level-moves';
|
||||||
import { default as PokemonSpecies, getPokemonSpecies } from './pokemon-species';
|
import { default as PokemonSpecies, getPokemonSpecies } from './pokemon-species';
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils';
|
||||||
|
@ -19,6 +19,7 @@ import { DamagePhase, FaintPhase, MessagePhase } from './battle-phases';
|
||||||
import { BattleStat } from './battle-stat';
|
import { BattleStat } from './battle-stat';
|
||||||
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag';
|
import { BattleTag, BattleTagLapseType, BattleTagType, getBattleTag } from './battle-tag';
|
||||||
import { Species } from './species';
|
import { Species } from './species';
|
||||||
|
import { WeatherType } from './weather';
|
||||||
|
|
||||||
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
public id: integer;
|
public id: integer;
|
||||||
|
@ -246,11 +247,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
this.getZoomSprite().play(this.getBattleSpriteKey());
|
this.getZoomSprite().play(this.getBattleSpriteKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
getBattleStat(stat: Stat) {
|
getBattleStat(stat: Stat): integer {
|
||||||
if (stat === Stat.HP)
|
if (stat === Stat.HP)
|
||||||
return this.stats[Stat.HP];
|
return this.stats[Stat.HP];
|
||||||
const statLevel = this.summonData.battleStats[(stat + 1) as BattleStat];
|
const statLevel = this.summonData.battleStats[(stat - 1) as BattleStat];
|
||||||
let ret = this.stats[stat] * (Math.max(2, 2 + statLevel) / Math.max(2, 2 - statLevel));
|
let ret = this.stats[stat] * (Math.max(2, 2 + statLevel) / Math.max(2, 2 - statLevel));
|
||||||
|
if (stat === Stat.SPDEF && this.scene.arena.weather?.weatherType === WeatherType.SANDSTORM)
|
||||||
|
ret *= 1.5;
|
||||||
if (this.status && this.status.effect === StatusEffect.PARALYSIS)
|
if (this.status && this.status.effect === StatusEffect.PARALYSIS)
|
||||||
ret >>= 2;
|
ret >>= 2;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -429,7 +432,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
|
|
||||||
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
|
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
|
||||||
let result: MoveResult;
|
let result: MoveResult;
|
||||||
let success = false;
|
|
||||||
const move = battlerMove.getMove();
|
const move = battlerMove.getMove();
|
||||||
const moveCategory = move.category;
|
const moveCategory = move.category;
|
||||||
let damage = 0;
|
let damage = 0;
|
||||||
|
@ -439,6 +441,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
|
||||||
const power = new Utils.NumberHolder(move.power);
|
const power = new Utils.NumberHolder(move.power);
|
||||||
const typeMultiplier = getTypeDamageMultiplier(move.type, this.species.type1) * (this.species.type2 > -1 ? getTypeDamageMultiplier(move.type, this.species.type2) : 1);
|
const typeMultiplier = getTypeDamageMultiplier(move.type, this.species.type1) * (this.species.type2 > -1 ? getTypeDamageMultiplier(move.type, this.species.type2) : 1);
|
||||||
|
const weatherTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type);
|
||||||
|
applyMoveAttrs(VariablePowerAttr, source, this, move, power);
|
||||||
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
|
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
|
||||||
const critChance = new Utils.IntegerHolder(16);
|
const critChance = new Utils.IntegerHolder(16);
|
||||||
applyMoveAttrs(HighCritAttr, source, this, move, critChance);
|
applyMoveAttrs(HighCritAttr, source, this, move, critChance);
|
||||||
|
@ -447,7 +451,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
|
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 stabMultiplier = source.species.type1 === move.type || (source.species.type2 > -1 && source.species.type2 === move.type) ? 1.5 : 1;
|
||||||
const criticalMultiplier = isCritical ? 2 : 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;
|
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * weatherTypeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
|
||||||
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
|
||||||
damage = Math.floor(damage / 2);
|
damage = Math.floor(damage / 2);
|
||||||
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
move.getAttrs(HitsTagAttr).map(hta => hta as HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
|
||||||
|
@ -484,26 +488,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case MoveResult.EFFECTIVE:
|
|
||||||
success = true;
|
|
||||||
break;
|
|
||||||
case MoveResult.SUPER_EFFECTIVE:
|
case MoveResult.SUPER_EFFECTIVE:
|
||||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s super effective!'));
|
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s super effective!'));
|
||||||
success = true;
|
|
||||||
break;
|
break;
|
||||||
case MoveResult.NOT_VERY_EFFECTIVE:
|
case MoveResult.NOT_VERY_EFFECTIVE:
|
||||||
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s not very effective!'))
|
this.scene.unshiftPhase(new MessagePhase(this.scene, 'It\'s not very effective!'));
|
||||||
success = true;
|
|
||||||
break;
|
break;
|
||||||
case MoveResult.NO_EFFECT:
|
case MoveResult.NO_EFFECT:
|
||||||
this.scene.unshiftPhase(new MessagePhase(this.scene, `It doesn\'t affect ${this.name}!`))
|
this.scene.unshiftPhase(new MessagePhase(this.scene, `It doesn\'t affect ${this.name}!`));
|
||||||
success = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MoveCategory.STATUS:
|
case MoveCategory.STATUS:
|
||||||
result = MoveResult.STATUS;
|
result = MoveResult.STATUS;
|
||||||
success = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
162
src/weather.ts
162
src/weather.ts
|
@ -1,4 +1,8 @@
|
||||||
import { Biome } from "./biome";
|
import { Biome } from "./biome";
|
||||||
|
import { getPokemonMessage } from "./messages";
|
||||||
|
import Pokemon from "./pokemon";
|
||||||
|
import { Type } from "./type";
|
||||||
|
import Move, { AttackMove } from "./move";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
|
|
||||||
export enum WeatherType {
|
export enum WeatherType {
|
||||||
|
@ -28,6 +32,148 @@ export class Weather {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isImmutable(): boolean {
|
||||||
|
switch (this.weatherType) {
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
case WeatherType.STRONG_WINDS:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDamaging(): boolean {
|
||||||
|
switch (this.weatherType) {
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
isTypeDamageImmune(type: Type): boolean {
|
||||||
|
switch (this.weatherType) {
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
return type === Type.GROUND || type === Type.ROCK || type === Type.STEEL;
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
return type === Type.ICE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAttackTypeMultiplier(attackType: Type): number {
|
||||||
|
switch (this.weatherType) {
|
||||||
|
case WeatherType.SUNNY:
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
if (attackType === Type.FIRE)
|
||||||
|
return 1.5;
|
||||||
|
if (attackType === Type.WATER)
|
||||||
|
return 0.5;
|
||||||
|
break;
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
if (attackType === Type.FIRE)
|
||||||
|
return 0.5;
|
||||||
|
if (attackType === Type.WATER)
|
||||||
|
return 1.5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMoveWeatherCancelled(move: Move): boolean {
|
||||||
|
switch (this.weatherType) {
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
return move instanceof AttackMove && move.type === Type.WATER;
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
return move instanceof AttackMove && move.type === Type.FIRE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeatherStartMessage(weatherType: WeatherType) {
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.SUNNY:
|
||||||
|
return 'The sunlight got bright!';
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
return 'A downpour started!';
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
return 'A sandstorm brewed!';
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
return 'It started to hail!';
|
||||||
|
case WeatherType.FOG:
|
||||||
|
return 'A thick fog emerged!'
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
return 'A heavy downpour started!'
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
return 'The sunlight got hot!'
|
||||||
|
case WeatherType.STRONG_WINDS:
|
||||||
|
return 'A heavy wind began!';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeatherLapseMessage(weatherType: WeatherType) {
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.SUNNY:
|
||||||
|
return 'The sunlight is strong.';
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
return 'The downpour continues.';
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
return 'The sandstorm rages.';
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
return 'Hail continues to fall.';
|
||||||
|
case WeatherType.FOG:
|
||||||
|
return 'The fog continues.';
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
return 'The heavy downpour continues.'
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
return 'The sun is scorching hot.'
|
||||||
|
case WeatherType.STRONG_WINDS:
|
||||||
|
return 'The wind blows intensely.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon) {
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
return getPokemonMessage(pokemon, ' is buffeted\nby the sandstorm!');
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
return getPokemonMessage(pokemon, ' is pelted\nby the hail!');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWeatherClearMessage(weatherType: WeatherType) {
|
||||||
|
switch (weatherType) {
|
||||||
|
case WeatherType.SUNNY:
|
||||||
|
return 'The sunlight faded.';
|
||||||
|
case WeatherType.RAIN:
|
||||||
|
return 'The rain stopped.';
|
||||||
|
case WeatherType.SANDSTORM:
|
||||||
|
return 'The sandstorm subsided.';
|
||||||
|
case WeatherType.HAIL:
|
||||||
|
return 'The hail stopped.';
|
||||||
|
case WeatherType.FOG:
|
||||||
|
return 'The fog disappeared.'
|
||||||
|
case WeatherType.HEAVY_RAIN:
|
||||||
|
return 'The heavy rain stopped.'
|
||||||
|
case WeatherType.HARSH_SUN:
|
||||||
|
return 'The harsh sunlight faded.'
|
||||||
|
case WeatherType.STRONG_WINDS:
|
||||||
|
return 'The heavy wind stopped.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WeatherPoolEntry {
|
interface WeatherPoolEntry {
|
||||||
|
@ -35,7 +181,7 @@ interface WeatherPoolEntry {
|
||||||
weight: integer;
|
weight: integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRandomWeather(biome: Biome): Weather {
|
export function getRandomWeatherType(biome: Biome): WeatherType {
|
||||||
let weatherPool: WeatherPoolEntry[] = [];
|
let weatherPool: WeatherPoolEntry[] = [];
|
||||||
switch (biome) {
|
switch (biome) {
|
||||||
case Biome.GRASS:
|
case Biome.GRASS:
|
||||||
|
@ -46,11 +192,9 @@ export function getRandomWeather(biome: Biome): Weather {
|
||||||
break;
|
break;
|
||||||
case Biome.TALL_GRASS:
|
case Biome.TALL_GRASS:
|
||||||
weatherPool = [
|
weatherPool = [
|
||||||
{ weatherType: WeatherType.NONE, weight: 2 },
|
{ weatherType: WeatherType.SUNNY, weight: 8 },
|
||||||
{ weatherType: WeatherType.SUNNY, weight: 6 },
|
{ weatherType: WeatherType.RAIN, weight: 5 },
|
||||||
{ weatherType: WeatherType.RAIN, weight: 4 },
|
{ weatherType: WeatherType.FOG, weight: 2 }
|
||||||
{ weatherType: WeatherType.FOG, weight: 2 },
|
|
||||||
{ weatherType: WeatherType.HAIL, weight: 1 }
|
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case Biome.FOREST:
|
case Biome.FOREST:
|
||||||
|
@ -161,11 +305,11 @@ export function getRandomWeather(biome: Biome): Weather {
|
||||||
for (let weather of weatherPool) {
|
for (let weather of weatherPool) {
|
||||||
w += weather.weight;
|
w += weather.weight;
|
||||||
if (rand < w)
|
if (rand < w)
|
||||||
return new Weather(weather.weatherType);
|
return weather.weatherType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return weatherPool.length
|
return weatherPool.length
|
||||||
? new Weather(weatherPool[0].weatherType)
|
? weatherPool[0].weatherType
|
||||||
: null;
|
: WeatherType.NONE;
|
||||||
}
|
}
|
Loading…
Reference in New Issue