Add enemy buffs to balance endless mode

pull/2/head
Flashfyre 2023-10-29 16:05:17 -04:00
parent cb62d79280
commit 5567b39f20
7 changed files with 214 additions and 67 deletions

View File

@ -5,7 +5,7 @@ import { allMoves, applyMoveAttrs, BypassSleepAttr, ChargeAttr, applyFilteredMov
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler"; import { Command } from "./ui/command-ui-handler";
import { Stat } from "./data/pokemon-stat"; 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 PartyUiHandler, { PartyOption, PartyUiMode } from "./ui/party-ui-handler";
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor, PokeballType } from "./data/pokeball";
import { CommonAnim, CommonBattleAnim, MoveAnim, initMoveAnim, loadMoveAnimAssets } from "./data/battle-anims"; 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 { BattlePhase } from "./battle-phase";
import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./data/battle-stat";
import { Biome, biomeLinks } from "./data/biome"; 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 SoundFade from "phaser3-rex-plugins/plugins/soundfade";
import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag"; import { BattlerTagLapseType, BattlerTagType, HideSpriteTag as HiddenTag, TrappedTag } from "./data/battler-tag";
import { getPokemonMessage } from "./messages"; import { getPokemonMessage } from "./messages";
@ -1561,8 +1561,11 @@ class MoveEffectPhase extends PokemonPhase {
if (!isProtected && !chargeEffect) { if (!isProtected && !chargeEffect) {
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT, applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT,
user, target, this.move.getMove()); user, target, this.move.getMove());
if (!target.isFainted()) if (!target.isFainted()) {
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult); applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move, hitResult);
if (!user.isPlayer())
user.scene.applyModifiers(EnemyAttackStatusEffectChanceModifier, false, target);
}
if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) if (this.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT))
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex()); this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target.getFieldIndex());
} }
@ -1987,8 +1990,15 @@ export class FaintPhase extends PokemonPhase {
if (!--instantReviveModifier.stackCount) if (!--instantReviveModifier.stackCount)
this.scene.removeModifier(instantReviveModifier); this.scene.removeModifier(instantReviveModifier);
this.scene.updateModifiers(this.player); this.scene.updateModifiers(this.player);
this.end(); return this.end();
return; }
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); this.scene.queueMessage(getPokemonMessage(pokemon, ' fainted!'), null, true);
@ -2113,9 +2123,11 @@ export class VictoryPhase extends PokemonPhase {
if (this.scene.currentBattle.battleType === BattleType.TRAINER) if (this.scene.currentBattle.battleType === BattleType.TRAINER)
this.scene.pushPhase(new TrainerVictoryPhase(this.scene)); this.scene.pushPhase(new TrainerVictoryPhase(this.scene));
if (this.scene.gameMode === GameMode.ENDLESS || this.scene.currentBattle.waveIndex < this.scene.finalWave) { 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)); 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 ModifierRewardPhase(this.scene, modifierTypes.GOLDEN_EXP_CHARM))
this.scene.pushPhase(new NewBattlePhase(this.scene)); this.scene.pushPhase(new NewBattlePhase(this.scene));
} else } else
@ -2794,13 +2806,13 @@ export class SelectModifierPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
this.scene.resetSeed(); this.updateSeed();
const party = this.scene.getParty(); const party = this.scene.getParty();
regenerateModifierPoolThresholds(party, ModifierPoolType.PLAYER); regenerateModifierPoolThresholds(party, this.getPoolType());
const modifierCount = new Utils.IntegerHolder(3); const modifierCount = new Utils.IntegerHolder(3);
this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount); this.scene.applyModifiers(ExtraModifierModifier, true, modifierCount);
const typeOptions: Array<ModifierTypeOption> = getPlayerModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, modifierCount.value, party); const typeOptions: ModifierTypeOption[] = this.getModifierTypeOptions(modifierCount.value);
const modifierSelectCallback = (cursor: integer) => { const modifierSelectCallback = (cursor: integer) => {
if (cursor < 0) { if (cursor < 0) {
@ -2810,7 +2822,7 @@ export class SelectModifierPhase extends BattlePhase {
} else if (cursor >= typeOptions.length) { } else if (cursor >= typeOptions.length) {
this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.MODIFIER_TRANSFER, -1, (fromSlotIndex: integer, itemIndex: integer, toSlotIndex: integer) => { 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) { 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 const itemModifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
&& (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[]; && (m as PokemonHeldItemModifier).getTransferrable(true) && (m as PokemonHeldItemModifier).pokemonId === party[fromSlotIndex].id) as PokemonHeldItemModifier[];
const itemModifier = itemModifiers[itemIndex]; const itemModifier = itemModifiers[itemIndex];
@ -2820,11 +2832,11 @@ export class SelectModifierPhase extends BattlePhase {
this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.setMode(Mode.MESSAGE);
super.end(); super.end();
} else } else
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback);
}); });
}); });
} else } else
this.scene.ui.setMode(Mode.MODIFIER_SELECT, typeOptions, modifierSelectCallback); this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback);
}, PartyUiHandler.FilterItemMaxStacks); }, PartyUiHandler.FilterItemMaxStacks);
return; return;
} }
@ -2841,7 +2853,7 @@ export class SelectModifierPhase extends BattlePhase {
: undefined; : undefined;
this.scene.ui.setModeWithoutClear(Mode.PARTY, partyUiMode, -1, (slotIndex: integer, option: PartyOption) => { this.scene.ui.setModeWithoutClear(Mode.PARTY, partyUiMode, -1, (slotIndex: integer, option: PartyOption) => {
if (slotIndex < 6) { 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 modifierType = typeOptions[cursor].type;
const modifier = !isMoveModifier const modifier = !isMoveModifier
? modifierType.newModifier(party[slotIndex]) ? modifierType.newModifier(party[slotIndex])
@ -2851,15 +2863,63 @@ export class SelectModifierPhase extends BattlePhase {
this.scene.addModifier(modifier, true).then(() => super.end()); this.scene.addModifier(modifier, true).then(() => super.end());
}); });
} else } 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); }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId);
} else { } 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.clearText();
this.scene.ui.setMode(Mode.MESSAGE); 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<void> {
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<void> {
return this.scene.addEnemyModifier(modifier as PersistentModifier);
} }
} }

