Improved item transfer

This commit is contained in:
Wlowscha 2025-06-09 18:14:26 +02:00
parent 928d8a8f97
commit ff73c5b038
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
4 changed files with 36 additions and 26 deletions

View File

@ -2759,8 +2759,9 @@ export default class BattleScene extends SceneBase {
}
const countTaken = Math.min(transferQuantity, itemStack, maxStackCount - matchingItemStack);
const data = source.heldItemManager[heldItemId].data;
source.heldItemManager.remove(heldItemId, countTaken);
target.heldItemManager.add(heldItemId, countTaken);
target.heldItemManager.add(heldItemId, countTaken, data);
if (source.heldItemManager.getStack(heldItemId) === 0 && itemLost) {
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);

View File

@ -70,7 +70,6 @@ import { allAbilities, allMoves } from "../data-lists";
import {
BerryModifier,
PokemonHeldItemModifier,
PokemonMultiHitModifier,
PreserveBerryModifier,
} from "../../modifier/modifier";
import type { BattlerIndex } from "../../battle";
@ -122,8 +121,10 @@ import { MultiHitType } from "#enums/MultiHitType";
import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves";
import { TrainerVariant } from "#app/field/trainer";
import { SelectBiomePhase } from "#app/phases/select-biome-phase";
import { applyHeldItems } from "#app/items/all-held-items";
import { allHeldItems, applyHeldItems } from "#app/items/all-held-items";
import { ITEM_EFFECT } from "#app/items/held-item";
import { berryTypeToHeldItem } from "#app/items/held-items/berry";
import { HeldItemId } from "#enums/held-item-id";
type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean;
type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean;
@ -816,7 +817,7 @@ export default class Move implements Localizable {
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !source.heldItemManager.hasItem(HeldItemId.MULTI_LENS)) {
power.value = 60;
}
@ -919,7 +920,7 @@ export default class Move implements Localizable {
* Returns `true` if this move can be given additional strikes
* by enhancing effects.
* Currently used for {@link https://bulbapedia.bulbagarden.net/wiki/Parental_Bond_(Ability) | Parental Bond}
* and {@linkcode PokemonMultiHitModifier | Multi-Lens}.
* and {@linkcode MultiHitHeldItem | Multi-Lens}.
* @param user The {@linkcode Pokemon} using the move
* @param restrictSpread `true` if the enhancing effect
* should not affect multi-target moves (default `false`)
@ -1506,7 +1507,7 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// first, determine if the hit is coming from multi lens or not
const lensCount = user.getHeldItems().find(i => i instanceof PokemonMultiHitModifier)?.getStackCount() ?? 0;
const lensCount = user.heldItemManager.getStack(HeldItemId.MULTI_LENS);
if (lensCount <= 0) {
// no multi lenses; we can just halve the target's hp and call it a day
(args[0] as NumberHolder).value = toDmgValue(target.hp / 2);
@ -2574,7 +2575,12 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
return false;
}
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: stolenItem.type.name }));
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:stoleItem",
{ pokemonName: getPokemonNameWithAffix(user),
targetName: getPokemonNameWithAffix(target),
itemName: allHeldItems[stolenItem].name
}
));
return true;
}
@ -2630,10 +2636,10 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
// Considers entire transferrable item pool by default (Knock Off).
// Otherwise only consider berries (Incinerate).
let heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable);
let heldItems = target.heldItemManager.getTransferableHeldItems();
if (this.berriesOnly) {
heldItems = heldItems.filter(m => m instanceof BerryModifier && m.pokemonId === target.id, target.isPlayer());
heldItems = heldItems.filter(m => m in Object.values(berryTypeToHeldItem));
}
if (!heldItems.length) {
@ -2647,9 +2653,11 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
globalScene.updateModifiers(target.isPlayer());
if (this.berriesOnly) {
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name }));
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:incineratedItem",
{ pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[removedItem].name }));
} else {
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:knockedOffItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name }));
globalScene.phaseManager.queueMessage(i18next.t("moveTriggers:knockedOffItem",
{ pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: allHeldItems[removedItem].name }));
}
return true;
@ -7932,14 +7940,14 @@ const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Po
const failIfGhostTypeCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => !target.isOfType(PokemonType.GHOST);
const failIfNoTargetHeldItemsCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.getHeldItems().filter(i => i.isTransferable)?.length > 0;
const failIfNoTargetHeldItemsCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => target.heldItemManager.getTransferableHeldItems().length > 0;
const attackedByItemMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => {
const heldItems = target.getHeldItems().filter(i => i.isTransferable);
const heldItems = target.heldItemManager.getTransferableHeldItems();
if (heldItems.length === 0) {
return "";
}
const itemName = heldItems[0]?.type?.name ?? "item";
const itemName = allHeldItems[heldItems[0]].name ?? "item";
const message: string = i18next.t("moveTriggers:attackedByItem", { pokemonName: getPokemonNameWithAffix(target), itemName: itemName });
return message;
};
@ -9123,7 +9131,7 @@ export function initMoves() {
.condition((user, target, move) => !target.status && !target.isSafeguarded(user))
.reflectable(),
new AttackMove(MoveId.KNOCK_OFF, PokemonType.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, 0, 3)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.getHeldItems().filter(i => i.isTransferable).length > 0 ? 1.5 : 1)
.attr(MovePowerMultiplierAttr, (user, target, move) => target.heldItemManager.getTransferableHeldItems().length > 0 ? 1.5 : 1)
.attr(RemoveHeldItemAttr, false)
.edgeCase(),
// Should not be able to remove held item if user faints due to Rough Skin, Iron Barbs, etc.
@ -9838,7 +9846,7 @@ export function initMoves() {
.condition((user, target, move) => !target.turnData.acted)
.attr(ForceLastAttr),
new AttackMove(MoveId.ACROBATICS, PokemonType.FLYING, MoveCategory.PHYSICAL, 55, 100, 15, -1, 0, 5)
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.getHeldItems().filter(i => i.isTransferable).reduce((v, m) => v + m.stackCount, 0))),
.attr(MovePowerMultiplierAttr, (user, target, move) => Math.max(1, 2 - 0.2 * user.heldItemManager.getTransferableHeldItems().reduce((v, m) => v + user.heldItemManager.getStack(m), 0))),
new StatusMove(MoveId.REFLECT_TYPE, PokemonType.NORMAL, -1, 15, -1, 0, 5)
.ignoresSubstitute()
.attr(CopyTypeAttr),

