Converted White Herb

This commit is contained in:
Wlowscha 2025-06-01 20:14:10 +02:00
parent cf19a01c37
commit 41a4c9ec2d
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
6 changed files with 168 additions and 53 deletions

View File

@ -39,7 +39,7 @@ export class PokemonItemManager {
return itemType in this.getHeldItems() ? this.heldItems[itemType].stack : 0;
}
addHeldItem(itemType: HeldItems, addStack = 1) {
add(itemType: HeldItems, addStack = 1) {
const maxStack = allHeldItems[itemType].getMaxStackCount();
if (this.hasItem(itemType)) {
@ -49,4 +49,12 @@ export class PokemonItemManager {
this.heldItems[itemType] = { stack: Math.min(addStack, maxStack), disabled: false };
}
}
remove(itemType: HeldItems, removeStack = 1) {
this.heldItems[itemType].stack -= removeStack;
if (this.heldItems[itemType].stack <= 0) {
delete this.heldItems[itemType];
}
}
}

View File

@ -107,3 +107,20 @@ export class HeldItem {
return 1;
}
}
export class ConsumableHeldItem extends HeldItem {
applyConsumable(_pokemon: Pokemon): boolean {
return true;
}
apply(pokemon: Pokemon): boolean {
const consumed = this.applyConsumable(pokemon);
if (consumed) {
pokemon.heldItemManager.remove(this.type, 1);
return true;
}
return false;
}
}

View File

@ -0,0 +1,70 @@
import type Pokemon from "#app/field/pokemon";
import { globalScene } from "#app/global-scene";
import { BATTLE_STATS } from "#enums/stat";
import i18next from "i18next";
import { ConsumableHeldItem } from "../held-item";
import { getPokemonNameWithAffix } from "#app/messages";
import { allHeldItems } from "../all-held-items";
/**
* Modifier used for held items, namely White Herb, that restore adverse stat
* stages in battle.
* @extends PokemonHeldItemModifier
* @see {@linkcode apply}
*/
export class ResetNegativeStatStageHeldItem extends ConsumableHeldItem {
get name(): string {
return i18next.t("modifierType:ModifierType.WHITE_HERB.name");
}
get description(): string {
return i18next.t("modifierType:ModifierType.WHITE_HERB.description");
}
get icon(): string {
return "shell_bell";
}
/**
* Goes through the holder's stat stages and, if any are negative, resets that
* stat stage back to 0.
* @param pokemon {@linkcode Pokemon} that holds the item
* @returns `true` if any stat stages were reset, false otherwise
*/
applyConsumable(pokemon: Pokemon): boolean {
let statRestored = false;
for (const s of BATTLE_STATS) {
if (pokemon.getStatStage(s) < 0) {
pokemon.setStatStage(s, 0);
statRestored = true;
}
}
if (statRestored) {
globalScene.queueMessage(
i18next.t("modifier:resetNegativeStatStageApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
typeName: this.name,
}),
);
}
return statRestored;
}
getMaxHeldItemCount(_pokemon: Pokemon): number {
return 2;
}
}
// TODO: Do we need this to return true/false?
export function applyResetNegativeStatStageHeldItem(pokemon: Pokemon): boolean {
let applied = false;
if (pokemon) {
for (const item of Object.keys(pokemon.heldItemManager.getHeldItems())) {
if (allHeldItems[item] instanceof ResetNegativeStatStageHeldItem) {
applied ||= allHeldItems[item].apply(pokemon);
}
}
}
return applied;
}

View File

@ -2096,6 +2096,14 @@ export const modifierTypes = {
"reviver_seed",
(type, args) => new PokemonInstantReviveModifier(type, (args[0] as Pokemon).id),
),
WHITE_HERB_REWARD: () =>
new PokemonHeldItemReward(
HeldItems.WHITE_HERB,
(type, args) => new ResetNegativeStatStageModifier(type, (args[0] as Pokemon).id),
),
// TODO: Remove the old one
WHITE_HERB: () =>
new PokemonHeldItemModifierType(
"modifierType:ModifierType.WHITE_HERB",
@ -3140,7 +3148,7 @@ const wildModifierPool: ModifierPool = {
}),
[ModifierTier.ULTRA]: [
new WeightedModifierType(modifierTypes.ATTACK_TYPE_BOOSTER, 10),
new WeightedModifierType(modifierTypes.WHITE_HERB, 0),
new WeightedModifierType(modifierTypes.WHITE_HERB_REWARD, 0),
].map(m => {
m.setTier(ModifierTier.ULTRA);
return m;
@ -3685,10 +3693,9 @@ export function getEnemyHeldItemsForWave(
poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER,
upgradeChance = 0,
): PokemonHeldItemReward[] {
const ret = new Array(count)
.fill(0)
.map(
const ret = new Array(count).fill(0).map(
() =>
// TODO: Change this to get held items (this function really could just return a list of ids honestly)
getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !randSeedInt(upgradeChance) ? 1 : 0)
?.type as PokemonHeldItemReward,
);
@ -3740,13 +3747,53 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): PokemonHeld
function getNewModifierTypeOption(
party: Pokemon[],
poolType: ModifierPoolType,
tier?: ModifierTier,
baseTier?: ModifierTier,
upgradeCount?: number,
retryCount = 0,
allowLuckUpgrades = true,
): ModifierTypeOption | null {
const player = !poolType;
const pool = getModifierPoolForType(poolType);
const thresholds = getPoolThresholds(poolType);
const tier = determineTier(party, player, baseTier, upgradeCount, retryCount, allowLuckUpgrades);
const tierThresholds = Object.keys(thresholds[tier]);
const totalWeight = Number.parseInt(tierThresholds[tierThresholds.length - 1]);
const value = randSeedInt(totalWeight);
let index: number | undefined;
for (const t of tierThresholds) {
const threshold = Number.parseInt(t);
if (value < threshold) {
index = thresholds[tier][threshold];
break;
}
}
if (index === undefined) {
return null;
}
if (player) {
console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]);
}
let modifierType: ModifierType | null = pool[tier][index].modifierType;
if (modifierType instanceof ModifierTypeGenerator) {
modifierType = (modifierType as ModifierTypeGenerator).generateType(party);
if (modifierType === null) {
if (player) {
console.log(ModifierTier[tier], upgradeCount);
}
return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount);
}
}
console.log(modifierType, !player ? "(enemy)" : "");
return new ModifierTypeOption(modifierType as ModifierType, upgradeCount!); // TODO: is this bang correct?
}
function getPoolThresholds(poolType: ModifierPoolType) {
let thresholds: object;
switch (poolType) {
case ModifierPoolType.PLAYER:
@ -3765,6 +3812,17 @@ function getNewModifierTypeOption(
thresholds = dailyStarterModifierPoolThresholds;
break;
}
return thresholds;
}
function determineTier(
party: Pokemon[],
player: boolean,
tier?: ModifierTier,
upgradeCount?: number,
retryCount = 0,
allowLuckUpgrades = true,
): ModifierTier {
if (tier === undefined) {
const tierValue = randSeedInt(1024);
if (!upgradeCount) {
@ -3819,40 +3877,7 @@ function getNewModifierTypeOption(
retryCount = 0;
tier--;
}
const tierThresholds = Object.keys(thresholds[tier]);
const totalWeight = Number.parseInt(tierThresholds[tierThresholds.length - 1]);
const value = randSeedInt(totalWeight);
let index: number | undefined;
for (const t of tierThresholds) {
const threshold = Number.parseInt(t);
if (value < threshold) {
index = thresholds[tier][threshold];
break;
}
}
if (index === undefined) {
return null;
}
if (player) {
console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]);
}
let modifierType: ModifierType | null = pool[tier][index].modifierType;
if (modifierType instanceof ModifierTypeGenerator) {
modifierType = (modifierType as ModifierTypeGenerator).generateType(party);
if (modifierType === null) {
if (player) {
console.log(ModifierTier[tier], upgradeCount);
}
return getNewModifierTypeOption(party, poolType, tier, upgradeCount, ++retryCount);
}
}
console.log(modifierType, !player ? "(enemy)" : "");
return new ModifierTypeOption(modifierType as ModifierType, upgradeCount!); // TODO: is this bang correct?
return tier;
}
export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType {

View File

@ -359,7 +359,7 @@ export class SelectModifierPhase extends BattlePhase {
(slotIndex: number, _option: PartyOption) => {
if (slotIndex < 6) {
globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => {
party[slotIndex].heldItemManager.addHeldItem(reward.itemId);
party[slotIndex].heldItemManager.add(reward.itemId);
globalScene.ui.clearText();
globalScene.ui.setMode(UiMode.MESSAGE);
super.end();

View File

@ -15,7 +15,6 @@ import { ArenaTagSide, MistTag } from "#app/data/arena-tag";
import type { ArenaTag } from "#app/data/arena-tag";
import type Pokemon from "#app/field/pokemon";
import { getPokemonNameWithAffix } from "#app/messages";
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
import { handleTutorial, Tutorial } from "#app/tutorial";
import { NumberHolder, BooleanHolder, isNullOrUndefined } from "#app/utils/common";
import i18next from "i18next";
@ -23,6 +22,7 @@ import { PokemonPhase } from "./pokemon-phase";
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
import { OctolockTag } from "#app/data/battler-tags";
import { ArenaTagType } from "#app/enums/arena-tag-type";
import { applyResetNegativeStatStageHeldItem } from "#app/items/held-items/reset-negative-stat-stage";
export type StatStageChangeCallback = (
target: Pokemon | null,
@ -239,14 +239,9 @@ export class StatStageChangePhase extends PokemonPhase {
);
if (!(existingPhase instanceof StatStageChangePhase)) {
// Apply White Herb if needed
const whiteHerb = globalScene.applyModifier(
ResetNegativeStatStageModifier,
this.player,
pokemon,
) as ResetNegativeStatStageModifier;
// If the White Herb was applied, consume it
const whiteHerb = applyResetNegativeStatStageHeldItem(pokemon);
// If the White Herb was applied, update scene modifiers
if (whiteHerb) {
pokemon.loseHeldItem(whiteHerb);
globalScene.updateModifiers(this.player);
}
}