diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index f458dc6053c..7eedc81c282 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -122,7 +122,7 @@ import { MoveFlags } from "#enums/MoveFlags"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; -import { applyAttackTypeBoosterHeldItem } from "#app/modifier/held-items"; +import { applyAttackTypeBoosterHeldItem } from "#app/modifier/all-held-items"; import { TrainerVariant } from "#app/field/trainer"; import { SelectBiomePhase } from "#app/phases/select-biome-phase"; diff --git a/src/enums/held-items.ts b/src/enums/held-items.ts index 9a15559ac14..7871eeedc74 100644 --- a/src/enums/held-items.ts +++ b/src/enums/held-items.ts @@ -74,3 +74,15 @@ export const HeldItems = { }; export type HeldItems = (typeof HeldItems)[keyof typeof HeldItems]; + +type HeldItemName = keyof typeof HeldItems; +type HeldItemValue = typeof HeldItems[HeldItemName]; + +// Use a type-safe reducer to force number keys and values +export const HeldItemNames: Record = Object.entries(HeldItems).reduce( + (acc, [key, value]) => { + acc[value as HeldItemValue] = key as HeldItemName; + return acc; + }, + {} as Record +); \ No newline at end of file diff --git a/src/modifier/all-held-items.ts b/src/modifier/all-held-items.ts index 293a91d9207..8417c97a581 100644 --- a/src/modifier/all-held-items.ts +++ b/src/modifier/all-held-items.ts @@ -1,8 +1,10 @@ import type Pokemon from "#app/field/pokemon"; import { globalScene } from "#app/global-scene"; import type { Localizable } from "#app/interfaces/locales"; -import type { NumberHolder } from "#app/utils/common"; -import { HeldItems } from "#enums/held-items"; +import { getPokemonNameWithAffix } from "#app/messages"; +import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; +import { toDmgValue, type NumberHolder } from "#app/utils/common"; +import { HeldItemNames, HeldItems } from "#enums/held-items"; import { PokemonType } from "#enums/pokemon-type"; import i18next from "i18next"; @@ -137,7 +139,7 @@ export class AttackTypeBoosterHeldItem extends HeldItem { } getName(): string { - return i18next.t(`modifierType:AttackTypeBoosterItem.${HeldItems[this.type]?.toLowerCase()}`); + return i18next.t(`modifierType:AttackTypeBoosterItem.${HeldItemNames[this.type]?.toLowerCase()}`); } getDescription(): string { @@ -147,7 +149,7 @@ export class AttackTypeBoosterHeldItem extends HeldItem { } getIcon(): string { - return `${HeldItems[this.type]?.toLowerCase()}`; + return `${HeldItemNames[this.type]?.toLowerCase()}`; } apply(stackCount: number, moveType: PokemonType, movePower: NumberHolder): void { @@ -167,6 +169,48 @@ export function applyAttackTypeBoosterHeldItem(pokemon: Pokemon, moveType: Pokem } } +export class TurnHealHeldItem extends HeldItem { + getName(): string { + return i18next.t("modifierType:ModifierType.LEFTOVERS.name") + " (new)"; + } + + getDescription(): string { + return i18next.t("modifierType:ModifierType.LEFTOVERS.description"); + } + + getIcon(): string { + return "leftovers"; + } + + apply(stackCount: number, pokemon: Pokemon): boolean { + if (!pokemon.isFullHp()) { + globalScene.unshiftPhase( + new PokemonHealPhase( + pokemon.getBattlerIndex(), + toDmgValue(pokemon.getMaxHp() / 16) * stackCount, + i18next.t("modifier:turnHealApply", { + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), + typeName: this.name, + }), + true, + ), + ); + return true; + } + return false; + } +} + +export function applyTurnHealHeldItem(pokemon: Pokemon) { + if (pokemon) { + for (const [item, props] of Object.entries(pokemon.heldItemManager.getHeldItems())) { + if (allHeldItems[item] instanceof TurnHealHeldItem) { + allHeldItems[item].apply(props.stack, pokemon); + } + } + } +} + export const allHeldItems = {}; export function initHeldItems() { @@ -175,4 +219,6 @@ export function initHeldItems() { const pokemonType = Number(typeKey) as PokemonType; allHeldItems[heldItemType] = new AttackTypeBoosterHeldItem(heldItemType, 99, pokemonType, 0.2); } + allHeldItems[HeldItems.LEFTOVERS] = new TurnHealHeldItem(HeldItems.LEFTOVERS, 4); + console.log(allHeldItems); } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 145d9264b8d..15689f79d6e 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -129,7 +129,7 @@ import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat"; import { StatusEffect } from "#enums/status-effect"; import i18next from "i18next"; import { timedEventManager } from "#app/global-event-manager"; -import type { HeldItems } from "#enums/held-items"; +import { HeldItems } from "#enums/held-items"; import { allHeldItems, attackTypeToHeldItem } from "./all-held-items"; import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants"; @@ -177,6 +177,10 @@ export class ModifierType { return i18next.t(`${this.localeKey}.description` as any); } + getIcon(): string { + return this.iconImage; + } + setTier(tier: ModifierTier): void { this.tier = tier; } @@ -423,17 +427,10 @@ export class PokemonHeldItemModifierType extends PokemonModifierType { export class PokemonHeldItemReward extends PokemonModifierType { public itemId: HeldItems; - constructor( - itemId: HeldItems, - localeKey: string, - iconImage: string, - newModifierFunc: NewModifierFunc, - group?: string, - soundName?: string, - ) { + constructor(itemId: HeldItems, newModifierFunc: NewModifierFunc, group?: string, soundName?: string) { super( - localeKey, - iconImage, + "", + "", newModifierFunc, (pokemon: PlayerPokemon) => { const hasItem = pokemon.heldItemManager.hasItem(this.itemId); @@ -465,9 +462,12 @@ export class PokemonHeldItemReward extends PokemonModifierType { } getDescription(): string { - // TODO: Need getTypeName? return allHeldItems[this.itemId].getDescription(); } + + getIcon(): string { + return allHeldItems[this.itemId].getIcon(); + } } export class TerastallizeModifierType extends PokemonModifierType { @@ -866,8 +866,7 @@ export class AttackTypeBoosterReward extends PokemonHeldItemReward implements Ge const itemId = attackTypeToHeldItem[moveType]; super( itemId, - "", - allHeldItems[itemId].getIcon(), + // Next argument is useless (_type, args) => new AttackTypeBoosterModifier(this, (args[0] as Pokemon).id, moveType, boostPercent), ); this.moveType = moveType; @@ -2016,7 +2015,7 @@ export type GeneratorModifierOverride = { type?: Nature; } | { - name: keyof Pick; + name: keyof Pick; type?: PokemonType; } | { @@ -2371,6 +2370,9 @@ export const modifierTypes = { (type, args) => new FlinchChanceModifier(type, (args[0] as Pokemon).id), ), + LEFTOVERS_REWARD: () => + new PokemonHeldItemReward(HeldItems.LEFTOVERS, (type, args) => new TurnHealModifier(type, (args[0] as Pokemon).id)), + LEFTOVERS: () => new PokemonHeldItemModifierType( "modifierType:ModifierType.LEFTOVERS", @@ -3058,7 +3060,7 @@ const modifierPool: ModifierPool = { [ModifierTier.ROGUE]: [ new WeightedModifierType(modifierTypes.ROGUE_BALL, () => (hasMaximumBalls(PokeballType.ROGUE_BALL) ? 0 : 16), 16), new WeightedModifierType(modifierTypes.RELIC_GOLD, skipInLastClassicWaveOrDefault(2)), - new WeightedModifierType(modifierTypes.LEFTOVERS, 3), + new WeightedModifierType(modifierTypes.LEFTOVERS_REWARD, 3), new WeightedModifierType(modifierTypes.SHELL_BELL, 3), new WeightedModifierType(modifierTypes.BERRY_POUCH, 4), new WeightedModifierType(modifierTypes.GRIP_CLAW, 5), diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 42e0155bdd8..2ec849be799 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -718,7 +718,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { const item = globalScene.add.sprite(16, this.virtualStackCount ? 8 : 16, "items"); item.setScale(0.5); item.setOrigin(0, 0.5); - item.setTexture("items", this.type.iconImage); + item.setTexture("items", this.type.getIcon()); container.add(item); const stackText = this.getIconStackText(); diff --git a/src/phases/select-modifier-phase.ts b/src/phases/select-modifier-phase.ts index bf5cad31cdd..9c62444d77c 100644 --- a/src/phases/select-modifier-phase.ts +++ b/src/phases/select-modifier-phase.ts @@ -161,8 +161,7 @@ export class SelectModifierPhase extends BattlePhase { //TODO: is the bang correct? if (modifierType instanceof PokemonHeldItemReward) { this.openGiveHeldItemMenu(modifierType, modifierSelectCallback); - } - if (modifierType instanceof FusePokemonModifierType) { + } else if (modifierType instanceof FusePokemonModifierType) { this.openFusionMenu(modifierType, cost, modifierSelectCallback); } else { this.openModifierMenu(modifierType, cost, modifierSelectCallback); @@ -235,26 +234,6 @@ export class SelectModifierPhase extends BattlePhase { return true; } - private openGiveHeldItemMenu(reward, modifierSelectCallback) { - const party = globalScene.getPlayerParty(); - const partyUiMode = PartyUiMode.MODIFIER; - globalScene.ui.setModeWithoutClear( - UiMode.PARTY, - partyUiMode, - -1, - (slotIndex: number, _option: PartyOption) => { - if (slotIndex < 6) { - globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { - party[slotIndex].heldItemManager.addHeldItem(reward.itemId); - }); - } else { - this.resetModifierSelect(modifierSelectCallback); - } - }, - reward.selectFilter, - ); - } - // Toggle reroll lock private toggleRerollLock() { const rerollCost = this.getRerollCost(globalScene.lockModifierTiers); @@ -370,6 +349,29 @@ export class SelectModifierPhase extends BattlePhase { ); } + private openGiveHeldItemMenu(reward, modifierSelectCallback) { + const party = globalScene.getPlayerParty(); + const partyUiMode = PartyUiMode.MODIFIER; + globalScene.ui.setModeWithoutClear( + UiMode.PARTY, + partyUiMode, + -1, + (slotIndex: number, _option: PartyOption) => { + if (slotIndex < 6) { + globalScene.ui.setMode(UiMode.MODIFIER_SELECT, this.isPlayer()).then(() => { + party[slotIndex].heldItemManager.addHeldItem(reward.itemId); + globalScene.ui.clearText(); + globalScene.ui.setMode(UiMode.MESSAGE); + super.end(); + }); + } else { + this.resetModifierSelect(modifierSelectCallback); + } + }, + reward.selectFilter, + ); + } + // Function that determines how many reward slots are available private getModifierCount(): number { const modifierCountHolder = new NumberHolder(3); diff --git a/src/phases/turn-end-phase.ts b/src/phases/turn-end-phase.ts index 756c497802b..1838f4cb8ae 100644 --- a/src/phases/turn-end-phase.ts +++ b/src/phases/turn-end-phase.ts @@ -6,7 +6,6 @@ import { TurnEndEvent } from "#app/events/battle-scene"; import type Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { - TurnHealModifier, EnemyTurnHealModifier, EnemyStatusEffectHealChanceModifier, TurnStatusEffectModifier, @@ -16,6 +15,7 @@ import i18next from "i18next"; import { FieldPhase } from "./field-phase"; import { PokemonHealPhase } from "./pokemon-heal-phase"; import { globalScene } from "#app/global-scene"; +import { applyTurnHealHeldItem } from "#app/modifier/all-held-items"; export class TurnEndPhase extends FieldPhase { start() { @@ -30,7 +30,7 @@ export class TurnEndPhase extends FieldPhase { if (!pokemon.switchOutStatus) { pokemon.lapseTags(BattlerTagLapseType.TURN_END); - globalScene.applyModifiers(TurnHealModifier, pokemon.isPlayer(), pokemon); + applyTurnHealHeldItem(pokemon); if (globalScene.arena.terrain?.terrainType === TerrainType.GRASSY && pokemon.isGrounded()) { globalScene.unshiftPhase( diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 7f5bf997f88..e70ef4b4e55 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -763,7 +763,7 @@ class ModifierOption extends Phaser.GameObjects.Container { this.add(this.itemContainer); const getItem = () => { - const item = globalScene.add.sprite(0, 0, "items", this.modifierTypeOption.type?.iconImage); + const item = globalScene.add.sprite(0, 0, "items", this.modifierTypeOption.type?.getIcon()); return item; };