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

View File

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

View File

@ -1,20 +1,34 @@
import { Arena } from "../arena"; import { Arena } from "../arena";
import { Type } from "./type"; import { Type } from "./type";
import * as Utils from "../utils"; 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 { export enum ArenaTagType {
NONE, NONE,
MUD_SPORT, MUD_SPORT,
WATER_SPORT WATER_SPORT,
SPIKES,
TOXIC_SPIKES,
STEALTH_ROCK,
TRICK_ROOM
} }
export class ArenaTag { export abstract class ArenaTag {
public tagType: ArenaTagType; public tagType: ArenaTagType;
public turnCount: integer; 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.tagType = tagType;
this.turnCount = turnCount; this.turnCount = turnCount;
this.sourceMove = sourceMove;
this.sourceId = sourceId;
} }
apply(args: any[]): boolean { apply(args: any[]): boolean {
@ -23,20 +37,28 @@ export class ArenaTag {
onAdd(arena: Arena): void { } 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 { } onOverlap(arena: Arena): void { }
lapse(arena: Arena): boolean { 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; private weakenedType: Type;
constructor(tagType: ArenaTagType, turnCount: integer, type: Type) { constructor(tagType: ArenaTagType, turnCount: integer, type: Type, sourceMove: Moves, sourceId: integer) {
super(tagType, turnCount); super(tagType, turnCount, sourceMove, sourceId);
this.weakenedType = type; this.weakenedType = type;
} }
@ -51,39 +73,189 @@ export class WeakenTypeTag extends ArenaTag {
} }
} }
class MudSportTag extends WeakenTypeTag { class MudSportTag extends WeakenMoveTypeTag {
constructor(turnCount: integer) { constructor(turnCount: integer, sourceId: integer) {
super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC); super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC, Moves.MUD_SPORT, sourceId);
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage('Electricity\'s power was weakened!'); 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 { class WaterSportTag extends WeakenMoveTypeTag {
constructor(turnCount: integer) { constructor(turnCount: integer, sourceId: integer) {
super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE); super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE, Moves.WATER_SPORT, sourceId);
} }
onAdd(arena: Arena): void { onAdd(arena: Arena): void {
arena.scene.queueMessage('Fire\'s power was weakened!'); arena.scene.queueMessage('Fire\'s power was weakened!');
} }
}
onRemove(arena: Arena): void { export class ArenaTrapTag extends ArenaTag {
arena.scene.queueMessage('WATER SPORT\'s effect wore off.'); 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) { switch (tagType) {
case ArenaTagType.MUD_SPORT: case ArenaTagType.MUD_SPORT:
return new MudSportTag(turnCount); return new MudSportTag(turnCount, sourceId);
case ArenaTagType.WATER_SPORT: 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 tagType: BattlerTagType;
public lapseType: BattlerTagLapseType; public lapseType: BattlerTagLapseType;
public turnCount: integer; public turnCount: integer;
public sourceId: integer;
public sourceMove: Moves; 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.tagType = tagType;
this.lapseType = lapseType; this.lapseType = lapseType;
this.turnCount = turnCount; this.turnCount = turnCount;
this.sourceId = sourceId;
this.sourceMove = sourceMove; this.sourceMove = sourceMove;
this.sourceId = sourceId;
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -73,6 +73,10 @@ export class BattlerTag {
return --this.turnCount > 0; return --this.turnCount > 0;
} }
isSourceLinked(): boolean {
return false;
}
getMoveName(): string { getMoveName(): string {
return this.sourceMove return this.sourceMove
? allMoves[this.sourceMove].name ? allMoves[this.sourceMove].name
@ -81,8 +85,8 @@ export class BattlerTag {
} }
export class RechargingTag extends BattlerTag { export class RechargingTag extends BattlerTag {
constructor() { constructor(sourceMove: Moves) {
super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1); super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1, sourceMove);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -102,8 +106,8 @@ export class RechargingTag extends BattlerTag {
} }
export class TrappedTag extends BattlerTag { export class TrappedTag extends BattlerTag {
constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceId: integer, sourceMove: Moves) { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, turnCount: integer, sourceMove: Moves, sourceId: integer) {
super(tagType, lapseType, turnCount, sourceId, sourceMove); super(tagType, lapseType, turnCount, sourceMove, sourceId);
} }
canAdd(pokemon: Pokemon): boolean { canAdd(pokemon: Pokemon): boolean {
@ -122,14 +126,18 @@ export class TrappedTag extends BattlerTag {
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getMoveName()}!`)); pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` was freed\nfrom ${this.getMoveName()}!`));
} }
isSourceLinked(): boolean {
return true;
}
getTrapMessage(pokemon: Pokemon): string { getTrapMessage(pokemon: Pokemon): string {
return getPokemonMessage(pokemon, ' can no\nlonger escape!'); return getPokemonMessage(pokemon, ' can no\nlonger escape!');
} }
} }
export class FlinchedTag extends BattlerTag { export class FlinchedTag extends BattlerTag {
constructor() { constructor(sourceMove: Moves) {
super(BattlerTagType.FLINCHED, BattlerTagLapseType.MOVE, 0); super(BattlerTagType.FLINCHED, BattlerTagLapseType.MOVE, 0, sourceMove);
} }
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -143,8 +151,8 @@ export class FlinchedTag extends BattlerTag {
} }
export class ConfusedTag extends BattlerTag { export class ConfusedTag extends BattlerTag {
constructor(turnCount: integer) { constructor(turnCount: integer, sourceMove: Moves) {
super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount); super(BattlerTagType.CONFUSED, BattlerTagLapseType.MOVE, turnCount, sourceMove);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -190,7 +198,7 @@ export class ConfusedTag extends BattlerTag {
export class SeedTag extends BattlerTag { export class SeedTag extends BattlerTag {
constructor() { constructor() {
super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1); super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1, Moves.LEECH_SEED);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -217,7 +225,7 @@ export class SeedTag extends BattlerTag {
export class NightmareTag extends BattlerTag { export class NightmareTag extends BattlerTag {
constructor() { constructor() {
super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1); super(BattlerTagType.NIGHTMARE, BattlerTagLapseType.AFTER_MOVE, 1, Moves.NIGHTMARE);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -250,7 +258,7 @@ export class NightmareTag extends BattlerTag {
export class IngrainTag extends TrappedTag { export class IngrainTag extends TrappedTag {
constructor(sourceId: integer) { 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 { lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
@ -270,7 +278,7 @@ export class IngrainTag extends TrappedTag {
export class AquaRingTag extends BattlerTag { export class AquaRingTag extends BattlerTag {
constructor() { 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 { onAdd(pokemon: Pokemon): void {
@ -291,7 +299,7 @@ export class AquaRingTag extends BattlerTag {
export class DrowsyTag extends BattlerTag { export class DrowsyTag extends BattlerTag {
constructor() { constructor() {
super(BattlerTagType.DROWSY, BattlerTagLapseType.TURN_END, 2); super(BattlerTagType.DROWSY, BattlerTagLapseType.TURN_END, 2, Moves.YAWN);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -313,8 +321,8 @@ export class DrowsyTag extends BattlerTag {
export abstract class DamagingTrapTag extends TrappedTag { export abstract class DamagingTrapTag extends TrappedTag {
private commonAnim: CommonAnim; private commonAnim: CommonAnim;
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceId: integer, sourceMove: Moves) { constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceMove: Moves, sourceId: integer) {
super(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceId, sourceMove); super(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove, sourceId);
this.commonAnim = commonAnim; this.commonAnim = commonAnim;
} }
@ -341,7 +349,7 @@ export abstract class DamagingTrapTag extends TrappedTag {
export class BindTag extends DamagingTrapTag { export class BindTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { getTrapMessage(pokemon: Pokemon): string {
@ -351,7 +359,7 @@ export class BindTag extends DamagingTrapTag {
export class WrapTag extends DamagingTrapTag { export class WrapTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { getTrapMessage(pokemon: Pokemon): string {
@ -360,8 +368,8 @@ export class WrapTag extends DamagingTrapTag {
} }
export abstract class VortexTrapTag extends DamagingTrapTag { export abstract class VortexTrapTag extends DamagingTrapTag {
constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceId: integer, sourceMove: Moves) { constructor(tagType: BattlerTagType, commonAnim: CommonAnim, turnCount: integer, sourceMove: Moves, sourceId: integer) {
super(tagType, commonAnim, turnCount, sourceId, sourceMove); super(tagType, commonAnim, turnCount, sourceMove, sourceId);
} }
getTrapMessage(pokemon: Pokemon): string { getTrapMessage(pokemon: Pokemon): string {
@ -371,19 +379,19 @@ export abstract class VortexTrapTag extends DamagingTrapTag {
export class FireSpinTag extends VortexTrapTag { export class FireSpinTag extends VortexTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { export class WhirlpoolTag extends VortexTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { export class ClampTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { getTrapMessage(pokemon: Pokemon): string {
@ -393,7 +401,7 @@ export class ClampTag extends DamagingTrapTag {
export class SandTombTag extends DamagingTrapTag { export class SandTombTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { getTrapMessage(pokemon: Pokemon): string {
@ -403,7 +411,7 @@ export class SandTombTag extends DamagingTrapTag {
export class MagmaStormTag extends DamagingTrapTag { export class MagmaStormTag extends DamagingTrapTag {
constructor(turnCount: integer, sourceId: integer) { 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 { getTrapMessage(pokemon: Pokemon): string {
@ -412,8 +420,8 @@ export class MagmaStormTag extends DamagingTrapTag {
} }
export class ProtectedTag extends BattlerTag { export class ProtectedTag extends BattlerTag {
constructor() { constructor(sourceMove: Moves) {
super(BattlerTagType.PROTECTED, BattlerTagLapseType.CUSTOM, 0); super(BattlerTagType.PROTECTED, BattlerTagLapseType.CUSTOM, 0, sourceMove);
} }
onAdd(pokemon: Pokemon): void { onAdd(pokemon: Pokemon): void {
@ -434,8 +442,8 @@ export class ProtectedTag extends BattlerTag {
} }
export class HideSpriteTag extends BattlerTag { export class HideSpriteTag extends BattlerTag {
constructor(tagType: BattlerTagType, turnCount: integer) { constructor(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves) {
super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount); super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount, sourceMove);
} }
onAdd(pokemon: Pokemon): void { 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) { switch (tagType) {
case BattlerTagType.RECHARGING: case BattlerTagType.RECHARGING:
return new RechargingTag(); return new RechargingTag(sourceMove);
case BattlerTagType.FLINCHED: case BattlerTagType.FLINCHED:
return new FlinchedTag(); return new FlinchedTag(sourceMove);
case BattlerTagType.CONFUSED: case BattlerTagType.CONFUSED:
return new ConfusedTag(turnCount); return new ConfusedTag(turnCount, sourceMove);
case BattlerTagType.SEEDED: case BattlerTagType.SEEDED:
return new SeedTag(); return new SeedTag();
case BattlerTagType.NIGHTMARE: case BattlerTagType.NIGHTMARE:
@ -473,7 +481,7 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.DROWSY: case BattlerTagType.DROWSY:
return new DrowsyTag(); return new DrowsyTag();
case BattlerTagType.TRAPPED: case BattlerTagType.TRAPPED:
return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceId, sourceMove); return new TrappedTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId);
case BattlerTagType.BIND: case BattlerTagType.BIND:
return new BindTag(turnCount, sourceId); return new BindTag(turnCount, sourceId);
case BattlerTagType.WRAP: case BattlerTagType.WRAP:
@ -489,17 +497,17 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
case BattlerTagType.MAGMA_STORM: case BattlerTagType.MAGMA_STORM:
return new MagmaStormTag(turnCount, sourceId); return new MagmaStormTag(turnCount, sourceId);
case BattlerTagType.PROTECTED: case BattlerTagType.PROTECTED:
return new ProtectedTag(); return new ProtectedTag(sourceMove);
case BattlerTagType.FLYING: case BattlerTagType.FLYING:
case BattlerTagType.UNDERGROUND: case BattlerTagType.UNDERGROUND:
return new HideSpriteTag(tagType, turnCount); return new HideSpriteTag(tagType, turnCount, sourceMove);
case BattlerTagType.NO_CRIT: 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: 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: case BattlerTagType.IGNORE_FLYING:
return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount); return new BattlerTag(tagType, BattlerTagLapseType.TURN_END, turnCount, sourceMove);
default: 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) { switch (statusEffect) {
case StatusEffect.POISON: case StatusEffect.POISON:
return '\nwas poisoned!'; return `\nwas poisoned${sourceClause}!`;
case StatusEffect.TOXIC: case StatusEffect.TOXIC:
return '\nwas badly poisoned!'; return `\nwas badly poisoned${sourceClause}!`;
case StatusEffect.PARALYSIS: 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: case StatusEffect.SLEEP:
return '\nfell asleep!'; return `\nfell asleep${sourceClause}!`;
case StatusEffect.FREEZE: case StatusEffect.FREEZE:
return '\nwas frozen solid!'; return `\nwas frozen solid${sourceClause}!`;
case StatusEffect.BURN: case StatusEffect.BURN:
return '\nwas burned!'; return `\nwas burned${sourceClause}!`;
} }
return ''; return '';

View File

@ -19,7 +19,9 @@ export enum Type {
FAIRY 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) { switch (defType) {
case Type.NORMAL: case Type.NORMAL:
switch (attackType) { switch (attackType) {

View File

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

View File

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