Reworked various effects that steal items

This commit is contained in:
Wlowscha 2025-06-09 14:43:00 +02:00
parent c607a73ebc
commit b271dc724b
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
5 changed files with 50 additions and 53 deletions

View File

@ -2717,11 +2717,12 @@ export default class BattleScene extends SceneBase {
}
/**
* Try to transfer a held item to another pokemon.
* Try to transfer a held item from source to target.
* If the recepient already has the maximum amount allowed for this item, the transfer is cancelled.
* The quantity to transfer is automatically capped at how much the recepient can take before reaching the maximum stack size for the item.
* A transfer that moves a quantity smaller than what is specified in the transferQuantity parameter is still considered successful.
* @param heldItemId {@linkcode HeldItemId} item to transfer
* @param source {@linkcode Pokemon} giver in this transfer
* @param target {@linkcode Pokemon} recepient in this transfer
* @param playSound `true` to play a sound when transferring the item
* @param transferQuantity How many items of the stack to transfer. Optional, defaults to `1`

View File

@ -35,7 +35,7 @@ import {
} from "#app/data/moves/move";
import { allMoves } from "../data-lists";
import { ArenaTagSide } from "#app/data/arena-tag";
import { BerryModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
import { BerryModifier, type PokemonHeldItemModifier } from "#app/modifier/modifier";
import { TerrainType } from "#app/data/terrain";
import {
SpeciesFormChangeAbilityTrigger,
@ -91,6 +91,7 @@ import type Move from "#app/data/moves/move";
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves";
import { HeldItemId } from "#enums/held-item-id";
import { allHeldItems } from "#app/items/all-held-items";
export class BlockRecoilDamageAttr extends AbAttr {
constructor() {
@ -2540,17 +2541,11 @@ export class AllyStatMultiplierAbAttr extends AbAttr {
* @extends AbAttr
*/
export class ExecutedMoveAbAttr extends AbAttr {
canApplyExecutedMove(
_pokemon: Pokemon,
_simulated: boolean,
): boolean {
canApplyExecutedMove(_pokemon: Pokemon, _simulated: boolean): boolean {
return true;
}
applyExecutedMove(
_pokemon: Pokemon,
_simulated: boolean,
): void {}
applyExecutedMove(_pokemon: Pokemon, _simulated: boolean): void {}
}
/**
@ -2558,7 +2553,7 @@ export class ExecutedMoveAbAttr extends AbAttr {
* @extends ExecutedMoveAbAttr
*/
export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr {
constructor(showAbility: boolean = false) {
constructor(showAbility = false) {
super(showAbility);
}
@ -2575,7 +2570,7 @@ export class GorillaTacticsAbAttr extends ExecutedMoveAbAttr {
export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
private stealCondition: PokemonAttackCondition | null;
private stolenItem?: PokemonHeldItemModifier;
private stolenItem?: HeldItemId;
constructor(stealCondition?: PokemonAttackCondition) {
super();
@ -2598,11 +2593,11 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
hitResult < HitResult.NO_EFFECT &&
(!this.stealCondition || this.stealCondition(pokemon, defender, move))
) {
const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable);
const heldItems = defender.heldItemManager.getTransferableHeldItems();
if (heldItems.length) {
// Ensure that the stolen item in testing is the same as when the effect is applied
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
if (globalScene.canTransferHeldItem(this.stolenItem, defender, pokemon)) {
return true;
}
}
@ -2620,28 +2615,21 @@ export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr {
_hitResult: HitResult,
_args: any[],
): void {
const heldItems = this.getTargetHeldItems(defender).filter(i => i.isTransferable);
const heldItems = defender.heldItemManager.getTransferableHeldItems();
if (!this.stolenItem) {
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
}
if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) {
if (globalScene.tryTransferHeldItem(this.stolenItem, defender, pokemon, false)) {
globalScene.phaseManager.queueMessage(
i18next.t("abilityTriggers:postAttackStealHeldItem", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
defenderName: defender.name,
stolenItemType: this.stolenItem.type.name,
stolenItemType: allHeldItems[this.stolenItem].name,
}),
);
}
this.stolenItem = undefined;
}
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === target.id,
target.isPlayer(),
) as PokemonHeldItemModifier[];
}
}
export class PostAttackApplyStatusEffectAbAttr extends PostAttackAbAttr {
@ -2762,7 +2750,7 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr {
export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
private condition?: PokemonDefendCondition;
private stolenItem?: PokemonHeldItemModifier;
private stolenItem?: HeldItemId;
constructor(condition?: PokemonDefendCondition) {
super();
@ -2780,10 +2768,10 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
_args: any[],
): boolean {
if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable);
const heldItems = attacker.heldItemManager.getTransferableHeldItems();
if (heldItems.length) {
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
if (globalScene.canTransferHeldItemModifier(this.stolenItem, pokemon)) {
if (globalScene.canTransferHeldItem(this.stolenItem, attacker, pokemon)) {
return true;
}
}
@ -2800,28 +2788,21 @@ export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr {
_hitResult: HitResult,
_args: any[],
): void {
const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable);
const heldItems = attacker.heldItemManager.getTransferableHeldItems();
if (!this.stolenItem) {
this.stolenItem = heldItems[pokemon.randBattleSeedInt(heldItems.length)];
}
if (globalScene.tryTransferHeldItemModifier(this.stolenItem, pokemon, false)) {
if (globalScene.tryTransferHeldItem(this.stolenItem, attacker, pokemon, false)) {
globalScene.phaseManager.queueMessage(
i18next.t("abilityTriggers:postDefendStealHeldItem", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
attackerName: attacker.name,
stolenItemType: this.stolenItem.type.name,
stolenItemType: allHeldItems[this.stolenItem].name,
}),
);
}
this.stolenItem = undefined;
}
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(
m => m instanceof PokemonHeldItemModifier && m.pokemonId === target.id,
target.isPlayer(),
) as PokemonHeldItemModifier[];
}
}
/**
@ -7782,7 +7763,7 @@ export function applyPreAttackAbAttrs(
export function applyExecutedMoveAbAttrs(
attrType: Constructor<ExecutedMoveAbAttr>,
pokemon: Pokemon,
simulated: boolean = false,
simulated = false,
...args: any[]
): void {
applyAbAttrsInternal<ExecutedMoveAbAttr>(

View File

@ -2559,16 +2559,18 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
return false;
}
const heldItems = this.getTargetHeldItems(target).filter((i) => i.isTransferable);
const heldItems = target.heldItemManager.getTransferableHeldItems();
if (!heldItems.length) {
return false;
}
const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
const highestItemTier = heldItems.map((m) => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
const tierHeldItems = heldItems.filter((m) => m.type.getOrInferTier(poolType) === highestItemTier);
const stolenItem = tierHeldItems[user.randBattleSeedInt(tierHeldItems.length)];
if (!globalScene.tryTransferHeldItemModifier(stolenItem, user, false)) {
const stolenItem = heldItems[user.randBattleSeedInt(heldItems.length)];
// const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD;
// const highestItemTier = heldItems.map((m) => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct?
// const tierHeldItems = heldItems.filter((m) => m.type.getOrInferTier(poolType) === highestItemTier);
// const stolenItem = tierHeldItems[user.randBattleSeedInt(tierHeldItems.length)];
if (!globalScene.tryTransferHeldItem(stolenItem, target, user, false)) {
return false;
}
@ -2576,18 +2578,13 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr {
return true;
}
getTargetHeldItems(target: Pokemon): PokemonHeldItemModifier[] {
return globalScene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& m.pokemonId === target.id, target.isPlayer()) as PokemonHeldItemModifier[];
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
const heldItems = this.getTargetHeldItems(target);
const heldItems = target.heldItemManager.getTransferableHeldItems();
return heldItems.length ? 5 : 0;
}
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
const heldItems = this.getTargetHeldItems(target);
const heldItems = target.heldItemManager.getTransferableHeldItems();
return heldItems.length ? -5 : 0;
}
}

View File

@ -35,6 +35,24 @@ export class PokemonItemManager {
return Object.keys(this.heldItems).map(k => Number(k));
}
getTransferableHeldItems(): number[] {
return Object.keys(this.heldItems)
.filter(k => allHeldItems[k].isTransferable)
.map(k => Number(k));
}
getStealableHeldItems(): number[] {
return Object.keys(this.heldItems)
.filter(k => allHeldItems[k].isStealable)
.map(k => Number(k));
}
getSuppressableHeldItems(): number[] {
return Object.keys(this.heldItems)
.filter(k => allHeldItems[k].isSuppressable)
.map(k => Number(k));
}
hasItem(itemType: HeldItemId): boolean {
return itemType in this.heldItems;
}

View File

@ -47,7 +47,7 @@ export abstract class ItemTransferHeldItem extends HeldItem {
// TODO: Change this logic to use held items
const transferredModifierTypes: HeldItemId[] = [];
const heldItems = targetPokemon.heldItemManager.getHeldItemKeys();
const heldItems = targetPokemon.heldItemManager.getTransferableHeldItems();
for (let i = 0; i < transferredItemCount; i++) {
if (!heldItems.length) {
@ -56,7 +56,7 @@ export abstract class ItemTransferHeldItem extends HeldItem {
const randItemIndex = pokemon.randBattleSeedInt(heldItems.length);
const randItem = heldItems[randItemIndex];
// TODO: Fix this after updating the various methods in battle-scene.ts
if (globalScene.tryTransferHeldItemModifier(randItem, pokemon, false)) {
if (globalScene.tryTransferHeldItem(randItem, targetPokemon, pokemon, false)) {
transferredModifierTypes.push(randItem);
heldItems.splice(randItemIndex, 1);
}