Implement Protosynthesis and Quark Drive
parent
aa1e1a480f
commit
5c02455c97
|
@ -1539,6 +1539,24 @@ export default class BattleScene extends Phaser.Scene {
|
|||
return this.phaseQueue.find(phaseFilter);
|
||||
}
|
||||
|
||||
tryReplacePhase(phaseFilter: (phase: Phase) => boolean, phase: Phase): boolean {
|
||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueue[phaseIndex] = phase;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
tryRemovePhase(phaseFilter: (phase: Phase) => boolean): boolean {
|
||||
const phaseIndex = this.phaseQueue.findIndex(phaseFilter);
|
||||
if (phaseIndex > -1) {
|
||||
this.phaseQueue.splice(phaseIndex, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void {
|
||||
const priority = priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority;
|
||||
const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < priority);
|
||||
|
|
|
@ -84,8 +84,8 @@ export abstract class AbAttr {
|
|||
public showAbility: boolean;
|
||||
private extraCondition: AbAttrCondition;
|
||||
|
||||
constructor(showAbility?: boolean) {
|
||||
this.showAbility = showAbility === undefined || showAbility;
|
||||
constructor(showAbility: boolean = true) {
|
||||
this.showAbility = showAbility;
|
||||
}
|
||||
|
||||
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
|
||||
|
@ -946,6 +946,34 @@ function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition {
|
|||
};
|
||||
}
|
||||
|
||||
export class PostWeatherChangeAbAttr extends AbAttr {
|
||||
applyPostWeatherChange(pokemon: Pokemon, weather: WeatherType, args: any[]): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class PostWeatherChangeAddBattlerTagAttr extends PostWeatherChangeAbAttr {
|
||||
private tagType: BattlerTagType;
|
||||
private turnCount: integer;
|
||||
private weatherTypes: WeatherType[];
|
||||
|
||||
constructor(tagType: BattlerTagType, turnCount: integer, ...weatherTypes: WeatherType[]) {
|
||||
super();
|
||||
|
||||
this.tagType = tagType;
|
||||
this.turnCount = turnCount;
|
||||
this.weatherTypes = weatherTypes;
|
||||
}
|
||||
|
||||
applyPostWeatherChange(pokemon: Pokemon, weather: WeatherType, args: any[]): boolean {
|
||||
console.log(this.weatherTypes.find(w => weather === w), WeatherType[weather]);
|
||||
if (!this.weatherTypes.find(w => weather === w))
|
||||
return false;
|
||||
|
||||
return pokemon.addTag(this.tagType, this.turnCount);
|
||||
}
|
||||
}
|
||||
|
||||
export class PostWeatherLapseAbAttr extends AbAttr {
|
||||
protected weatherTypes: WeatherType[];
|
||||
|
||||
|
@ -1006,6 +1034,33 @@ export class PostWeatherLapseDamageAbAttr extends PostWeatherLapseAbAttr {
|
|||
}
|
||||
}
|
||||
|
||||
export class PostTerrainChangeAbAttr extends AbAttr {
|
||||
applyPostTerrainChange(pokemon: Pokemon, terrain: TerrainType, args: any[]): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr {
|
||||
private tagType: BattlerTagType;
|
||||
private turnCount: integer;
|
||||
private terrainTypes: TerrainType[];
|
||||
|
||||
constructor(tagType: BattlerTagType, turnCount: integer, ...terrainTypes: TerrainType[]) {
|
||||
super();
|
||||
|
||||
this.tagType = tagType;
|
||||
this.turnCount = turnCount;
|
||||
this.terrainTypes = terrainTypes;
|
||||
}
|
||||
|
||||
applyPostTerrainChange(pokemon: Pokemon, terrain: TerrainType, args: any[]): boolean {
|
||||
if (!this.terrainTypes.find(t => terrain === terrain))
|
||||
return false;
|
||||
|
||||
return pokemon.addTag(this.tagType, this.turnCount);
|
||||
}
|
||||
}
|
||||
|
||||
function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition {
|
||||
return (pokemon: Pokemon) => {
|
||||
const terrainType = pokemon.scene.arena.terrain?.terrainType;
|
||||
|
@ -1420,11 +1475,21 @@ export function applyPostTurnAbAttrs(attrType: { new(...args: any[]): PostTurnAb
|
|||
return applyAbAttrsInternal<PostTurnAbAttr>(attrType, pokemon, attr => attr.applyPostTurn(pokemon, args));
|
||||
}
|
||||
|
||||
export function applyPostWeatherChangeAbAttrs(attrType: { new(...args: any[]): PostWeatherChangeAbAttr },
|
||||
pokemon: Pokemon, weather: WeatherType, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostWeatherChangeAbAttr>(attrType, pokemon, attr => attr.applyPostWeatherChange(pokemon, weather, args));
|
||||
}
|
||||
|
||||
export function applyPostWeatherLapseAbAttrs(attrType: { new(...args: any[]): PostWeatherLapseAbAttr },
|
||||
pokemon: Pokemon, weather: Weather, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostWeatherLapseAbAttr>(attrType, pokemon, attr => attr.applyPostWeatherLapse(pokemon, weather, args));
|
||||
}
|
||||
|
||||
export function applyPostTerrainChangeAbAttrs(attrType: { new(...args: any[]): PostTerrainChangeAbAttr },
|
||||
pokemon: Pokemon, terrain: TerrainType, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<PostTerrainChangeAbAttr>(attrType, pokemon, attr => attr.applyPostTerrainChange(pokemon, terrain, args));
|
||||
}
|
||||
|
||||
export function applyCheckTrappedAbAttrs(attrType: { new(...args: any[]): CheckTrappedAbAttr },
|
||||
pokemon: Pokemon, trapped: Utils.BooleanHolder, ...args: any[]): Promise<void> {
|
||||
return applyAbAttrsInternal<CheckTrappedAbAttr>(attrType, pokemon, attr => attr.applyCheckTrapped(pokemon, trapped, args), true);
|
||||
|
@ -2312,9 +2377,13 @@ export function initAbilities() {
|
|||
new Ability(Abilities.COMMANDER, "Commander (N)", "When the Pokémon enters a battle, it goes inside the mouth of an ally Dondozo if one is on the field. The Pokémon then issues commands from there.", 9)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
new Ability(Abilities.ELECTROMORPHOSIS, "Electromorphosis (N)", "The Pokémon becomes charged when it takes damage, boosting the power of the next Electric-type move the Pokémon uses.", 9),
|
||||
new Ability(Abilities.PROTOSYNTHESIS, "Protosynthesis (N)", "Boosts the Pokémon's most proficient stat in harsh sunlight or if the Pokémon is holding Booster Energy.", 9)
|
||||
new Ability(Abilities.PROTOSYNTHESIS, "Protosynthesis", "Boosts the Pokémon's most proficient stat in harsh sunlight or if the Pokémon is holding Booster Energy.", 9)
|
||||
.conditionalAttr(getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN), PostSummonAddBattlerTagAbAttr, BattlerTagType.PROTOSYNTHESIS, 0, true)
|
||||
.attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.PROTOSYNTHESIS, 0, WeatherType.SUNNY, WeatherType.HARSH_SUN)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
new Ability(Abilities.QUARK_DRIVE, "Quark Drive (N)", "Boosts the Pokémon's most proficient stat on Electric Terrain or if the Pokémon is holding Booster Energy.", 9)
|
||||
new Ability(Abilities.QUARK_DRIVE, "Quark Drive", "Boosts the Pokémon's most proficient stat on Electric Terrain or if the Pokémon is holding Booster Energy.", 9)
|
||||
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), PostSummonAddBattlerTagAbAttr, BattlerTagType.QUARK_DRIVE, 0, true)
|
||||
.attr(PostTerrainChangeAddBattlerTagAttr, BattlerTagType.QUARK_DRIVE, 0, TerrainType.ELECTRIC)
|
||||
.attr(ProtectAbilityAbAttr),
|
||||
new Ability(Abilities.GOOD_AS_GOLD, "Good as Gold (N)", "A body of pure, solid gold gives the Pokémon full immunity to other Pokémon's status moves.", 9)
|
||||
.ignorable(),
|
||||
|
|
|
@ -2,7 +2,7 @@ import { CommonAnim, CommonBattleAnim } from "./battle-anims";
|
|||
import { CommonAnimPhase, MoveEffectPhase, MovePhase, PokemonHealPhase, ShowAbilityPhase } from "../phases";
|
||||
import { getPokemonMessage } from "../messages";
|
||||
import Pokemon, { MoveResult, HitResult } from "../field/pokemon";
|
||||
import { Stat } from "./pokemon-stat";
|
||||
import { Stat, getStatName } from "./pokemon-stat";
|
||||
import { StatusEffect } from "./status-effect";
|
||||
import * as Utils from "../utils";
|
||||
import { Moves } from "./enums/moves";
|
||||
|
@ -11,6 +11,7 @@ import { Type } from "./type";
|
|||
import { Abilities, FlinchEffectAbAttr, applyAbAttrs } from "./ability";
|
||||
import { BattlerTagType } from "./enums/battler-tag-type";
|
||||
import { TerrainType } from "./terrain";
|
||||
import { WeatherType } from "./weather";
|
||||
|
||||
export enum BattlerTagLapseType {
|
||||
FAINT,
|
||||
|
@ -65,6 +66,14 @@ export class BattlerTag {
|
|||
}
|
||||
}
|
||||
|
||||
export interface WeatherBattlerTag {
|
||||
weatherTypes: WeatherType[];
|
||||
}
|
||||
|
||||
export interface TerrainBattlerTag {
|
||||
terrainTypes: TerrainType[];
|
||||
}
|
||||
|
||||
export class RechargingTag extends BattlerTag {
|
||||
constructor(sourceMove: Moves) {
|
||||
super(BattlerTagType.RECHARGING, BattlerTagLapseType.MOVE, 1, sourceMove);
|
||||
|
@ -722,6 +731,66 @@ export class SlowStartTag extends AbilityBattlerTag {
|
|||
}
|
||||
}
|
||||
|
||||
export class HighestStatBoostTag extends AbilityBattlerTag {
|
||||
public stat: Stat;
|
||||
public multiplier: number;
|
||||
|
||||
constructor(tagType: BattlerTagType, ability: Abilities) {
|
||||
super(tagType, ability, BattlerTagLapseType.CUSTOM, 1);
|
||||
}
|
||||
|
||||
onAdd(pokemon: Pokemon): void {
|
||||
super.onAdd(pokemon);
|
||||
|
||||
const stats = [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ];
|
||||
let highestStat: Stat;
|
||||
stats.map(s => pokemon.getBattleStat(s)).reduce((highestValue: integer, value: integer, i: integer) => {
|
||||
if (value > highestValue) {
|
||||
highestStat = stats[i];
|
||||
return highestValue += value;
|
||||
}
|
||||
return highestValue;
|
||||
}, 0);
|
||||
|
||||
this.stat = highestStat;
|
||||
|
||||
switch (this.stat) {
|
||||
case Stat.SPD:
|
||||
this.multiplier = 1.5;
|
||||
break;
|
||||
default:
|
||||
this.multiplier = 1.3;
|
||||
break;
|
||||
}
|
||||
|
||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, `'s ${getStatName(highestStat)}\nwas heightened!`), null, false, null, true);
|
||||
}
|
||||
|
||||
onRemove(pokemon: Pokemon): void {
|
||||
super.onRemove(pokemon);
|
||||
|
||||
pokemon.scene.queueMessage(`The effects of ${getPokemonMessage(pokemon, `'s\n${pokemon.getAbility().name} wore off!`)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class WeatherHighestStatBoostTag extends HighestStatBoostTag implements WeatherBattlerTag {
|
||||
public weatherTypes: WeatherType[];
|
||||
|
||||
constructor(tagType: BattlerTagType, ability: Abilities, ...weatherTypes: WeatherType[]) {
|
||||
super(tagType, ability);
|
||||
this.weatherTypes = weatherTypes;
|
||||
}
|
||||
}
|
||||
|
||||
export class TerrainHighestStatBoostTag extends HighestStatBoostTag implements TerrainBattlerTag {
|
||||
public terrainTypes: TerrainType[];
|
||||
|
||||
constructor(tagType: BattlerTagType, ability: Abilities, ...terrainTypes: TerrainType[]) {
|
||||
super(tagType, ability);
|
||||
this.terrainTypes = terrainTypes;
|
||||
}
|
||||
}
|
||||
|
||||
export class HideSpriteTag extends BattlerTag {
|
||||
constructor(tagType: BattlerTagType, turnCount: integer, sourceMove: Moves) {
|
||||
super(tagType, BattlerTagLapseType.MOVE_EFFECT, turnCount, sourceMove);
|
||||
|
@ -840,6 +909,10 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: integer, sourc
|
|||
return new TruantTag();
|
||||
case BattlerTagType.SLOW_START:
|
||||
return new SlowStartTag();
|
||||
case BattlerTagType.PROTOSYNTHESIS:
|
||||
return new WeatherHighestStatBoostTag(tagType, Abilities.PROTOSYNTHESIS, WeatherType.SUNNY, WeatherType.HARSH_SUN);
|
||||
case BattlerTagType.QUARK_DRIVE:
|
||||
return new TerrainHighestStatBoostTag(tagType, Abilities.QUARK_DRIVE, TerrainType.ELECTRIC);
|
||||
case BattlerTagType.FLYING:
|
||||
case BattlerTagType.UNDERGROUND:
|
||||
case BattlerTagType.HIDDEN:
|
||||
|
|
|
@ -28,6 +28,8 @@ export enum BattlerTagType {
|
|||
PERISH_SONG = "PERISH_SONG",
|
||||
TRUANT = "TRUANT",
|
||||
SLOW_START = "SLOW_START",
|
||||
PROTOSYNTHESIS = "PROTOSYNTHESIS",
|
||||
QUARK_DRIVE = "QUARK_DRIVE",
|
||||
FLYING = "FLYING",
|
||||
UNDERGROUND = "UNDERGROUND",
|
||||
HIDDEN = "HIDDEN",
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Type } from "./type";
|
|||
import Move, { AttackMove } from "./move";
|
||||
import * as Utils from "../utils";
|
||||
import BattleScene from "../battle-scene";
|
||||
import { SuppressWeatherEffectAbAttr, applyPreWeatherEffectAbAttrs } from "./ability";
|
||||
import { SuppressWeatherEffectAbAttr } from "./ability";
|
||||
import { TerrainType } from "./terrain";
|
||||
|
||||
export enum WeatherType {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import BattleScene from "../battle-scene";
|
||||
import { BiomePoolTier, BiomeTierPokemonPools, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes";
|
||||
import { BiomePoolTier, PokemonPools, BiomeTierTrainerPools, biomePokemonPools, biomeTrainerPools } from "../data/biomes";
|
||||
import { Biome } from "../data/enums/biome";
|
||||
import * as Utils from "../utils";
|
||||
import PokemonSpecies, { getPokemonSpecies } from "../data/pokemon-species";
|
||||
import { Species } from "../data/enums/species";
|
||||
import { Weather, WeatherType, getTerrainClearMessage, getTerrainStartMessage, getWeatherClearMessage, getWeatherStartMessage } from "../data/weather";
|
||||
import { CommonAnimPhase } from "../phases";
|
||||
import { CommonAnimPhase, WeatherEffectPhase } from "../phases";
|
||||
import { CommonAnim } from "../data/battle-anims";
|
||||
import { Type } from "../data/type";
|
||||
import Move from "../data/move";
|
||||
|
@ -16,6 +16,7 @@ import { BattlerIndex } from "../battle";
|
|||
import { Moves } from "../data/enums/moves";
|
||||
import { TimeOfDay } from "../data/enums/time-of-day";
|
||||
import { Terrain, TerrainType } from "../data/terrain";
|
||||
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
||||
|
||||
const WEATHER_OVERRIDE = WeatherType.NONE;
|
||||
|
||||
|
@ -268,10 +269,18 @@ export class Arena {
|
|||
this.weather = weather ? new Weather(weather, viaMove ? 5 : 0) : null;
|
||||
|
||||
if (this.weather) {
|
||||
this.scene.tryReplacePhase(phase => phase instanceof WeatherEffectPhase && phase.weather.weatherType === oldWeatherType, new WeatherEffectPhase(this.scene, this.weather));
|
||||
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
|
||||
this.scene.queueMessage(getWeatherStartMessage(weather));
|
||||
} else
|
||||
} else {
|
||||
this.scene.tryRemovePhase(phase => phase instanceof WeatherEffectPhase && phase.weather.weatherType === oldWeatherType);
|
||||
this.scene.queueMessage(getWeatherClearMessage(oldWeatherType));
|
||||
}
|
||||
|
||||
this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => {
|
||||
pokemon.findAndRemoveTags(t => 'weatherTypes' in t && !(t.weatherTypes as WeatherType[]).find(t => t === weather));
|
||||
applyPostWeatherChangeAbAttrs(PostWeatherChangeAbAttr, pokemon, weather);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -291,6 +300,11 @@ export class Arena {
|
|||
} else
|
||||
this.scene.queueMessage(getTerrainClearMessage(oldTerrainType));
|
||||
|
||||
this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => {
|
||||
pokemon.findAndRemoveTags(t => 'terrainTypes' in t && !(t.terrainTypes as TerrainType[]).find(t => t === terrain));
|
||||
applyPostTerrainChangeAbAttrs(PostTerrainChangeAbAttr, pokemon, terrain);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1333,15 +1333,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return !!tag;
|
||||
}
|
||||
|
||||
removeTagsBySourceId(sourceId: integer): void {
|
||||
findAndRemoveTags(tagFilter: ((tag: BattlerTag) => boolean)): boolean {
|
||||
if (!this.summonData)
|
||||
return false;
|
||||
const tags = this.summonData.tags;
|
||||
tags.filter(t => t.sourceId === sourceId).forEach(t => {
|
||||
t.onRemove(this);
|
||||
tags.splice(tags.indexOf(t), 1);
|
||||
});
|
||||
const tagsToRemove = tags.filter(t => tagFilter(t));
|
||||
for (let tag of tagsToRemove) {
|
||||
tag.turnCount = 0;
|
||||
tag.onRemove(this);
|
||||
tags.splice(tags.indexOf(tag), 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
removeTagsBySourceId(sourceId: integer): void {
|
||||
this.findAndRemoveTags(t => t.sourceId === sourceId);
|
||||
}
|
||||
|
||||
transferTagsBySourceId(sourceId: integer, newSourceId: integer): void {
|
||||
if (!this.summonData)
|
||||
return;
|
||||
const tags = this.summonData.tags;
|
||||
tags.filter(t => t.sourceId === sourceId).forEach(t => t.sourceId = newSourceId);
|
||||
}
|
||||
|
|
|
@ -1840,7 +1840,7 @@ export class TurnEndPhase extends FieldPhase {
|
|||
|
||||
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
|
||||
this.scene.unshiftPhase(new PokemonHealPhase(this.scene, pokemon.getBattlerIndex(),
|
||||
Math.max(pokemon.getMaxHp() >> 4, 1), getPokemonMessage(pokemon, ' regained\nhealth from the Grassy Terrain!'), true));
|
||||
Math.max(pokemon.getMaxHp() >> 4, 1), getPokemonMessage(pokemon, '\'s HP was restored.'), true));
|
||||
}
|
||||
|
||||
applyPostTurnAbAttrs(PostTurnAbAttr, pokemon);
|
||||
|
@ -2505,7 +2505,7 @@ export class StatChangePhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
export class WeatherEffectPhase extends CommonAnimPhase {
|
||||
private weather: Weather;
|
||||
public weather: Weather;
|
||||
|
||||
constructor(scene: BattleScene, weather: Weather) {
|
||||
super(scene, undefined, undefined, CommonAnim.SUNNY + (weather.weatherType - 1));
|
||||
|
|
Loading…
Reference in New Issue