diff --git a/public/audio/se/low_hp.wav b/public/audio/se/low_hp.wav new file mode 100644 index 000000000..cefc70c5e Binary files /dev/null and b/public/audio/se/low_hp.wav differ diff --git a/public/audio/se/upgrade.wav b/public/audio/se/upgrade.wav new file mode 100644 index 000000000..b04f5e6f7 Binary files /dev/null and b/public/audio/se/upgrade.wav differ diff --git a/src/auto-play.ts b/src/auto-play.ts index beeee1504..660598166 100644 --- a/src/auto-play.ts +++ b/src/auto-play.ts @@ -1,6 +1,6 @@ import { SelectModifierPhase } from "./battle-phases"; import BattleScene, { Button } from "./battle-scene"; -import { ModifierTier, ModifierType, PokemonBaseStatBoosterModifierType, PokemonHpRestoreModifierType, PokemonReviveModifierType } from "./modifier-type"; +import { ModifierTier, ModifierType, ModifierTypeOption, PokemonBaseStatBoosterModifierType, PokemonHpRestoreModifierType, PokemonReviveModifierType } from "./modifier-type"; import Pokemon, { AiType, EnemyPokemon, PlayerPokemon, PokemonMove } from "./pokemon"; import { Species } from "./species"; import { getTypeDamageMultiplier } from "./type"; @@ -169,10 +169,10 @@ export function initAutoPlay() { } } - const tryGetBestModifier = (modifierTypes: Array, predicate: Function) => { - for (let mt = 0; mt < modifierTypes.length; mt++) { - const modifierType = modifierTypes[mt]; - if (predicate(modifierType)) { + const tryGetBestModifier = (modifierTypeOptions: Array, predicate: Function) => { + for (let mt = 0; mt < modifierTypeOptions.length; mt++) { + const modifierTypeOption = modifierTypeOptions[mt]; + if (predicate(modifierTypeOption.type)) { return mt; } } @@ -194,12 +194,12 @@ export function initAutoPlay() { originalModifierSelectUiHandlerShow.apply(this, [ args ]); const party = thisArg.getParty(); - const modifierTypes = modifierSelectUiHandler.options.map(o => o.modifierType); + const modifierTypeOptions = modifierSelectUiHandler.options.map(o => o.modifierTypeOption); const faintedPartyMemberIndex = party.findIndex(p => !p.hp); const lowHpPartyMemberIndex = party.findIndex(p => p.getHpRatio() <= 0.5); const criticalHpPartyMemberIndex = party.findIndex(p => p.getHpRatio() <= 0.25); - let optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => { + let optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => { if (modifierType instanceof PokemonHpRestoreModifierType) { if (modifierType instanceof PokemonReviveModifierType) { if (faintedPartyMemberIndex > -1) { @@ -214,7 +214,7 @@ export function initAutoPlay() { }); if (optionIndex === -1) { - optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => { + optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => { if (modifierType.tier >= ModifierTier.ULTRA) { nextPartyMemberIndex = 0; return true; @@ -223,7 +223,7 @@ export function initAutoPlay() { } if (optionIndex === -1) { - optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => { + optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => { if (modifierType instanceof PokemonBaseStatBoosterModifierType) { nextPartyMemberIndex = 0; return true; @@ -232,7 +232,7 @@ export function initAutoPlay() { } if (optionIndex === -1) { - optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => { + optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => { if (lowHpPartyMemberIndex && modifierType instanceof PokemonHpRestoreModifierType && !(ModifierType instanceof PokemonReviveModifierType)) { nextPartyMemberIndex = lowHpPartyMemberIndex; return true; @@ -248,7 +248,7 @@ export function initAutoPlay() { thisArg.time.delayedCall(20, () => { modifierSelectUiHandler.processInput(Button.ACTION); thisArg.time.delayedCall(250, () => { - console.log(modifierTypes[optionIndex]?.name); + console.log(modifierTypeOptions[optionIndex]?.type.name); if (thisArg.getCurrentPhase() instanceof SelectModifierPhase) { if (optionIndex < modifierSelectUiHandler.options.length - 1) { optionIndex++; diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 1d3a79be9..3c1f800e2 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -16,7 +16,7 @@ import { EvolutionPhase } from "./evolution-phase"; import { BattlePhase } from "./battle-phase"; import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat"; import { Biome, biomeLinks } from "./biome"; -import { ModifierType, PokemonModifierType, PokemonMoveModifierType, getModifierTypesForWave, regenerateModifierPoolThresholds } from "./modifier-type"; +import { ModifierType, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier-type"; export class SelectStarterPhase extends BattlePhase { constructor(scene: BattleScene) { @@ -1294,7 +1294,7 @@ export class SelectModifierPhase extends BattlePhase { regenerateModifierPoolThresholds(party); const modifierCount = new Utils.IntegerHolder(3); this.scene.applyModifiers(ExtraModifierModifier, modifierCount); - const types: Array = getModifierTypesForWave(this.scene.currentBattle.waveIndex - 1, modifierCount.value, party); + const types: Array = getModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex - 1, modifierCount.value, party); const modifierSelectCallback = (cursor: integer) => { if (cursor < 0) { @@ -1312,8 +1312,8 @@ export class SelectModifierPhase extends BattlePhase { this.scene.ui.setMode(Mode.MODIFIER_SELECT); const modifierType = types[cursor]; const modifier = !isMoveModifier - ? modifierType.newModifier(party[slotIndex]) - : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); + ? modifierType.type.newModifier(party[slotIndex]) + : modifierType.type.newModifier(party[slotIndex], option - PartyOption.MOVE_1); this.scene.addModifier(modifier).then(() => super.end()); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE); @@ -1321,7 +1321,7 @@ export class SelectModifierPhase extends BattlePhase { this.scene.ui.setMode(Mode.MODIFIER_SELECT, types, modifierSelectCallback); }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined); } else { - this.scene.addModifier(types[cursor].newModifier()).then(() => super.end()); + this.scene.addModifier(types[cursor].type.newModifier()).then(() => super.end()); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE); } diff --git a/src/battle-scene.ts b/src/battle-scene.ts index d71a8fd74..a410a96f7 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -195,6 +195,7 @@ export default class BattleScene extends Phaser.Scene { this.loadSe('stat_down'); this.loadSe('faint'); this.loadSe('flee'); + this.loadSe('low_hp'); this.loadSe('exp'); this.loadSe('level_up'); this.loadSe('sparkle'); @@ -202,6 +203,7 @@ export default class BattleScene extends Phaser.Scene { this.loadSe('shine'); this.loadSe('charge'); this.loadSe('beam'); + this.loadSe('upgrade'); this.loadSe('error'); this.loadSe('pb'); diff --git a/src/modifier-type.ts b/src/modifier-type.ts index 0661f9231..ded653d36 100644 --- a/src/modifier-type.ts +++ b/src/modifier-type.ts @@ -328,15 +328,16 @@ export function regenerateModifierPoolThresholds(party: PlayerPokemon[]) { console.log(modifierPoolThresholds) } -export function getModifierTypesForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierType[] { +export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] { if (waveIndex % 10 === 0) - return modifierPool[ModifierTier.LUXURY]; - return new Array(count).fill(0).map(() => getNewModifierType(party)); + return modifierPool[ModifierTier.LUXURY].map(m => new ModifierTypeOption(m, false)); + return new Array(count).fill(0).map(() => getNewModifierTypeOption(party)); } -function getNewModifierType(party: PlayerPokemon[]): ModifierType { +function getNewModifierTypeOption(party: PlayerPokemon[]): ModifierTypeOption { const tierValue = Utils.randInt(256); - const tier = tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER; + const upgrade = Utils.randInt(32) === 0; + const tier: ModifierTier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0); const thresholds = Object.keys(modifierPoolThresholds[tier]); const totalWeight = parseInt(thresholds[thresholds.length - 1]); const value = Utils.randInt(totalWeight); @@ -354,5 +355,15 @@ function getNewModifierType(party: PlayerPokemon[]): ModifierType { modifierType = (modifierType as WeightedModifierType).modifierType; if (modifierType instanceof ModifierTypeGenerator) modifierType = (modifierType as ModifierTypeGenerator).generateType(party); - return modifierType as ModifierType; + return new ModifierTypeOption(modifierType as ModifierType, upgrade); +} + +export class ModifierTypeOption { + public type: ModifierType; + public upgraded: boolean; + + constructor(type: ModifierType, upgraded: boolean) { + this.type = type; + this.upgraded = upgraded; + } } \ No newline at end of file diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 67c3d1766..a45f2eb45 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -1,5 +1,5 @@ import BattleScene, { Button } from "../battle-scene"; -import { ModifierTier, ModifierType } from "../modifier-type"; +import { ModifierTier, ModifierType, ModifierTypeOption } from "../modifier-type"; import { getPokeballAtlasKey, PokeballType } from "../pokeball"; import { addTextObject, TextStyle } from "../text"; import AwaitableUiHandler from "./awaitable-ui-handler"; @@ -48,16 +48,18 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.getUi().clearText(); - const types = args[0] as ModifierType[]; - for (let m = 0; m < types.length; m++) { - const sliceWidth = (this.scene.game.canvas.width / 6) / (types.length + 2); - const option = new ModifierOption(this.scene, sliceWidth * (m + 1) + (sliceWidth * 0.5), -this.scene.game.canvas.height / 12 - 24, types[m]); + const typeOptions = args[0] 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]); option.setScale(0.5); this.scene.add.existing(option); this.modifierContainer.add(option); this.options.push(option); } + const hasUpgrade = typeOptions.filter(to => to.upgraded).length; + this.scene.tweens.add({ targets: this.overlayBg, alpha: 0.5, @@ -72,15 +74,15 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { duration: 1250, onUpdate: t => { const value = t.getValue(); - const index = Math.floor(value * types.length); - if (index > i && index <= types.length) { + const index = Math.floor(value * typeOptions.length); + if (index > i && index <= typeOptions.length) { const option = this.options[i++]; - option?.show(Math.floor((1 - value) * 1250) * 0.325); + option?.show(Math.floor((1 - value) * 1250) * 0.325 + (hasUpgrade ? 2000 : 0)); } } }); - this.scene.time.delayedCall(4000, () => { + this.scene.time.delayedCall(4000 + (hasUpgrade ? 2000 : 0), () => { this.setCursor(0); this.awaitingActionInput = true; this.onActionInput = args[1]; @@ -140,7 +142,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { const sliceWidth = (this.scene.game.canvas.width / 6) / (this.options.length + 2); this.cursorObj.setPosition(sliceWidth * (cursor + 1) + (sliceWidth * 0.5) - 20, -this.scene.game.canvas.height / 12 - 20); - ui.showText(this.options[this.cursor].modifierType.description); + ui.showText(this.options[this.cursor].modifierTypeOption.type.description); return ret; } @@ -179,33 +181,43 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { } class ModifierOption extends Phaser.GameObjects.Container { - public modifierType: ModifierType; + public modifierTypeOption: ModifierTypeOption; private pb: Phaser.GameObjects.Sprite; + private pbTint: Phaser.GameObjects.Sprite; private itemContainer: Phaser.GameObjects.Container; private item: Phaser.GameObjects.Sprite; private itemTint: Phaser.GameObjects.Sprite; private itemText: Phaser.GameObjects.Text; - constructor(scene: BattleScene, x: number, y: number, modifierType: ModifierType) { + constructor(scene: BattleScene, x: number, y: number, modifierTypeOption: ModifierTypeOption) { super(scene, x, y); - this.modifierType = modifierType; + this.modifierTypeOption = modifierTypeOption; this.setup(); } setup() { - this.pb = this.scene.add.sprite(0, -150, 'pb', this.getPbAtlasKey()); - this.pb.setScale(2); + const getPb = (): Phaser.GameObjects.Sprite => { + const pb = this.scene.add.sprite(0, -150, 'pb', this.getPbAtlasKey(true)); + pb.setScale(2); + return pb; + }; + + this.pb = getPb(); this.add(this.pb); + this.pbTint = getPb(); + this.pbTint.setVisible(false); + this.add(this.pbTint); + this.itemContainer = this.scene.add.container(0, 0); this.itemContainer.setScale(0.5); this.itemContainer.setAlpha(0); this.add(this.itemContainer); const getItem = () => { - const item = this.scene.add.sprite(0, 0, 'items', this.modifierType.iconImage); + const item = this.scene.add.sprite(0, 0, 'items', this.modifierTypeOption.type.iconImage); return item; }; @@ -216,7 +228,7 @@ class ModifierOption extends Phaser.GameObjects.Container { this.itemTint.setTintFill(Phaser.Display.Color.GetColor(255, 192, 255)); this.itemContainer.add(this.itemTint); - this.itemText = addTextObject(this.scene, 0, 35, this.modifierType.name, TextStyle.PARTY, { align: 'center' }); + this.itemText = addTextObject(this.scene, 0, 35, this.modifierTypeOption.type.name, TextStyle.PARTY, { align: 'center' }); this.itemText.setOrigin(0.5, 0); this.itemText.setAlpha(0); this.itemText.setTint(this.getTextTint()); @@ -253,11 +265,39 @@ class ModifierOption extends Phaser.GameObjects.Container { } }); + if (this.modifierTypeOption.upgraded) { + this.scene.time.delayedCall(remainingDuration, () => { + this.scene.sound.play('upgrade'); + this.pbTint.setPosition(this.pb.x, this.pb.y); + this.pbTint.setTintFill(0xFFFFFF); + this.pbTint.setAlpha(0); + this.pbTint.setVisible(true); + this.scene.tweens.add({ + targets: this.pbTint, + alpha: 1, + duration: 1000, + ease: 'Sine.easeIn', + onComplete: () => { + this.pb.setTexture('pb', this.getPbAtlasKey(false)); + this.scene.tweens.add({ + targets: this.pbTint, + alpha: 0, + duration: 1000, + ease: 'Sine.easeOut', + onComplete: () => { + this.pbTint.setVisible(false); + } + }); + } + }); + }); + } + this.scene.time.delayedCall(remainingDuration + 2000, () => { if (!this.scene) return; - this.pb.setTexture('pb', `${this.getPbAtlasKey()}_open`); + this.pb.setTexture('pb', `${this.getPbAtlasKey(false)}_open`); this.scene.sound.play('pb_rel'); this.scene.tweens.add({ @@ -292,12 +332,12 @@ class ModifierOption extends Phaser.GameObjects.Container { }) } - getPbAtlasKey() { - return getPokeballAtlasKey(this.modifierType.tier as integer as PokeballType); + getPbAtlasKey(beforeUpgrade: boolean) { + return getPokeballAtlasKey((this.modifierTypeOption.type.tier - (beforeUpgrade && this.modifierTypeOption.upgraded ? 1 : 0)) as integer as PokeballType); } getTextTint(): integer { - switch (this.modifierType.tier) { + switch (this.modifierTypeOption.type.tier) { case ModifierTier.COMMON: return 0xffffff case ModifierTier.GREAT: