diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 6dcc2842522..2f062667808 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2806,12 +2806,9 @@ export default class BattleScene extends SceneBase { modifiers.splice(modifiers.indexOf(modifier), 1); } } - const nullifiedModifiers = modifiers.filter(modifier => - !(modifier instanceof PokemonHeldItemModifier) || !modifier.isNullified - ); this.updatePartyForModifiers(player ? this.getPlayerParty() : this.getEnemyParty(), instant).then(() => { - (player ? this.modifierBar : this.enemyModifierBar).updateModifiers(nullifiedModifiers); + (player ? this.modifierBar : this.enemyModifierBar).updateModifiers(modifiers); if (!player) { this.updateUIPositions(); } diff --git a/src/data/move.ts b/src/data/move.ts index 349091a8b1f..7281cedec14 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -7660,7 +7660,6 @@ export class ExposedMoveAttr extends AddBattlerTagAttr { return true; } } - /** * Nullifies a Pokemon's held item until the battle ends. * Simulates the item being removed, but it just neutralizes it until the next battle @@ -7670,8 +7669,10 @@ export class ExposedMoveAttr extends AddBattlerTagAttr { * @see {@linkcode apply} */ export class NullifyHeldItemAttr extends MoveEffectAttr { - constructor() { + public turnCount: integer; + constructor(turnCount: integer) { super(false, { trigger: MoveEffectTrigger.HIT }); + this.turnCount = turnCount; } /** @@ -7692,19 +7693,20 @@ export class NullifyHeldItemAttr extends MoveEffectAttr { return false; } - const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable); + const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferable && !i.isNullified); if (heldItems.length) { const nullifiedItem = heldItems[user.randSeedInt(heldItems.length)]; - nullifiedItem.nullify(); - nullifiedItem.isTransferable = false; + nullifiedItem.nullify(this.turnCount); target.scene.updateModifiers(target.isPlayer()); user.scene.queueMessage(i18next.t("moveTriggers:corrosiveGasItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: nullifiedItem.type.name })); } - applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false); + if (move.id === Moves.CORROSIVE_GAS) { + applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false); + } return true; } @@ -10249,7 +10251,7 @@ export function initMoves() { .makesContact(false), new StatusMove(Moves.CORROSIVE_GAS, Type.POISON, 100, 40, -1, 0, 8) .target(MoveTarget.ALL_NEAR_OTHERS) - .attr(NullifyHeldItemAttr), + .attr(NullifyHeldItemAttr, 1), new StatusMove(Moves.COACHING, Type.FIGHTING, -1, 10, -1, 0, 8) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], 1) .target(MoveTarget.NEAR_ALLY) diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 90e14970294..3e4f1b20f5d 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -648,11 +648,17 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { public pokemonId: number; public isTransferable: boolean = true; public isNullified: boolean = false; + /** The maximum amount of battles the modifier will exist for */ + private maxBattles: number | undefined; + /** The current amount of battles the modifier will exist for */ + private battleCount: number | undefined; - constructor(type: ModifierType, pokemonId: number, stackCount?: number) { + constructor(type: ModifierType, pokemonId: number, stackCount?: number, maxBattles?: number, battleCount?: number) { super(type, stackCount); this.pokemonId = pokemonId; + this.maxBattles = maxBattles; + this.battleCount = battleCount ?? this.maxBattles; } abstract matchType(_modifier: Modifier): boolean; @@ -665,7 +671,8 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return [ this.pokemonId ]; } - nullify() { + nullify(count: number) { + this.setNewBattleCount(count); this.isNullified = true; } @@ -709,6 +716,11 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { item.setScale(0.5); item.setOrigin(0, 0.5); item.setTexture("items", this.type.iconImage); + if (this.isNullified) { + item.setTint(0x838383); + } else { + item.clearTint(); + } container.add(item); const stackText = this.getIconStackText(scene); @@ -724,6 +736,28 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { container.setScale(0.5); } + if (this.battleCount && this.maxBattles && this.getPokemon(scene)?.isPlayer()) { + let hue; + if (this.isNullified) { + hue = 0; + } else { + hue = Math.floor(120 * (this.battleCount / this.maxBattles) + 5); + } + + // Generates the color hex code with a constant saturation and lightness but varying hue + const typeHex = hslToHex(hue, 0.5, 0.9); + const strokeHex = hslToHex(hue, 0.7, 0.3); + + const battleCountText = addTextObject(scene, 27, 0, this.battleCount.toString(), TextStyle.PARTY, { + fontSize: "66px", + color: typeHex, + }); + battleCountText.setShadow(0, 0); + battleCountText.setStroke(strokeHex, 16); + battleCountText.setOrigin(1, 0); + container.add(battleCountText); + } + return container; } @@ -763,6 +797,57 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { } abstract getMaxHeldItemCount(pokemon?: Pokemon): number; + + /** + * Lapses the {@linkcode battleCount} by 1. + * @param _args passed arguments (not in use here) + * @returns `true` if the {@linkcode battleCount} is greater than 0 + */ + public lapse(..._args: unknown[]): void { + if (this.battleCount) { + this.battleCount--; + if (this.battleCount === 0 && this.isNullified) { + this.removeNullification(); + } + } + } + + getIconStackText(_scene: BattleScene, _virtual?: boolean): Phaser.GameObjects.BitmapText | null { + return null; + } + + getBattleCount(): number { + if (this.battleCount) { + return this.battleCount; + } else { + return -1; + } + } + + resetBattleCount(): void { + this.battleCount = this.maxBattles; + } + + /** + * Updates an existing modifier with a new `maxBattles` and `battleCount`. + */ + setNewBattleCount(count: number): void { + this.maxBattles = count; + this.battleCount = count; + } + + getMaxBattles(): number { + if (this.maxBattles) { + return this.maxBattles; + } else { + return -1; + } + } + + getBattleCountArgs(): any[] { + return [ this.maxBattles, this.battleCount ]; + } + } export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier { diff --git a/src/phases/battle-end-phase.ts b/src/phases/battle-end-phase.ts index 57fa020a41a..69818119c5b 100644 --- a/src/phases/battle-end-phase.ts +++ b/src/phases/battle-end-phase.ts @@ -43,15 +43,6 @@ export class BattleEndPhase extends BattlePhase { for (const pokemon of this.scene.getPokemonAllowedInBattle()) { applyPostBattleAbAttrs(PostBattleAbAttr, pokemon); - const heldItems = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier) as PokemonHeldItemModifier[]; - for (const item of heldItems) { - if (item.isNullified) { - item.removeNullification(); - if (item.isTransferable === false) { - item.isTransferable = true; - } - } - } } if (this.scene.currentBattle.moneyScattered) { @@ -60,14 +51,16 @@ export class BattleEndPhase extends BattlePhase { this.scene.clearEnemyHeldItemModifiers(); - const lapsingModifiers = this.scene.findModifiers(m => m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[]; + const lapsingModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isNullified || m instanceof LapsingPersistentModifier || m instanceof LapsingPokemonHeldItemModifier) as (PokemonHeldItemModifier | LapsingPersistentModifier | LapsingPokemonHeldItemModifier)[]; for (const m of lapsingModifiers) { const args: any[] = []; if (m instanceof LapsingPokemonHeldItemModifier) { args.push(this.scene.getPokemonById(m.pokemonId)); } if (!m.lapse(...args)) { - this.scene.removeModifier(m); + if (!(m instanceof PokemonHeldItemModifier)) { + this.scene.removeModifier(m); + } } } diff --git a/src/test/moves/corrosive_gas.test.ts b/src/test/moves/corrosive_gas.test.ts index a8ead57c5f6..5139c93f1f7 100644 --- a/src/test/moves/corrosive_gas.test.ts +++ b/src/test/moves/corrosive_gas.test.ts @@ -264,19 +264,12 @@ describe("Moves - Corrosive Gas", () => { ); await game.classicMode.startBattle([ Species.STAKATAKA, Species.SALAZZLE, Species.SALANDIT ]); const playerPokemon = game.scene.getPlayerField()!; - const enemyPokemon = game.scene.getEnemyField()!; const staka = playerPokemon[0]; - const karp1 = enemyPokemon[0]; - const karp2 = enemyPokemon[1]; staka.hp *= 0.5; - karp1.hp *= 0.5; - karp2.hp *= 0.5; const stakaHeldItems = staka.getHeldItems(); - const magikarpItems1 = karp1.getHeldItems(); - const magikarpItems2 = karp2.getHeldItems(); game.move.select(Moves.SPLASH); game.move.select(Moves.CORROSIVE_GAS); @@ -294,11 +287,6 @@ describe("Moves - Corrosive Gas", () => { await game.toNextTurn(); expect(stakaHeldItems[0].isNullified).toEqual(true); - expect(magikarpItems1[0].isNullified).toEqual(true); - expect(magikarpItems2[0].isNullified).toEqual(true); - expect(staka.getHpRatio()).toBeCloseTo(0.5); - expect(karp1.getHpRatio()).toBeCloseTo(0.5); - expect(karp2.getHpRatio()).toBeCloseTo(0.5); }); });