Mostly implement the Harvest ability (#1081)
* add harvest ability, move berry phase before turn end phase * readd removing berry from memory after harvest
This commit is contained in:
parent
7ae09a31a5
commit
cd7e818a01
|
@ -13,7 +13,7 @@ import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, RecoilAttr, Stat
|
||||||
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
|
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
|
||||||
import { ArenaTagType } from "./enums/arena-tag-type";
|
import { ArenaTagType } from "./enums/arena-tag-type";
|
||||||
import { Stat } from "./pokemon-stat";
|
import { Stat } from "./pokemon-stat";
|
||||||
import { PokemonHeldItemModifier } from "../modifier/modifier";
|
import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier";
|
||||||
import { Moves } from "./enums/moves";
|
import { Moves } from "./enums/moves";
|
||||||
import { TerrainType } from "./terrain";
|
import { TerrainType } from "./terrain";
|
||||||
import { SpeciesFormChangeManualTrigger } from "./pokemon-forms";
|
import { SpeciesFormChangeManualTrigger } from "./pokemon-forms";
|
||||||
|
@ -23,6 +23,7 @@ import { Command } from "../ui/command-ui-handler";
|
||||||
import Battle from "#app/battle.js";
|
import Battle from "#app/battle.js";
|
||||||
import { ability } from "#app/locales/en/ability.js";
|
import { ability } from "#app/locales/en/ability.js";
|
||||||
import { PokeballType, getPokeballName } from "./pokeball";
|
import { PokeballType, getPokeballName } from "./pokeball";
|
||||||
|
import { BerryModifierType } from "#app/modifier/modifier-type";
|
||||||
|
|
||||||
export class Ability implements Localizable {
|
export class Ability implements Localizable {
|
||||||
public id: Abilities;
|
public id: Abilities;
|
||||||
|
@ -127,7 +128,7 @@ export abstract class AbAttr {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCondition(): AbAttrCondition {
|
getCondition(): AbAttrCondition | null {
|
||||||
return this.extraCondition || null;
|
return this.extraCondition || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2228,6 +2229,71 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After the turn ends, try to create an extra item
|
||||||
|
*/
|
||||||
|
export class PostTurnLootAbAttr extends PostTurnAbAttr {
|
||||||
|
/**
|
||||||
|
* @param itemType - The type of item to create
|
||||||
|
* @param procChance - Chance to create an item
|
||||||
|
* @see {@linkcode applyPostTurn()}
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
/** Extend itemType to add more options */
|
||||||
|
private itemType: "EATEN_BERRIES" | "HELD_BERRIES",
|
||||||
|
private procChance: (pokemon: Pokemon) => number
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean {
|
||||||
|
const pass = Phaser.Math.RND.realInRange(0, 1);
|
||||||
|
// Clamp procChance to [0, 1]. Skip if didn't proc (less than pass)
|
||||||
|
if (Math.max(Math.min(this.procChance(pokemon), 1), 0) < pass) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.itemType === "EATEN_BERRIES") {
|
||||||
|
return this.createEatenBerry(pokemon);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new berry chosen randomly from the berries the pokemon ate this battle
|
||||||
|
* @param pokemon The pokemon with this ability
|
||||||
|
* @returns whether a new berry was created
|
||||||
|
*/
|
||||||
|
createEatenBerry(pokemon: Pokemon): boolean {
|
||||||
|
const berriesEaten = pokemon.battleData.berriesEaten;
|
||||||
|
|
||||||
|
if (!berriesEaten.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const randomIdx = Utils.randSeedInt(berriesEaten.length);
|
||||||
|
const chosenBerry = new BerryModifierType(berriesEaten[randomIdx]);
|
||||||
|
berriesEaten.splice(randomIdx) // Remove berry from memory
|
||||||
|
|
||||||
|
const berryModifier = pokemon.scene.findModifier(
|
||||||
|
(m) => m instanceof BerryModifier && m.berryType === berriesEaten[randomIdx],
|
||||||
|
pokemon.isPlayer()
|
||||||
|
) as BerryModifier | undefined;
|
||||||
|
|
||||||
|
if (!berryModifier) {
|
||||||
|
pokemon.scene.addModifier(new BerryModifier(chosenBerry, pokemon.id, berriesEaten[randomIdx], 1));
|
||||||
|
} else {
|
||||||
|
berryModifier.stackCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ` harvested one ${chosenBerry.name}!`));
|
||||||
|
pokemon.scene.updateModifiers(pokemon.isPlayer());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class MoodyAbAttr extends PostTurnAbAttr {
|
export class MoodyAbAttr extends PostTurnAbAttr {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(true);
|
super(true);
|
||||||
|
@ -3418,7 +3484,13 @@ export function initAbilities() {
|
||||||
new Ability(Abilities.FLARE_BOOST, 5)
|
new Ability(Abilities.FLARE_BOOST, 5)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user.status?.effect === StatusEffect.BURN, 1.5),
|
.attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user.status?.effect === StatusEffect.BURN, 1.5),
|
||||||
new Ability(Abilities.HARVEST, 5)
|
new Ability(Abilities.HARVEST, 5)
|
||||||
.unimplemented(),
|
.attr(
|
||||||
|
PostTurnLootAbAttr,
|
||||||
|
"EATEN_BERRIES",
|
||||||
|
/** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */
|
||||||
|
(pokemon) => 0.5 * (getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)(pokemon) ? 2 : 1)
|
||||||
|
)
|
||||||
|
.partial(),
|
||||||
new Ability(Abilities.TELEPATHY, 5)
|
new Ability(Abilities.TELEPATHY, 5)
|
||||||
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move.getMove() instanceof AttackMove)
|
.attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move.getMove() instanceof AttackMove)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
|
|
|
@ -2076,6 +2076,8 @@ export class TurnStartPhase extends FieldPhase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.scene.pushPhase(new BerryPhase(this.scene));
|
||||||
|
|
||||||
if (this.scene.arena.weather)
|
if (this.scene.arena.weather)
|
||||||
this.scene.pushPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather));
|
this.scene.pushPhase(new WeatherEffectPhase(this.scene, this.scene.arena.weather));
|
||||||
|
|
||||||
|
@ -2090,6 +2092,42 @@ export class TurnStartPhase extends FieldPhase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The phase after attacks where the pokemon eat berries */
|
||||||
|
export class BerryPhase extends FieldPhase {
|
||||||
|
start() {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
this.executeForAll((pokemon) => {
|
||||||
|
const hasUsableBerry = !!this.scene.findModifier((m) => m instanceof BerryModifier && m.shouldApply([pokemon]), pokemon.isPlayer());
|
||||||
|
|
||||||
|
if (hasUsableBerry) {
|
||||||
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
pokemon.getOpponents().map((opp) => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
|
||||||
|
|
||||||
|
if (cancelled.value) {
|
||||||
|
pokemon.scene.queueMessage(getPokemonMessage(pokemon, " is too\nnervous to eat berries!"));
|
||||||
|
} else {
|
||||||
|
this.scene.unshiftPhase(new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM));
|
||||||
|
|
||||||
|
for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon) as BerryModifier[]) {
|
||||||
|
if (berryModifier.consumed) {
|
||||||
|
if (!--berryModifier.stackCount) {
|
||||||
|
this.scene.removeModifier(berryModifier);
|
||||||
|
} else {
|
||||||
|
berryModifier.consumed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene.updateModifiers(pokemon.isPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class TurnEndPhase extends FieldPhase {
|
export class TurnEndPhase extends FieldPhase {
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
@ -2108,10 +2146,6 @@ export class TurnEndPhase extends FieldPhase {
|
||||||
pokemon.summonData.disabledMove = Moves.NONE;
|
pokemon.summonData.disabledMove = Moves.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUsableBerry = !!this.scene.findModifier(m => m instanceof BerryModifier && m.shouldApply([ pokemon ]), pokemon.isPlayer());
|
|
||||||
if (hasUsableBerry)
|
|
||||||
this.scene.unshiftPhase(new BerryPhase(this.scene, pokemon.getBattlerIndex()));
|
|
||||||
|
|
||||||
this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon);
|
this.scene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon);
|
||||||
|
|
||||||
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
|
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) {
|
||||||
|
@ -4093,38 +4127,6 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BerryPhase extends CommonAnimPhase {
|
|
||||||
constructor(scene: BattleScene, battlerIndex: BattlerIndex) {
|
|
||||||
super(scene, battlerIndex, undefined, CommonAnim.USE_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
let berryModifiers: BerryModifier[];
|
|
||||||
|
|
||||||
const pokemon = this.getPokemon();
|
|
||||||
|
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
|
||||||
pokemon.getOpponents().map(opp => applyAbAttrs(PreventBerryUseAbAttr, opp, cancelled));
|
|
||||||
|
|
||||||
if (cancelled.value)
|
|
||||||
pokemon.scene.queueMessage(getPokemonMessage(pokemon, ' is too\nnervous to eat berries!'));
|
|
||||||
else if ((berryModifiers = this.scene.applyModifiers(BerryModifier, this.player, pokemon) as BerryModifier[])) {
|
|
||||||
for (let berryModifier of berryModifiers) {
|
|
||||||
if (berryModifier.consumed) {
|
|
||||||
if (!--berryModifier.stackCount)
|
|
||||||
this.scene.removeModifier(berryModifier);
|
|
||||||
else
|
|
||||||
berryModifier.consumed = false;
|
|
||||||
this.scene.updateModifiers(this.player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PokemonHealPhase extends CommonAnimPhase {
|
export class PokemonHealPhase extends CommonAnimPhase {
|
||||||
private hpHealed: integer;
|
private hpHealed: integer;
|
||||||
private message: string;
|
private message: string;
|
||||||
|
|
Loading…
Reference in New Issue