Moved various files

This commit is contained in:
Wlowscha 2025-09-06 22:37:34 +02:00
parent 6e52baebee
commit c89b584b41
No known key found for this signature in database
GPG Key ID: 3C8F1AD330565D04
81 changed files with 1906 additions and 1803 deletions

View File

@ -3,7 +3,7 @@ import { getHeldItemCategory, HeldItemCategoryId } from "#enums/held-item-id";
import { MoveId } from "#enums/move-id";
import { RewardId } from "#enums/reward-id";
import { SpeciesId } from "#enums/species-id";
import { HeldItemReward } from "#items/reward";
import { HeldItemReward } from "#items/rewards/held-item-reward";
import { GameManager } from "#test/test-utils/game-manager";
import { generateRewardForTest } from "#test/test-utils/reward-test-utils";
import Phaser from "phaser";

View File

@ -1,9 +1,9 @@
import type { PartyMemberStrength } from "#enums/party-member-strength";
import type { SpeciesId } from "#enums/species-id";
import type { EnemyPokemon } from "#field/pokemon";
import type { TrainerItemConfiguration } from "#items/trainer-item-data-types";
import type { TrainerConfig } from "#trainers/trainer-config";
import type { TrainerPartyTemplate } from "#trainers/trainer-party-template";
import type { TrainerItemConfiguration } from "./trainer-item-data-types";
export type PartyTemplateFunc = () => TrainerPartyTemplate;
export type PartyMemberFunc = (level: number, strength: PartyMemberStrength) => EnemyPokemon;

View File

@ -79,19 +79,11 @@ import { PokemonSpriteSparkleHandler } from "#field/pokemon-sprite-sparkle-handl
import { Trainer } from "#field/trainer";
import { applyHeldItems } from "#items/all-held-items";
import { applyTrainerItems } from "#items/all-trainer-items";
import type { HeldItemConfiguration } from "#items/held-item-data-types";
import { assignEnemyHeldItemsForWave, assignItemsFromConfiguration } from "#items/held-item-pool";
import type { MatchExact, Reward } from "#items/reward";
import type { EnemyAttackStatusEffectChanceTrainerItem } from "#items/trainer-item";
import {
isTrainerItemPool,
isTrainerItemSpecs,
type TrainerItemConfiguration,
type TrainerItemSaveData,
} from "#items/trainer-item-data-types";
import { TrainerItemManager } from "#items/trainer-item-manager";
import type { TrainerItemEffectParamMap } from "#items/trainer-item-parameter";
import { getNewTrainerItemFromPool } from "#items/trainer-item-pool";
import type { EnemyAttackStatusEffectChanceTrainerItem } from "#items/trainer-items/enemy-tokens";
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
import { allMysteryEncounters, mysteryEncountersByBiome } from "#mystery-encounters/mystery-encounters";
@ -110,7 +102,15 @@ import type { TrainerData } from "#system/trainer-data";
import type { Voucher } from "#system/voucher";
import { vouchers } from "#system/voucher";
import { trainerConfigs } from "#trainers/trainer-config";
import type { HeldItemConfiguration } from "#types/held-item-data-types";
import type { Localizable } from "#types/locales";
import {
isTrainerItemPool,
isTrainerItemSpecs,
type TrainerItemConfiguration,
type TrainerItemSaveData,
} from "#types/trainer-item-data-types";
import type { TrainerItemEffectParamMap } from "#types/trainer-item-parameter";
import { AbilityBar } from "#ui/ability-bar";
import { ArenaFlyout } from "#ui/arena-flyout";
import { CandyBar } from "#ui/candy-bar";

View File

@ -15,7 +15,6 @@ import { TrainerSlot } from "#enums/trainer-slot";
import type { MysteryEncounterSpriteConfig } from "#field/mystery-encounter-intro";
import type { Pokemon } from "#field/pokemon";
import { EnemyPokemon } from "#field/pokemon";
import type { HeldItemConfiguration, HeldItemSpecs, PokemonItemMap } from "#items/held-item-data-types";
import { getPartyBerries } from "#items/item-utility";
import { PokemonMove } from "#moves/pokemon-move";
import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils";
@ -31,6 +30,7 @@ import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import { HeldItemRequirement } from "#mystery-encounters/mystery-encounter-requirements";
import type { HeldItemConfiguration, HeldItemSpecs, PokemonItemMap } from "#types/held-item-data-types";
import { pickWeightedIndex, randInt } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";
import i18next from "i18next";

View File

@ -2,6 +2,7 @@ import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
import { globalScene } from "#app/global-scene";
import { EncounterBattleAnim } from "#data/battle-anims";
import { allAbilities } from "#data/data-lists";
import { getHeldItemTier } from "#data/items/held-item-default-tiers";
import { CustomPokemonData } from "#data/pokemon-data";
import { AbilityId } from "#enums/ability-id";
import { BattlerIndex } from "#enums/battler-index";
@ -21,7 +22,6 @@ import { SpeciesId } from "#enums/species-id";
import { TrainerType } from "#enums/trainer-type";
import { UiMode } from "#enums/ui-mode";
import type { PlayerPokemon } from "#field/pokemon";
import { getHeldItemTier } from "#items/held-item-default-tiers";
import { assignItemsFromConfiguration } from "#items/held-item-pool";
import { PokemonMove } from "#moves/pokemon-move";
import { showEncounterDialogue, showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";

View File

@ -7,13 +7,13 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import type { PokemonType } from "#enums/pokemon-type";
import { RewardId } from "#enums/reward-id";
import { SpeciesId } from "#enums/species-id";
import type { HeldItemConfiguration } from "#items/held-item-data-types";
import type { EnemyPartyConfig, EnemyPokemonConfig } from "#mystery-encounters/encounter-phase-utils";
import { initBattleWithEnemyConfig, leaveEncounterWithoutBattle } from "#mystery-encounters/encounter-phase-utils";
import { getRandomPlayerPokemon, getRandomSpeciesByStarterCost } from "#mystery-encounters/encounter-pokemon-utils";
import type { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterBuilder } from "#mystery-encounters/mystery-encounter";
import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encounter-option";
import type { HeldItemConfiguration } from "#types/held-item-data-types";
import { isNullOrUndefined, randSeedInt } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";

View File

@ -8,9 +8,10 @@ import { RewardPoolType } from "#enums/reward-pool-type";
import { RarityTier } from "#enums/reward-tier";
import { TrainerItemId } from "#enums/trainer-item-id";
import type { Pokemon } from "#field/pokemon";
import type { RewardOption, TrainerItemReward } from "#items/reward";
import type { RewardOption } from "#items/reward";
import { generatePlayerRewardOptions, generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils";
import { isTmReward } from "#items/reward-utils";
import type { TrainerItemReward } from "#items/rewards/trainer-item-reward";
import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig } from "#mystery-encounters/encounter-phase-utils";
import {

View File

@ -3,6 +3,7 @@ import { timedEventManager } from "#app/global-event-manager";
import { globalScene } from "#app/global-scene";
import { allHeldItems, allSpecies } from "#data/data-lists";
import { Gender, getGenderSymbol } from "#data/gender";
import { getHeldItemTier } from "#data/items/held-item-default-tiers";
import { getNatureName } from "#data/nature";
import { getPokeballAtlasKey, getPokeballTintColor } from "#data/pokeball";
import type { PokemonSpecies } from "#data/pokemon-species";
@ -21,7 +22,6 @@ import { TrainerType } from "#enums/trainer-type";
import { doShinySparkleAnim } from "#field/anims";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { EnemyPokemon } from "#field/pokemon";
import { getHeldItemTier } from "#items/held-item-default-tiers";
import type { RewardOption } from "#items/reward";
import { generatePlayerRewardOptions, generateRewardPoolWeights, getRewardPoolForType } from "#items/reward-pool-utils";
import { isTmReward } from "#items/reward-utils";

View File

@ -11,7 +11,6 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { PokeballType } from "#enums/pokeball";
import { Stat } from "#enums/stat";
import type { EnemyPokemon, Pokemon } from "#field/pokemon";
import type { HeldItemSpecs } from "#items/held-item-data-types";
import { getPartyBerries } from "#items/item-utility";
import { PokemonMove } from "#moves/pokemon-move";
import { queueEncounterMessage } from "#mystery-encounters/encounter-dialogue-utils";
@ -34,6 +33,7 @@ import { MysteryEncounterOptionBuilder } from "#mystery-encounters/mystery-encou
import { HeldItemRequirement, MoveRequirement } from "#mystery-encounters/mystery-encounter-requirements";
import { CHARMING_MOVES } from "#mystery-encounters/requirement-groups";
import { PokemonData } from "#system/pokemon-data";
import type { HeldItemSpecs } from "#types/held-item-data-types";
import { isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common";
/** the i18n namespace for the encounter */

View File

@ -19,7 +19,6 @@ import { StatusEffect } from "#enums/status-effect";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { TrainerType } from "#enums/trainer-type";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import type { HeldItemConfiguration } from "#items/held-item-data-types";
import { PokemonMove } from "#moves/pokemon-move";
import { showEncounterText } from "#mystery-encounters/encounter-dialogue-utils";
import type { EnemyPartyConfig, EnemyPokemonConfig } from "#mystery-encounters/encounter-phase-utils";
@ -40,6 +39,7 @@ import { achvs } from "#system/achv";
import { PokemonData } from "#system/pokemon-data";
import { trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyTemplate } from "#trainers/trainer-party-template";
import type { HeldItemConfiguration } from "#types/held-item-data-types";
import { isNullOrUndefined, NumberHolder, randSeedInt, randSeedShuffle } from "#utils/common";
import { getPokemonSpecies } from "#utils/pokemon-utils";

View File

@ -20,7 +20,6 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { TrainerType } from "#enums/trainer-type";
import { TrainerVariant } from "#enums/trainer-variant";
import type { EnemyPokemon } from "#field/pokemon";
import type { HeldItemConfiguration } from "#items/held-item-data-types";
import { PokemonMove } from "#moves/pokemon-move";
import { getIsInitialized, initI18n } from "#plugins/i18n";
import type { EvilTeam } from "#trainers/evil-admin-trainer-pools";
@ -33,6 +32,7 @@ import {
TrainerPartyTemplate,
trainerPartyTemplates,
} from "#trainers/trainer-party-template";
import type { HeldItemConfiguration } from "#types/held-item-data-types";
import type { SilentReward } from "#types/rewards";
import type {
GenAIFunc,

View File

@ -112,7 +112,6 @@ import { UiMode } from "#enums/ui-mode";
import { WeatherType } from "#enums/weather-type";
import { doShinySparkleAnim } from "#field/anims";
import { applyHeldItems } from "#items/all-held-items";
import type { HeldItemConfiguration } from "#items/held-item-data-types";
import { HeldItemManager } from "#items/held-item-manager";
import { assignItemsFromConfiguration } from "#items/held-item-pool";
import { applyMoveAttrs } from "#moves/apply-attrs";
@ -129,6 +128,7 @@ import { RibbonData } from "#system/ribbons/ribbon-data";
import { awardRibbonsToSpeciesLine } from "#system/ribbons/ribbon-methods";
import type { AbAttrMap, AbAttrString, TypeMultiplierAbAttrParams } from "#types/ability-types";
import type { DamageCalculationResult, DamageResult } from "#types/damage-result";
import type { HeldItemConfiguration } from "#types/held-item-data-types";
import type { IllusionData } from "#types/illusion-data";
import type { TurnMove } from "#types/turn-move";
import { BattleInfo } from "#ui/battle-info";

View File

@ -12,11 +12,11 @@ import { TrainerSlot } from "#enums/trainer-slot";
import { TrainerType } from "#enums/trainer-type";
import { TrainerVariant } from "#enums/trainer-variant";
import type { EnemyPokemon } from "#field/pokemon";
import type { TrainerItemConfiguration } from "#items/trainer-item-data-types";
import { getIsInitialized, initI18n } from "#plugins/i18n";
import type { TrainerConfig } from "#trainers/trainer-config";
import { trainerConfigs } from "#trainers/trainer-config";
import { TrainerPartyCompoundTemplate, type TrainerPartyTemplate } from "#trainers/trainer-party-template";
import type { TrainerItemConfiguration } from "#types/trainer-item-data-types";
import { randSeedInt, randSeedItem } from "#utils/common";
import { getRandomLocaleEntry } from "#utils/i18n";
import { getPokemonSpecies } from "#utils/pokemon-utils";

View File

@ -33,8 +33,8 @@ import { EvolutionStatBoostHeldItem, SpeciesStatBoostHeldItem } from "#items/sta
import { SurviveChanceHeldItem } from "#items/survive-chance";
import { TurnEndHealHeldItem } from "#items/turn-end-heal";
import { TurnEndStatusHeldItem } from "#items/turn-end-status";
import type { HeldItemEffectParamMap } from "#types/held-item-parameter";
import { getEnumValues } from "#utils/enums";
import type { HeldItemEffectParamMap } from "./held-item-parameter";
export function initHeldItems() {
for (const berry of getEnumValues(BerryType)) {

View File

@ -3,40 +3,33 @@ import { RewardId } from "#enums/reward-id";
import { RarityTier } from "#enums/reward-tier";
import { TrainerItemId } from "#enums/trainer-item-id";
import { VoucherType } from "#system/voucher";
import { EmptyReward, type Reward, type RewardGenerator } from "./reward";
import { EvolutionItemRewardGenerator } from "./rewards/evolution-item";
import { FormChangeItemRewardGenerator } from "./rewards/form-change";
import { FusePokemonReward } from "./rewards/fuse";
import {
AddMoneyReward,
AddPokeballReward,
AddVoucherReward,
AllPokemonFullReviveReward,
AllPokemonLevelIncrementReward,
AttackTypeBoosterRewardGenerator,
BaseStatBoosterRewardGenerator,
BerryRewardGenerator,
EvolutionItemRewardGenerator,
FormChangeItemRewardGenerator,
FusePokemonReward,
LapsingTrainerItemReward,
MintRewardGenerator,
NoneReward,
PokemonAllMovePpRestoreReward,
PokemonHpRestoreReward,
PokemonLevelIncrementReward,
PokemonPpRestoreReward,
PokemonPpUpReward,
PokemonReviveReward,
PokemonStatusHealReward,
RememberMoveReward,
type Reward,
type RewardGenerator,
SpeciesStatBoosterRewardGenerator,
TempStatStageBoosterRewardGenerator,
TeraTypeRewardGenerator,
TmRewardGenerator,
} from "./reward";
} from "./rewards/held-item-reward";
import { AllPokemonLevelIncrementReward, PokemonLevelIncrementReward } from "./rewards/level-increment";
import { AddMoneyReward } from "./rewards/money";
import { MintRewardGenerator } from "./rewards/nature-change";
import { AddPokeballReward } from "./rewards/pokeball";
import { PokemonAllMovePpRestoreReward, PokemonPpRestoreReward } from "./rewards/pp-restore";
import { PokemonPpUpReward } from "./rewards/pp-up";
import { RememberMoveReward } from "./rewards/remember-move";
import { AllPokemonFullReviveReward, PokemonHpRestoreReward, PokemonReviveReward } from "./rewards/restore";
import { SpeciesStatBoosterRewardGenerator } from "./rewards/species-stat-booster";
import { PokemonStatusHealReward } from "./rewards/status-heal";
import { TeraTypeRewardGenerator } from "./rewards/tera-type";
import { TmRewardGenerator } from "./rewards/tm";
import { LapsingTrainerItemReward, TempStatStageBoosterRewardGenerator } from "./rewards/trainer-item-reward";
import { AddVoucherReward } from "./rewards/voucher";
// TODO: Move to `reward-utils.ts` and un-exportt
export const allRewards = {
[RewardId.NONE]: new NoneReward(),
[RewardId.NONE]: new EmptyReward(),
// Pokeball rewards
[RewardId.POKEBALL]: new AddPokeballReward("pb", PokeballType.POKEBALL, 5, RewardId.POKEBALL),

View File

@ -5,14 +5,6 @@ import type { TrainerItemEffect } from "#enums/trainer-item-effect";
import { TrainerItemId } from "#enums/trainer-item-id";
import {
CriticalCatchChanceBoosterTrainerItem,
DoubleBattleChanceBoosterTrainerItem,
EnemyAttackStatusEffectChanceTrainerItem,
EnemyDamageBoosterTrainerItem,
EnemyDamageReducerTrainerItem,
EnemyEndureChanceTrainerItem,
EnemyFusionChanceTrainerItem,
EnemyStatusEffectHealChanceTrainerItem,
EnemyTurnHealTrainerItem,
ExpBoosterTrainerItem,
ExtraRewardTrainerItem,
HealingBoosterTrainerItem,
@ -22,14 +14,26 @@ import {
MoneyMultiplierTrainerItem,
PreserveBerryTrainerItem,
ShinyRateBoosterTrainerItem,
TrainerItem,
} from "#items/trainer-item";
import type { TrainerItemEffectParamMap } from "#types/trainer-item-parameter";
import type { TrainerItemManager } from "./trainer-item-manager";
import {
EnemyAttackStatusEffectChanceTrainerItem,
EnemyDamageBoosterTrainerItem,
EnemyDamageReducerTrainerItem,
EnemyEndureChanceTrainerItem,
EnemyFusionChanceTrainerItem,
EnemyStatusEffectHealChanceTrainerItem,
EnemyTurnHealTrainerItem,
} from "./trainer-items/enemy-tokens";
import { DoubleBattleChanceBoosterTrainerItem } from "./trainer-items/lure";
import {
TempAccuracyBoosterTrainerItem,
TempCritBoosterTrainerItem,
TempStatStageBoosterTrainerItem,
TrainerItem,
tempStatToTrainerItem,
} from "#items/trainer-item";
import type { TrainerItemManager } from "./trainer-item-manager";
import type { TrainerItemEffectParamMap } from "./trainer-item-parameter";
} from "./trainer-items/x-items";
export function initTrainerItems() {
allTrainerItems[TrainerItemId.MAP] = new TrainerItem(TrainerItemId.MAP, 1);

View File

@ -13,7 +13,7 @@ import {
type HeldItemSaveData,
type HeldItemSpecs,
isHeldItemSpecs,
} from "#items/held-item-data-types";
} from "#types/held-item-data-types";
export class HeldItemManager {
// TODO: There should be a way of making these private...

View File

@ -7,7 +7,7 @@ import { RarityTier } from "#enums/reward-tier";
import { PERMANENT_STATS } from "#enums/stat";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import { attackTypeToHeldItem } from "#items/attack-type-booster";
import { permanentStatToHeldItem } from "#items/base-stat-booster";
import { permanentStatToHeldItem } from "#items/base-stat-multiply";
import { berryTypeToHeldItem } from "#items/berry";
import {
type HeldItemConfiguration,
@ -19,7 +19,7 @@ import {
isHeldItemCategoryEntry,
isHeldItemPool,
isHeldItemSpecs,
} from "#items/held-item-data-types";
} from "#types/held-item-data-types";
import { coerceArray, isNullOrUndefined, pickWeightedIndex, randSeedInt } from "#utils/common";
import { getEnumValues } from "#utils/enums";

View File

@ -3,9 +3,9 @@ import { globalScene } from "#app/global-scene";
import type { HeldItemEffect } from "#enums/held-item-effect";
import { type HeldItemId, HeldItemNames } from "#enums/held-item-id";
import type { Pokemon } from "#field/pokemon";
import type { HeldItemEffectParamMap } from "#types/held-item-parameter";
import type { UniqueArray } from "#utils/common";
import i18next from "i18next";
import type { HeldItemEffectParamMap } from "./held-item-parameter";
export abstract class HeldItemBase {
public type: HeldItemId;

View File

@ -1,7 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id";
import { HeldItem } from "#items/held-item";
import type { AccuracyBoostParams } from "#items/held-item-parameter";
import type { AccuracyBoostParams } from "#types/held-item-parameter";
/**
* @sealed

View File

@ -2,7 +2,7 @@ import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId, HeldItemNames } from "#enums/held-item-id";
import { PokemonType } from "#enums/pokemon-type";
import { HeldItem } from "#items/held-item";
import type { AttackTypeBoostParams } from "#items/held-item-parameter";
import type { AttackTypeBoostParams } from "#types/held-item-parameter";
import i18next from "i18next";
interface AttackTypeToHeldItemMap {

View File

@ -3,7 +3,7 @@ import type { HeldItemId } from "#enums/held-item-id";
import { Stat } from "#enums/stat";
import type { Pokemon } from "#field/pokemon";
import { HeldItem } from "#items/held-item";
import type { BaseStatParams } from "#items/held-item-parameter";
import type { BaseStatParams } from "#types/held-item-parameter";
import i18next from "i18next";
/**

View File

@ -2,7 +2,7 @@ import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItemId } from "#enums/held-item-id";
import { getStatKey, type PermanentStat, Stat } from "#enums/stat";
import { HeldItem } from "#items/held-item";
import type { BaseStatParams } from "#items/held-item-parameter";
import type { BaseStatParams } from "#types/held-item-parameter";
import i18next from "i18next";
type PermanentStatToHeldItemMap = {

View File

@ -1,6 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { BatonParams } from "#items/held-item-parameter";
import type { BatonParams } from "#types/held-item-parameter";
export class BatonHeldItem extends HeldItem<[typeof HeldItemEffect.BATON]> {
public readonly effects = [HeldItemEffect.BATON] as const;
@ -9,7 +9,7 @@ export class BatonHeldItem extends HeldItem<[typeof HeldItemEffect.BATON]> {
* Applies {@linkcode SwitchEffectTransferModifier}
* @returns always `true`
*/
apply(_effect: typeof HeldItemEffect.BATON, { pokemon }: BatonParams): true {
apply(_effect: typeof HeldItemEffect.BATON, _params: BatonParams): true {
return true;
}
}

View File

@ -6,7 +6,7 @@ import { HeldItemId } from "#enums/held-item-id";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { BerryUsedEvent } from "#events/battle-scene";
import { ConsumableHeldItem } from "#items/held-item";
import type { BerryParams } from "#items/held-item-parameter";
import type { BerryParams } from "#types/held-item-parameter";
import type { ObjectValues } from "#types/type-helpers";
import { BooleanHolder } from "#utils/common";

View File

@ -3,7 +3,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { Command } from "#enums/command";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { BypassSpeedChanceParams } from "#items/held-item-parameter";
import type { BypassSpeedChanceParams } from "#types/held-item-parameter";
import i18next from "i18next";
/**

View File

@ -2,7 +2,7 @@ import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id";
import type { SpeciesId } from "#enums/species-id";
import { HeldItem } from "#items/held-item";
import type { CritBoostParams } from "#items/held-item-parameter";
import type { CritBoostParams } from "#types/held-item-parameter";
/**
* Modifier used for held items that apply critical-hit stage boost(s).

View File

@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
import { HeldItemEffect } from "#enums/held-item-effect";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { HeldItem } from "#items/held-item";
import type { DamageMoneyRewardParams } from "#items/held-item-parameter";
import type { DamageMoneyRewardParams } from "#types/held-item-parameter";
import { NumberHolder } from "#utils/common";
export class DamageMoneyRewardHeldItem extends HeldItem<[typeof HeldItemEffect.DAMAGE_MONEY_REWARD]> {

View File

@ -1,7 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id";
import { HeldItem } from "#items/held-item";
import type { ExpBoostParams } from "#items/held-item-parameter";
import type { ExpBoostParams } from "#types/held-item-parameter";
import i18next from "i18next";
export class ExpBoosterHeldItem extends HeldItem<[typeof HeldItemEffect.EXP_BOOSTER]> {

View File

@ -1,6 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { FieldEffectParams } from "#items/held-item-parameter";
import type { FieldEffectParams } from "#types/held-item-parameter";
/**
* Modifier used for held items, namely Mystical Rock, that extend the

View File

@ -1,7 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id";
import { HeldItem } from "#items/held-item";
import type { FlinchChanceParams } from "#items/held-item-parameter";
import type { FlinchChanceParams } from "#types/held-item-parameter";
/**
* Modifier used for held items, namely Toxic Orb and Flame Orb, that apply a

View File

@ -1,6 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { FriendshipBoostParams } from "#items/held-item-parameter";
import type { FriendshipBoostParams } from "#types/held-item-parameter";
import i18next from "i18next";
export class FriendshipBoosterHeldItem extends HeldItem<[typeof HeldItemEffect.FRIENDSHIP_BOOSTER]> {

View File

@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { HitHealParams } from "#items/held-item-parameter";
import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import type { HitHealParams } from "#types/held-item-parameter";
import { toDmgValue } from "#utils/common";
import i18next from "i18next";

View File

@ -3,8 +3,8 @@ import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import { ConsumableHeldItem } from "#items/held-item";
import type { InstantReviveParams } from "#items/held-item-parameter";
import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import type { InstantReviveParams } from "#types/held-item-parameter";
import { toDmgValue } from "#utils/common";
import i18next from "i18next";

View File

@ -5,7 +5,7 @@ import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id";
import { Pokemon } from "#field/pokemon";
import { type EffectTuple, HeldItem } from "#items/held-item";
import type { ItemStealParams } from "#items/held-item-parameter";
import type { ItemStealParams } from "#types/held-item-parameter";
import { coerceArray, randSeedFloat } from "#utils/common";
import i18next from "i18next";

View File

@ -1,7 +1,7 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { Stat } from "#enums/stat";
import { HeldItem } from "#items/held-item";
import type { StatBoostParams } from "#items/held-item-parameter";
import type { StatBoostParams } from "#types/held-item-parameter";
import i18next from "i18next";
/**

View File

@ -2,7 +2,7 @@ import { allMoves } from "#data/data-lists";
import { HeldItemEffect } from "#enums/held-item-effect";
import type { Pokemon } from "#field/pokemon";
import { HeldItem } from "#items/held-item";
import type { HeldItemEffectParamMap, MultiHitCountParams, MultiHitDamageParams } from "#items/held-item-parameter";
import type { HeldItemEffectParamMap, MultiHitCountParams, MultiHitDamageParams } from "#types/held-item-parameter";
import type { NumberHolder } from "#utils/common";
import i18next from "i18next";

View File

@ -1,6 +1,6 @@
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { NatureWeightBoostParams } from "#items/held-item-parameter";
import type { NatureWeightBoostParams } from "#types/held-item-parameter";
export class NatureWeightBoosterHeldItem extends HeldItem<[typeof HeldItemEffect.NATURE_WEIGHT_BOOSTER]> {
public readonly effects = [HeldItemEffect.NATURE_WEIGHT_BOOSTER] as const;

View File

@ -3,7 +3,7 @@ import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import { BATTLE_STATS } from "#enums/stat";
import { ConsumableHeldItem } from "#items/held-item";
import type { ResetNegativeStatStageParams } from "#items/held-item-parameter";
import type { ResetNegativeStatStageParams } from "#types/held-item-parameter";
import i18next from "i18next";
/**

View File

@ -5,7 +5,7 @@ import type { SpeciesId } from "#enums/species-id";
import type { Stat } from "#enums/stat";
import type { Pokemon } from "#field/pokemon";
import { HeldItem } from "#items/held-item";
import type { StatBoostParams } from "#items/held-item-parameter";
import type { StatBoostParams } from "#types/held-item-parameter";
/**
* Modifier used for held items that Applies {@linkcode Stat} boost(s)

View File

@ -2,7 +2,7 @@ import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { SurviveChanceParams } from "#items/held-item-parameter";
import type { SurviveChanceParams } from "#types/held-item-parameter";
import i18next from "i18next";
/**

View File

@ -2,8 +2,8 @@ import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { HeldItemEffect } from "#enums/held-item-effect";
import { HeldItem } from "#items/held-item";
import type { TurnEndHealParams } from "#items/held-item-parameter";
import { PokemonHealPhase } from "#phases/pokemon-heal-phase";
import type { TurnEndHealParams } from "#types/held-item-parameter";
import { toDmgValue } from "#utils/common";
import i18next from "i18next";

View File

@ -2,7 +2,7 @@ import { HeldItemEffect } from "#enums/held-item-effect";
import type { HeldItemId } from "#enums/held-item-id";
import type { StatusEffect } from "#enums/status-effect";
import { HeldItem } from "#items/held-item";
import type { TurnEndStatusParams } from "#items/held-item-parameter";
import type { TurnEndStatusParams } from "#types/held-item-parameter";
/**
* Modifier used for held items, namely Toxic Orb and Flame Orb, that apply a

View File

@ -1,9 +1,9 @@
import { globalScene } from "#app/global-scene";
import Overrides from "#app/overrides";
import type { Pokemon } from "#field/pokemon";
import type { HeldItemConfiguration } from "#items/held-item-data-types";
import { assignItemsFromConfiguration } from "#items/held-item-pool";
import type { TrainerItemConfiguration } from "#items/trainer-item-data-types";
import type { HeldItemConfiguration } from "#types/held-item-data-types";
import type { TrainerItemConfiguration } from "#types/trainer-item-data-types";
/**
* Uses either `MODIFIER_OVERRIDE` in overrides.ts to set {@linkcode PersistentModifier}s for either:

View File

@ -3,7 +3,7 @@ import { allHeldItems, allTrainerItems } from "#data/data-lists";
import { HeldItemCategoryId, type HeldItemId, isItemInCategory } from "#enums/held-item-id";
import type { TrainerItemId } from "#enums/trainer-item-id";
import type { Pokemon } from "#field/pokemon";
import type { PokemonItemMap } from "./held-item-data-types";
import type { PokemonItemMap } from "#types/held-item-data-types";
export const trainerItemSortFunc = (a: TrainerItemId, b: TrainerItemId): number => {
const itemNameMatch = allTrainerItems[a].name.localeCompare(allTrainerItems[b].name);

View File

@ -1,23 +1,17 @@
import { heldItemRarities } from "#data/items/held-item-default-tiers";
import { rewardRarities } from "#data/items/reward-defaults-tiers";
import { trainerItemRarities } from "#data/items/trainer-item-default-tiers";
import type { HeldItemId } from "#enums/held-item-id";
import { getRewardCategory, RewardCategoryId, RewardId } from "#enums/reward-id";
import type { RarityTier } from "#enums/reward-tier";
import type { TrainerItemId } from "#enums/trainer-item-id";
import { allRewards } from "#items/all-rewards";
import type { RewardPoolId, RewardSpecs } from "#types/rewards";
import { heldItemRarities } from "./held-item-default-tiers";
import {
HeldItemReward,
NoneReward,
type PokemonMoveReward,
type RememberMoveReward,
type Reward,
RewardGenerator,
RewardOption,
type TmReward,
TrainerItemReward,
} from "./reward";
import { rewardRarities } from "./reward-defaults-tiers";
import { trainerItemRarities } from "./trainer-item-default-tiers";
import { EmptyReward, type PokemonMoveReward, type Reward, RewardGenerator, RewardOption } from "./reward";
import { HeldItemReward } from "./rewards/held-item-reward";
import type { RememberMoveReward } from "./rewards/remember-move";
import type { TmReward } from "./rewards/tm";
import { TrainerItemReward } from "./rewards/trainer-item-reward";
export function isTmReward(reward: Reward): reward is TmReward {
return getRewardCategory(reward.id) === RewardCategoryId.TM;
@ -66,7 +60,7 @@ export function generateRewardOptionFromId<T extends RewardPoolId>(
const tempReward = allRewards[id] as Reward | RewardGenerator;
let reward = tempReward instanceof RewardGenerator ? tempReward.generateReward(pregenArgs) : tempReward;
if (!reward) {
reward = new NoneReward();
reward = new EmptyReward();
}
const tier = tierOverride ?? rewardRarities[id];
return new RewardOption(reward, upgradeCount, tier, cost);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,135 @@
import { globalScene } from "#app/global-scene";
import { EvolutionItem, FusionSpeciesFormEvolution, pokemonEvolutions } from "#balance/pokemon-evolutions";
import { SpeciesFormKey } from "#enums/species-form-key";
import { SpeciesId } from "#enums/species-id";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, RewardGenerator } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { randSeedItem } from "#utils/common";
import i18next from "i18next";
export class EvolutionItemReward extends PokemonReward {
public evolutionItem: EvolutionItem;
constructor(evolutionItem: EvolutionItem) {
super("", EvolutionItem[evolutionItem].toLowerCase(), (pokemon: PlayerPokemon) => {
if (
pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) &&
pokemonEvolutions[pokemon.species.speciesId].filter(e => e.validate(pokemon, false, this.evolutionItem))
.length &&
pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX
) {
return null;
}
if (
pokemon.isFusion() &&
pokemon.fusionSpecies &&
pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) &&
pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.validate(pokemon, true, this.evolutionItem))
.length &&
pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX
) {
return null;
}
return PartyUiHandler.NoEffectMessage;
});
this.evolutionItem = evolutionItem;
}
get name(): string {
return i18next.t(`modifierType:EvolutionItem.${EvolutionItem[this.evolutionItem]}`);
}
get description(): string {
return i18next.t("modifierType:ModifierType.EvolutionItemModifierType.description");
}
/**
* Applies {@linkcode EvolutionItemConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that should evolve via item
* @returns `true` if the evolution was successful
*/
apply({ pokemon }: PokemonRewardParams): boolean {
let matchingEvolution = pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId)
? pokemonEvolutions[pokemon.species.speciesId].find(
e => e.evoItem === this.evolutionItem && e.validate(pokemon, false, e.item!),
)
: null;
if (!matchingEvolution && pokemon.isFusion()) {
matchingEvolution = pokemonEvolutions[pokemon.fusionSpecies!.speciesId].find(
e => e.evoItem === this.evolutionItem && e.validate(pokemon, true, e.item!),
);
if (matchingEvolution) {
matchingEvolution = new FusionSpeciesFormEvolution(pokemon.species.speciesId, matchingEvolution);
}
}
if (matchingEvolution) {
globalScene.phaseManager.unshiftNew("EvolutionPhase", pokemon, matchingEvolution, pokemon.level - 1);
return true;
}
return false;
}
}
export class EvolutionItemRewardGenerator extends RewardGenerator {
private rare: boolean;
constructor(rare: boolean) {
super();
this.rare = rare;
}
override generateReward(pregenArgs?: EvolutionItem) {
if (pregenArgs !== undefined) {
return new EvolutionItemReward(pregenArgs);
}
const party = globalScene.getPlayerParty();
const evolutionItemPool = [
party
.filter(
p =>
pokemonEvolutions.hasOwnProperty(p.species.speciesId) &&
(!p.pauseEvolutions ||
p.species.speciesId === SpeciesId.SLOWPOKE ||
p.species.speciesId === SpeciesId.EEVEE ||
p.species.speciesId === SpeciesId.KIRLIA ||
p.species.speciesId === SpeciesId.SNORUNT),
)
.flatMap(p => {
const evolutions = pokemonEvolutions[p.species.speciesId];
return evolutions.filter(e => e.isValidItemEvolution(p));
}),
party
.filter(
p =>
p.isFusion() &&
p.fusionSpecies &&
pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) &&
(!p.pauseEvolutions ||
p.fusionSpecies.speciesId === SpeciesId.SLOWPOKE ||
p.fusionSpecies.speciesId === SpeciesId.EEVEE ||
p.fusionSpecies.speciesId === SpeciesId.KIRLIA ||
p.fusionSpecies.speciesId === SpeciesId.SNORUNT),
)
.flatMap(p => {
const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId];
return evolutions.filter(e => e.isValidItemEvolution(p, true));
}),
]
.flat()
.flatMap(e => e.evoItem)
.filter(i => !!i && i > 50 === this.rare);
if (!evolutionItemPool.length) {
return null;
}
return new EvolutionItemReward(randSeedItem(evolutionItemPool));
}
}

View File

@ -0,0 +1,152 @@
import { globalScene } from "#app/global-scene";
import { allHeldItems } from "#data/data-lists";
import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers";
import { pokemonFormChanges, SpeciesFormChangeCondition } from "#data/pokemon-forms";
import { FormChangeItemId } from "#enums/form-change-item-id";
import { RewardId } from "#enums/reward-id";
import { SpeciesFormKey } from "#enums/species-form-key";
import { SpeciesId } from "#enums/species-id";
import { TrainerItemId } from "#enums/trainer-item-id";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, RewardGenerator } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { randSeedItem } from "#utils/common";
/**
* Class that represents form changing items
*/
export class FormChangeItemReward extends PokemonReward {
public formChangeItem: FormChangeItemId;
constructor(formChangeItem: FormChangeItemId) {
super("", allHeldItems[formChangeItem].iconName, (pokemon: PlayerPokemon) => {
// Make sure the Pokemon has alternate forms
if (
pokemonFormChanges.hasOwnProperty(pokemon.species.speciesId) &&
// Get all form changes for this species with an item trigger, including any compound triggers
pokemonFormChanges[pokemon.species.speciesId]
.filter(
fc => fc.trigger.hasTriggerType(SpeciesFormChangeItemTrigger) && fc.preFormKey === pokemon.getFormKey(),
)
// Returns true if any form changes match this item
.flatMap(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
.flatMap(fc => fc.item)
.includes(this.formChangeItem)
) {
return null;
}
return PartyUiHandler.NoEffectMessage;
});
this.formChangeItem = formChangeItem;
this.id = RewardId.FORM_CHANGE_ITEM;
}
get name(): string {
return allHeldItems[this.formChangeItem].name;
}
get description(): string {
return allHeldItems[this.formChangeItem].description;
}
apply({ pokemon }: PokemonRewardParams): boolean {
if (pokemon.heldItemManager.hasItem(this.formChangeItem)) {
return false;
}
pokemon.heldItemManager.add(this.formChangeItem);
pokemon.heldItemManager.toggleActive(this.formChangeItem);
// TODO: revise logic of this trigger based on active/inactive item
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeItemTrigger);
globalScene.updateItems(true);
return true;
}
}
export class FormChangeItemRewardGenerator extends RewardGenerator {
private isRareFormChangeItem: boolean;
constructor(isRareFormChangeItem: boolean) {
super();
this.isRareFormChangeItem = isRareFormChangeItem;
}
override generateReward(pregenArgs?: FormChangeItemId) {
if (pregenArgs !== undefined) {
return new FormChangeItemReward(pregenArgs);
}
const party = globalScene.getPlayerParty();
// TODO: REFACTOR THIS FUCKERY PLEASE
const formChangeItemPool = [
...new Set(
party
.filter(p => pokemonFormChanges.hasOwnProperty(p.species.speciesId))
.flatMap(p => {
const formChanges = pokemonFormChanges[p.species.speciesId];
let formChangeItemTriggers = formChanges
.filter(
fc =>
((fc.formKey.indexOf(SpeciesFormKey.MEGA) === -1 &&
fc.formKey.indexOf(SpeciesFormKey.PRIMAL) === -1) ||
globalScene.trainerItems.hasItem(TrainerItemId.MEGA_BRACELET)) &&
((fc.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) === -1 &&
fc.formKey.indexOf(SpeciesFormKey.ETERNAMAX) === -1) ||
globalScene.trainerItems.hasItem(TrainerItemId.DYNAMAX_BAND)) &&
(!fc.conditions.length ||
fc.conditions.filter(cond => cond instanceof SpeciesFormChangeCondition && cond.predicate(p))
.length) &&
fc.preFormKey === p.getFormKey(),
)
.map(fc => fc.findTrigger(SpeciesFormChangeItemTrigger) as SpeciesFormChangeItemTrigger)
.filter(t => t?.active && !p.heldItemManager.hasItem(t.item));
if (p.species.speciesId === SpeciesId.NECROZMA) {
// technically we could use a simplified version and check for formChanges.length > 3, but in case any code changes later, this might break...
let foundULTRA_Z = false,
foundN_LUNA = false,
foundN_SOLAR = false;
formChangeItemTriggers.forEach((fc, _i) => {
console.log("Checking ", fc.item);
switch (fc.item) {
case FormChangeItemId.ULTRANECROZIUM_Z:
foundULTRA_Z = true;
break;
case FormChangeItemId.N_LUNARIZER:
foundN_LUNA = true;
break;
case FormChangeItemId.N_SOLARIZER:
foundN_SOLAR = true;
break;
}
});
if (foundULTRA_Z && foundN_LUNA && foundN_SOLAR) {
// all three items are present -> user hasn't acquired any of the N_*ARIZERs -> block ULTRANECROZIUM_Z acquisition.
formChangeItemTriggers = formChangeItemTriggers.filter(
fc => fc.item !== FormChangeItemId.ULTRANECROZIUM_Z,
);
} else {
console.log("DID NOT FIND ");
}
}
return formChangeItemTriggers;
}),
),
]
.flat()
.flatMap(fc => fc.item)
.filter(i => (i && i < 100) === this.isRareFormChangeItem);
// convert it into a set to remove duplicate values, which can appear when the same species with a potential form change is in the party.
if (!formChangeItemPool.length) {
return null;
}
return new FormChangeItemReward(randSeedItem(formChangeItemPool));
}
}

32
src/items/rewards/fuse.ts Normal file
View File

@ -0,0 +1,32 @@
import { RewardId } from "#enums/reward-id";
import type { PlayerPokemon } from "#field/pokemon";
import { type PokemonFusionRewardParams, PokemonReward } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import i18next from "i18next";
export class FusePokemonReward extends PokemonReward {
constructor(localeKey: string, iconImage: string) {
super(localeKey, iconImage, (pokemon: PlayerPokemon) => {
if (pokemon.isFusion()) {
return PartyUiHandler.NoEffectMessage;
}
return null;
});
this.id = RewardId.DNA_SPLICERS;
}
get description(): string {
return i18next.t("modifierType:ModifierType.FusePokemonModifierType.description");
}
/**
* Applies {@linkcode FusePokemonConsumable}
* @param playerPokemon {@linkcode PlayerPokemon} that should be fused
* @param playerPokemon2 {@linkcode PlayerPokemon} that should be fused with {@linkcode playerPokemon}
* @returns always Promise<true>
*/
apply({ pokemon, pokemon2 }: PokemonFusionRewardParams): boolean {
pokemon.fuse(pokemon2);
return true;
}
}

View File

@ -0,0 +1,92 @@
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { allHeldItems } from "#data/data-lists";
import type { BerryType } from "#enums/berry-type";
import type { HeldItemId } from "#enums/held-item-id";
import type { PokemonType } from "#enums/pokemon-type";
import type { PermanentStat } from "#enums/stat";
import type { PlayerPokemon } from "#field/pokemon";
import { attackTypeToHeldItem } from "#items/attack-type-booster";
import { permanentStatToHeldItem } from "#items/base-stat-multiply";
import { berryTypeToHeldItem } from "#items/berry";
import { getNewAttackTypeBoosterHeldItem, getNewBerryHeldItem, getNewVitaminHeldItem } from "#items/held-item-pool";
import { PokemonReward, type PokemonRewardParams, RewardGenerator } from "#items/reward";
import i18next from "i18next";
export class HeldItemReward extends PokemonReward {
public itemId: HeldItemId;
constructor(itemId: HeldItemId, group?: string, soundName?: string) {
super(
"",
"",
(pokemon: PlayerPokemon) => {
const hasItem = pokemon.heldItemManager.hasItem(this.itemId);
const maxStackCount = allHeldItems[this.itemId].getMaxStackCount();
if (!maxStackCount) {
return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.inoperable", {
pokemonName: getPokemonNameWithAffix(pokemon),
});
}
if (hasItem && pokemon.heldItemManager.getStack(this.itemId) === maxStackCount) {
return i18next.t("modifierType:ModifierType.PokemonHeldItemModifierType.extra.tooMany", {
pokemonName: getPokemonNameWithAffix(pokemon),
});
}
return null;
},
group,
soundName,
);
this.itemId = itemId;
}
get name(): string {
return allHeldItems[this.itemId].name;
}
get description(): string {
return allHeldItems[this.itemId].description;
}
get iconName(): string {
return allHeldItems[this.itemId].iconName;
}
apply({ pokemon }: PokemonRewardParams): boolean {
return pokemon.heldItemManager.add(this.itemId);
}
}
export class BerryRewardGenerator extends RewardGenerator {
override generateReward(pregenArgs?: BerryType): HeldItemReward {
if (pregenArgs !== undefined) {
const item = berryTypeToHeldItem[pregenArgs];
return new HeldItemReward(item);
}
const item = getNewBerryHeldItem();
return new HeldItemReward(item);
}
}
export class AttackTypeBoosterRewardGenerator extends RewardGenerator {
override generateReward(pregenArgs?: PokemonType) {
if (pregenArgs !== undefined) {
const item = attackTypeToHeldItem[pregenArgs];
return new HeldItemReward(item);
}
const item = getNewAttackTypeBoosterHeldItem(globalScene.getPlayerParty());
return item ? new HeldItemReward(item) : null;
}
}
export class BaseStatBoosterRewardGenerator extends RewardGenerator {
override generateReward(pregenArgs?: PermanentStat) {
if (pregenArgs !== undefined) {
const item = permanentStatToHeldItem[pregenArgs];
return new HeldItemReward(item);
}
return new HeldItemReward(getNewVitaminHeldItem());
}
}

View File

@ -0,0 +1,76 @@
import { globalScene } from "#app/global-scene";
import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#balance/starters";
import { getLevelTotalExp } from "#data/exp";
import { RewardId } from "#enums/reward-id";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { TrainerItemId } from "#enums/trainer-item-id";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, Reward } from "#items/reward";
import { NumberHolder } from "#utils/common";
import i18next from "i18next";
function incrementLevelWithCandy(pokemon: Pokemon): boolean {
const levelCount = new NumberHolder(1);
globalScene.applyPlayerItems(TrainerItemEffect.LEVEL_INCREMENT_BOOSTER, { numberHolder: levelCount });
pokemon.level += levelCount.value;
if (pokemon.level <= globalScene.getMaxExpLevel(true)) {
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
pokemon.levelExp = 0;
}
if (pokemon.isPlayer()) {
pokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY);
globalScene.phaseManager.unshiftNew(
"LevelUpPhase",
globalScene.getPlayerParty().indexOf(pokemon),
pokemon.level - levelCount.value,
pokemon.level,
);
}
return true;
}
export class PokemonLevelIncrementReward extends PokemonReward {
constructor(localeKey: string, iconImage: string) {
super(localeKey, iconImage, (_pokemon: PlayerPokemon) => null);
this.id = RewardId.RARE_CANDY;
}
get description(): string {
let levels = 1;
const candyJarStack = globalScene.trainerItems.getStack(TrainerItemId.CANDY_JAR);
levels += candyJarStack;
return i18next.t("modifierType:ModifierType.PokemonLevelIncrementModifierType.description", { levels });
}
/**
* Applies {@linkcode PokemonLevelIncrementConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that should get levels incremented
* @param levelCount The amount of levels to increment
* @returns always `true`
*/
apply({ pokemon }: PokemonRewardParams): boolean {
return incrementLevelWithCandy(pokemon);
}
}
export class AllPokemonLevelIncrementReward extends Reward {
id = RewardId.RARER_CANDY;
get description(): string {
let levels = 1;
const candyJarStack = globalScene.trainerItems.getStack(TrainerItemId.CANDY_JAR);
levels += candyJarStack;
return i18next.t("modifierType:ModifierType.AllPokemonLevelIncrementModifierType.description", { levels });
}
apply(): boolean {
for (const pokemon of globalScene.getPlayerParty()) {
incrementLevelWithCandy(pokemon);
}
return true;
}
}

