diff --git a/src/arena.ts b/src/arena.ts index 91c1b1aeb27..6c4a61c77f3 100644 --- a/src/arena.ts +++ b/src/arena.ts @@ -33,28 +33,34 @@ enum PoolTier { export class Arena { private scene: BattleScene; - public type: integer; + public arenaType: integer; private bgm: string; private pokemonPool: PokemonSpecies[][]; - constructor(scene: BattleScene, type: integer, bgm: string) { + constructor(scene: BattleScene, arenaType: integer, bgm: string) { this.scene = scene; - this.type = type; + this.arenaType = arenaType; this.bgm = bgm; - const predicate = arenaPoolPredicates[type] || (() => {}); - this.pokemonPool = Utils.getEnumValues(PoolTier).map(t => allSpecies.filter(p => predicate(p, t))); + if (arenaPools.hasOwnProperty(arenaType)) + this.pokemonPool = arenaPools[arenaType]; + else { + const predicate = arenaPoolPredicates[arenaType] || (() => {}); + this.pokemonPool = Utils.getEnumValues(PoolTier).map(t => allSpecies.filter(p => predicate(p, t))); + } } - randomSpecies(waveIndex: integer): PokemonSpecies { - const tier: PoolTier = Utils.randInt(5); + const tierValue = Utils.randInt(512); + const tier = tierValue >= 156 ? PoolTier.COMMON : tierValue >= 32 ? PoolTier.UNCOMMON : tierValue >= 6 ? PoolTier.RARE : tierValue >= 1 ? PoolTier.ULTRA_RARE : PoolTier.LEGENDARY; const tierPool = this.pokemonPool[tier]; let ret: PokemonSpecies; if (!tierPool.length) ret = this.scene.randomSpecies(); - else - ret = tierPool[Utils.randInt(tierPool.length)]; + else { + const species = tierPool[Utils.randInt(tierPool.length)]; + ret = species instanceof PokemonSpecies ? species : getPokemonSpecies(species); + } const newSpeciesId = ret.getSpeciesForLevel(5); if (newSpeciesId !== ret.speciesId) { console.log('Replaced', Species[ret.speciesId], 'with', Species[newSpeciesId]); @@ -69,6 +75,16 @@ export class Arena { } } +const arenaPools = { + [ArenaType.PLAINS]: { + [PoolTier.COMMON]: [ Species.CATERPIE, Species.METAPOD, Species.WEEDLE, Species.KAKUNA, Species.PIDGEY, Species.RATTATA, Species.SPEAROW, Species.SENTRET, Species.HOOTHOOT, Species.HOPPIP, Species.SUNKERN, Species.POOCHYENA, Species.ZIGZAGOON, Species.WURMPLE, Species.SILCOON, Species.CASCOON, Species.TAILLOW, Species.STARLY, Species.BIDOOF, Species.KRICKETOT, Species.PATRAT, Species.LILLIPUP, Species.PIDOVE, Species.COTTONEE, Species.PETILIL, Species.MINCCINO, Species.FOONGUS ], + [PoolTier.UNCOMMON]: [ Species.EKANS, Species.NIDORAN_F, Species.NIDORAN_M, Species.PARAS, Species.VENONAT, Species.MEOWTH, Species.BELLSPROUT, Species.LEDYBA, Species.SPINARAK, Species.PINECO, Species.LOTAD, Species.SEEDOT, Species.SHROOMISH, Species.NINCADA, Species.AZURILL, Species.WHISMUR, Species.SKITTY, Species.GULPIN, Species.BUDEW, Species.BURMY, Species.COMBEE, Species.CHERUBI, Species.VENIPEDE ], + [PoolTier.RARE]: [ Species.PICHU, Species.CLEFFA, Species.IGGLYBUFF, Species.WOOPER, Species.RALTS, Species.SURSKIT, Species.SLAKOTH, Species.BARBOACH, Species.DUCKLETT ], + [PoolTier.ULTRA_RARE]: [ Species.EEVEE, Species.TOGEPI, Species.TYROGUE ], + [PoolTier.LEGENDARY]: [ Species.DITTO ] + } +}; + const arenaPoolPredicates = { [ArenaType.PLAINS]: (p, t) => { if (p.isOfType(Type.GHOST) || p.isOfType(Type.STEEL) || p.isOfType(Type.ICE) || p.isOfType(Type.DRAGON)) diff --git a/src/auto-play.ts b/src/auto-play.ts index 5ba8d86d63a..2798d10a261 100644 --- a/src/auto-play.ts +++ b/src/auto-play.ts @@ -66,7 +66,7 @@ export function initAutoPlay(speed: number) { let bestPartyMemberEffectiveness = 0.5; for (let p = 0; p < party.length; p++) { const pokemon = party[p]; - if ((pokemon.hp / pokemon.getMaxHp()) <= 0.4) + if (pokemon.getHpRatio() <= 0.4) continue; const effectiveness = getMaxMoveEffectiveness(pokemon, enemyPokemon) / getMaxMoveEffectiveness(enemyPokemon, pokemon); if (effectiveness > bestPartyMemberEffectiveness) { @@ -199,8 +199,8 @@ export function initAutoPlay(speed: number) { const party = thisArg.getParty(); const modifierTypes = modifierSelectUiHandler.options.map(o => o.modifierType); const faintedPartyMemberIndex = party.findIndex(p => !p.hp); - const lowHpPartyMemberIndex = party.findIndex(p => (p.hp / p.getMaxHp()) <= 0.5); - const criticalHpPartyMemberIndex = party.findIndex(p => (p.hp / p.getMaxHp()) <= 0.25); + const lowHpPartyMemberIndex = party.findIndex(p => p.getHpRatio() <= 0.5); + const criticalHpPartyMemberIndex = party.findIndex(p => p.getHpRatio() <= 0.25); let optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => { if (modifierType instanceof PokemonHpRestoreModifierType) { diff --git a/src/battle-info.ts b/src/battle-info.ts index b46d811a596..9f56bde1154 100644 --- a/src/battle-info.ts +++ b/src/battle-info.ts @@ -82,47 +82,47 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } } - initInfo(battler: Pokemon) { - this.nameText.setText(battler.name); - this.lastName = battler.species.name; + initInfo(pokemon: Pokemon) { + this.nameText.setText(pokemon.name); + this.lastName = pokemon.species.name; - this.hpBar.setScale(battler.hp / battler.getMaxHp(), 1); + this.hpBar.setScale(pokemon.getHpRatio(), 1); if (this.player) - this.setHpNumbers(battler.hp, battler.getMaxHp()); - this.lastHp = battler.hp; + this.setHpNumbers(pokemon.hp, pokemon.getMaxHp()); + this.lastHp = pokemon.hp; this.lastHpFrame = this.hpBar.scaleX > 0.5 ? 'high' : this.hpBar.scaleX > 0.25 ? 'medium' : 'low'; - this.lastMaxHp = battler.getMaxHp(); + this.lastMaxHp = pokemon.getMaxHp(); - this.setLevel(battler.level); - this.lastLevel = battler.level; + this.setLevel(pokemon.level); + this.lastLevel = pokemon.level; if (this.player) { - this.expBar.setScale(battler.levelExp / getLevelTotalExp(battler.level, battler.species.growthRate), 1); - this.lastExp = battler.exp; - this.lastLevelExp = battler.levelExp; + this.expBar.setScale(pokemon.levelExp / getLevelTotalExp(pokemon.level, pokemon.species.growthRate), 1); + this.lastExp = pokemon.exp; + this.lastLevelExp = pokemon.levelExp; } } - updateInfo(battler: Pokemon, callback?: Function) { + updateInfo(pokemon: Pokemon, callback?: Function) { if (!this.scene) return; - if (this.lastName !== battler.species.name) { - this.nameText.setText(battler.name); - this.lastName = battler.species.name; + if (this.lastName !== pokemon.species.name) { + this.nameText.setText(pokemon.name); + this.lastName = pokemon.species.name; } const updatePokemonHp = () => { - const duration = Utils.clampInt(Math.abs((this.lastHp) - battler.hp) * 5, 250, 5000); + const duration = Utils.clampInt(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000); this.scene.tweens.add({ targets: this.hpBar, ease: 'Sine.easeOut', - scaleX: battler.hp / battler.getMaxHp(), + scaleX: pokemon.getHpRatio(), duration: duration, onUpdate: () => { - if (this.player && this.lastHp !== battler.hp) { - const tweenHp = Math.ceil(this.hpBar.scaleX * battler.getMaxHp()); - this.setHpNumbers(tweenHp, battler.getMaxHp()) + if (this.player && this.lastHp !== pokemon.hp) { + const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp()); + this.setHpNumbers(tweenHp, pokemon.getMaxHp()) this.lastHp = tweenHp; } @@ -140,20 +140,20 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } }); if (!this.player) - this.lastHp = battler.hp; - this.lastMaxHp = battler.getMaxHp(); + this.lastHp = pokemon.hp; + this.lastMaxHp = pokemon.getMaxHp(); }; - if (this.player && this.lastExp !== battler.exp) { + if (this.player && this.lastExp !== pokemon.exp) { const originalCallback = callback; - callback = () => this.updatePokemonExp(battler, originalCallback); + callback = () => this.updatePokemonExp(pokemon, originalCallback); } - if (this.lastHp !== battler.hp || this.lastMaxHp !== battler.getMaxHp()) + if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) updatePokemonHp(); - else if (!this.player && this.lastLevel !== battler.level) { - this.setLevel(battler.level); - this.lastLevel = battler.level; + else if (!this.player && this.lastLevel !== pokemon.level) { + this.setLevel(pokemon.level); + this.lastLevel = pokemon.level; if (callback) callback(); } else if (callback) diff --git a/src/battle-phase.ts b/src/battle-phase.ts index ce545611689..d50ee8701ef 100644 --- a/src/battle-phase.ts +++ b/src/battle-phase.ts @@ -6,7 +6,7 @@ import { Mode } from './ui/ui'; import { Command } from "./ui/command-ui-handler"; import { interp } from "./temp_interpreter"; import { Stat } from "./pokemon-stat"; -import { ExpBoosterModifier, getNewModifierType, PokemonBaseStatModifier as PokemonBaseStatModifier, PokemonModifierType } from "./modifier"; +import { ExpBoosterModifier, getNewModifierType, PokemonBaseStatModifier, PokemonModifierType, regenerateModifierPoolThresholds } from "./modifier"; import PartyUiHandler from "./ui/party-ui-handler"; import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getTintColor as getPokeballTintColor, PokeballType } from "./pokeball"; import { pokemonLevelMoves } from "./pokemon-level-moves"; @@ -332,7 +332,7 @@ abstract class MovePhase extends BattlePhase { this.move = move; } - abstract getDamagePhase(): MoveEffectPhase; + abstract getEffectPhase(): MoveEffectPhase; canMove(): boolean { return !!this.pokemon.hp; @@ -347,7 +347,7 @@ abstract class MovePhase extends BattlePhase { console.log(this.pokemon.moveset); this.scene.ui.showText(`${this.pokemon.name} used\n${this.move.getName()}!`, null, () => this.end(), 500); if (this.move.getMove().category !== MOVE_CATEGORY.STATUS) - this.scene.unshiftPhase(this.getDamagePhase()); + this.scene.unshiftPhase(this.getEffectPhase()); } } @@ -356,7 +356,7 @@ export class PlayerMovePhase extends MovePhase { super(scene, pokemon, move); } - getDamagePhase(): MoveEffectPhase { + getEffectPhase(): MoveEffectPhase { return new PlayerMoveEffectPhase(this.scene, this.move); } } @@ -366,7 +366,7 @@ export class EnemyMovePhase extends MovePhase { super(scene, pokemon, move); } - getDamagePhase(): MoveEffectPhase { + getEffectPhase(): MoveEffectPhase { return new EnemyMoveEffectPhase(this.scene, this.move); } } @@ -756,7 +756,8 @@ export class SelectModifierPhase extends BattlePhase { start() { super.start(); - const types = [ getNewModifierType(), getNewModifierType(), getNewModifierType() ]; + regenerateModifierPoolThresholds(this.scene.getParty()); + const types = [ getNewModifierType(), getNewModifierType(), getNewModifierType(), getNewModifierType(), getNewModifierType(), getNewModifierType() ]; this.scene.ui.setMode(Mode.MODIFIER_SELECT, types, (cursor: integer) => { if (cursor < 0) { diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 61e9bf22a3c..a6a95de6611 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -12,7 +12,7 @@ import { initAutoPlay } from './auto-play'; export default class BattleScene extends Phaser.Scene { private auto: boolean; - private autoSpeed: integer = 10; + private autoSpeed: integer = 3; private phaseQueue: Array; private phaseQueuePrepend: Array; @@ -198,10 +198,10 @@ export default class BattleScene extends Phaser.Scene { this.arena = arena; - this.arenaBg = this.add.image(0, 0, `arena_${Utils.padInt(arena.type, 2)}`); - this.arenaPlayer = this.add.image(340, 20, `arena_${Utils.padInt(arena.type, 2)}a`); - this.arenaEnemy = this.add.image(-240, 13, `arena_${Utils.padInt(arena.type, 2)}b`); - this.arenaEnemy2 = this.add.image(-240, 13, `arena_${Utils.padInt(arena.type, 2)}b`); + this.arenaBg = this.add.image(0, 0, `arena_${Utils.padInt(arena.arenaType, 2)}`); + this.arenaPlayer = this.add.image(340, 20, `arena_${Utils.padInt(arena.arenaType, 2)}a`); + this.arenaEnemy = this.add.image(-240, 13, `arena_${Utils.padInt(arena.arenaType, 2)}b`); + this.arenaEnemy2 = this.add.image(-240, 13, `arena_${Utils.padInt(arena.arenaType, 2)}b`); [this.arenaBg, this.arenaPlayer, this.arenaEnemy, this.arenaEnemy2].forEach(a => { a.setOrigin(0, 0); diff --git a/src/modifier.ts b/src/modifier.ts index 2bf5f68d468..8a6454a416c 100644 --- a/src/modifier.ts +++ b/src/modifier.ts @@ -232,7 +232,7 @@ export class PokemonHpRestoreModifier extends ConsumablePokemonModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; - pokemon.hp = Math.min(pokemon.hp + (this.restorePercent * 0.01) * pokemon.getMaxHp(), pokemon.getMaxHp()); + pokemon.hp = Math.min(pokemon.hp + Math.max((this.restorePercent * 0.01) * pokemon.getMaxHp(), this.restorePercent), pokemon.getMaxHp()); return true; } @@ -338,7 +338,7 @@ export class PokemonHpRestoreModifierType extends PokemonModifierType { protected restorePercent: integer; constructor(name: string, restorePercent: integer, iconImage?: string) { - super(name, `Restore ${restorePercent}% HP for one POKéMON`, (_type, args) => new PokemonHpRestoreModifier(this, args[0], this.restorePercent), + super(name, `Restore ${restorePercent} HP or ${restorePercent}% HP for one POKéMON, whichever is higher`, (_type, args) => new PokemonHpRestoreModifier(this, args[0], this.restorePercent), (pokemon: PlayerPokemon) => { if (pokemon.hp >= pokemon.getMaxHp()) return PartyUiHandler.NoEffectMessage; @@ -372,21 +372,17 @@ export class PokemonBaseStatBoosterModifierType extends PokemonModifierType { } } -class AllPokemonHpRestoreModifierType extends ModifierType { - private restorePercent: integer; - - constructor(name: string, restorePercent: integer, iconImage?: string) { - super(name, `Restore ${restorePercent}% HP for all POKéMON`, (_type, _args) => new PokemonHpRestoreModifier(this, -1, this.restorePercent), iconImage); - - this.restorePercent = restorePercent; +class AllPokemonFullHpRestoreModifierType extends ModifierType { + constructor(name: string, iconImage?: string) { + super(name, `Restore 100% HP for all POKéMON`, (_type, _args) => new PokemonHpRestoreModifier(this, -1, 100), iconImage); } } class WeightedModifierType { public modifierType: ModifierType; - public weight: integer; + public weight: integer | Function; - constructor(modifierType: ModifierType, weight: integer) { + constructor(modifierType: ModifierType, weight: integer | Function) { this.modifierType = modifierType; this.weight = weight; } @@ -399,8 +395,15 @@ class WeightedModifierType { const modifierPool = { [ModifierTier.COMMON]: [ new WeightedModifierType(new AddPokeballModifierType(PokeballType.POKEBALL, 5, 'pb'), 2), - new WeightedModifierType(new PokemonHpRestoreModifierType('POTION', 20), 3), - new PokemonHpRestoreModifierType('SUPER POTION', 50), + new WeightedModifierType(new PokemonHpRestoreModifierType('POTION', 20), (party: Array) => { + const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.9).length; + console.log(thresholdPartyMemberCount, party.map(p => p.getHpRatio())); + return thresholdPartyMemberCount; + }), + new WeightedModifierType(new PokemonHpRestoreModifierType('SUPER POTION', 50), (party: Array) => { + const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.75).length; + return Math.ceil(thresholdPartyMemberCount / 3); + }), new PokemonBaseStatBoosterModifierType('HP-UP', Stat.HP), new PokemonBaseStatBoosterModifierType('PROTEIN', Stat.ATK), new PokemonBaseStatBoosterModifierType('IRON', Stat.DEF), @@ -410,12 +413,21 @@ const modifierPool = { ].map(m => { m.setTier(ModifierTier.COMMON); return m; }), [ModifierTier.GREAT]: [ new AddPokeballModifierType(PokeballType.GREAT_BALL, 5, 'gb'), - new PokemonReviveModifierType('REVIVE', 50), - new PokemonHpRestoreModifierType('HYPER POTION', 100) + new WeightedModifierType(new PokemonReviveModifierType('REVIVE', 50), (party: Array) => { + const faintedPartyMemberCount = party.filter(p => !p.hp).length; + return faintedPartyMemberCount; + }), + new WeightedModifierType(new PokemonHpRestoreModifierType('HYPER POTION', 80), (party: Array) => { + const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.6).length; + return Math.ceil(thresholdPartyMemberCount / 3); + }) ].map(m => { m.setTier(ModifierTier.GREAT); return m; }), [ModifierTier.ULTRA]: [ new AddPokeballModifierType(PokeballType.ULTRA_BALL, 5, 'ub'), - new AllPokemonHpRestoreModifierType('MAX POTION', 100), + new WeightedModifierType(new AllPokemonFullHpRestoreModifierType('MAX POTION'), (party: Array) => { + const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.5).length; + return Math.ceil(thresholdPartyMemberCount / 3); + }), new ModifierType('LUCKY EGG', 'Increases gain of EXP. Points by 25%', (type, _args) => new ExpBoosterModifier(type)) ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), [ModifierTier.MASTER]: [ @@ -424,18 +436,36 @@ const modifierPool = { ].map(m => { m.setTier(ModifierTier.MASTER); return m; }) }; -const modifierPoolThresholds = Object.fromEntries(new Map(Object.keys(modifierPool).map(t => { - const thresholds = new Map(); - let i = 0; - modifierPool[t].reduce((total: integer, modifierType: ModifierType | WeightedModifierType) => { - total += modifierType instanceof WeightedModifierType ? (modifierType as WeightedModifierType).weight : 1; - thresholds.set(total, i++); - return total; - }, 0); - return [ t, Object.fromEntries(thresholds) ] -}))); +let modifierPoolThresholds = {}; +let ignoredPoolIndexes = {}; -console.log(modifierPoolThresholds) +export function regenerateModifierPoolThresholds(party: Array) { + ignoredPoolIndexes = {}; + modifierPoolThresholds = Object.fromEntries(new Map(Object.keys(modifierPool).map(t => { + ignoredPoolIndexes[t] = []; + const thresholds = new Map(); + let i = 0; + modifierPool[t].reduce((total: integer, modifierType: ModifierType | WeightedModifierType) => { + if (modifierType instanceof WeightedModifierType) { + const weightedModifierType = modifierType as WeightedModifierType; + const weight = weightedModifierType.weight instanceof Function + ? (weightedModifierType.weight as Function)(party) + : weightedModifierType.weight as integer; + if (weight) + total += weight; + else { + ignoredPoolIndexes[t].push(i++); + return total; + } + } else + total++; + thresholds.set(total, i++); + return total; + }, 0); + return [ t, Object.fromEntries(thresholds) ] + }))); + console.log(modifierPoolThresholds) +} export function getNewModifierType(): ModifierType { const tierValue = Utils.randInt(256); @@ -451,6 +481,7 @@ export function getNewModifierType(): ModifierType { break; } } + console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]) let modifierType: ModifierType | WeightedModifierType = modifierPool[tier][index]; if (modifierType instanceof WeightedModifierType) return (modifierType as WeightedModifierType).modifierType; diff --git a/src/pokemon.ts b/src/pokemon.ts index f319e57195c..9df267b3a0d 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -222,6 +222,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.stats[Stat.HP]; } + getHpRatio() { + return Math.floor((this.hp / this.getMaxHp()) * 100) / 100; + } + generateAndPopulateMoveset() { this.moveset = []; const movePool = []; @@ -450,7 +454,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Gen 1-4 formula // return ((this.pokemon.baseExp * this.level) / 7) * (1 / 1) // TODO: Update for exp share - return Math.floor(((this.species.baseExp * this.level) / 5) * (1 / 1) * ((Math.round(Math.sqrt(2 * this.level + 10)) * Math.pow(2 * this.level + 10, 2)) / (Math.round(Math.sqrt(this.level + victorLevel + 10)) * Math.pow(this.level + victorLevel + 10, 2)))) + 1; + const constantMultiplier = 4; + return Math.floor(((this.species.baseExp * this.level) / 5) * (1 / 1) * constantMultiplier * ((Math.round(Math.sqrt(2 * this.level + 10)) * Math.pow(2 * this.level + 10, 2)) / (Math.round(Math.sqrt(this.level + victorLevel + 10)) * Math.pow(this.level + victorLevel + 10, 2)))) + 1; } tint(color: number, alpha?: number, duration?: integer, ease?: string) { diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 2e2e8d46d03..7d361abee71 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -285,7 +285,7 @@ class PartySlot extends Phaser.GameObjects.Container { slotHpBar.setPositionRelative(slotBg, this.slotIndex ? 72 : 8, this.slotIndex ? 7 : 31); slotHpBar.setOrigin(0, 0); - const hpRatio = this.pokemon.hp / this.pokemon.getMaxHp(); + const hpRatio = this.pokemon.getHpRatio(); const slotHpOverlay = this.scene.add.sprite(0, 0, 'party_slot_hp_overlay', hpRatio > 0.5 ? 'high' : hpRatio > 0.25 ? 'medium' : 'low'); slotHpOverlay.setPositionRelative(slotHpBar, 16, 2);