Refactor move attribute logic and add arena trap moves

pull/1/head
Flashfyre 2023-04-24 14:30:21 -04:00
parent 77b3f136dd
commit 92c8583129
9 changed files with 1222 additions and 615 deletions

View File

@ -8,7 +8,7 @@ import { Weather, WeatherType, getWeatherClearMessage, getWeatherStartMessage }
import { CommonAnimPhase } from "./battle-phases";
import { CommonAnim } from "./data/battle-anims";
import { Type } from "./data/type";
import Move from "./data/move";
import Move, { Moves } from "./data/move";
import { ArenaTag, ArenaTagType, getArenaTag } from "./data/arena-tag";
export class Arena {
@ -224,14 +224,14 @@ export class Arena {
tags.forEach(t => t.apply(args));
}
addTag(tagType: ArenaTagType, turnCount: integer): boolean {
addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer): boolean {
const existingTag = this.getTag(tagType);
if (existingTag) {
existingTag.onOverlap(this);
return false;
}
const newTag = getArenaTag(tagType, turnCount || 0);
const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId);
this.tags.push(newTag);
newTag.onAdd(this);

View File

@ -1,7 +1,7 @@
import BattleScene, { startingLevel, startingWave } from "./battle-scene";
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
import * as Utils from './utils';
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, ConditionalMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveFlags, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr } from "./data/move";
import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveFlags, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr, VariableAccuracyAttr } from "./data/move";
import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat";
@ -24,6 +24,7 @@ import { Starter } from "./ui/starter-select-ui-handler";
import { Gender } from "./data/gender";
import { Weather, WeatherType, getRandomWeatherType, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather";
import { TempBattleStat } from "./data/temp-battle-stat";
import { ArenaTrapTag, TrickRoomTag } from "./data/arena-tag";
export class SelectStarterPhase extends BattlePhase {
constructor(scene: BattleScene) {
@ -117,6 +118,8 @@ export class EncounterPhase extends BattlePhase {
end() {
if (this.scene.getEnemyPokemon().shiny)
this.scene.unshiftPhase(new ShinySparklePhase(this.scene, false));
this.scene.arena.applyTags(ArenaTrapTag, this.scene.getEnemyPokemon());
// TODO: Remove
//this.scene.unshiftPhase(new SelectModifierPhase(this.scene));
@ -329,6 +332,7 @@ export class SummonPhase extends BattlePhase {
playerPokemon.getSprite().clearTint();
playerPokemon.resetSummonData();
this.scene.time.delayedCall(1000, () => this.end());
this.scene.arena.applyTags(ArenaTrapTag, playerPokemon);
}
});
}
@ -504,8 +508,11 @@ export class CommandPhase extends BattlePhase {
case Command.RUN:
return true;
}
const speedDelayed = new Utils.BooleanHolder(playerSpeed < enemySpeed);
this.scene.arena.applyTags(TrickRoomTag, speedDelayed);
return playerSpeed < enemySpeed || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
return speedDelayed.value || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
};
let playerMove: PokemonMove;
@ -633,7 +640,10 @@ export class TurnEndPhase extends BattlePhase {
const playerSpeed = playerPokemon?.getBattleStat(Stat.SPD) || 0;
const enemySpeed = enemyPokemon?.getBattleStat(Stat.SPD) || 0;
const isDelayed = playerSpeed < enemySpeed || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
const speedDelayed = new Utils.BooleanHolder(playerSpeed < enemySpeed);
this.scene.arena.applyTags(TrickRoomTag, speedDelayed);
const isDelayed = speedDelayed.value || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
if (!isDelayed)
handlePokemon(playerPokemon);
@ -767,15 +777,15 @@ export abstract class MovePhase extends BattlePhase {
if (!moveQueue.length || !moveQueue.shift().ignorePP)
this.move.ppUsed++;
const failed = new Utils.BooleanHolder(false);
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) {
let success = this.move.getMove().applyConditions(this.pokemon, target, this.move.getMove());
if (success && this.scene.arena.isMoveWeatherCancelled(this.move.getMove()))
success = false;
if (success)
this.scene.unshiftPhase(this.getEffectPhase());
else {
this.pokemon.getMoveHistory().push({ move: this.move.moveId, result: MoveResult.FAILED, virtual: this.move.virtual });
this.scene.queueMessage('But it failed!');
} else
this.scene.unshiftPhase(this.getEffectPhase());
}
this.end();
};
@ -1175,12 +1185,14 @@ export class WeatherEffectPhase extends CommonAnimPhase {
export class ObtainStatusEffectPhase extends PokemonPhase {
private statusEffect: StatusEffect;
private cureTurn: integer;
private sourceText: string;
constructor(scene: BattleScene, player: boolean, statusEffect: StatusEffect, cureTurn?: integer) {
constructor(scene: BattleScene, player: boolean, statusEffect: StatusEffect, cureTurn?: integer, sourceText?: string) {
super(scene, player);
this.statusEffect = statusEffect;
this.cureTurn = cureTurn;
this.sourceText = sourceText;
}
start() {
@ -1191,7 +1203,7 @@ export class ObtainStatusEffectPhase extends PokemonPhase {
pokemon.status.cureTurn = this.cureTurn;
pokemon.updateInfo(true);
new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => {
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect)));
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectObtainText(this.statusEffect, this.sourceText)));
if (pokemon.status.isPostTurn())
this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.player));
this.end();

View File

@ -1,20 +1,34 @@
import { Arena } from "../arena";
import { Type } from "./type";
import * as Utils from "../utils";
import { Moves, allMoves } from "./move";
import { getPokemonMessage } from "../messages";
import Pokemon, { DamageResult, MoveResult } from "../pokemon";
import { DamagePhase, ObtainStatusEffectPhase } from "../battle-phases";
import { StatusEffect } from "./status-effect";
import { BattlerTagType } from "./battler-tag";
export enum ArenaTagType {
NONE,
MUD_SPORT,
WATER_SPORT
WATER_SPORT,
SPIKES,
TOXIC_SPIKES,
STEALTH_ROCK,
TRICK_ROOM
}
export class ArenaTag {
export abstract class ArenaTag {
public tagType: ArenaTagType;
public turnCount: integer;
public sourceMove: Moves;
public sourceId: integer;
constructor(tagType: ArenaTagType, turnCount: integer) {
constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer) {
this.tagType = tagType;
this.turnCount = turnCount;
this.sourceMove = sourceMove;
this.sourceId = sourceId;
}
apply(args: any[]): boolean {
@ -23,20 +37,28 @@ export class ArenaTag {
onAdd(arena: Arena): void { }
onRemove(arena: Arena): void { }
onRemove(arena: Arena): void {
arena.scene.queueMessage(`${this.getMoveName()}\'s effect wore off.`);
}
onOverlap(arena: Arena): void { }
lapse(arena: Arena): boolean {
return --this.turnCount > 0;
return this.turnCount < 1 || !!(--this.turnCount);
}
getMoveName(): string {
return this.sourceMove
? allMoves[this.sourceMove].name
: null;
}
}
export class WeakenTypeTag extends ArenaTag {
export class WeakenMoveTypeTag extends ArenaTag {
private weakenedType: Type;
constructor(tagType: ArenaTagType, turnCount: integer, type: Type) {
super(tagType, turnCount);
constructor(tagType: ArenaTagType, turnCount: integer, type: Type, sourceMove: Moves, sourceId: integer) {
super(tagType, turnCount, sourceMove, sourceId);
this.weakenedType = type;
}
@ -51,39 +73,189 @@ export class WeakenTypeTag extends ArenaTag {
}
}
class MudSportTag extends WeakenTypeTag {
constructor(turnCount: integer) {
super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC);
class MudSportTag extends WeakenMoveTypeTag {
constructor(turnCount: integer, sourceId: integer) {
super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC, Moves.MUD_SPORT, sourceId);
}
onAdd(arena: Arena): void {
arena.scene.queueMessage('Electricity\'s power was weakened!');
}
onRemove(arena: Arena): void {
arena.scene.queueMessage('MUD SPORT\'s effect wore off.');
}
}
class WaterSportTag extends WeakenTypeTag {
constructor(turnCount: integer) {
super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE);
class WaterSportTag extends WeakenMoveTypeTag {
constructor(turnCount: integer, sourceId: integer) {
super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE, Moves.WATER_SPORT, sourceId);
}
onAdd(arena: Arena): void {
arena.scene.queueMessage('Fire\'s power was weakened!');
}
}
onRemove(arena: Arena): void {
arena.scene.queueMessage('WATER SPORT\'s effect wore off.');
export class ArenaTrapTag extends ArenaTag {
public layers: integer;
public maxLayers: integer;
constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, maxLayers: integer) {
super(tagType, 0, sourceMove, sourceId);
this.layers = 1;
this.maxLayers = maxLayers;
}
onOverlap(arena: Arena): void {
if (this.layers < this.maxLayers) {
this.layers++;
this.onAdd(arena);
}
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
if (this.sourceId === pokemon.id || pokemon.scene.getPokemonById(this.sourceId).isPlayer() === pokemon.isPlayer())
return false;
return this.activateTrap(pokemon);
}
activateTrap(pokemon: Pokemon): boolean {
return false;
}
}
export function getArenaTag(tagType: ArenaTagType, turnCount: integer): ArenaTag {
class SpikesTag extends ArenaTrapTag {
constructor(sourceId: integer) {
super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, 3);
}
onAdd(arena: Arena): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
const target = source.isPlayer() ? source.scene.getEnemyPokemon() : source.scene.getPlayerPokemon();
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`);
}
activateTrap(pokemon: Pokemon): boolean {
if ((!pokemon.isOfType(Type.FLYING) || pokemon.getTag(BattlerTagType.IGNORE_FLYING))) {
const damageHpRatio = 1 / (10 - 2 * this.layers);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is hurt\nby the spikes!'));
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), MoveResult.OTHER));
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
return true;
}
return false;
}
}
class ToxicSpikesTag extends ArenaTrapTag {
constructor(sourceId: integer) {
super(ArenaTagType.TOXIC_SPIKES, Moves.TOXIC_SPIKES, sourceId, 2);
}
onAdd(arena: Arena): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
const target = source.isPlayer() ? source.scene.getEnemyPokemon() : source.scene.getPlayerPokemon();
arena.scene.queueMessage(`${this.getMoveName()} were scattered\nall around ${target.name}'s feet!`);
}
activateTrap(pokemon: Pokemon): boolean {
if (!pokemon.status && (!pokemon.isOfType(Type.FLYING) || pokemon.getTag(BattlerTagType.IGNORE_FLYING))) {
const toxic = this.layers > 1;
pokemon.scene.unshiftPhase(new ObtainStatusEffectPhase(pokemon.scene, pokemon.isPlayer(),
!toxic ? StatusEffect.POISON : StatusEffect.TOXIC, null, `the ${this.getMoveName()}`));
return true;
}
return false;
}
}
class StealthRockTag extends ArenaTrapTag {
constructor(sourceId: integer) {
super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, 1);
}
onAdd(arena: Arena): void {
super.onAdd(arena);
const source = arena.scene.getPokemonById(this.sourceId);
const target = source.isPlayer() ? source.scene.getEnemyPokemon() : source.scene.getPlayerPokemon();
arena.scene.queueMessage(`Pointed stones float in the air\naround ${target.name}!`);
}
activateTrap(pokemon: Pokemon): boolean {
const effectiveness = pokemon.getAttackMoveEffectiveness(Type.ROCK);
let damageHpRatio: number;
switch (effectiveness) {
case 0:
damageHpRatio = 0;
break;
case 0.25:
damageHpRatio = 0.03125;
break;
case 0.5:
damageHpRatio = 0.0625;
break;
case 1:
damageHpRatio = 0.125;
break;
case 2:
damageHpRatio = 0.25;
break;
case 4:
damageHpRatio = 0.5;
break;
}
if (damageHpRatio) {
pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`);
pokemon.scene.unshiftPhase(new DamagePhase(pokemon.scene, pokemon.isPlayer(), MoveResult.OTHER));
pokemon.damage(Math.ceil(pokemon.getMaxHp() * damageHpRatio));
}
return false;
}
}
export class TrickRoomTag extends ArenaTag {
constructor(turnCount: integer, sourceId: integer) {
super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId);
}
apply(args: any[]): boolean {
const speedDelayed = args[0] as Utils.BooleanHolder;
speedDelayed.value = !speedDelayed.value;
return true;
}
onAdd(arena: Arena): void {
arena.scene.queueMessage(getPokemonMessage(arena.scene.getPokemonById(this.sourceId), ' twisted\nthe dimensions!'));
}
}
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer): ArenaTag {
switch (tagType) {
case ArenaTagType.MUD_SPORT:
return new MudSportTag(turnCount);
return new MudSportTag(turnCount, sourceId);
case ArenaTagType.WATER_SPORT:
return new WaterSportTag(turnCount);
return new WaterSportTag(turnCount, sourceId);
case ArenaTagType.SPIKES:
return new SpikesTag(sourceId);
case ArenaTagType.TOXIC_SPIKES:
return new ToxicSpikesTag(sourceId);
case ArenaTagType.STEALTH_ROCK:
return new StealthRockTag(sourceId);
case ArenaTagType.TRICK_ROOM:
return new TrickRoomTag(turnCount, sourceId);
}
}

View File

@ -48,15 +48,15 @@ export class BattlerTag {
public tagType: BattlerTagType;
public lapseType: BattlerTagLapseType;
public turnCount: integer;
public sourceId: integer;
public sourceMove: Moves;
public sourceId?: integer;
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceId?: integer, sourceMove?: Moves) {
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceMove: Moves, sourceId?: integer) {
this.tagType = tagType;
this.lapseType = lapseType;
this.turnCount = turnCount;
this.sourceId = sourceId;
this.sourceMove = sourceMove;
this.sourceId = sourceId;
}
canAdd(pokemon: Pokemon): boolean {
@ -73,6 +73,10 @@ export class BattlerTag {
return --this.turnCount > 0;
}
isSourceLinked(): boolean {
return false;
}
getMoveName(): string {
return this.sourceMove
? allMoves[this.sourceMove].name
@ -81,8 +85,8 @@ export class BattlerTag {
}
export class RechargingTag extends BattlerTag {
constructor() {
super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1);
constructor(sourceMove: Moves) {
super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1, sourceMove);
}
onAdd(pokemon: Pokemon): void {
@ -102,8 +106,8 @@ export class RechargingTag extends BattlerTag {
}
export class TrappedTag extends BattlerTag {
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceId: integer, sourceMove: Moves) {
super(tagType, lapseType, turnCount, sourceId, sourceMove);
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceMove: Moves, sourceId: integer) {
super(tagType, lapseType, turnCount, sourceMove, sourceId);
}
canAdd(pokemon: Pokemon): boolean {
@ -122,14 +126,18 @@ export class TrappedTag extends BattlerTag {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getMoveName()}!`));
}
isSourceLinked(): boolean {
return true;
}
getTrapMessage(pokemon: Pokemon): string {
return getPokemonMessage(pokemon, ' can no\nlonger escape!');
}
}
export class FlinchedTag extends BattlerTag {
constructor() {
super(BattlerTagType.FLINCHED, BattlerTagLapseType.MOVE, 0);
constructor(sourceMove: Moves) {
super(BattlerTagType.FLINCHED, BattlerTagLapseType.MOVE, 0, sourceMove);
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -143,8 +151,8 @@ export class FlinchedTag extends BattlerTag {
}
export class ConfusedTag extends BattlerTag {
constructor(turnCount: integer) {
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount);
constructor(turnCount: integer, sourceMove: Moves) {
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove);
}
onAdd(pokemon: Pokemon): void {
@ -190,7 +198,7 @@ export class ConfusedTag extends BattlerTag {
export class SeedTag extends BattlerTag {
constructor() {
super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1);
super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1, Moves.LEECH_SEED);
}
onAdd(pokemon: Pokemon): void {
@ -217,7 +225,7 @@ export class SeedTag extends BattlerTag {
export class NightmareTag extends BattlerTag {
constructor() {
super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1);
super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1, Moves.NIGHTMARE);
}
onAdd(pokemon: Pokemon): void {
@ -250,7 +258,7 @@ export class NightmareTag extends BattlerTag {
export class IngrainTag extends TrappedTag {
constructor(sourceId: integer) {
super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1, sourceId, Moves.INGRAIN);
super(BattlerTagType.INGRAIN, BattlerTagLapseType.TURN_END, 1, Moves.INGRAIN, sourceId);
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -270,7 +278,7 @@ export class IngrainTag extends TrappedTag {
export class AquaRingTag extends BattlerTag {
constructor() {
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, undefined, Moves.AQUA_RING);
super(BattlerTagType.AQUA_RING, BattlerTagLapseType.TURN_END, 1, Moves.AQUA_RING, undefined);
}
onAdd(pokemon: Pokemon): void {
@ -291,7 +299,7 @@ export class AquaRingTag extends BattlerTag {
export class DrowsyTag extends BattlerTag {
constructor() {
super(BattlerTagType.DROWSY, BattlerTagLapseType.TURN_END, 2);
super(BattlerTagType.DROWSY, BattlerTagLapseType.TURN_END, 2, Moves.YAWN);
}
onAdd(pokemon: Pokemon): void {
@ -313,8 +321,8 @@ export class DrowsyTag extends BattlerTag {
export abstract class DamagingTrapTag extends TrappedTag {
private commonAnim: CommonAnim;
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceId: integer, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceId, sourceMove);
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceMove: Moves, sourceId: integer) {
super(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove, sourceId);
this.commonAnim = commonAnim;
}
@ -341,7 +349,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
export class BindTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.BIND, CommonAnim.BIND, turnCount, sourceId, Moves.BIND);
super(BattlerTagType.BIND, CommonAnim.BIND, turnCount, Moves.BIND, sourceId);
}
getTrapMessage(pokemon: Pokemon): string {
@ -351,7 +359,7 @@ export class BindTag extends DamagingTrapTag {
export class WrapTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.WRAP, CommonAnim.WRAP, turnCount, sourceId, Moves.WRAP);
super(BattlerTagType.WRAP, CommonAnim.WRAP, turnCount, Moves.WRAP, sourceId);
}
getTrapMessage(pokemon: Pokemon): string {
@ -360,8 +368,8 @@ export class WrapTag extends DamagingTrapTag {
}
export abstract class VortexTrapTag extends DamagingTrapTag {
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceId: integer, sourceMove: Moves) {
super(tagType, commonAnim, turnCount, sourceId, sourceMove);
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceMove: Moves, sourceId: integer) {
super(tagType, commonAnim, turnCount, sourceMove, sourceId);
}
getTrapMessage(pokemon: Pokemon): string {
@ -371,19 +379,19 @@ export abstract class VortexTrapTag extends DamagingTrapTag {
export class FireSpinTag extends VortexTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.FIRE_SPIN, CommonAnim.FIRE_SPIN, turnCount, sourceId, Moves.FIRE_SPIN);
super(BattlerTagType.FIRE_SPIN, CommonAnim.FIRE_SPIN, turnCount, Moves.FIRE_SPIN, sourceId);
}
}
export class WhirlpoolTag extends VortexTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.WHIRLPOOL, CommonAnim.WHIRLPOOL, turnCount, sourceId, Moves.WHIRLPOOL);
super(BattlerTagType.WHIRLPOOL, CommonAnim.WHIRLPOOL, turnCount, Moves.WHIRLPOOL, sourceId);
}
}
export class ClampTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.CLAMP, CommonAnim.CLAMP, turnCount, sourceId, Moves.CLAMP);
super(BattlerTagType.CLAMP, CommonAnim.CLAMP, turnCount, Moves.CLAMP, sourceId);
}
getTrapMessage(pokemon: Pokemon): string {
@ -393,7 +401,7 @@ export class ClampTag extends DamagingTrapTag {
export class SandTombTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.SAND_TOMB, CommonAnim.SAND_TOMB, turnCount, sourceId, Moves.SAND_TOMB);
super(BattlerTagType.SAND_TOMB, CommonAnim.SAND_TOMB, turnCount, Moves.SAND_TOMB, sourceId);
}
getTrapMessage(pokemon: Pokemon): string {
@ -403,7 +411,7 @@ export class SandTombTag extends DamagingTrapTag {
export class MagmaStormTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) {
super(BattlerTagType.MAGMA_STORM, CommonAnim.MAGMA_STORM, turnCount, sourceId, Moves.MAGMA_STORM);
super(BattlerTagType.MAGMA_STORM, CommonAnim.MAGMA_STORM, turnCount, Moves.MAGMA_STORM, sourceId);
}
getTrapMessage(pokemon: Pokemon): string {
@ -412,8 +420,8 @@ export class MagmaStormTag extends DamagingTrapTag {
}
export class ProtectedTag extends BattlerTag {
constructor() {
super(BattlerTagType.PROTECTED, BattlerTagLapseType.CUSTOM, 0);
constructor(sourceMove: Moves) {
super(BattlerTagType.PROTECTED, BattlerTagLapseType.CUSTOM, 0, sourceMove);
}
onAdd(pokemon: Pokemon): void {
@ -434,8 +442,8 @@ export class ProtectedTag extends BattlerTag {
}
export class HideSpriteTag extends BattlerTag {
constructor(tagType: BattlerTagType, turnCount: integer) {
super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount);
constructor(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount, sourceMove);
}
onAdd(pokemon: Pokemon): void {
@ -454,14 +462,14 @@ export class HideSpriteTag extends BattlerTag {
}
}
export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourceId: integer, sourceMove: Moves): BattlerTag {
export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves, sourceId: integer): BattlerTag {
switch (tagType) {
case BattlerTagType.RECHARGING:
return new RechargingTag();
return new RechargingTag(sourceMove);
case BattlerTagType.FLINCHED:
return new FlinchedTag();
return new FlinchedTag(sourceMove);
case BattlerTagType.CONFUSED:
return new ConfusedTag(turnCount);
return new ConfusedTag(turnCount, sourceMove);
case BattlerTagType.SEEDED:
return new SeedTag();
case BattlerTagType.NIGHTMARE:
@ -473,7 +481,7 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.DROWSY:
return new DrowsyTag();
case BattlerTagType.TRAPPED:
return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceId, sourceMove);
return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
case BattlerTagType.BIND:
return new BindTag(turnCount, sourceId);
case BattlerTagType.WRAP:
@ -489,17 +497,17 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.MAGMA_STORM:
return new MagmaStormTag(turnCount, sourceId);
case BattlerTagType.PROTECTED:
return new ProtectedTag();
return new ProtectedTag(sourceMove);
case BattlerTagType.FLYING:
case BattlerTagType.UNDERGROUND:
return new HideSpriteTag(tagType, turnCount);
return new HideSpriteTag(tagType, turnCount, sourceMove);
case BattlerTagType.NO_CRIT:
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount);
return new BattlerTag(tagType, BattlerTagLapseType.AFTER_MOVE, turnCount, sourceMove);
case BattlerTagType.BYPASS_SLEEP:
return new BattlerTag(BattlerTagType.BYPASS_SLEEP, BattlerTagLapseType.TURN_END, turnCount);
return new BattlerTag(BattlerTagType.BYPASS_SLEEP, BattlerTagLapseType.TURN_END, turnCount, sourceMove);
case BattlerTagType.IGNORE_FLYING:
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount);
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove);
default:
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount);
return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -32,20 +32,21 @@ export class Status {
}
}
export function getStatusEffectObtainText(statusEffect: StatusEffect): string {
export function getStatusEffectObtainText(statusEffect: StatusEffect, sourceText?: string): string {
const sourceClause = sourceText ? ` ${statusEffect !== StatusEffect.SLEEP ? 'by' : 'from'} ${sourceText}` : '';
switch (statusEffect) {
case StatusEffect.POISON:
return '\nwas poisoned!';
return `\nwas poisoned${sourceClause}!`;
case StatusEffect.TOXIC:
return '\nwas badly poisoned!';
return `\nwas badly poisoned${sourceClause}!`;
case StatusEffect.PARALYSIS:
return ' is paralyzed!\nIt may be unable to move!';
return ` was paralyzed${sourceClause}!\nIt may be unable to move!`;
case StatusEffect.SLEEP:
return '\nfell asleep!';
return `\nfell asleep${sourceClause}!`;
case StatusEffect.FREEZE:
return '\nwas frozen solid!';
return `\nwas frozen solid${sourceClause}!`;
case StatusEffect.BURN:
return '\nwas burned!';
return `\nwas burned${sourceClause}!`;
}
return '';

View File

@ -19,7 +19,9 @@ export enum Type {
FAIRY
};
export function getTypeDamageMultiplier(attackType: integer, defType: integer) {
export type TypeDamageMultiplier = 0 | 0.25 | 0.5 | 1 | 2 | 4;
export function getTypeDamageMultiplier(attackType: integer, defType: integer): TypeDamageMultiplier {
switch (defType) {
case Type.NORMAL:
switch (attackType) {

View File

@ -5,7 +5,7 @@ import Move, { StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedD
import { pokemonLevelMoves } from './data/pokemon-level-moves';
import { default as PokemonSpecies, PokemonSpeciesForm, getPokemonSpecies } from './data/pokemon-species';
import * as Utils from './utils';
import { Type, getTypeDamageMultiplier } from './data/type';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier } from './data/type';
import { getLevelTotalExp } from './data/exp';
import { Stat } from './data/pokemon-stat';
import { AttackTypeBoosterModifier, PokemonBaseStatModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier } from './modifier/modifier';
@ -21,7 +21,7 @@ import { BattlerTag, BattlerTagLapseType, BattlerTagType, getBattlerTag } from '
import { Species } from './data/species';
import { WeatherType } from './data/weather';
import { TempBattleStat } from './data/temp-battle-stat';
import { WeakenTypeTag as WeakenMoveTypeTag } from './data/arena-tag';
import { WeakenMoveTypeTag } from './data/arena-tag';
import { Biome } from './data/biome';
export default abstract class Pokemon extends Phaser.GameObjects.Container {
@ -348,6 +348,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return this.getTypes().indexOf(type) > -1;
}
getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier {
const types = this.getTypes();
return getTypeDamageMultiplier(moveType, types[0]) * (types.length ? getTypeDamageMultiplier(moveType, types[1]) : 1) as TypeDamageMultiplier;
}
getEvolution(): SpeciesEvolution {
if (!pokemonEvolutions.hasOwnProperty(this.species.speciesId))
return null;
@ -568,14 +573,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
}
addTag(tagType: BattlerTagType, turnCount?: integer, sourceId?: integer, sourceMove?: Moves): boolean {
addTag(tagType: BattlerTagType, turnCount?: integer, sourceMove?: Moves, sourceId?: integer): boolean {
const existingTag = this.getTag(tagType);
if (existingTag) {
existingTag.onOverlap(this);
return false;
}
const newTag = getBattlerTag(tagType, turnCount || 0, sourceId, sourceMove);
const newTag = getBattlerTag(tagType, turnCount || 0, sourceMove, sourceId);
if (newTag.canAdd(this)) {
this.summonData.tags.push(newTag);
@ -951,8 +956,7 @@ export class EnemyPokemon extends Pokemon {
if (move.category === MoveCategory.STATUS)
moveScore++;
else {
const targetTypes = target.getTypes();
const effectiveness = getTypeDamageMultiplier(move.type, targetTypes[0]) * (targetTypes.length > 1 ? getTypeDamageMultiplier(move.type, targetTypes[1]) : 1);
const effectiveness = this.getAttackMoveEffectiveness(move.type);
moveScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2;
if (moveScore) {
if (move.category === MoveCategory.PHYSICAL) {

View File

@ -65,10 +65,7 @@ export function initAutoPlay() {
let maxEffectiveness = 0.5;
for (let m of attacker.moveset) {
const moveType = m.getMove().type;
const defenderTypes = defender.getTypes();
let effectiveness = getTypeDamageMultiplier(moveType, defenderTypes[0]);
if (defenderTypes.length > 1)
effectiveness *= getTypeDamageMultiplier(moveType, defenderTypes[1]);
const effectiveness = defender.getAttackMoveEffectiveness(moveType);
if (effectiveness > maxEffectiveness)
maxEffectiveness = effectiveness;
}