View File

@ -0,0 +1,59 @@
import { globalScene } from "#app/global-scene";
import { HeldItemId } from "#enums/held-item-id";
import type { RewardId } from "#enums/reward-id";
import { SpeciesId } from "#enums/species-id";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { Reward } from "#items/reward";
import { formatMoney, NumberHolder } from "#utils/common";
import i18next from "i18next";
export class AddMoneyReward extends Reward {
private moneyMultiplier: number;
private moneyMultiplierDescriptorKey: string;
constructor(
localeKey: string,
iconImage: string,
moneyMultiplier: number,
moneyMultiplierDescriptorKey: string,
id: RewardId,
) {
super(localeKey, iconImage, "money", "se/buy");
this.moneyMultiplier = moneyMultiplier;
this.moneyMultiplierDescriptorKey = moneyMultiplierDescriptorKey;
this.id = id;
}
get description(): string {
const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
globalScene.applyPlayerItems(TrainerItemEffect.MONEY_MULTIPLIER, { numberHolder: moneyAmount });
const formattedMoney = formatMoney(globalScene.moneyFormat, moneyAmount.value);
return i18next.t("modifierType:ModifierType.MoneyRewardModifierType.description", {
moneyMultiplier: i18next.t(this.moneyMultiplierDescriptorKey as any),
moneyAmount: formattedMoney,
});
}
/**
* Applies {@linkcode AddMoneyReward}
* @returns always `true`
*/
apply(): boolean {
const moneyAmount = new NumberHolder(globalScene.getWaveMoneyAmount(this.moneyMultiplier));
globalScene.applyPlayerItems(TrainerItemEffect.MONEY_MULTIPLIER, { numberHolder: moneyAmount });
globalScene.addMoney(moneyAmount.value);
for (const p of globalScene.getPlayerParty()) {
if (p.hasSpecies(SpeciesId.GIMMIGHOUL)) {
const factor = Math.min(Math.floor(this.moneyMultiplier), 3);
p.heldItemManager.add(HeldItemId.GIMMIGHOUL_EVO_TRACKER, factor);
}
}
return true;
}
}

View File

@ -0,0 +1,69 @@
import { globalScene } from "#app/global-scene";
import { getNatureName, getNatureStatMultiplier } from "#data/nature";
import { Nature } from "#enums/nature";
import { RewardId } from "#enums/reward-id";
import { Stat } from "#enums/stat";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, RewardGenerator } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { randSeedItem } from "#utils/common";
import { getEnumKeys, getEnumValues } from "#utils/enums";
import i18next from "i18next";
export class PokemonNatureChangeReward extends PokemonReward {
protected nature: Nature;
constructor(nature: Nature) {
super(
"",
`mint_${
getEnumKeys(Stat)
.find(s => getNatureStatMultiplier(nature, Stat[s]) > 1)
?.toLowerCase() || "neutral"
}`,
(pokemon: PlayerPokemon) => {
if (pokemon.getNature() === this.nature) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"mint",
);
this.nature = nature;
this.id = RewardId.MINT;
}
get name(): string {
return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.name", {
natureName: getNatureName(this.nature),
});
}
get description(): string {
return i18next.t("modifierType:ModifierType.PokemonNatureChangeModifierType.description", {
natureName: getNatureName(this.nature, true, true, true),
});
}
/**
* Applies {@linkcode PokemonNatureChangeConsumable}
* @param playerPokemon {@linkcode PlayerPokemon} to apply the {@linkcode Nature} change to
* @returns
*/
apply({ pokemon }: PokemonRewardParams): boolean {
pokemon.setCustomNature(this.nature);
globalScene.gameData.unlockSpeciesNature(pokemon.species, this.nature);
return true;
}
}
export class MintRewardGenerator extends RewardGenerator {
override generateReward(pregenArgs?: Nature) {
if (pregenArgs !== undefined) {
return new PokemonNatureChangeReward(pregenArgs);
}
return new PokemonNatureChangeReward(randSeedItem(getEnumValues(Nature)));
}
}

View File

@ -0,0 +1,51 @@
import { globalScene } from "#app/global-scene";
import { getPokeballCatchMultiplier, getPokeballName, MAX_PER_TYPE_POKEBALLS } from "#data/pokeball";
import type { PokeballType } from "#enums/pokeball";
import type { RewardId } from "#enums/reward-id";
import { Reward } from "#items/reward";
import i18next from "i18next";
export class AddPokeballReward extends Reward {
private pokeballType: PokeballType;
private count: number;
constructor(iconImage: string, pokeballType: PokeballType, count: number, id: RewardId) {
super("", iconImage, "pb", "se/pb_bounce_1");
this.pokeballType = pokeballType;
this.count = count;
this.id = id;
}
get name(): string {
return i18next.t("modifierType:ModifierType.AddPokeballModifierType.name", {
modifierCount: this.count,
pokeballName: getPokeballName(this.pokeballType),
});
}
get description(): string {
return i18next.t("modifierType:ModifierType.AddPokeballModifierType.description", {
modifierCount: this.count,
pokeballName: getPokeballName(this.pokeballType),
catchRate:
getPokeballCatchMultiplier(this.pokeballType) > -1
? `${getPokeballCatchMultiplier(this.pokeballType)}x`
: "100%",
pokeballAmount: `${globalScene.pokeballCounts[this.pokeballType]}`,
});
}
/**
* Applies {@linkcode AddPokeballReward}
* @returns always `true`
*/
apply(): boolean {
const pokeballCounts = globalScene.pokeballCounts;
pokeballCounts[this.pokeballType] = Math.min(
pokeballCounts[this.pokeballType] + this.count,
MAX_PER_TYPE_POKEBALLS,
);
return true;
}
}

View File

@ -0,0 +1,102 @@
import type { RewardId } from "#enums/reward-id";
import type { PlayerPokemon } from "#field/pokemon";
import {
PokemonMoveReward,
type PokemonMoveRewardParams,
PokemonReward,
type PokemonRewardParams,
} from "#items/reward";
import type { PokemonMove } from "#moves/pokemon-move";
import { PartyUiHandler } from "#ui/party-ui-handler";
import i18next from "i18next";
export class PokemonPpRestoreReward extends PokemonMoveReward {
protected restorePoints: number;
constructor(localeKey: string, iconImage: string, id: RewardId, restorePoints: number) {
super(
localeKey,
iconImage,
id,
(_pokemon: PlayerPokemon) => {
return null;
},
(pokemonMove: PokemonMove) => {
if (!pokemonMove.ppUsed) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"ether",
);
this.restorePoints = restorePoints;
}
get description(): string {
return this.restorePoints > -1
? i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.description", {
restorePoints: this.restorePoints,
})
: i18next.t("modifierType:ModifierType.PokemonPpRestoreModifierType.extra.fully");
}
/**
* Applies {@linkcode PokemonPpRestoreConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that should get move pp restored
* @returns always `true`
*/
apply({ pokemon, moveIndex }: PokemonMoveRewardParams): boolean {
const move = pokemon.getMoveset()[moveIndex];
if (move) {
move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
}
return true;
}
}
export class PokemonAllMovePpRestoreReward extends PokemonReward {
protected restorePoints: number;
constructor(localeKey: string, iconImage: string, id: RewardId, restorePoints: number) {
super(
localeKey,
iconImage,
(pokemon: PlayerPokemon) => {
if (!pokemon.getMoveset().filter(m => m.ppUsed).length) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"elixir",
);
this.restorePoints = restorePoints;
this.id = id;
}
get description(): string {
return this.restorePoints > -1
? i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.description", {
restorePoints: this.restorePoints,
})
: i18next.t("modifierType:ModifierType.PokemonAllMovePpRestoreModifierType.extra.fully");
}
/**
* Applies {@linkcode PokemonAllMovePpRestoreConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that should get all move pp restored
* @returns always `true`
*/
apply({ pokemon }: PokemonRewardParams): boolean {
for (const move of pokemon.getMoveset()) {
if (move) {
move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0;
}
}
return true;
}
}

View File