View File

@ -63,28 +63,18 @@ export default class Battle {
private getLevelForWave(): integer { private getLevelForWave(): integer {
let baseLevel = 1 + this.waveIndex / 2 + Math.pow(this.waveIndex / 25, 2); 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)) { if (!(this.waveIndex % 10)) {
const ret = Math.floor(baseLevel * bossMultiplier); const ret = Math.floor(baseLevel * bossMultiplier);
if (this.waveIndex === 200 || !(this.waveIndex % 250)) if (this.waveIndex === 200 || !(this.waveIndex % 250))
return Math.ceil(ret / 25) * 25; 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 deviation = 10 / this.waveIndex;
const randLevel = Math.max(Math.round(baseLevel + Math.abs(Utils.randSeedGauss(deviation))), 1); return 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);
} }
getBattlerCount(): integer { getBattlerCount(): integer {

View File

@ -2635,7 +2635,8 @@ export function initMoves() {
.ignoresVirtual() .ignoresVirtual()
.target(MoveTarget.OTHER), .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) 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) 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) .attr(StatChangeAttr, BattleStat.SPD, -1)
.target(MoveTarget.ALL_NEAR_ENEMIES), .target(MoveTarget.ALL_NEAR_ENEMIES),

View File

@ -12,6 +12,7 @@ import { TempBattleStat, getTempBattleStatBoosterItemName, getTempBattleStatName
import { BerryType, getBerryEffectDescription, getBerryName } from '../data/berry'; import { BerryType, getBerryEffectDescription, getBerryName } from '../data/berry';
import { Unlockables } from '../system/unlockables'; import { Unlockables } from '../system/unlockables';
import { GameMode } from '../game-mode'; import { GameMode } from '../game-mode';
import { StatusEffect, getStatusEffectDescriptor } from '../data/status-effect';
type Modifier = Modifiers.Modifier; type Modifier = Modifiers.Modifier;
@ -26,7 +27,8 @@ export enum ModifierTier {
export enum ModifierPoolType { export enum ModifierPoolType {
PLAYER, PLAYER,
WILD, WILD,
TRAINER TRAINER,
ENEMY_BUFF
} }
type NewModifierFunc = (type: ModifierType, args: any[]) => Modifier; 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; export type ModifierTypeFunc = () => ModifierType;
type WeightedModifierTypeWeightFunc = (party: Pokemon[]) => integer; type WeightedModifierTypeWeightFunc = (party: Pokemon[]) => integer;
@ -662,7 +676,14 @@ export const modifierTypes = {
(type, _args) => new Modifiers.ExtraModifierModifier(type), 'pb_gold', null, 'pb_bounce_1'), (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_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 = { const modifierPool = {
@ -800,6 +821,23 @@ const trainerModifierPool = {
].map(m => { m.setTier(ModifierTier.MASTER); return m; }) ].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 { export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType {
const modifierType = modifierTypeFunc(); const modifierType = modifierTypeFunc();
if (!modifierType.id) if (!modifierType.id)
@ -813,9 +851,12 @@ let ignoredPoolIndexes = {};
let enemyModifierPoolThresholds = {}; let enemyModifierPoolThresholds = {};
let enemyIgnoredPoolIndexes = {}; let enemyIgnoredPoolIndexes = {};
let enemyBuffModifierPoolThresholds = {};
let enemyBuffIgnoredPoolIndexes = {};
export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType) { export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: ModifierPoolType) {
const player = !poolType; 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 ignoredIndexes = {};
const thresholds = Object.fromEntries(new Map(Object.keys(pool).map(t => { const thresholds = Object.fromEntries(new Map(Object.keys(pool).map(t => {
ignoredIndexes[t] = []; ignoredIndexes[t] = [];
@ -843,9 +884,12 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
if (player) { if (player) {
modifierPoolThresholds = thresholds; modifierPoolThresholds = thresholds;
ignoredPoolIndexes = ignoredIndexes; ignoredPoolIndexes = ignoredIndexes;
} else { } else if (poolType !== ModifierPoolType.ENEMY_BUFF) {
enemyModifierPoolThresholds = thresholds; enemyModifierPoolThresholds = thresholds;
enemyIgnoredPoolIndexes = ignoredIndexes; enemyIgnoredPoolIndexes = ignoredIndexes;
} else {
enemyBuffModifierPoolThresholds = thresholds;
enemyBuffIgnoredPoolIndexes = ignoredIndexes;
} }
} }
@ -868,6 +912,19 @@ export function getPlayerModifierTypeOptionsForWave(waveIndex: integer, count: i
return options; 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[] { 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); const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType).type as PokemonHeldItemModifierType);
if ((gameMode === GameMode.CLASSIC && waveIndex === 200) || !(waveIndex % 1000)) 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 { function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgrade?: boolean): ModifierTypeOption {
const player = !poolType; const player = !poolType;
const pool = player ? modifierPool : poolType === ModifierPoolType.WILD ? wildModifierPool : poolType === ModifierPoolType.TRAINER ? trainerModifierPool : enemyBuffModifierPool;
if (tier === undefined) { if (tier === undefined) {
const tierValue = Utils.randSeedInt(256); const tierValue = Utils.randSeedInt(256);
if (player && tierValue) { if (player && tierValue) {
@ -886,23 +944,26 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType,
} else } else
upgrade = false; upgrade = false;
tier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0); 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 thresholds = player ? modifierPoolThresholds : pool !== enemyBuffModifierPool ? enemyModifierPoolThresholds : enemyBuffModifierPoolThresholds;
const totalWeight = parseInt(thresholds[thresholds.length - 1]); const tierThresholds = Object.keys(thresholds[tier]);
const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]);
const value = Utils.randSeedInt(totalWeight); const value = Utils.randSeedInt(totalWeight);
let index: integer; let index: integer;
for (let t of thresholds) { for (let t of tierThresholds) {
let threshold = parseInt(t); let threshold = parseInt(t);
if (value < threshold) { if (value < threshold) {
index = (player ? modifierPoolThresholds : enemyModifierPoolThresholds)[tier][threshold]; index = thresholds[tier][threshold];
break; break;
} }
} }
if (player) if (player)
console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]) 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) { if (modifierType instanceof ModifierTypeGenerator) {
modifierType = (modifierType as ModifierTypeGenerator).generateType(party); modifierType = (modifierType as ModifierTypeGenerator).generateType(party);
if (modifierType === null) { if (modifierType === null) {

View File

@ -1231,6 +1231,10 @@ export abstract class EnemyPersistentModifer extends PersistentModifier {
constructor(type: ModifierType, stackCount?: integer) { constructor(type: ModifierType, stackCount?: integer) {
super(type, stackCount); super(type, stackCount);
} }
getMaxStackCount(): number {
return 5;
}
} }
export class EnemyDamageBoosterModifier extends EnemyPersistentModifer { export class EnemyDamageBoosterModifier extends EnemyPersistentModifer {
@ -1251,10 +1255,6 @@ export class EnemyDamageBoosterModifier extends EnemyPersistentModifer {
return true; return true;
} }
getMaxStackCount(): number {
return 5;
}
} }
export class EnemyDamageReducerModifier extends EnemyPersistentModifer { export class EnemyDamageReducerModifier extends EnemyPersistentModifer {
@ -1275,19 +1275,17 @@ export class EnemyDamageReducerModifier extends EnemyPersistentModifer {
return true; return true;
} }
getMaxStackCount(): number {
return 5;
}
} }
export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifer { export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModifer {
public effect: StatusEffect; 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); super(type, stackCount);
this.effect = effect; this.effect = effect;
this.chance = chancePercent / 100;
} }
match(modifier: Modifier): boolean { match(modifier: Modifier): boolean {
@ -1300,13 +1298,45 @@ export class EnemyAttackStatusEffectChanceModifier extends EnemyPersistentModife
apply(args: any[]): boolean { apply(args: any[]): boolean {
const target = (args[0] as Pokemon); 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)); target.scene.unshiftPhase(new ObtainStatusEffectPhase(target.scene, target.getBattlerIndex(), this.effect));
return true; return true;
} }
getMaxStackCount(): number { return false;
return 5; }
}
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;
} }
} }

View File

@ -116,7 +116,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler {
} }
} }
clear(): void { clear() {
super.clear(); super.clear();
} }