View File

@ -4,11 +4,13 @@ import type { FormChangeItem } from "#app/data/pokemon-forms";
import type { BASE_STAT_TOTAL_DATA } from "#app/items/held-items/base-stat-total";
import type { BASE_STAT_FLAT_DATA } from "#app/items/held-items/base-stat-flat";
type HELD_ITEM_DATA = BASE_STAT_TOTAL_DATA | BASE_STAT_FLAT_DATA;
interface HeldItemProperties {
stack: number;
disabled: boolean;
cooldown?: number;
data?: BASE_STAT_TOTAL_DATA | BASE_STAT_FLAT_DATA;
data?: HELD_ITEM_DATA;
}
type HeldItemPropertyMap = {
@ -66,14 +68,14 @@ export class PokemonItemManager {
return itemType in this.heldItems ? this.heldItems[itemType].stack : 0;
}
add(itemType: HeldItemId, addStack = 1) {
add(itemType: HeldItemId, addStack = 1, data?: HELD_ITEM_DATA) {
const maxStack = allHeldItems[itemType].getMaxStackCount();
if (this.hasItem(itemType)) {
// TODO: We may want an error message of some kind instead
this.heldItems[itemType].stack = Math.min(this.heldItems[itemType].stack + addStack, maxStack);
} else {
this.heldItems[itemType] = { stack: Math.min(addStack, maxStack), disabled: false };
this.heldItems[itemType] = { stack: Math.min(addStack, maxStack), disabled: false, data: data };
}
}

View File

@ -92,7 +92,6 @@ import {
ShinyRateBoosterModifier,
TempStatStageBoosterModifier,
TempCritBoosterModifier,
EvoTrackerModifier,
} from "#app/modifier/modifier";
import { PokeballType } from "#enums/pokeball";
import { Gender } from "#app/data/gender";
@ -242,7 +241,7 @@ import { loadMoveAnimations } from "#app/sprites/pokemon-asset-loader";
import { PokemonItemManager } from "./pokemon-held-item-manager";
import { applyHeldItems } from "#app/items/all-held-items";
import { ITEM_EFFECT } from "#app/items/held-item";
import type { HeldItemId } from "#enums/held-item-id";
import { HeldItemId } from "#enums/held-item-id";
export enum LearnMoveSituation {
MISC,
@ -4375,7 +4374,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.setScale(this.getSpriteScale());
this.loadAssets().then(() => {
this.calculateStats();
globalScene.updateModifiers(this.isPlayer(), true);
globalScene.updateModifiers(this.isPlayer());
Promise.all([this.updateInfo(), globalScene.updateFieldScale()]).then(() => resolve());
});
});
@ -5801,9 +5800,9 @@ export class PlayerPokemon extends Pokemon {
});
};
if (preEvolution.speciesId === SpeciesId.GIMMIGHOUL) {
const evotracker = this.getHeldItems().filter(m => m instanceof EvoTrackerModifier)[0] ?? null;
const evotracker = this.heldItemManager.hasItem(HeldItemId.GIMMIGHOUL_EVO_TRACKER);
if (evotracker) {
globalScene.removeModifier(evotracker);
this.heldItemManager.remove(HeldItemId.GIMMIGHOUL_EVO_TRACKER, 0, true);
}
}
if (!globalScene.gameMode.isDaily || this.metBiome > -1) {
@ -5911,7 +5910,7 @@ export class PlayerPokemon extends Pokemon {
const updateAndResolve = () => {
this.loadAssets().then(() => {
this.calculateStats();
globalScene.updateModifiers(true, true);
globalScene.updateModifiers(true);
this.updateInfo(true).then(() => resolve());
});
};
@ -5983,7 +5982,7 @@ export class PlayerPokemon extends Pokemon {
true,
) as PokemonHeldItemModifier[];
for (const modifier of fusedPartyMemberHeldModifiers) {
globalScene.tryTransferHeldItemModifier(modifier, this, false, modifier.getStackCount(), true, true, false);
globalScene.tryTransferHeldItem(modifier, pokemon, this, false, modifier.getStackCount(), true, false);
}
globalScene.updateModifiers(true);
globalScene.getPlayerParty().splice(fusedPartyMemberIndex, 1)[0];