@ -0,0 +1,49 @@
import type { RewardId } from "#enums/reward-id";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonMoveReward, type PokemonMoveRewardParams } from "#items/reward";
import type { PokemonMove } from "#moves/pokemon-move";
import { PartyUiHandler } from "#ui/party-ui-handler";
import i18next from "i18next";
export class PokemonPpUpReward extends PokemonMoveReward {
protected upPoints: number;
constructor(localeKey: string, iconImage: string, id: RewardId, upPoints: number) {
super(
localeKey,
iconImage,
id,
(_pokemon: PlayerPokemon) => {
return null;
},
(pokemonMove: PokemonMove) => {
if (pokemonMove.getMove().pp < 5 || pokemonMove.ppUp >= 3 || pokemonMove.maxPpOverride) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"ppUp",
);
this.upPoints = upPoints;
}
get description(): string {
return i18next.t("modifierType:ModifierType.PokemonPpUpModifierType.description", { upPoints: this.upPoints });
}
/**
* Applies {@linkcode PokemonPpUpConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that gets a pp up on move-slot {@linkcode moveIndex}
* @returns
*/
apply({ pokemon, moveIndex }: PokemonMoveRewardParams): boolean {
const move = pokemon.getMoveset()[moveIndex];
if (move && !move.maxPpOverride) {
move.ppUp = Math.min(move.ppUp + this.upPoints, 3);
}
return true;
}
}

View File

@ -0,0 +1,40 @@
import { globalScene } from "#app/global-scene";
import { LearnMoveType } from "#enums/learn-move-type";
import { RewardId } from "#enums/reward-id";
import type { PlayerPokemon } from "#field/pokemon";
import { type PokemonMoveRecallRewardParams, PokemonReward } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
export class RememberMoveReward extends PokemonReward {
constructor(localeKey: string, iconImage: string, group?: string) {
super(
localeKey,
iconImage,
(pokemon: PlayerPokemon) => {
if (!pokemon.getLearnableLevelMoves().length) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
group,
);
this.id = RewardId.MEMORY_MUSHROOM;
}
/**
* Applies {@linkcode RememberMoveConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that should remember the move
* @returns always `true`
*/
apply({ pokemon, moveIndex, cost }: PokemonMoveRecallRewardParams): boolean {
globalScene.phaseManager.unshiftNew(
"LearnMovePhase",
globalScene.getPlayerParty().indexOf(pokemon as PlayerPokemon),
pokemon.getLearnableLevelMoves()[moveIndex],
LearnMoveType.MEMORY,
cost,
);
return true;
}
}

View File

@ -0,0 +1,160 @@
// TODO: Consider removing `revive` from the signature of PokemonHealPhase in the wake of this
// (was only used for revives)
import { globalScene } from "#app/global-scene";
import { BattlerTagType } from "#enums/battler-tag-type";
import { RewardId } from "#enums/reward-id";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import type { PlayerPokemon, Pokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, Reward } from "#items/reward";
import { PartyUiHandler, type PokemonSelectFilter } from "#ui/party-ui-handler";
import { NumberHolder, toDmgValue } from "#utils/common";
import i18next from "i18next";
/**
* Helper function to instantly restore a Pokemon's hp.
* @param pokemon - The {@linkcode Pokemon} being healed
* @param percentToRestore - The percentage of the Pokemon's {@linkcode Stat.HP | maximum HP} to heal
* @param pointsToRestore - A minimum amount of HP points to restore; default `0`
* @param healStatus - Whether to also heal status ailments; default `false`
* @param fainted - Whether to allow reviving fainted Pokemon; default `false`.
* If `true`, will also disable the effect of {@linkcode TrainerItemEffect.HEALING_BOOSTER | Healing Charms}.
* @returns Whether the healing succeeded
*/
function restorePokemonHp(
pokemon: Pokemon,
percentToRestore: number,
{
pointsToRestore = 0,
healStatus = false,
fainted = false,
}: {
pointsToRestore?: number;
healStatus?: boolean;
fainted?: boolean;
} = {},
): boolean {
if (pokemon.isFainted() !== fainted) {
return false;
}
if (fainted || healStatus) {
pokemon.resetStatus(true, true, false, false);
}
// Apply HealingCharm
const hpRestoreMultiplier = new NumberHolder(1);
if (!fainted) {
this.applyPlayerItems(TrainerItemEffect.HEALING_BOOSTER, { numberHolder: hpRestoreMultiplier });
}
const restorePoints = toDmgValue(pointsToRestore * hpRestoreMultiplier.value);
const restorePercent = toDmgValue((percentToRestore / 100) * hpRestoreMultiplier.value * pokemon.getMaxHp());
pokemon.heal(Math.max(restorePercent, restorePoints));
return true;
}
export class PokemonHpRestoreReward extends PokemonReward {
protected restorePoints: number;
protected restorePercent: number;
protected healStatus: boolean;
constructor(
localeKey: string,
iconImage: string,
id: RewardId,
restorePoints: number,
restorePercent: number,
healStatus = false,
selectFilter?: PokemonSelectFilter,
group?: string,
) {
super(
localeKey,
iconImage,
selectFilter ||
((pokemon: PlayerPokemon) => {
if (
!pokemon.hp ||
(pokemon.isFullHp() && (!this.healStatus || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))))
) {
return PartyUiHandler.NoEffectMessage;
}
return null;
}),
group || "potion",
);
this.restorePoints = restorePoints;
this.restorePercent = restorePercent;
this.healStatus = healStatus;
this.id = id;
}
get description(): string {
return this.restorePoints
? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.description", {
restorePoints: this.restorePoints,
restorePercent: this.restorePercent,
})
: this.healStatus
? i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.extra.fullyWithStatus")
: i18next.t("modifierType:ModifierType.PokemonHpRestoreModifierType.extra.fully");
}
apply({ pokemon }: PokemonRewardParams): boolean {
return restorePokemonHp(pokemon, this.restorePercent, {
pointsToRestore: this.restorePoints,
healStatus: this.healStatus,
});
}
}
export class PokemonReviveReward extends PokemonHpRestoreReward {
constructor(localeKey: string, iconImage: string, id: RewardId, restorePercent: number) {
super(
localeKey,
iconImage,
id,
0,
restorePercent,
false,
(pokemon: PlayerPokemon) => {
if (!pokemon.isFainted()) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"revive",
);
this.selectFilter = (pokemon: PlayerPokemon) => {
if (pokemon.hp) {
return PartyUiHandler.NoEffectMessage;
}
return null;
};
}
get description(): string {
return i18next.t("modifierType:ModifierType.PokemonReviveModifierType.description", {
restorePercent: this.restorePercent,
});
}
apply({ pokemon }: PokemonRewardParams): boolean {
return restorePokemonHp(pokemon, this.restorePercent, { fainted: true });
}
}
export class AllPokemonFullReviveReward extends Reward {
constructor(localeKey: string, iconImage: string) {
super(localeKey, iconImage, "modifierType:ModifierType.AllPokemonFullReviveModifierType");
this.id = RewardId.SACRED_ASH;
}
apply(): boolean {
for (const pokemon of globalScene.getPlayerParty()) {
restorePokemonHp(pokemon, 100, { fainted: true });
}
return true;
}
}

View File

@ -0,0 +1,83 @@
import { globalScene } from "#app/global-scene";
import { allHeldItems } from "#data/data-lists";
import { HeldItemId } from "#enums/held-item-id";
import { SpeciesId } from "#enums/species-id";
import { RewardGenerator } from "#items/reward";
import type { SpeciesStatBoosterItemId, SpeciesStatBoostHeldItem } from "#items/stat-boost";
import { randSeedInt } from "#utils/common";
import { HeldItemReward } from "./held-item-reward";
/**
* Consumable type generator for {@linkcode SpeciesStatBoosterReward}, which
* encapsulates the logic for weighting the most useful held item from
* the current list of {@linkcode items}.
* @extends RewardGenerator
*/
export class SpeciesStatBoosterRewardGenerator extends RewardGenerator {
/** Object comprised of the currently available species-based stat boosting held items */
private rare: boolean;
constructor(rare: boolean) {
super();
this.rare = rare;
}
override generateReward(pregenArgs?: SpeciesStatBoosterItemId) {
if (pregenArgs !== undefined) {
return new HeldItemReward(pregenArgs);
}
// Get a pool of items based on the rarity.
const tierItems = this.rare
? [HeldItemId.LIGHT_BALL, HeldItemId.THICK_CLUB, HeldItemId.METAL_POWDER, HeldItemId.QUICK_POWDER]
: [HeldItemId.DEEP_SEA_SCALE, HeldItemId.DEEP_SEA_TOOTH];
const weights = new Array(tierItems.length).fill(0);
for (const p of globalScene.getPlayerParty()) {
const speciesId = p.getSpeciesForm(true).speciesId;
const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null;
// TODO: Use commented boolean when Fling is implemented
const hasFling = false; /* p.getMoveset(true).some(m => m.moveId === MoveId.FLING) */
for (const i in tierItems) {
const checkedSpecies = (allHeldItems[tierItems[i]] as SpeciesStatBoostHeldItem).species;
// If party member already has the item being weighted currently, skip to the next item
const hasItem = p.heldItemManager.hasItem(tierItems[i]);
if (!hasItem) {
if (checkedSpecies.includes(speciesId) || (!!fusionSpeciesId && checkedSpecies.includes(fusionSpeciesId))) {
// Add weight if party member has a matching species or, if applicable, a matching fusion species
weights[i]++;
} else if (checkedSpecies.includes(SpeciesId.PIKACHU) && hasFling) {
// Add weight to Light Ball if party member has Fling
weights[i]++;
}
}
}
}
// TODO: Replace this with a helper function
let totalWeight = 0;
for (const weight of weights) {
totalWeight += weight;
}
if (totalWeight !== 0) {
const randInt = randSeedInt(totalWeight, 1);
let weight = 0;
for (const i in weights) {
if (weights[i] !== 0) {
const curWeight = weight + weights[i];
if (randInt <= weight + weights[i]) {
return new HeldItemReward(tierItems[i]);
}
weight = curWeight;
}
}
}
return null;
}
}

View File

@ -0,0 +1,27 @@
import { BattlerTagType } from "#enums/battler-tag-type";
import { RewardId } from "#enums/reward-id";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import i18next from "i18next";
export class PokemonStatusHealReward extends PokemonReward {
constructor(localeKey: string, iconImage: string) {
super(localeKey, iconImage, (pokemon: PlayerPokemon) => {
if (!pokemon.hp || (!pokemon.status && !pokemon.getTag(BattlerTagType.CONFUSED))) {
return PartyUiHandler.NoEffectMessage;
}
return null;
});
this.id = RewardId.FULL_HEAL;
}
get description(): string {
return i18next.t("modifierType:ModifierType.PokemonStatusHealModifierType.description");
}
apply({ pokemon }: PokemonRewardParams): boolean {
pokemon.resetStatus(true, true, false, false);
return true;
}
}

View File

@ -0,0 +1,102 @@
import { globalScene } from "#app/global-scene";
import { PokemonType } from "#enums/pokemon-type";
import { SpeciesId } from "#enums/species-id";
import { TrainerItemId } from "#enums/trainer-item-id";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, RewardGenerator } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { randSeedInt, randSeedItem } from "#utils/common";
import { getEnumValues } from "#utils/enums";
import { toCamelCase } from "#utils/strings";
import i18next from "i18next";
export class ChangeTeraTypeReward extends PokemonReward {
private teraType: PokemonType;
constructor(teraType: PokemonType) {
super(
"",
`${PokemonType[teraType].toLowerCase()}_tera_shard`,
(pokemon: PlayerPokemon) => {
if (
[pokemon.species.speciesId, pokemon.fusionSpecies?.speciesId].filter(
s => s === SpeciesId.TERAPAGOS || s === SpeciesId.OGERPON || s === SpeciesId.SHEDINJA,
).length > 0
) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"tera_shard",
);
this.teraType = teraType;
}
get name(): string {
return i18next.t("modifierType:ModifierType.ChangeTeraTypeModifierType.name", {
teraType: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.teraType])}`),
});
}
get description(): string {
return i18next.t("modifierType:ModifierType.ChangeTeraTypeModifierType.description", {
teraType: i18next.t(`pokemonInfo:type.${toCamelCase(PokemonType[this.teraType])}`),
});
}
/**
* Checks if {@linkcode TerrastalizeConsumable} should be applied
* @param playerPokemon The {@linkcode PlayerPokemon} that consumes the item
* @returns `true` if the {@linkcode TerrastalizeConsumable} should be applied
*/
shouldApply({ pokemon }: PokemonRewardParams): boolean {
return (
pokemon.teraType !== this.teraType &&
![SpeciesId.SHEDINJA, SpeciesId.OGERPON, SpeciesId.TERAPAGOS].some(s => pokemon.hasSpecies(s))
);
}
/**
* Applies {@linkcode TerrastalizeConsumable}
* @param pokemon The {@linkcode PlayerPokemon} that consumes the item
* @returns `true` if hp was restored
*/
apply({ pokemon }: PokemonRewardParams): boolean {
pokemon.teraType = this.teraType;
return true;
}
} // todo: denest
export class TeraTypeRewardGenerator extends RewardGenerator {
override generateReward(pregenArgs?: PokemonType) {
if (pregenArgs !== undefined) {
return new ChangeTeraTypeReward(pregenArgs[0]);
}
if (!globalScene.trainerItems.hasItem(TrainerItemId.TERA_ORB)) {
return null;
}
const shardType = this.getTeraType();
return new ChangeTeraTypeReward(shardType);
}
private getTeraType(): PokemonType {
// If all party members have a given Tera Type, omit it from the pool
const excludedType = globalScene.getPlayerParty().reduce((prevType, p) => {
if (
// Ignore Pokemon with fixed Tera Types
p.hasSpecies(SpeciesId.TERAPAGOS) ||
p.hasSpecies(SpeciesId.OGERPON) ||
p.hasSpecies(SpeciesId.SHEDINJA)
) {
return prevType;
}
return prevType === p.teraType ? prevType : PokemonType.UNKNOWN;
}, PokemonType.UNKNOWN);
const validTypes = getEnumValues(PokemonType).filter(t => t !== excludedType);
// 1/64 chance for tera stellar
return randSeedInt(64) ? randSeedItem(validTypes) : PokemonType.STELLAR;
}
}

100
src/items/rewards/tm.ts Normal file
View File

@ -0,0 +1,100 @@
import { globalScene } from "#app/global-scene";
import { tmPoolTiers, tmSpecies } from "#balance/tms";
import { allMoves } from "#data/data-lists";
import { LearnMoveType } from "#enums/learn-move-type";
import type { MoveId } from "#enums/move-id";
import { PokemonType } from "#enums/pokemon-type";
import type { RarityTier } from "#enums/reward-tier";
import type { PlayerPokemon } from "#field/pokemon";
import { PokemonReward, type PokemonRewardParams, RewardGenerator } from "#items/reward";
import { PartyUiHandler } from "#ui/party-ui-handler";
import { padInt, randSeedItem } from "#utils/common";
import i18next from "i18next";
export class TmReward extends PokemonReward {
public moveId: MoveId;
constructor(moveId: MoveId) {
super(
"",
`tm_${PokemonType[allMoves[moveId].type].toLowerCase()}`,
(pokemon: PlayerPokemon) => {
if (
pokemon.compatibleTms.indexOf(moveId) === -1 ||
pokemon.getMoveset().filter(m => m.moveId === moveId).length
) {
return PartyUiHandler.NoEffectMessage;
}
return null;
},
"tm",
);
this.moveId = moveId;
}
get name(): string {
return i18next.t("modifierType:ModifierType.TmModifierType.name", {
moveId: padInt(Object.keys(tmSpecies).indexOf(this.moveId.toString()) + 1, 3),
moveName: allMoves[this.moveId].name,
});
}
get description(): string {
return i18next.t(
globalScene.enableMoveInfo
? "modifierType:ModifierType.TmModifierTypeWithInfo.description"
: "modifierType:ModifierType.TmModifierType.description",
{ moveName: allMoves[this.moveId].name },
);
}
/**
* Applies {@linkcode TmConsumable}
* @param playerPokemon The {@linkcode PlayerPokemon} that should learn the TM
* @returns always `true`
*/
apply({ pokemon }: PokemonRewardParams): boolean {
globalScene.phaseManager.unshiftNew(
"LearnMovePhase",
globalScene.getPlayerParty().indexOf(pokemon),
this.moveId,
LearnMoveType.TM,
);
return true;
}
}
export class TmRewardGenerator extends RewardGenerator {
private tier: RarityTier;
constructor(tier: RarityTier) {
super();
this.tier = tier;
}
override generateReward(pregenArgs?: MoveId) {
if (pregenArgs !== undefined) {
return new TmReward(pregenArgs);
}
const party = globalScene.getPlayerParty();
const partyMemberCompatibleTms = party.map(p => {
const previousLevelMoves = p.getLearnableLevelMoves();
return (p as PlayerPokemon).compatibleTms.filter(
tm => !p.moveset.find(m => m.moveId === tm) && !previousLevelMoves.find(lm => lm === tm),
);
});
const tierUniqueCompatibleTms = partyMemberCompatibleTms
.flat()
.filter(tm => tmPoolTiers[tm] === this.tier)
.filter(tm => !allMoves[tm].name.endsWith(" (N)"))
.filter((tm, i, array) => array.indexOf(tm) === i);
if (!tierUniqueCompatibleTms.length) {
return null;
}
const randTmIndex = randSeedItem(tierUniqueCompatibleTms);
return new TmReward(randTmIndex);
}
}

View File

@ -0,0 +1,53 @@
import { globalScene } from "#app/global-scene";
import { allTrainerItems } from "#data/data-lists";
import { Stat, TEMP_BATTLE_STATS, type TempBattleStat } from "#enums/stat";
import type { TrainerItemId } from "#enums/trainer-item-id";
import { Reward, RewardGenerator } from "#items/reward";
import { tempStatToTrainerItem } from "#items/trainer-items/x-items";
import { randSeedItem } from "#utils/common";
export class TrainerItemReward extends Reward {
// TODO: This should not be public
public itemId: TrainerItemId;
constructor(itemId: TrainerItemId, group?: string) {
super("", "", group, "se/restore");
this.itemId = itemId;
}
get name(): string {
return allTrainerItems[this.itemId].name;
}
get description(): string {
return allTrainerItems[this.itemId].description;
}
get iconName(): string {
return allTrainerItems[this.itemId].iconName;
}
apply(): boolean {
return globalScene.trainerItems.add(this.itemId);
}
}
export class LapsingTrainerItemReward extends TrainerItemReward {
apply(): boolean {
return globalScene.trainerItems.add(this.itemId, allTrainerItems[this.itemId].getMaxStackCount());
}
}
export class TempStatStageBoosterRewardGenerator extends RewardGenerator {
public static readonly items: Record<TempBattleStat, string> = {
[Stat.ATK]: "x_attack",
[Stat.DEF]: "x_defense",
[Stat.SPATK]: "x_sp_atk",
[Stat.SPDEF]: "x_sp_def",
[Stat.SPD]: "x_speed",
[Stat.ACC]: "x_accuracy",
};
override generateReward(pregenArgs?: TempBattleStat) {
return new LapsingTrainerItemReward(tempStatToTrainerItem[pregenArgs ?? randSeedItem(TEMP_BATTLE_STATS)]);
}
}

View File

@ -0,0 +1,43 @@
import { globalScene } from "#app/global-scene";
import type { RewardId } from "#enums/reward-id";
import { Reward } from "#items/reward";
import { getVoucherTypeIcon, getVoucherTypeName, type VoucherType } from "#system/voucher";
import i18next from "i18next";
export class AddVoucherReward extends Reward {
private voucherType: VoucherType;
private count: number;
constructor(voucherType: VoucherType, count: number, id: RewardId) {
super("", getVoucherTypeIcon(voucherType), "voucher");
this.count = count;
this.voucherType = voucherType;
this.id = id;
}
get name(): string {
return i18next.t("modifierType:ModifierType.AddVoucherConsumableType.name", {
modifierCount: this.count,
voucherTypeName: getVoucherTypeName(this.voucherType),
});
}
get description(): string {
return i18next.t("modifierType:ModifierType.AddVoucherConsumableType.description", {
modifierCount: this.count,
voucherTypeName: getVoucherTypeName(this.voucherType),
});
}
/**
* Applies {@linkcode AddVoucherReward}
* @param battleScene {@linkcode BattleScene}
* @returns always `true`
*/
apply(): boolean {
const voucherCounts = globalScene.gameData.voucherCounts;
voucherCounts[this.voucherType] += this.count;
return true;
}
}

View File

@ -6,7 +6,7 @@ import {
type TrainerItemDataMap,
type TrainerItemSaveData,
type TrainerItemSpecs,
} from "#items/trainer-item-data-types";
} from "#types/trainer-item-data-types";
import { getTypedEntries, getTypedKeys } from "#utils/common";
export class TrainerItemManager {

View File

@ -1,22 +1,12 @@
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { getStatusEffectDescriptor, getStatusEffectHealText } from "#data/status-effect";
import { BattlerTagType } from "#enums/battler-tag-type";
import { getStatKey, Stat, type TempBattleStat } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import { TextStyle } from "#enums/text-style";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { TrainerItemId, TrainerItemNames } from "#enums/trainer-item-id";
import { type TrainerItemId, TrainerItemNames } from "#enums/trainer-item-id";
import type { TrainerItemManager } from "#items/trainer-item-manager";
import type { NumberHolderParams, PreserveBerryParams } from "#types/trainer-item-parameter";
import { addTextObject } from "#ui/text";
import { hslToHex, randSeedFloat, toDmgValue } from "#utils/common";
import { hslToHex } from "#utils/common";
import i18next from "i18next";
import type {
BooleanHolderParams,
NumberHolderParams,
PokemonParams,
PreserveBerryParams,
} from "./trainer-item-parameter";
export class TrainerItem {
// public pokemonId: number;
@ -25,9 +15,6 @@ export class TrainerItem {
public isLapsing = false;
public effects: TrainerItemEffect[] = [];
//TODO: If this is actually never changed by any subclass, perhaps it should not be here
public soundName = "se/restore";
constructor(type: TrainerItemId, maxStackCount = 1) {
this.type = type;
this.maxStackCount = maxStackCount;
@ -83,6 +70,35 @@ export class TrainerItem {
}
}
export class LapsingTrainerItem extends TrainerItem {
isLapsing = true;
createIcon(battleCount: number): Phaser.GameObjects.Container {
const container = globalScene.add.container(0, 0);
const item = globalScene.add.sprite(0, 12, "items").setFrame(this.iconName).setOrigin(0, 0.5);
container.add(item);
// Linear interpolation on hue
const hue = Math.floor(120 * (battleCount / this.getMaxStackCount()) + 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(27, 0, 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;
}
}
// Candy Jar
export class LevelIncrementBoosterTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.LEVEL_INCREMENT_BOOSTER];
@ -211,326 +227,3 @@ export class HealShopCostTrainerItem extends TrainerItem {
moneyCost.value = Math.floor(moneyCost.value * this.shopMultiplier);
}
}
export class LapsingTrainerItem extends TrainerItem {
isLapsing = true;
createIcon(battleCount: number): Phaser.GameObjects.Container {
const container = globalScene.add.container(0, 0);
const item = globalScene.add.sprite(0, 12, "items").setFrame(this.iconName).setOrigin(0, 0.5);
container.add(item);
// Linear interpolation on hue
const hue = Math.floor(120 * (battleCount / this.getMaxStackCount()) + 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(27, 0, 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;
}
}
export class DoubleBattleChanceBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.DOUBLE_BATTLE_CHANCE_BOOSTER];
get description(): string {
return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", {
battleCount: this.getMaxStackCount(),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const doubleBattleChance = params.numberHolder;
// This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using randSeedInt
// A double battle will initiate if the generated number is 0
doubleBattleChance.value /= 4;
}
}
type TempStatToTrainerItemMap = {
[key in TempBattleStat]: TrainerItemId;
};
export const tempStatToTrainerItem: TempStatToTrainerItemMap = {
[Stat.ATK]: TrainerItemId.X_ATTACK,
[Stat.DEF]: TrainerItemId.X_DEFENSE,
[Stat.SPATK]: TrainerItemId.X_SP_ATK,
[Stat.SPDEF]: TrainerItemId.X_SP_DEF,
[Stat.SPD]: TrainerItemId.X_SPEED,
[Stat.ACC]: TrainerItemId.X_ACCURACY,
};
export class TempStatStageBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.TEMP_STAT_STAGE_BOOSTER];
private stat: TempBattleStat;
constructor(type: TrainerItemId, stat: TempBattleStat, stackCount?: number) {
super(type, stackCount);
this.stat = stat;
}
get name(): string {
return i18next.t(`modifierType:TempStatStageBoosterItem.${TrainerItemNames[this.type]?.toLowerCase()}`);
}
get description(): string {
console.log();
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t(getStatKey(this.stat)),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.percentage"),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const statLevel = params.numberHolder;
const boost = 0.3;
statLevel.value += boost;
}
}
export class TempAccuracyBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.TEMP_ACCURACY_BOOSTER];
get name(): string {
return i18next.t(`modifierType:TempStatStageBoosterItem.${TrainerItemNames[this.type]?.toLowerCase()}`);
}
get description(): string {
console.log();
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t(getStatKey(Stat.ACC)),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.percentage"),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const statLevel = params.numberHolder;
const boost = 1;
statLevel.value += boost;
}
}
export class TempCritBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.TEMP_CRIT_BOOSTER];
get description(): string {
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises"),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage"),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const critLevel = params.numberHolder;
critLevel.value++;
}
}
export class EnemyDamageBoosterTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_DAMAGE_BOOSTER];
public damageBoost = 1.05;
get iconName(): string {
return "wl_item_drop";
}
apply(manager: TrainerItemManager, params: NumberHolderParams): boolean {
const stack = manager.getStack(this.type);
const multiplier = params.numberHolder;
multiplier.value = toDmgValue(multiplier.value * Math.pow(this.damageBoost, stack));
return true;
}
getMaxStackCount(): number {
return 999;
}
}
export class EnemyDamageReducerTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_DAMAGE_REDUCER];
public damageReduction = 0.975;
get iconName(): string {
return "wl_guard_spec";
}
apply(manager: TrainerItemManager, params: NumberHolderParams): boolean {
const stack = manager.getStack(this.type);
const multiplier = params.numberHolder;
multiplier.value = toDmgValue(multiplier.value * Math.pow(this.damageReduction, stack));
return true;
}
getMaxStackCount(): number {
return globalScene.currentBattle.waveIndex < 2000 ? 99 : 999;
}
}
export class EnemyTurnHealTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_HEAL];
public healPercent = 2;
get iconName(): string {
return "wl_potion";
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const enemyPokemon = params.pokemon;
if (!enemyPokemon.isFullHp()) {
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
enemyPokemon.getBattlerIndex(),
Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * stack, 1),
i18next.t("modifier:enemyTurnHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon),
}),
true,
false,
false,
false,
true,
);
return true;
}
return false;
}
}
export class EnemyAttackStatusEffectChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_ATTACK_STATUS_CHANCE];
public effect: StatusEffect;
constructor(type: TrainerItemId, effect: StatusEffect, stackCount?: number) {
super(type, stackCount);
this.effect = effect;
}
get iconName(): string {
if (this.effect === StatusEffect.POISON) {
return "wl_antidote";
}
if (this.effect === StatusEffect.PARALYSIS) {
return "wl_paralyze_heal";
}
if (this.effect === StatusEffect.BURN) {
return "wl_burn_heal";
}
return "";
}
get description(): string {
return i18next.t("modifierType:ModifierType.EnemyAttackStatusEffectChanceModifierType.description", {
chancePercent: this.getChance() * 100,
statusEffect: getStatusEffectDescriptor(this.effect),
});
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const enemyPokemon = params.pokemon;
const chance = this.getChance();
if (randSeedFloat() <= chance * stack) {
return enemyPokemon.trySetStatus(this.effect);
}
return false;
}
getChance(): number {
return 0.025 * (this.effect === StatusEffect.BURN || this.effect === StatusEffect.POISON ? 2 : 1);
}
}
export class EnemyStatusEffectHealChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_STATUS_HEAL_CHANCE];
public chance = 0.025;
get iconName(): string {
return "wl_full_heal";
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const enemyPokemon = params.pokemon;
if (!enemyPokemon.status || randSeedFloat() > this.chance * stack) {
return false;
}
globalScene.phaseManager.queueMessage(
getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)),
);
enemyPokemon.resetStatus();
enemyPokemon.updateInfo();
return true;
}
}
export class EnemyEndureChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_ENDURE_CHANCE];
public chance = 2;
get iconName(): string {
return "wl_reset_urge";
}
get description(): string {
return i18next.t("modifierType:ModifierType.EnemyEndureChanceModifierType.description", {
chancePercent: this.chance,
});
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const target = params.pokemon;
if (target.waveData.endured || target.randBattleSeedInt(100) >= this.chance * stack) {
return false;
}
target.addTag(BattlerTagType.ENDURE_TOKEN, 1);
target.waveData.endured = true;
return true;
}
}
export class EnemyFusionChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_FUSED_CHANCE];
public chance = 0.01;
get iconName(): string {
return "wl_custom_spliced";
}
apply(manager: TrainerItemManager, params: BooleanHolderParams) {
const stack = manager.getStack(this.type);
const isFusion = params.booleanHolder;
if (randSeedFloat() > this.chance * stack) {
return false;
}
isFusion.value = true;
}
}

View File

@ -0,0 +1,209 @@
import { globalScene } from "#app/global-scene";
import { getPokemonNameWithAffix } from "#app/messages";
import { getStatusEffectDescriptor, getStatusEffectHealText } from "#data/status-effect";
import { BattlerTagType } from "#enums/battler-tag-type";
import { StatusEffect } from "#enums/status-effect";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import type { TrainerItemId } from "#enums/trainer-item-id";
import { TrainerItem } from "#items/trainer-item";
import type { TrainerItemManager } from "#items/trainer-item-manager";
import type { BooleanHolderParams, NumberHolderParams, PokemonParams } from "#types/trainer-item-parameter";
import { randSeedFloat, toDmgValue } from "#utils/common";
import i18next from "i18next";
export class EnemyDamageBoosterTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_DAMAGE_BOOSTER];
public damageBoost = 1.05;
get iconName(): string {
return "wl_item_drop";
}
apply(manager: TrainerItemManager, params: NumberHolderParams): boolean {
const stack = manager.getStack(this.type);
const multiplier = params.numberHolder;
multiplier.value = toDmgValue(multiplier.value * Math.pow(this.damageBoost, stack));
return true;
}
getMaxStackCount(): number {
return 999;
}
}
export class EnemyDamageReducerTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_DAMAGE_REDUCER];
public damageReduction = 0.975;
get iconName(): string {
return "wl_guard_spec";
}
apply(manager: TrainerItemManager, params: NumberHolderParams): boolean {
const stack = manager.getStack(this.type);
const multiplier = params.numberHolder;
multiplier.value = toDmgValue(multiplier.value * Math.pow(this.damageReduction, stack));
return true;
}
getMaxStackCount(): number {
return globalScene.currentBattle.waveIndex < 2000 ? 99 : 999;
}
}
export class EnemyTurnHealTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_HEAL];
public healPercent = 2;
get iconName(): string {
return "wl_potion";
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const enemyPokemon = params.pokemon;
if (!enemyPokemon.isFullHp()) {
globalScene.phaseManager.unshiftNew(
"PokemonHealPhase",
enemyPokemon.getBattlerIndex(),
Math.max(Math.floor(enemyPokemon.getMaxHp() / (100 / this.healPercent)) * stack, 1),
i18next.t("modifier:enemyTurnHealApply", {
pokemonNameWithAffix: getPokemonNameWithAffix(enemyPokemon),
}),
true,
false,
false,
false,
true,
);
return true;
}
return false;
}
}
export class EnemyAttackStatusEffectChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_ATTACK_STATUS_CHANCE];
public effect: StatusEffect;
constructor(type: TrainerItemId, effect: StatusEffect, stackCount?: number) {
super(type, stackCount);
this.effect = effect;
}
get iconName(): string {
if (this.effect === StatusEffect.POISON) {
return "wl_antidote";
}
if (this.effect === StatusEffect.PARALYSIS) {
return "wl_paralyze_heal";
}
if (this.effect === StatusEffect.BURN) {
return "wl_burn_heal";
}
return "";
}
get description(): string {
return i18next.t("modifierType:ModifierType.EnemyAttackStatusEffectChanceModifierType.description", {
chancePercent: this.getChance() * 100,
statusEffect: getStatusEffectDescriptor(this.effect),
});
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const enemyPokemon = params.pokemon;
const chance = this.getChance();
if (randSeedFloat() <= chance * stack) {
return enemyPokemon.trySetStatus(this.effect);
}
return false;
}
getChance(): number {
return 0.025 * (this.effect === StatusEffect.BURN || this.effect === StatusEffect.POISON ? 2 : 1);
}
}
export class EnemyStatusEffectHealChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_STATUS_HEAL_CHANCE];
public chance = 0.025;
get iconName(): string {
return "wl_full_heal";
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const enemyPokemon = params.pokemon;
if (!enemyPokemon.status || randSeedFloat() > this.chance * stack) {
return false;
}
globalScene.phaseManager.queueMessage(
getStatusEffectHealText(enemyPokemon.status.effect, getPokemonNameWithAffix(enemyPokemon)),
);
enemyPokemon.resetStatus();
enemyPokemon.updateInfo();
return true;
}
}
export class EnemyEndureChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_ENDURE_CHANCE];
public chance = 2;
get iconName(): string {
return "wl_reset_urge";
}
get description(): string {
return i18next.t("modifierType:ModifierType.EnemyEndureChanceModifierType.description", {
chancePercent: this.chance,
});
}
apply(manager: TrainerItemManager, params: PokemonParams): boolean {
const stack = manager.getStack(this.type);
const target = params.pokemon;
if (target.waveData.endured || target.randBattleSeedInt(100) >= this.chance * stack) {
return false;
}
target.addTag(BattlerTagType.ENDURE_TOKEN, 1);
target.waveData.endured = true;
return true;
}
}
export class EnemyFusionChanceTrainerItem extends TrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.ENEMY_FUSED_CHANCE];
public chance = 0.01;
get iconName(): string {
return "wl_custom_spliced";
}
apply(manager: TrainerItemManager, params: BooleanHolderParams) {
const stack = manager.getStack(this.type);
const isFusion = params.booleanHolder;
if (randSeedFloat() > this.chance * stack) {
return false;
}
isFusion.value = true;
}
}

View File

@ -0,0 +1,22 @@
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { LapsingTrainerItem } from "#items/trainer-item";
import type { TrainerItemManager } from "#items/trainer-item-manager";
import type { NumberHolderParams } from "#types/trainer-item-parameter";
import i18next from "i18next";
export class DoubleBattleChanceBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.DOUBLE_BATTLE_CHANCE_BOOSTER];
get description(): string {
return i18next.t("modifierType:ModifierType.DoubleBattleChanceBoosterModifierType.description", {
battleCount: this.getMaxStackCount(),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const doubleBattleChance = params.numberHolder;
// This is divided because the chance is generated as a number from 0 to doubleBattleChance.value using randSeedInt
// A double battle will initiate if the generated number is 0
doubleBattleChance.value /= 4;
}
}

View File

@ -0,0 +1,87 @@
import { getStatKey, Stat, type TempBattleStat } from "#enums/stat";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { TrainerItemId, TrainerItemNames } from "#enums/trainer-item-id";
import { LapsingTrainerItem } from "#items/trainer-item";
import type { TrainerItemManager } from "#items/trainer-item-manager";
import type { NumberHolderParams } from "#types/trainer-item-parameter";
import i18next from "i18next";
type TempStatToTrainerItemMap = {
[key in TempBattleStat]: TrainerItemId;
};
export const tempStatToTrainerItem: TempStatToTrainerItemMap = {
[Stat.ATK]: TrainerItemId.X_ATTACK,
[Stat.DEF]: TrainerItemId.X_DEFENSE,
[Stat.SPATK]: TrainerItemId.X_SP_ATK,
[Stat.SPDEF]: TrainerItemId.X_SP_DEF,
[Stat.SPD]: TrainerItemId.X_SPEED,
[Stat.ACC]: TrainerItemId.X_ACCURACY,
};
export class TempStatStageBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.TEMP_STAT_STAGE_BOOSTER];
private stat: TempBattleStat;
constructor(type: TrainerItemId, stat: TempBattleStat, stackCount?: number) {
super(type, stackCount);
this.stat = stat;
}
get name(): string {
return i18next.t(`modifierType:TempStatStageBoosterItem.${TrainerItemNames[this.type]?.toLowerCase()}`);
}
get description(): string {
console.log();
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t(getStatKey(this.stat)),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.percentage"),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const statLevel = params.numberHolder;
const boost = 0.3;
statLevel.value += boost;
}
}
export class TempAccuracyBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.TEMP_ACCURACY_BOOSTER];
get name(): string {
return i18next.t(`modifierType:TempStatStageBoosterItem.${TrainerItemNames[this.type]?.toLowerCase()}`);
}
get description(): string {
console.log();
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t(getStatKey(Stat.ACC)),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.percentage"),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const statLevel = params.numberHolder;
const boost = 1;
statLevel.value += boost;
}
}
export class TempCritBoosterTrainerItem extends LapsingTrainerItem {
public effects: TrainerItemEffect[] = [TrainerItemEffect.TEMP_CRIT_BOOSTER];
get description(): string {
return i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.description", {
stat: i18next.t("modifierType:ModifierType.DIRE_HIT.extra.raises"),
amount: i18next.t("modifierType:ModifierType.TempStatStageBoosterModifierType.extra.stage"),
});
}
apply(_manager: TrainerItemManager, params: NumberHolderParams) {
const critLevel = params.numberHolder;
critLevel.value++;
}
}

View File

@ -21,10 +21,10 @@ import { TrainerType } from "#enums/trainer-type";
import { Unlockables } from "#enums/unlockables";
import { VariantTier } from "#enums/variant-tier";
import { WeatherType } from "#enums/weather-type";
import { HeldItemConfiguration } from "#items/held-item-data-types";
import { TrainerItemConfiguration } from "#items/trainer-item-data-types";
import { Variant } from "#sprites/variant";
import { HeldItemConfiguration } from "#types/held-item-data-types";
import { RewardSpecs } from "#types/rewards";
import { TrainerItemConfiguration } from "#types/trainer-item-data-types";
/**
* This comment block exists to prevent IDEs from automatically removing unused imports

View File

@ -5,14 +5,15 @@ import { RewardPoolType } from "#enums/reward-pool-type";
import type { RarityTier } from "#enums/reward-tier";
import { TrainerItemEffect } from "#enums/trainer-item-effect";
import { UiMode } from "#enums/ui-mode";
import type {
PokemonMoveRecallRewardParams,
PokemonMoveRewardParams,
PokemonRewardParams,
Reward,
RewardOption,
import {
type PokemonMoveRecallRewardParams,
type PokemonMoveReward,
type PokemonMoveRewardParams,
PokemonReward,
type PokemonRewardParams,
type Reward,
type RewardOption,
} from "#items/reward";
import { FusePokemonReward, type PokemonMoveReward, PokemonReward, RememberMoveReward, TmReward } from "#items/reward";
import {
type CustomRewardSettings,
generatePlayerRewardOptions,
@ -20,6 +21,9 @@ import {
getRewardPoolForType,
} from "#items/reward-pool-utils";
import { getPlayerShopRewardOptionsForWave, isMoveReward, isRememberMoveReward, isTmReward } from "#items/reward-utils";
import { FusePokemonReward } from "#items/rewards/fuse";
import { RememberMoveReward } from "#items/rewards/remember-move";
import { TmReward } from "#items/rewards/tm";
import { BattlePhase } from "#phases/battle-phase";
import { PartyOption, PartyUiHandler, PartyUiMode, type PokemonMoveSelectFilter } from "#ui/party-ui-handler";
import { type RewardSelectUiHandler, SHOP_OPTIONS_ROW_LIMIT } from "#ui/reward-select-ui-handler";

View File

@ -36,7 +36,6 @@ import { Unlockables } from "#enums/unlockables";
import { WeatherType } from "#enums/weather-type";
import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#events/arena";
import type { EnemyPokemon, PlayerPokemon, Pokemon } from "#field/pokemon";
import type { TrainerItemConfiguration, TrainerItemSaveData } from "#items/trainer-item-data-types";
import { MysteryEncounterSaveData } from "#mystery-encounters/mystery-encounter-save-data";
import type { Variant } from "#sprites/variant";
import { achvs } from "#system/achv";
@ -59,6 +58,7 @@ import {
import { VoucherType, vouchers } from "#system/voucher";
import { trainerConfigs } from "#trainers/trainer-config";
import type { DexData, DexEntry } from "#types/dex-data";
import type { TrainerItemConfiguration, TrainerItemSaveData } from "#types/trainer-item-data-types";
import { RUN_HISTORY_LIMIT } from "#ui/run-history-ui-handler";
import { applyChallenges } from "#utils/challenge-utils";
import { executeIf, fixedInt, isLocal, NumberHolder, randInt, randSeedItem } from "#utils/common";

View File

@ -11,10 +11,10 @@ import type { PokemonType } from "#enums/pokemon-type";
import type { SpeciesId } from "#enums/species-id";
import { TrainerSlot } from "#enums/trainer-slot";
import { EnemyPokemon, Pokemon } from "#field/pokemon";
import type { HeldItemSaveData } from "#items/held-item-data-types";
import { saveDataToConfig } from "#items/held-item-pool";
import { PokemonMove } from "#moves/pokemon-move";
import type { Variant } from "#sprites/variant";
import type { HeldItemSaveData } from "#types/held-item-data-types";
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
export class PokemonData {