View File

@ -12,6 +12,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
private transferButtonContainer: Phaser.GameObjects.Container; private transferButtonContainer: Phaser.GameObjects.Container;
private lastCursor: integer = 0; private lastCursor: integer = 0;
private player: boolean;
public options: ModifierOption[]; public options: ModifierOption[];
@ -47,26 +48,28 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
show(args: any[]) { show(args: any[]) {
if (this.active) { if (this.active) {
if (args.length === 2) { if (args.length === 3) {
this.awaitingActionInput = true; this.awaitingActionInput = true;
this.onActionInput = args[1]; this.onActionInput = args[2];
} }
return; 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; return;
super.show(args); super.show(args);
this.getUi().clearText(); 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.setVisible(false);
this.transferButtonContainer.setAlpha(0); this.transferButtonContainer.setAlpha(0);
const typeOptions = args[0] as ModifierTypeOption[]; const typeOptions = args[1] as ModifierTypeOption[];
for (let m = 0; m < typeOptions.length; m++) { for (let m = 0; m < typeOptions.length; m++) {
const sliceWidth = (this.scene.game.canvas.width / 6) / (typeOptions.length + 2); 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]); 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.setCursor(0);
this.awaitingActionInput = true; this.awaitingActionInput = true;
this.onActionInput = args[1]; this.onActionInput = args[2];
}); });
} }
@ -134,6 +137,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
originalOnActionInput(this.cursor); originalOnActionInput(this.cursor);
} }
} else if (button === Button.CANCEL) { } else if (button === Button.CANCEL) {
if (this.player) {
success = true; success = true;
if (this.onActionInput) { if (this.onActionInput) {
const originalOnActionInput = this.onActionInput; const originalOnActionInput = this.onActionInput;
@ -141,6 +145,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
this.onActionInput = null; this.onActionInput = null;
originalOnActionInput(-1); originalOnActionInput(-1);
} }
}
} else { } else {
switch (button) { switch (button) {
case Button.UP: case Button.UP: