Implement Protosynthesis and Quark Drive

pull/16/head
Flashfyre 2024-03-18 21:22:27 -04:00
parent aa1e1a480f
commit 5c02455c97
8 changed files with 203 additions and 16 deletions

View File

@ -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);

View File

@ -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(),

View File

@ -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:

View File

@ -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",

View File

@ -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 {

View File

@ -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;
}
@ -290,6 +299,11 @@ export class Arena {
this.scene.queueMessage(getTerrainStartMessage(terrain));
} 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;
}

View File

@ -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);
}

View File

@ -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));