From 446d00b4cd26af37627e0f8e23383e81105a3f3b Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 5 Nov 2023 10:56:09 -0500 Subject: [PATCH] Rework token modifiers in endless mode (WiP) --- src/battle-phases.ts | 34 +++++--------- src/modifier/modifier-type.ts | 48 ++++++++++++------- src/modifier/modifier.ts | 88 +++++++++++++++++++---------------- 3 files changed, 91 insertions(+), 79 deletions(-) diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 68ea9cc8a71..e4f2213da16 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -5,7 +5,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov import { Mode } from './ui/ui'; import { Command } from "./ui/command-ui-handler"; import { Stat } from "./data/pokemon-stat"; -import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyInstantReviveChanceModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; +import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyInstantReviveChanceModifier, EnemyPersistentModifier, EnemyStatusEffectHealChanceModifier, EnemyTurnHealModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, FusePokemonModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, Modifier, MultipleParticipantExpBonusModifier, PersistentModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; import PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims"; @@ -16,7 +16,7 @@ import { EvolutionPhase } from "./evolution-phase"; import { BattlePhase } from "./battle-phase"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat"; import { Biome, biomeLinks } from "./data/biome"; -import { FusePokemonModifierType, ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getEnemyBuffModifierTypeOptionsForWave, getModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; +import { FusePokemonModifierType, ModifierPoolType, ModifierTier, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getEnemyBuffModifierForWave, getModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag"; import { getPokemonMessage } from "./messages"; @@ -473,7 +473,7 @@ export class SelectBiomePhase extends BattlePhase { if (this.scene.gameMode === GameMode.CLASSIC && this.scene.currentBattle.waveIndex === this.scene.finalWave - 9) setNextBiome(Biome.END); else if (this.scene.gameMode !== GameMode.CLASSIC) { - if (this.scene.currentBattle.waveIndex % 50 === 0) + if (!(this.scene.currentBattle.waveIndex % 50)) setNextBiome(Biome.END); else { const allBiomes = Utils.getEnumValues(Biome); @@ -2155,8 +2155,8 @@ export class VictoryPhase extends PokemonPhase { if (this.scene.gameMode !== GameMode.CLASSIC || this.scene.currentBattle.waveIndex < this.scene.finalWave) { if (this.scene.currentBattle.waveIndex > 30 || this.scene.currentBattle.waveIndex % 10) { this.scene.pushPhase(new SelectModifierPhase(this.scene)); - if (this.scene.gameMode !== GameMode.CLASSIC && !(this.scene.currentBattle.waveIndex % 50)) - this.scene.pushPhase(new SelectEnemyBuffModifierPhase(this.scene)); + if (this.scene.gameMode !== GameMode.CLASSIC && !(this.scene.currentBattle.waveIndex % 50)) + this.scene.pushPhase(new AddEnemyBuffModifierPhase(this.scene)); } else this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_EXP_CHARM)) this.scene.pushPhase(new NewBattlePhase(this.scene)); @@ -2947,31 +2947,19 @@ export class SelectModifierPhase extends BattlePhase { } } -export class SelectEnemyBuffModifierPhase extends SelectModifierPhase { +export class AddEnemyBuffModifierPhase extends BattlePhase { constructor(scene: BattleScene) { super(scene); } start() { - this.scene.time.delayedCall(500, () => super.start()); - } + super.start(); - updateSeed(): void { } + const waveIndex = this.scene.currentBattle.waveIndex; + const tier = !(waveIndex % 1000) ? ModifierTier.ULTRA : !(waveIndex % 250) ? ModifierTier.GREAT : ModifierTier.COMMON; - isPlayer(): boolean { - return false; - } - - getPoolType(): ModifierPoolType { - return ModifierPoolType.ENEMY_BUFF; - } - - getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] { - return getEnemyBuffModifierTypeOptionsForWave(modifierCount); - } - - addModifier(modifier: Modifier): Promise { - return this.scene.addEnemyModifier(modifier as PersistentModifier); + regenerateModifierPoolThresholds(this.scene.getEnemyParty(), ModifierPoolType.ENEMY_BUFF); + this.scene.addEnemyModifier(getEnemyBuffModifierForWave(tier, this.scene.findModifiers(m => m instanceof EnemyPersistentModifier, false))).then(() => this.end()); } } diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 79e4ba4f160..7b8d6a7cf7f 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -555,7 +555,7 @@ export class EnemyAttackStatusEffectChanceModifierType extends ModifierType { } export class EnemyInstantReviveChanceModifierType extends ModifierType { - constructor(name: string, chancePercent: integer, fullHeal: boolean, iconImage?: string) { + constructor(name: string, chancePercent: number, fullHeal: boolean, iconImage?: string) { super(name, `Adds a ${chancePercent}% chance of reviving with ${fullHeal ? 100 : 50}% HP`, (type, _args) => new Modifiers.EnemyInstantReviveChanceModifier(type, fullHeal, chancePercent), iconImage, 'enemy_revive'); } } @@ -703,8 +703,8 @@ export const modifierTypes = { ENEMY_DAMAGE_BOOSTER: () => new ModifierType('Damage Token', 'Increases damage by 20%', (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type, 20), 'wl_item_drop'), ENEMY_DAMAGE_REDUCTION: () => new ModifierType('Protection Token', 'Reduces incoming damage by 10%', (type, _args) => new Modifiers.EnemyDamageReducerModifier(type, 10), 'wl_guard_spec'), - ENEMY_HEAL: () => new ModifierType('Recovery Token', 'Heals 10% of max HP every turn', (type, _args) => new Modifiers.EnemyTurnHealModifier(type, 10), 'wl_potion'), - ENEMY_SUPER_HEAL: () => new ModifierType('Recovery Token', 'Heals 25% of max HP every turn', (type, _args) => new Modifiers.EnemyTurnHealModifier(type, 25), 'wl_super_potion'), + //ENEMY_SUPER_EFFECT_BOOSTER: () => new ModifierType('Type Advantage Token', 'Increases damage of super effective attacks by 30%', (type, _args) => new Modifiers.EnemySuperEffectiveDamageBoosterModifier(type, 30), 'wl_super_potion'), + ENEMY_HEAL: () => new ModifierType('Recovery Token', 'Heals 5% of max HP every turn', (type, _args) => new Modifiers.EnemyTurnHealModifier(type, 10), 'wl_potion'), ENEMY_ATTACK_POISON_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Poison Token', 10, StatusEffect.POISON, 'wl_antidote'), ENEMY_ATTACK_PARALYZE_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Paralyze Token', 10, StatusEffect.PARALYSIS, 'wl_paralyze_heal'), ENEMY_ATTACK_SLEEP_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Sleep Token', 10, StatusEffect.SLEEP, 'wl_awakening'), @@ -869,8 +869,22 @@ const enemyBuffModifierPool = { new WeightedModifierType(modifierTypes.ENEMY_INSTANT_REVIVE_CHANCE, 5), new WeightedModifierType(modifierTypes.ENEMY_INSTANT_MAX_REVIVE_CHANCE, 3) ].map(m => { m.setTier(ModifierTier.COMMON); return m; }), - [ModifierTier.GREAT]: [ ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), - [ModifierTier.ULTRA]: [ ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), + [ModifierTier.GREAT]: [ + new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 5), + new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 5), + new WeightedModifierType(modifierTypes.ENEMY_HEAL, 5), + new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 5), + new WeightedModifierType(modifierTypes.ENEMY_INSTANT_REVIVE_CHANCE, 5), + new WeightedModifierType(modifierTypes.ENEMY_INSTANT_MAX_REVIVE_CHANCE, 3) + ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), + [ModifierTier.ULTRA]: [ + new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 5), + new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 5), + new WeightedModifierType(modifierTypes.ENEMY_HEAL, 5), + new WeightedModifierType(modifierTypes.ENEMY_STATUS_EFFECT_HEAL_CHANCE, 5), + new WeightedModifierType(modifierTypes.ENEMY_INSTANT_REVIVE_CHANCE, 5), + new WeightedModifierType(modifierTypes.ENEMY_INSTANT_MAX_REVIVE_CHANCE, 3) + ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), [ModifierTier.MASTER]: [ ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) }; @@ -948,17 +962,19 @@ export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: i return options; } -export function getEnemyBuffModifierTypeOptionsForWave(count: integer): ModifierTypeOption[] { - const options: ModifierTypeOption[] = []; - const retryCount = Math.min(count * 5, 50); - new Array(count).fill(0).map(() => { - let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, ModifierTier.COMMON); - let r = 0; - while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) - candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, candidate.type.tier); - options.push(candidate); - }); - return options; +export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: Modifiers.PersistentModifier[]): Modifiers.EnemyPersistentModifier { + const tierStackCount = tier === ModifierTier.ULTRA ? 10 : tier === ModifierTier.GREAT ? 5 : 1; + const retryCount = 35; + let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); + let r = 0; + let matchingModifier: Modifiers.PersistentModifier; + while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate.type.id)) && matchingModifier.getMaxStackCount() < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) + candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); + + const modifier = candidate.type.newModifier() as Modifiers.EnemyPersistentModifier; + modifier.stackCount = tierStackCount; + + return modifier; } export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, gameMode: GameMode): PokemonHeldItemModifierType[] { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 9437b65566b..11e902ba39e 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -457,7 +457,7 @@ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier { private moveType: Type; private boostMultiplier: number; - constructor(type: ModifierType, pokemonId: integer, moveType: Type, boostPercent: integer, stackCount?: integer) { + constructor(type: ModifierType, pokemonId: integer, moveType: Type, boostPercent: number, stackCount?: integer) { super(type, pokemonId, stackCount); this.moveType = moveType; @@ -763,10 +763,10 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier { export class PokemonHpRestoreModifier extends ConsumablePokemonModifier { private restorePoints: integer; - private restorePercent: integer; + private restorePercent: number; public fainted: boolean; - constructor(type: ModifierType, pokemonId: integer, restorePoints: integer, restorePercent: integer, fainted?: boolean) { + constructor(type: ModifierType, pokemonId: integer, restorePoints: integer, restorePercent: number, fainted?: boolean) { super(type, pokemonId); this.restorePoints = restorePoints; @@ -998,7 +998,7 @@ export class HealingBoosterModifier extends PersistentModifier { export class ExpBoosterModifier extends PersistentModifier { private boostMultiplier: integer; - constructor(type: ModifierType, boostPercent: integer, stackCount?: integer) { + constructor(type: ModifierType, boostPercent: number, stackCount?: integer) { super(type, stackCount); this.boostMultiplier = boostPercent * 0.01; @@ -1034,7 +1034,7 @@ export class ExpBoosterModifier extends PersistentModifier { export class PokemonExpBoosterModifier extends PokemonHeldItemModifier { private boostMultiplier: integer; - constructor(type: ModifierTypes.PokemonExpBoosterModifierType, pokemonId: integer, boostPercent: integer, stackCount?: integer) { + constructor(type: ModifierTypes.PokemonExpBoosterModifierType, pokemonId: integer, boostPercent: number, stackCount?: integer) { super(type, pokemonId, stackCount); this.boostMultiplier = boostPercent * 0.01; } @@ -1259,7 +1259,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModifier { private chance: number; - constructor(type: ModifierType, pokemonId: integer, chancePercent: integer, stackCount?: integer) { + constructor(type: ModifierType, pokemonId: integer, chancePercent: number, stackCount?: integer) { super(type, pokemonId, stackCount); this.chance = chancePercent / 100; @@ -1314,23 +1314,39 @@ export class ExtraModifierModifier extends PersistentModifier { } } -export abstract class EnemyPersistentModifer extends PersistentModifier { +export abstract class EnemyPersistentModifier extends PersistentModifier { constructor(type: ModifierType, stackCount?: integer) { super(type, stackCount); } - getMaxStackCount(): number { + getMaxStackCount(): integer { return this.type.tier ? 1 : 5; } } -export class EnemyDamageBoosterModifier extends EnemyPersistentModifer { - private damageMultiplier: number; +abstract class EnemyDamageMultiplierModifier extends EnemyPersistentModifier { + protected damageMultiplier: number; - constructor(type: ModifierType, boostPercent: integer, stackCount?: integer) { + constructor(type: ModifierType, damageMultiplier: number, stackCount?: integer) { super(type, stackCount); - this.damageMultiplier = 1 + ((boostPercent || 20) * 0.01); + this.damageMultiplier = damageMultiplier; + } + + apply(args: any[]): boolean { + (args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * Math.pow(this.damageMultiplier, this.getStackCount())); + + return true; + } + + getMaxStackCount(): integer { + return 99; + } +} + +export class EnemyDamageBoosterModifier extends EnemyDamageMultiplierModifier { + constructor(type: ModifierType, boostPercent: number, stackCount?: integer) { + super(type, 1 + ((boostPercent || 20) * 0.01), stackCount); } match(modifier: Modifier): boolean { @@ -1344,21 +1360,11 @@ export class EnemyDamageBoosterModifier extends EnemyPersistentModifer { getArgs(): any[] { return [ (this.damageMultiplier - 1) * 100 ]; } - - apply(args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (this.damageMultiplier * this.getStackCount())); - - return true; - } } -export class EnemyDamageReducerModifier extends EnemyPersistentModifer { - private damageMultiplier: number; - - constructor(type: ModifierType, reductionPercent: integer, stackCount?: integer) { - super(type, stackCount); - - this.damageMultiplier = 1 - ((reductionPercent || 10) * 0.01); +export class EnemyDamageReducerModifier extends EnemyDamageMultiplierModifier { + constructor(type: ModifierType, reductionPercent: number, stackCount?: integer) { + super(type, 1 - ((reductionPercent || 10) * 0.01), stackCount); } match(modifier: Modifier): boolean { @@ -1372,18 +1378,12 @@ export class EnemyDamageReducerModifier extends EnemyPersistentModifer { getArgs(): any[] { return [ (1 - this.damageMultiplier) * 100 ]; } - - apply(args: any[]): boolean { - (args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (this.damageMultiplier * this.getStackCount())); - - return true; - } } -export class EnemyTurnHealModifier extends EnemyPersistentModifer { - private healPercent: integer; +export class EnemyTurnHealModifier extends EnemyPersistentModifier { + private healPercent: number; - constructor(type: ModifierType, healPercent: integer, stackCount?: integer) { + constructor(type: ModifierType, healPercent: number, stackCount?: integer) { super(type, stackCount); this.healPercent = healPercent || 10; @@ -1413,13 +1413,17 @@ export class EnemyTurnHealModifier extends EnemyPersistentModifer { return false; } + + getMaxStackCount(): integer { + return 20; + } } -export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifer { +export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifier { public effect: StatusEffect; private chance: number; - constructor(type: ModifierType, effect: StatusEffect, chancePercent: integer, stackCount?: integer) { + constructor(type: ModifierType, effect: StatusEffect, chancePercent: number, stackCount?: integer) { super(type, stackCount); this.effect = effect; @@ -1449,10 +1453,10 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModife } } -export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifer { +export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifier { private chance: number; - constructor(type: ModifierType, chancePercent: integer, stackCount?: integer) { + constructor(type: ModifierType, chancePercent: number, stackCount?: integer) { super(type, stackCount); this.chance = (chancePercent || 10) / 100; @@ -1483,11 +1487,11 @@ export class EnemyStatusEffectHealChanceModifier extends EnemyPersistentModifer } } -export class EnemyInstantReviveChanceModifier extends EnemyPersistentModifer { +export class EnemyInstantReviveChanceModifier extends EnemyPersistentModifier { public fullHeal: boolean; private chance: number; - constructor(type: ModifierType, healFull: boolean, chancePercent: integer, stackCount?: integer) { + constructor(type: ModifierType, healFull: boolean, chancePercent: number, stackCount?: integer) { super(type, stackCount); this.fullHeal = healFull; @@ -1519,4 +1523,8 @@ export class EnemyInstantReviveChanceModifier extends EnemyPersistentModifer { return true; } + + getMaxStackCount(): integer { + return 10; + } } \ No newline at end of file