Added multi lens and wide lens

This commit is contained in:
Wlowscha 2025-06-08 00:29:14 +02:00
parent ff24aae54b
commit 0b32cfb622
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
8 changed files with 151 additions and 158 deletions

View File

@ -68,10 +68,8 @@ import {
} from "../abilities/ability";
import { allAbilities, allMoves } from "../data-lists";
import {
AttackTypeBoosterModifier,
BerryModifier,
PokemonHeldItemModifier,
PokemonMoveAccuracyBoosterModifier,
PokemonMultiHitModifier,
PreserveBerryModifier,
} from "../../modifier/modifier";
@ -781,7 +779,7 @@ export default class Move implements Localizable {
const isOhko = this.hasAttr(OneHitKOAccuracyAttr);
if (!isOhko) {
globalScene.applyModifiers(PokemonMoveAccuracyBoosterModifier, user.isPlayer(), user, moveAccuracy);
applyHeldItems(ITEM_EFFECT.ACCURACY_BOOSTER, { pokemon: user, moveAccuracy: moveAccuracy });
}
if (globalScene.arena.weather?.weatherType === WeatherType.FOG) {

View File

@ -96,7 +96,6 @@ import {
PokemonBaseStatTotalModifier,
PokemonIncrementingStatModifier,
EvoTrackerModifier,
PokemonMultiHitModifier,
} from "#app/modifier/modifier";
import { PokeballType } from "#enums/pokeball";
import { Gender } from "#app/data/gender";
@ -3725,14 +3724,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
if (fixedDamage.value) {
const multiLensMultiplier = new NumberHolder(1);
globalScene.applyModifiers(
PokemonMultiHitModifier,
source.isPlayer(),
source,
move.id,
null,
multiLensMultiplier,
);
applyHeldItems(ITEM_EFFECT.MULTI_HIT, {
pokemon: source,
moveId: move.id,
damageMultiplier: multiLensMultiplier,
});
fixedDamage.value = toDmgValue(fixedDamage.value * multiLensMultiplier.value);
return {
@ -3776,14 +3772,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
/** Multiplier for moves enhanced by Multi-Lens and/or Parental Bond */
const multiStrikeEnhancementMultiplier = new NumberHolder(1);
globalScene.applyModifiers(
PokemonMultiHitModifier,
source.isPlayer(),
source,
move.id,
null,
multiStrikeEnhancementMultiplier,
);
applyHeldItems(ITEM_EFFECT.MULTI_HIT, {
pokemon: source,
moveId: move.id,
damageMultiplier: multiStrikeEnhancementMultiplier,
});
if (!ignoreSourceAbility) {
applyPreAttackAbAttrs(
AddSecondStrikeAbAttr,

View File

@ -6,6 +6,7 @@ import { SpeciesId } from "#enums/species-id";
import { Stat, type PermanentStat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import { ITEM_EFFECT } from "./held-item";
import { type ACCURACY_BOOST_PARAMS, AccuracyBoosterHeldItem } from "./held-items/accuracy-booster";
import {
type ATTACK_TYPE_BOOST_PARAMS,
AttackTypeBoosterHeldItem,
@ -25,6 +26,7 @@ import { type FLINCH_CHANCE_PARAMS, FlinchChanceHeldItem } from "./held-items/fl
import { type FRIENDSHIP_BOOST_PARAMS, FriendshipBoosterHeldItem } from "./held-items/friendship-booster";
import { type HIT_HEAL_PARAMS, HitHealHeldItem } from "./held-items/hit-heal";
import { InstantReviveHeldItem, type INSTANT_REVIVE_PARAMS } from "./held-items/instant-revive";
import { type MULTI_HIT_PARAMS, MultiHitHeldItem } from "./held-items/multi-hit";
import { type NATURE_WEIGHT_BOOST_PARAMS, NatureWeightBoosterHeldItem } from "./held-items/nature-weight-booster";
import {
ResetNegativeStatStageHeldItem,
@ -115,6 +117,8 @@ export function initHeldItems() {
allHeldItems[HeldItemId.KINGS_ROCK] = new FlinchChanceHeldItem(HeldItemId.KINGS_ROCK, 3, 10);
allHeldItems[HeldItemId.MYSTICAL_ROCK] = new FieldEffectHeldItem(HeldItemId.MYSTICAL_ROCK, 2);
allHeldItems[HeldItemId.SOUL_DEW] = new NatureWeightBoosterHeldItem(HeldItemId.SOUL_DEW, 10);
allHeldItems[HeldItemId.WIDE_LENS] = new AccuracyBoosterHeldItem(HeldItemId.WIDE_LENS, 3, 5);
allHeldItems[HeldItemId.MULTI_LENS] = new MultiHitHeldItem(HeldItemId.MULTI_LENS, 2);
allHeldItems[HeldItemId.FLAME_ORB] = new TurnEndStatusHeldItem(HeldItemId.FLAME_ORB, 1, StatusEffect.BURN);
allHeldItems[HeldItemId.TOXIC_ORB] = new TurnEndStatusHeldItem(HeldItemId.TOXIC_ORB, 1, StatusEffect.TOXIC);
@ -144,6 +148,8 @@ type APPLY_HELD_ITEMS_PARAMS = {
[ITEM_EFFECT.FIELD_EFFECT]: FIELD_EFFECT_PARAMS;
[ITEM_EFFECT.FRIENDSHIP_BOOSTER]: FRIENDSHIP_BOOST_PARAMS;
[ITEM_EFFECT.NATURE_WEIGHT_BOOSTER]: NATURE_WEIGHT_BOOST_PARAMS;
[ITEM_EFFECT.ACCURACY_BOOSTER]: ACCURACY_BOOST_PARAMS;
[ITEM_EFFECT.MULTI_HIT]: MULTI_HIT_PARAMS;
};
export function applyHeldItems<T extends ITEM_EFFECT>(effect: T, params: APPLY_HELD_ITEMS_PARAMS[T]) {

View File

@ -23,6 +23,8 @@ export const ITEM_EFFECT = {
FIELD_EFFECT: 15,
FRIENDSHIP_BOOSTER: 16,
NATURE_WEIGHT_BOOSTER: 17,
ACCURACY_BOOSTER: 18,
MULTI_HIT: 19,
} as const;
export type ITEM_EFFECT = (typeof ITEM_EFFECT)[keyof typeof ITEM_EFFECT];

View File

@ -0,0 +1,47 @@
import type Pokemon from "#app/field/pokemon";
import type { NumberHolder } from "#app/utils/common";
import type { HeldItemId } from "#enums/held-item-id";
import { HeldItem, ITEM_EFFECT } from "../held-item";
export interface ACCURACY_BOOST_PARAMS {
/** The pokemon with the item */
pokemon: Pokemon;
/** The amount of exp to gain */
moveAccuracy: NumberHolder;
}
export class AccuracyBoosterHeldItem extends HeldItem {
public effects: ITEM_EFFECT[] = [ITEM_EFFECT.ACCURACY_BOOSTER];
private accuracyAmount: number;
constructor(type: HeldItemId, maxStackCount = 1, accuracy: number) {
super(type, maxStackCount);
this.accuracyAmount = accuracy;
}
/**
* Checks if {@linkcode PokemonMoveAccuracyBoosterModifier} should be applied
* @param pokemon The {@linkcode Pokemon} to apply the move accuracy boost to
* @param moveAccuracy {@linkcode NumberHolder} holding the move accuracy boost
* @returns `true` if {@linkcode PokemonMoveAccuracyBoosterModifier} should be applied
*/
// override shouldApply(pokemon?: Pokemon, moveAccuracy?: NumberHolder): boolean {
// return super.shouldApply(pokemon, moveAccuracy) && !!moveAccuracy;
// }
/**
* Applies {@linkcode PokemonMoveAccuracyBoosterModifier}
* @param _pokemon The {@linkcode Pokemon} to apply the move accuracy boost to
* @param moveAccuracy {@linkcode NumberHolder} holding the move accuracy boost
* @returns always `true`
*/
apply(params: ACCURACY_BOOST_PARAMS): boolean {
const pokemon = params.pokemon;
const moveAccuracy = params.moveAccuracy;
const stackCount = pokemon.heldItemManager.getStack(this.type);
moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * stackCount;
return true;
}
}

View File

@ -0,0 +1,84 @@
import type Pokemon from "#app/field/pokemon";
import { HeldItem, ITEM_EFFECT } from "#app/items/held-item";
import { isNullOrUndefined, type NumberHolder } from "#app/utils/common";
import type { MoveId } from "#enums/move-id";
import { allMoves } from "#app/data/data-lists";
export interface MULTI_HIT_PARAMS {
pokemon: Pokemon;
moveId: MoveId;
count?: NumberHolder;
damageMultiplier?: NumberHolder;
}
/**
* Modifier used for held items, namely Toxic Orb and Flame Orb, that apply a
* set {@linkcode StatusEffect} at the end of a turn.
* @extends PokemonHeldItemModifier
* @see {@linkcode apply}
*/
export class MultiHitHeldItem extends HeldItem {
public effects: ITEM_EFFECT[] = [ITEM_EFFECT.MULTI_HIT];
/**
* For each stack, converts 25 percent of attack damage into an additional strike.
* @param pokemon The {@linkcode Pokemon} using the move
* @param moveId The {@linkcode MoveId | identifier} for the move being used
* @param count {@linkcode NumberHolder} holding the move's hit count for this turn
* @param damageMultiplier {@linkcode NumberHolder} holding a damage multiplier applied to a strike of this move
* @returns always `true`
*/
apply(params: MULTI_HIT_PARAMS): boolean {
const pokemon = params.pokemon;
const move = allMoves[params.moveId];
/**
* The move must meet Parental Bond's restrictions for this item
* to apply. This means
* - Only attacks are boosted
* - Multi-strike moves, charge moves, and self-sacrificial moves are not boosted
* (though Multi-Lens can still affect moves boosted by Parental Bond)
* - Multi-target moves are not boosted *unless* they can only hit a single Pokemon
* - Fling, Uproar, Rollout, Ice Ball, and Endeavor are not boosted
*/
if (!move.canBeMultiStrikeEnhanced(pokemon)) {
return false;
}
if (!isNullOrUndefined(params.count)) {
return this.applyHitCountBoost(pokemon, params.count);
}
if (!isNullOrUndefined(params.damageMultiplier)) {
return this.applyDamageModifier(pokemon, params.damageMultiplier);
}
return false;
}
/** Adds strikes to a move equal to the number of stacked Multi-Lenses */
private applyHitCountBoost(pokemon: Pokemon, count: NumberHolder): boolean {
const stackCount = pokemon.heldItemManager.getStack(this.type);
count.value += stackCount;
return true;
}
/**
* If applied to the first hit of a move, sets the damage multiplier
* equal to (1 - the number of stacked Multi-Lenses).
* Additional strikes beyond that are given a 0.25x damage multiplier
*/
private applyDamageModifier(pokemon: Pokemon, damageMultiplier: NumberHolder): boolean {
const stackCount = pokemon.heldItemManager.getStack(this.type);
if (pokemon.turnData.hitsLeft === pokemon.turnData.hitCount) {
// Reduce first hit by 25% for each stack count
damageMultiplier.value *= 1 - 0.25 * stackCount;
return true;
}
if (pokemon.turnData.hitCount - pokemon.turnData.hitsLeft !== stackCount + 1) {
// Deal 25% damage for each remaining Multi Lens hit
damageMultiplier.value *= 0.25;
return true;
}
// An extra hit not caused by Multi Lens -- assume it is Parental Bond
return false;
}
}

View File

@ -1,7 +1,6 @@
import { FusionSpeciesFormEvolution, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
import { getBerryEffectFunc, getBerryPredicate } from "#app/data/berry";
import { getLevelTotalExp } from "#app/data/exp";
import { allMoves } from "#app/data/data-lists";
import { MAX_PER_TYPE_POKEBALLS } from "#app/data/pokeball";
import { type FormChangeItem, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
import { getStatusEffectHealText } from "#app/data/status-effect";
@ -17,7 +16,6 @@ import { addTextObject, TextStyle } from "#app/ui/text";
import { BooleanHolder, hslToHex, isNullOrUndefined, NumberHolder, toDmgValue } from "#app/utils/common";
import { BattlerTagType } from "#enums/battler-tag-type";
import { BerryType } from "#enums/berry-type";
import type { MoveId } from "#enums/move-id";
import type { Nature } from "#enums/nature";
import type { PokeballType } from "#enums/pokeball";
import { SpeciesId } from "#enums/species-id";
@ -32,8 +30,6 @@ import {
type ModifierOverride,
type ModifierType,
type PokemonBaseStatTotalModifierType,
type PokemonMoveAccuracyBoosterModifierType,
type PokemonMultiHitModifierType,
type TerastallizeModifierType,
type TmModifierType,
getModifierType,
@ -1692,138 +1688,6 @@ export class ExpBalanceModifier extends PersistentModifier {
}
}
export class PokemonMoveAccuracyBoosterModifier extends PokemonHeldItemModifier {
public override type: PokemonMoveAccuracyBoosterModifierType;
private accuracyAmount: number;
constructor(type: PokemonMoveAccuracyBoosterModifierType, pokemonId: number, accuracy: number, stackCount?: number) {
super(type, pokemonId, stackCount);
this.accuracyAmount = accuracy;
}
matchType(modifier: Modifier): boolean {
if (modifier instanceof PokemonMoveAccuracyBoosterModifier) {
const pokemonAccuracyBoosterModifier = modifier as PokemonMoveAccuracyBoosterModifier;
return pokemonAccuracyBoosterModifier.accuracyAmount === this.accuracyAmount;
}
return false;
}
clone(): PersistentModifier {
return new PokemonMoveAccuracyBoosterModifier(this.type, this.pokemonId, this.accuracyAmount, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.accuracyAmount);
}
/**
* Checks if {@linkcode PokemonMoveAccuracyBoosterModifier} should be applied
* @param pokemon The {@linkcode Pokemon} to apply the move accuracy boost to
* @param moveAccuracy {@linkcode NumberHolder} holding the move accuracy boost
* @returns `true` if {@linkcode PokemonMoveAccuracyBoosterModifier} should be applied
*/
override shouldApply(pokemon?: Pokemon, moveAccuracy?: NumberHolder): boolean {
return super.shouldApply(pokemon, moveAccuracy) && !!moveAccuracy;
}
/**
* Applies {@linkcode PokemonMoveAccuracyBoosterModifier}
* @param _pokemon The {@linkcode Pokemon} to apply the move accuracy boost to
* @param moveAccuracy {@linkcode NumberHolder} holding the move accuracy boost
* @returns always `true`
*/
override apply(_pokemon: Pokemon, moveAccuracy: NumberHolder): boolean {
moveAccuracy.value = moveAccuracy.value + this.accuracyAmount * this.getStackCount();
return true;
}
getMaxHeldItemCount(_pokemon: Pokemon): number {
return 3;
}
}
export class PokemonMultiHitModifier extends PokemonHeldItemModifier {
public override type: PokemonMultiHitModifierType;
matchType(modifier: Modifier): boolean {
return modifier instanceof PokemonMultiHitModifier;
}
clone(): PersistentModifier {
return new PokemonMultiHitModifier(this.type, this.pokemonId, this.stackCount);
}
/**
* For each stack, converts 25 percent of attack damage into an additional strike.
* @param pokemon The {@linkcode Pokemon} using the move
* @param moveId The {@linkcode MoveId | identifier} for the move being used
* @param count {@linkcode NumberHolder} holding the move's hit count for this turn
* @param damageMultiplier {@linkcode NumberHolder} holding a damage multiplier applied to a strike of this move
* @returns always `true`
*/
override apply(
pokemon: Pokemon,
moveId: MoveId,
count: NumberHolder | null = null,
damageMultiplier: NumberHolder | null = null,
): boolean {
const move = allMoves[moveId];
/**
* The move must meet Parental Bond's restrictions for this item
* to apply. This means
* - Only attacks are boosted
* - Multi-strike moves, charge moves, and self-sacrificial moves are not boosted
* (though Multi-Lens can still affect moves boosted by Parental Bond)
* - Multi-target moves are not boosted *unless* they can only hit a single Pokemon
* - Fling, Uproar, Rollout, Ice Ball, and Endeavor are not boosted
*/
if (!move.canBeMultiStrikeEnhanced(pokemon)) {
return false;
}
if (!isNullOrUndefined(count)) {
return this.applyHitCountBoost(count);
}
if (!isNullOrUndefined(damageMultiplier)) {
return this.applyDamageModifier(pokemon, damageMultiplier);
}
return false;
}
/** Adds strikes to a move equal to the number of stacked Multi-Lenses */
private applyHitCountBoost(count: NumberHolder): boolean {
count.value += this.getStackCount();
return true;
}
/**
* If applied to the first hit of a move, sets the damage multiplier
* equal to (1 - the number of stacked Multi-Lenses).
* Additional strikes beyond that are given a 0.25x damage multiplier
*/
private applyDamageModifier(pokemon: Pokemon, damageMultiplier: NumberHolder): boolean {
if (pokemon.turnData.hitsLeft === pokemon.turnData.hitCount) {
// Reduce first hit by 25% for each stack count
damageMultiplier.value *= 1 - 0.25 * this.getStackCount();
return true;
}
if (pokemon.turnData.hitCount - pokemon.turnData.hitsLeft !== this.getStackCount() + 1) {
// Deal 25% damage for each remaining Multi Lens hit
damageMultiplier.value *= 0.25;
return true;
}
// An extra hit not caused by Multi Lens -- assume it is Parental Bond
return false;
}
getMaxHeldItemCount(_pokemon: Pokemon): number {
return 2;
}
}
export class PokemonFormChangeItemModifier extends PokemonHeldItemModifier {
public override type: FormChangeItemModifierType;
public formChangeItem: FormChangeItem;

View File

@ -57,7 +57,6 @@ import {
DamageMoneyRewardModifier,
EnemyAttackStatusEffectChanceModifier,
EnemyEndureChanceModifier,
PokemonMultiHitModifier,
} from "#app/modifier/modifier";
import { PokemonPhase } from "#app/phases/pokemon-phase";
import { BooleanHolder, isNullOrUndefined, NumberHolder } from "#app/utils/common";
@ -322,7 +321,7 @@ export class MoveEffectPhase extends PokemonPhase {
// If Parental Bond is applicable, add another hit
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, hitCount, null);
// If Multi-Lens is applicable, add hits equal to the number of held Multi-Lenses
globalScene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, move.id, hitCount);
applyHeldItems(ITEM_EFFECT.MULTI_HIT, { pokemon: user, moveId: move.id, count: hitCount });
// Set the user's relevant turnData fields to reflect the final hit count
user.turnData.hitCount = hitCount.value;
user.turnData.hitsLeft = hitCount.value;