diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 2f2e5bc02..d24ec2009 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, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, HealingBoosterModifier, HitHealModifier, LapsingPersistentModifier, MapModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier, PokemonHeldItemModifier, PokemonInstantReviveModifier, SwitchEffectTransferModifier, TempBattleStatBoosterModifier, TurnHealModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; +import { BerryModifier, ContactHeldItemTransferChanceModifier, EnemyAttackStatusEffectChanceModifier, EnemyInstantReviveChanceModifier, ExpBalanceModifier, ExpBoosterModifier, ExpShareModifier, ExtraModifierModifier, FlinchChanceModifier, 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 { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getModifierType, getPlayerModifierTypeOptionsForWave, modifierTypes, regenerateModifierPoolThresholds } from "./modifier/modifier-type"; +import { ModifierPoolType, ModifierType, ModifierTypeFunc, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, TmModifierType, getEnemyBuffModifierTypeOptionsForWave, 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"; @@ -1561,8 +1561,11 @@ class MoveEffectPhase extends PokemonPhase { if (!isProtected && !chargeEffect) { applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT, user, target, this.move.getMove()); - if (!target.isFainted()) + if (!target.isFainted()) { applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult); + if (!user.isPlayer()) + user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target); + } if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex()); } @@ -1987,8 +1990,15 @@ export class FaintPhase extends PokemonPhase { if (!--instantReviveModifier.stackCount) this.scene.removeModifier(instantReviveModifier); this.scene.updateModifiers(this.player); - this.end(); - return; + return this.end(); + } + + if (!pokemon.isPlayer()) { + const enemyInstantReviveModifiers = this.scene.findModifiers(m => m instanceof EnemyInstantReviveChanceModifier); + for (let modifier of enemyInstantReviveModifiers) { + if (modifier.shouldApply([ pokemon ]) && modifier.apply([ pokemon ])) + return this.end(); + } } this.scene.queueMessage(getPokemonMessage(pokemon, ' fainted!'), null, true); @@ -2113,9 +2123,11 @@ export class VictoryPhase extends PokemonPhase { if (this.scene.currentBattle.battleType === BattleType.TRAINER) this.scene.pushPhase(new TrainerVictoryPhase(this.scene)); if (this.scene.gameMode === GameMode.ENDLESS || this.scene.currentBattle.waveIndex < this.scene.finalWave) { - if (this.scene.currentBattle.waveIndex > 30 || this.scene.currentBattle.waveIndex % 10) + if (this.scene.currentBattle.waveIndex > 30 || this.scene.currentBattle.waveIndex % 10) { this.scene.pushPhase(new SelectModifierPhase(this.scene)); - else + if (this.scene.gameMode === GameMode.ENDLESS && !(this.scene.currentBattle.waveIndex % 50)) + this.scene.pushPhase(new SelectEnemyBuffModifierPhase(this.scene)); + } else this.scene.pushPhase(new ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_EXP_CHARM)) this.scene.pushPhase(new NewBattlePhase(this.scene)); } else @@ -2794,13 +2806,13 @@ export class SelectModifierPhase extends BattlePhase { start() { super.start(); - this.scene.resetSeed(); + this.updateSeed(); const party = this.scene.getParty(); - regenerateModifierPoolThresholds(party, ModifierPoolType.PLAYER); + regenerateModifierPoolThresholds(party, this.getPoolType()); const modifierCount = new Utils.IntegerHolder(3); this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount); - const typeOptions: Array = getPlayerModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, modifierCount.value, party); + const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value); const modifierSelectCallback = (cursor: integer) => { if (cursor < 0) { @@ -2810,7 +2822,7 @@ export class SelectModifierPhase extends BattlePhase { } else if (cursor >= typeOptions.length) { this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, toSlotIndex: integer) => { if (toSlotIndex !== undefined && fromSlotIndex < 6 && toSlotIndex < 6 && fromSlotIndex !== toSlotIndex && itemIndex > -1) { - this.scene.ui.setMode(Mode.MODIFIER_SELECT).then(() => { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; const itemModifier = itemModifiers[itemIndex]; @@ -2820,11 +2832,11 @@ export class SelectModifierPhase extends BattlePhase { this.scene.ui.setMode(Mode.MESSAGE); super.end(); } else - this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback); }); }); } else - this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback); }, PartyUiHandler.FilterItemMaxStacks); return; } @@ -2841,7 +2853,7 @@ export class SelectModifierPhase extends BattlePhase { : undefined; this.scene.ui.setModeWithoutClear(Mode.PARTY, partyUiMode, -1, (slotIndex: integer, option: PartyOption) => { if (slotIndex < 6) { - this.scene.ui.setMode(Mode.MODIFIER_SELECT).then(() => { + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { const modifierType = typeOptions[cursor].type; const modifier = !isMoveModifier ? modifierType.newModifier(party[slotIndex]) @@ -2851,15 +2863,63 @@ export class SelectModifierPhase extends BattlePhase { this.scene.addModifier(modifier, true).then(() => super.end()); }); } else - this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, ); }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId); } else { - this.scene.addModifier(typeOptions[cursor].type.newModifier(), true).then(() => super.end()); + this.addModifier(typeOptions[cursor].type.newModifier()).then(() => super.end()); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE); } }; - this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); + this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback); + } + + updateSeed(): void { + this.scene.resetSeed(); + } + + isPlayer(): boolean { + return true; + } + + getPoolType(): ModifierPoolType { + return ModifierPoolType.PLAYER; + } + + getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] { + return getPlayerModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, modifierCount, this.scene.getParty()); + } + + addModifier(modifier: Modifier): Promise { + return this.scene.addModifier(modifier, true); + } +} + +export class SelectEnemyBuffModifierPhase extends SelectModifierPhase { + constructor(scene: BattleScene) { + super(scene); + } + + start() { + this.scene.time.delayedCall(500, () => super.start()); + } + + updateSeed(): void { } + + 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); } } diff --git a/src/battle.ts b/src/battle.ts index 0e6120842..be511ec1a 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -63,28 +63,18 @@ export default class Battle { private getLevelForWave(): integer { let baseLevel = 1 + this.waveIndex / 2 + Math.pow(this.waveIndex / 25, 2); - let bossMultiplier = this.waveIndex <= 250 ? 1.2 : Math.pow(1.2, this.waveIndex / 250); + const bossMultiplier = 1.2; if (!(this.waveIndex % 10)) { const ret = Math.floor(baseLevel * bossMultiplier); if (this.waveIndex === 200 || !(this.waveIndex % 250)) return Math.ceil(ret / 25) * 25; - return ret; + return ret + Math.round(Phaser.Math.RND.realInRange(-1, 1) * Math.floor(this.waveIndex / 10)); } const deviation = 10 / this.waveIndex; - const randLevel = Math.max(Math.round(baseLevel + Math.abs(Utils.randSeedGauss(deviation))), 1); - - if (this.waveIndex <= 250) - return randLevel; - - const waveRatio = Math.min(this.waveIndex - 250 / 250, 1); - - const easeInFunc = Phaser.Tweens.Builders.GetEaseFunction('Sine.easeIn'); - const easeOutFunc = Phaser.Tweens.Builders.GetEaseFunction('Sine.easeOut'); - - return Math.ceil(easeInFunc(waveRatio) * Math.floor(baseLevel * bossMultiplier) + easeOutFunc(1 - waveRatio) * randLevel); + return Math.max(Math.round(baseLevel + Math.abs(Utils.randSeedGauss(deviation))), 1); } getBattlerCount(): integer { diff --git a/src/data/move.ts b/src/data/move.ts index 81d7e03bb..bba1fe9e7 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2635,7 +2635,8 @@ export function initMoves() { .ignoresVirtual() .target(MoveTarget.OTHER), new StatusMove(Moves.TRANSFORM, "Transform", Type.NORMAL, -1, 10, -1, "User takes on the form and attacks of the opponent.", -1, 0, 1) - .attr(TransformAttr), + .attr(TransformAttr) + .ignoresProtect(), new AttackMove(Moves.BUBBLE, "Bubble", Type.WATER, MoveCategory.SPECIAL, 40, 100, 30, -1, "May lower opponent's Speed.", 10, 0, 1) .attr(StatChangeAttr, BattleStat.SPD, -1) .target(MoveTarget.ALL_NEAR_ENEMIES), diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 57c311448..399559310 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -12,6 +12,7 @@ import { TempBattleStat, getTempBattleStatBoosterItemName, getTempBattleStatName import { BerryType, getBerryEffectDescription, getBerryName } from '../data/berry'; import { Unlockables } from '../system/unlockables'; import { GameMode } from '../game-mode'; +import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect'; type Modifier = Modifiers.Modifier; @@ -26,7 +27,8 @@ export enum ModifierTier { export enum ModifierPoolType { PLAYER, WILD, - TRAINER + TRAINER, + ENEMY_BUFF } type NewModifierFunc = (type: ModifierType, args: any[]) => Modifier; @@ -524,6 +526,18 @@ export class TurnHeldItemTransferModifierType extends PokemonHeldItemModifierTyp } } +export class EnemyAttackStatusEffectChanceModifierType extends ModifierType { + constructor(name: string, chancePercent: integer, effect: StatusEffect, iconImage?: string) { + super(name, `Adds a ${chancePercent}% chance on hit to inflict ${getStatusEffectDescriptor(effect)}`, (type, args) => new Modifiers.EnemyAttackStatusEffectChanceModifier(type, effect, chancePercent), iconImage, 'enemy_status_chance') + } +} + +export class EnemyInstantReviveChanceModifierType extends ModifierType { + constructor(name: string, chancePercent: integer, 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'); + } +} + export type ModifierTypeFunc = () => ModifierType; type WeightedModifierTypeWeightFunc = (party: Pokemon[]) => integer; @@ -662,7 +676,14 @@ export const modifierTypes = { (type, _args) => new Modifiers.ExtraModifierModifier(type), 'pb_gold', null, 'pb_bounce_1'), ENEMY_DAMAGE_BOOSTER: () => new ModifierType('Damage Booster', 'Increases damage by 20%', (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type), 'wl_item_drop'), - ENEMY_DAMAGE_REDUCTION: () => new ModifierType('Damage Reducer', 'Reduces incoming damage by 10%', (type, _args) => new Modifiers.EnemyDamageReducerModifier(type), 'wl_guard_spec') + ENEMY_DAMAGE_REDUCTION: () => new ModifierType('Damage Reducer', 'Reduces incoming damage by 10%', (type, _args) => new Modifiers.EnemyDamageReducerModifier(type), 'wl_guard_spec'), + ENEMY_ATTACK_POISON_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Poison Hit', 10, StatusEffect.POISON, 'wl_antidote'), + ENEMY_ATTACK_PARALYZE_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Paralyze Hit', 10, StatusEffect.PARALYSIS, 'wl_paralyze_heal'), + ENEMY_ATTACK_SLEEP_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Sleep Hit', 10, StatusEffect.SLEEP, 'wl_awakening'), + ENEMY_ATTACK_FREEZE_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Freeze Hit', 10, StatusEffect.FREEZE, 'wl_ice_heal'), + ENEMY_ATTACK_BURN_CHANCE: () => new EnemyAttackStatusEffectChanceModifierType('Burn Hit', 10, StatusEffect.BURN, 'wl_burn_heal'), + ENEMY_INSTANT_REVIVE_CHANCE: () => new EnemyInstantReviveChanceModifierType('Reviver', 5, false, 'wl_revive'), + ENEMY_INSTANT_MAX_REVIVE_CHANCE: () => new EnemyInstantReviveChanceModifierType('Reviver', 2, true, 'wl_max_revive'), }; const modifierPool = { @@ -800,6 +821,23 @@ const trainerModifierPool = { ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) }; +const enemyBuffModifierPool = { + [ModifierTier.COMMON]: [ + new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_BOOSTER, 8), + new WeightedModifierType(modifierTypes.ENEMY_DAMAGE_REDUCTION, 8), + new WeightedModifierType(modifierTypes.ENEMY_ATTACK_POISON_CHANCE, 3), + new WeightedModifierType(modifierTypes.ENEMY_ATTACK_PARALYZE_CHANCE, 3), + new WeightedModifierType(modifierTypes.ENEMY_ATTACK_SLEEP_CHANCE, 3), + new WeightedModifierType(modifierTypes.ENEMY_ATTACK_FREEZE_CHANCE, 3), + new WeightedModifierType(modifierTypes.ENEMY_ATTACK_BURN_CHANCE, 3), + new WeightedModifierType(modifierTypes.ENEMY_INSTANT_REVIVE_CHANCE, 8), + new WeightedModifierType(modifierTypes.ENEMY_INSTANT_MAX_REVIVE_CHANCE, 8) + ].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.MASTER]: [ ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) +}; + export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType { const modifierType = modifierTypeFunc(); if (!modifierType.id) @@ -813,9 +851,12 @@ let ignoredPoolIndexes = {}; let enemyModifierPoolThresholds = {}; let enemyIgnoredPoolIndexes = {}; +let enemyBuffModifierPoolThresholds = {}; +let enemyBuffIgnoredPoolIndexes = {}; + export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType) { const player = !poolType; - const pool = player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : trainerModifierPool; + const pool = player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : poolType === ModifierPoolType.TRAINER ? trainerModifierPool : enemyBuffModifierPool; const ignoredIndexes = {}; const thresholds = Object.fromEntries(new Map(Object.keys(pool).map(t => { ignoredIndexes[t] = []; @@ -843,9 +884,12 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod if (player) { modifierPoolThresholds = thresholds; ignoredPoolIndexes = ignoredIndexes; - } else { + } else if (poolType !== ModifierPoolType.ENEMY_BUFF) { enemyModifierPoolThresholds = thresholds; enemyIgnoredPoolIndexes = ignoredIndexes; + } else { + enemyBuffModifierPoolThresholds = thresholds; + enemyBuffIgnoredPoolIndexes = ignoredIndexes; } } @@ -868,6 +912,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 getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, gameMode: GameMode): PokemonHeldItemModifierType[] { const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType).type as PokemonHeldItemModifierType); if ((gameMode === GameMode.CLASSIC && waveIndex === 200) || !(waveIndex % 1000)) @@ -877,6 +934,7 @@ export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgrade?: boolean): ModifierTypeOption { const player = !poolType; + const pool = player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : poolType === ModifierPoolType.TRAINER ? trainerModifierPool : enemyBuffModifierPool; if (tier === undefined) { const tierValue = Utils.randSeedInt(256); if (player && tierValue) { @@ -886,23 +944,26 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, } else upgrade = false; tier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0); + while (tier && !modifierPool[tier].length) + tier--; } - const thresholds = Object.keys((player ? modifierPoolThresholds : enemyModifierPoolThresholds)[tier]); - const totalWeight = parseInt(thresholds[thresholds.length - 1]); + const thresholds = player ? modifierPoolThresholds : pool !== enemyBuffModifierPool ? enemyModifierPoolThresholds : enemyBuffModifierPoolThresholds; + const tierThresholds = Object.keys(thresholds[tier]); + const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]); const value = Utils.randSeedInt(totalWeight); let index: integer; - for (let t of thresholds) { + for (let t of tierThresholds) { let threshold = parseInt(t); if (value < threshold) { - index = (player ? modifierPoolThresholds : enemyModifierPoolThresholds)[tier][threshold]; + index = thresholds[tier][threshold]; break; } } if (player) console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]) - let modifierType: ModifierType = ((player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : trainerModifierPool)[tier][index]).modifierType; + let modifierType: ModifierType = (pool[tier][index]).modifierType; if (modifierType instanceof ModifierTypeGenerator) { modifierType = (modifierType as ModifierTypeGenerator).generateType(party); if (modifierType === null) { diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 66e65f0fe..d4697b151 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -1231,6 +1231,10 @@ export abstract class EnemyPersistentModifer extends PersistentModifier { constructor(type: ModifierType, stackCount?: integer) { super(type, stackCount); } + + getMaxStackCount(): number { + return 5; + } } export class EnemyDamageBoosterModifier extends EnemyPersistentModifer { @@ -1251,10 +1255,6 @@ export class EnemyDamageBoosterModifier extends EnemyPersistentModifer { return true; } - - getMaxStackCount(): number { - return 5; - } } export class EnemyDamageReducerModifier extends EnemyPersistentModifer { @@ -1275,19 +1275,17 @@ export class EnemyDamageReducerModifier extends EnemyPersistentModifer { return true; } - - getMaxStackCount(): number { - return 5; - } } export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifer { public effect: StatusEffect; + private chance: number; - constructor(type: ModifierType, effect: StatusEffect, stackCount?: integer) { + constructor(type: ModifierType, effect: StatusEffect, chancePercent: integer, stackCount?: integer) { super(type, stackCount); this.effect = effect; + this.chance = chancePercent / 100; } match(modifier: Modifier): boolean { @@ -1300,13 +1298,45 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModife apply(args: any[]): boolean { const target = (args[0] as Pokemon); - if (Utils.randInt(10) < this.getStackCount()) + if (Utils.randIntRange(0, 1) < this.chance * this.getStackCount()) { target.scene.unshiftPhase(new ObtainStatusEffectPhase(target.scene, target.getBattlerIndex(), this.effect)); + return true; + } + + return false; + } +} + +export class EnemyInstantReviveChanceModifier extends EnemyPersistentModifer { + public fullHeal: boolean; + private chance: number; + + constructor(type: ModifierType, healFull: boolean, chancePercent: integer, stackCount?: integer) { + super(type, stackCount); + + this.fullHeal = healFull; + this.chance = chancePercent / 100; + } + + matchType(modifier: Modifier) { + return modifier instanceof EnemyInstantReviveChanceModifier && modifier.fullHeal === this.fullHeal; + } + + clone() { + return new EnemyInstantReviveChanceModifier(this.type, this.fullHeal, this.chance, this.stackCount); + } + + apply(args: any[]): boolean { + if (Utils.randIntRange(0, 1) >= this.chance * this.getStackCount()) + return false; + + const pokemon = args[0] as Pokemon; + + pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(), + Math.max(Math.floor(pokemon.getMaxHp() / (this.fullHeal ? 1 : 2)), 1), getPokemonMessage(pokemon, ` was revived\nby its ${this.type.name}!`), false, false, true)); + + pokemon.resetStatus(); return true; } - - getMaxStackCount(): number { - return 5; - } } \ No newline at end of file diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index 84deb3442..19d8a4dba 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -116,7 +116,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { } } - clear(): void { + clear() { super.clear(); } diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index b7b599ec4..f9a364cb4 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -12,6 +12,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { private transferButtonContainer: Phaser.GameObjects.Container; private lastCursor: integer = 0; + private player: boolean; public options: ModifierOption[]; @@ -47,26 +48,28 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { show(args: any[]) { if (this.active) { - if (args.length === 2) { + if (args.length === 3) { this.awaitingActionInput = true; - this.onActionInput = args[1]; + this.onActionInput = args[2]; } return; } - if (args.length !== 2 || !(args[0] instanceof Array) || !args[0].length || !(args[1] instanceof Function)) + if (args.length !== 3 || !(args[1] instanceof Array) || !args[1].length || !(args[2] instanceof Function)) return; super.show(args); this.getUi().clearText(); - const partyHasHeldItem = !!this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).getTransferrable(true)).length; + this.player = args[0]; + + const partyHasHeldItem = this.player && !!this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).getTransferrable(true)).length; this.transferButtonContainer.setVisible(false); this.transferButtonContainer.setAlpha(0); - const typeOptions = args[0] as ModifierTypeOption[]; + const typeOptions = args[1] as ModifierTypeOption[]; for (let m = 0; m < typeOptions.length; m++) { const sliceWidth = (this.scene.game.canvas.width / 6) / (typeOptions.length + 2); const option = new ModifierOption(this.scene, sliceWidth * (m + 1) + (sliceWidth * 0.5), -this.scene.game.canvas.height / 12 - 24, typeOptions[m]); @@ -113,7 +116,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.setCursor(0); this.awaitingActionInput = true; - this.onActionInput = args[1]; + this.onActionInput = args[2]; }); } @@ -134,12 +137,14 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { originalOnActionInput(this.cursor); } } else if (button === Button.CANCEL) { - success = true; - if (this.onActionInput) { - const originalOnActionInput = this.onActionInput; - this.awaitingActionInput = false; - this.onActionInput = null; - originalOnActionInput(-1); + if (this.player) { + success = true; + if (this.onActionInput) { + const originalOnActionInput = this.onActionInput; + this.awaitingActionInput = false; + this.onActionInput = null; + originalOnActionInput(-1); + } } } else { switch (button) {