From f49a3e51273e4b5991f0622082f85d3a7720acdb Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 14 Jan 2024 20:47:08 -0500 Subject: [PATCH] Add field zoom functionality and make final boss larger --- src/battle-phases.ts | 18 +++++++++++++--- src/battle-scene.ts | 44 ++++++++++++++++++++++++++++++++++------ src/data/move.ts | 13 ++++++------ src/form-change-phase.ts | 2 +- src/pipelines/sprite.ts | 24 ++++++++++++++-------- src/pokemon.ts | 7 +++++-- 6 files changed, 82 insertions(+), 26 deletions(-) diff --git a/src/battle-phases.ts b/src/battle-phases.ts index d32b9bf0daa..66cabf787db 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -449,8 +449,8 @@ export class EncounterPhase extends BattlePhase { doEncounter() { this.scene.playBgm(undefined, true); - this.scene.updateModifiers(false); + this.scene.setFieldScale(1); /*if (startingWave > 10) { for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++) @@ -617,6 +617,8 @@ export class NextEncounterPhase extends EncounterPhase { doEncounter(): void { this.scene.playBgm(undefined, true); + this.scene.arenaNextEnemy.setVisible(true); + const enemyField = this.scene.getEnemyField(); this.scene.tweens.add({ targets: [ this.scene.arenaEnemy, this.scene.arenaNextEnemy, this.scene.currentBattle.trainer, enemyField, this.scene.lastEnemyTrainer ].flat(), @@ -626,6 +628,7 @@ export class NextEncounterPhase extends EncounterPhase { this.scene.arenaEnemy.setX(this.scene.arenaNextEnemy.x); this.scene.arenaEnemy.setAlpha(1); this.scene.arenaNextEnemy.setX(this.scene.arenaNextEnemy.x - 300); + this.scene.arenaNextEnemy.setVisible(false); if (this.scene.lastEnemyTrainer) this.scene.lastEnemyTrainer.destroy(); @@ -836,7 +839,8 @@ export class SummonPhase extends PartyMemberPokemonPhase { this.scene.tweens.add({ targets: this.scene.trainer, x: -36, - duration: 1000 + duration: 1000, + onComplete: () => this.scene.trainer.setVisible(false) }); this.scene.time.delayedCall(750, () => this.summon()); } else { @@ -1060,6 +1064,8 @@ export class ShowTrainerPhase extends BattlePhase { start() { super.start(); + this.scene.trainer.setVisible(true) + this.scene.trainer.setTexture('trainer_m'); this.scene.tweens.add({ @@ -2138,7 +2144,12 @@ export class StatChangePhase extends PokemonPhase { pokemon.enableMask(); const pokemonMaskSprite = pokemon.maskSprite; - const statSprite = this.scene.add.tileSprite((this.player ? 106 : 236) * 6, ((this.player ? 148 : 84) + (levels.value >= 1 ? 160 : 0)) * 6, 156, 316, 'battle_stats', filteredStats.length > 1 ? 'mix' : BattleStat[filteredStats[0]].toLowerCase()); + const tileX = (this.player ? 106 : 236) * pokemon.getSpriteScale() * this.scene.field.scale; + const tileY = ((this.player ? 148 : 84) + (levels.value >= 1 ? 160 : 0)) * pokemon.getSpriteScale() * this.scene.field.scale; + const tileWidth = 156 * this.scene.field.scale * pokemon.getSpriteScale(); + const tileHeight = 316 * this.scene.field.scale * pokemon.getSpriteScale(); + + const statSprite = this.scene.add.tileSprite(tileX, tileY, tileWidth, tileHeight, 'battle_stats', filteredStats.length > 1 ? 'mix' : BattleStat[filteredStats[0]].toLowerCase()); statSprite.setPipeline(this.scene.fieldSpritePipeline); statSprite.setAlpha(0); statSprite.setScale(6); @@ -2388,6 +2399,7 @@ export class DamagePhase extends PokemonPhase { this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.name, null, () => { this.scene.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true); pokemon.generateAndPopulateMoveset(1); + this.scene.setFieldScale(0.75); this.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); this.scene.currentBattle.double = true; const availablePartyMembers = this.scene.getParty().filter(p => !p.isFainted()); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index c09402019e0..25e20b57556 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -48,6 +48,7 @@ import PokemonData from './system/pokemon-data'; import { Nature } from './data/nature'; import { SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges } from './data/pokemon-forms'; import { FormChangePhase, QuietFormChangePhase } from './form-change-phase'; +import { BattleSpec } from './enums/battle-spec'; const enableAuto = true; const quickStart = false; @@ -426,6 +427,14 @@ export default class BattleScene extends Phaser.Scene { } launchBattle() { + this.arenaBg = this.addFieldSprite(0, 0, 'plains_bg'); + this.arenaBgTransition = this.addFieldSprite(0, 0, `plains_bg`); + + [ this.arenaBgTransition, this.arenaBg ].forEach(a => { + a.setScale(6); + a.setOrigin(0); + }); + const field = this.add.container(0, 0); field.setScale(6); @@ -494,8 +503,6 @@ export default class BattleScene extends Phaser.Scene { this.quickStart = this.quickStart || this.isButtonPressed(Button.QUICK_START); - this.arenaBg = this.addFieldSprite(0, 0, 'plains_bg'); - this.arenaBgTransition = this.addFieldSprite(0, 0, `plains_bg`); this.arenaPlayer = new ArenaBase(this, true); this.arenaPlayerTransition = new ArenaBase(this, true); this.arenaEnemy = new ArenaBase(this, false); @@ -503,8 +510,9 @@ export default class BattleScene extends Phaser.Scene { this.arenaBgTransition.setVisible(false); this.arenaPlayerTransition.setVisible(false); + this.arenaNextEnemy.setVisible(false); - [ this.arenaBg, this.arenaBgTransition, this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy ].forEach(a => { + [ this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy ].forEach(a => { if (a instanceof Phaser.GameObjects.Sprite) a.setOrigin(0, 0); field.add(a); @@ -722,6 +730,7 @@ export default class BattleScene extends Phaser.Scene { this.arenaPlayer.setPosition(300, 0); this.arenaPlayerTransition.setPosition(0, 0); [ this.arenaEnemy, this.arenaNextEnemy ].forEach(a => a.setPosition(-280, 0)); + this.arenaNextEnemy.setVisible(false); this.trainer.setTexture('trainer_m'); this.trainer.setPosition(406, 132); @@ -827,7 +836,7 @@ export default class BattleScene extends Phaser.Scene { if (!waveIndex && lastBattle) { const isNewBiome = !(lastBattle.waveIndex % 10); - const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER; + const resetArenaState = isNewBiome || this.currentBattle.battleType === BattleType.TRAINER || this.currentBattle.battleSpec === BattleSpec.FINAL_BOSS; this.getEnemyParty().forEach(enemyPokemon => enemyPokemon.destroy()); this.trySpreadPokerus(); if (!isNewBiome && (newWaveIndex % 10) == 5) @@ -876,6 +885,29 @@ export default class BattleScene extends Phaser.Scene { return this.arena; } + setFieldScale(scale: number, instant: boolean = false): Promise { + return new Promise(resolve => { + scale *= 6; + if (this.field.scale === scale) + return resolve(); + + const defaultWidth = this.arenaBg.width * 6; + const defaultHeight = this.arenaBg.height * 6; + const scaledWidth = this.arenaBg.width * scale; + const scaledHeight = this.arenaBg.height * scale; + + this.tweens.add({ + targets: this.field, + scale: scale, + x: (defaultWidth - scaledWidth) / 2, + y: defaultHeight - scaledHeight, + duration: !instant ? Utils.fixedInt(Math.abs(this.field.scale - scale) * 200) : 0, + ease: 'Sine.easeInOut', + onComplete: () => resolve() + }); + }); + } + getSpeciesFormIndex(species: PokemonSpecies, gender?: Gender, nature?: Nature, ignoreArena?: boolean): integer { if (!species.forms?.length) return 0; @@ -1240,9 +1272,9 @@ export default class BattleScene extends Phaser.Scene { duration = 500; if (destroy === undefined) destroy = true; - const bgm = this.sound.get(this.bgm.key); + const bgm = this.sound.getAllPlaying().find(bgm => bgm.key === this.bgm.key); if (bgm) { - SoundFade.fadeOut(this, bgm, duration, destroy); + SoundFade.fadeOut(this, this.bgm, duration, destroy); return true; } diff --git a/src/data/move.ts b/src/data/move.ts index ba786539b94..e2c1808f123 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1614,10 +1614,6 @@ export class FaintCountdownAttr extends AddBattlerTagAttr { return true; } - - getCondition(): MoveConditionFunc { - return (user, target, move) => super.getCondition()(user, target, move) && !target.isBossImmune(); - } } export class HitsTagAttr extends MoveAttr { @@ -2007,6 +2003,8 @@ export class DiscourageFrequentUseAttr extends MoveAttr { const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY); +const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune(); + export type MoveAttrFilter = (attr: MoveAttr) => boolean; function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { @@ -2602,6 +2600,7 @@ export function initMoves() { .attr(FaintCountdownAttr) .ignoresProtect() .soundBased() + .condition(failOnBossCondition) .target(MoveTarget.ALL), new AttackMove(Moves.ICY_WIND, "Icy Wind", Type.ICE, MoveCategory.SPECIAL, 55, 95, 15, 34, "The user attacks with a gust of chilled air. This also lowers opposing Pokémon's Speed stats.", 100, 0, 2) .attr(StatChangeAttr, BattleStat.SPD, -1) @@ -2663,7 +2662,8 @@ export function initMoves() { new StatusMove(Moves.SAFEGUARD, "Safeguard (N)", Type.NORMAL, -1, 25, -1, "The user creates a protective field that prevents status conditions for five turns.", -1, 0, 2) .target(MoveTarget.USER_SIDE), new StatusMove(Moves.PAIN_SPLIT, "Pain Split", Type.NORMAL, -1, 20, -1, "The user adds its HP to the target's HP, then equally shares the combined HP with the target.", -1, 0, 2) - .attr(HpSplitAttr), + .attr(HpSplitAttr) + .condition(failOnBossCondition), new AttackMove(Moves.SACRED_FIRE, "Sacred Fire", Type.FIRE, MoveCategory.PHYSICAL, 100, 95, 5, -1, "The target is razed with a mystical fire of great intensity. This may also leave the target with a burn.", 50, 0, 2) .attr(StatusEffectAttr, StatusEffect.BURN) .makesContact(false), @@ -2796,7 +2796,8 @@ export function initMoves() { .condition((user, target, move) => !target.status), new AttackMove(Moves.KNOCK_OFF, "Knock Off (N)", Type.DARK, MoveCategory.PHYSICAL, 65, 100, 20, -1, "The user slaps down the target's held item, and that item can't be used in that battle. The move does more damage if the target has a held item.", -1, 0, 3), new AttackMove(Moves.ENDEAVOR, "Endeavor", Type.NORMAL, MoveCategory.PHYSICAL, -1, 100, 5, -1, "This attack move cuts down the target's HP to equal the user's HP.", -1, 0, 3) - .attr(MatchHpAttr), + .attr(MatchHpAttr) + .condition(failOnBossCondition), new AttackMove(Moves.ERUPTION, "Eruption", Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, "The user attacks opposing Pokémon with explosive fury. The lower the user's HP, the lower the move's power.", -1, 0, 3) .attr(HpPowerAttr) .target(MoveTarget.ALL_NEAR_ENEMIES), diff --git a/src/form-change-phase.ts b/src/form-change-phase.ts index 439ffa62263..44b3e2b8e60 100644 --- a/src/form-change-phase.ts +++ b/src/form-change-phase.ts @@ -235,7 +235,7 @@ export class QuietFormChangePhase extends BattlePhase { this.scene.tweens.add({ targets: pokemonFormTintSprite, delay: 250, - scale: 1, + scale: this.pokemon.getSpriteScale(), ease: 'Cubic.easeInOut', duration: 500, onComplete: () => { diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index 50cefcab8bd..2a3bf79e67b 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -25,6 +25,7 @@ uniform vec3 duskTint; uniform vec3 nightTint; uniform int hasShadow; uniform int yCenter; +uniform float fieldScale; uniform float vCutoff; uniform vec2 relPosition; uniform vec2 size; @@ -115,8 +116,8 @@ void main() if (hasShadow == 1) { float width = size.x - (yOffset / 2.0); - float spriteX = ((floor(outPosition.x / 6.0) - relPosition.x) / width) + 0.5; - float spriteY = ((floor(outPosition.y / 6.0) - relPosition.y) / size.y); + float spriteX = ((floor(outPosition.x / fieldScale) - relPosition.x) / width) + 0.5; + float spriteY = ((floor(outPosition.y / fieldScale) - relPosition.y) / size.y); if (yCenter == 1) { spriteY += 0.5; @@ -213,14 +214,19 @@ export default class SpritePipeline extends FieldSpritePipeline { const fusionSpriteColors = (ignoreOverride && data['fusionSpriteColorsBase']) || data['fusionSpriteColors'] || [] as number[][]; const isEntityObj = sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer; + const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer; + const fieldScaleRatio = field.scale / 6; const position = isEntityObj ? [ sprite.parentContainer.x, sprite.parentContainer.y ] : [ sprite.x, sprite.y ]; - position[0] += -(sprite.width - sprite.frame.width) / 2 + sprite.frame.x; + position[0] += field.x / field.scale; + position[1] += field.y / field.scale; + position[0] += -(sprite.width - (sprite.frame.width)) / 2 + sprite.frame.x; if (sprite.originY === 0.5) position[1] += (sprite.height / 2) * ((isEntityObj ? sprite.parentContainer : sprite).scale - 1); this.set1i('hasShadow', hasShadow ? 1 : 0); this.set1i('yCenter', sprite.originY === 0.5 ? 1 : 0); + this.set1f('fieldScale', field.scale); this.set2f('relPosition', position[0], position[1]); this.set2f('size', sprite.frame.width, sprite.height); this.set1f('yOffset', sprite.height - sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale)); @@ -247,14 +253,16 @@ export default class SpritePipeline extends FieldSpritePipeline { const hasShadow = sprite.pipelineData['hasShadow'] as boolean; if (hasShadow) { const isEntityObj = sprite.parentContainer instanceof Pokemon || sprite.parentContainer instanceof Trainer; + const field = isEntityObj ? sprite.parentContainer.parentContainer : sprite.parentContainer; + const fieldScaleRatio = field.scale / 6; const baseY = (isEntityObj ? sprite.parentContainer.y - : sprite.y + sprite.height) * 6; - const bottomPadding = Math.ceil(sprite.height * 0.05) * 6; - const yDelta = (baseY - y1) / 6; + : sprite.y + sprite.height) * 6 / fieldScaleRatio; + const bottomPadding = Math.ceil(sprite.height * 0.05) * 6 / fieldScaleRatio; + const yDelta = (baseY - y1) / field.scale; y2 = y1 = baseY + bottomPadding; - const pixelHeight = ((v1 - v0) / (sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale))); - v1 += (yDelta + bottomPadding / 6) * pixelHeight; + const pixelHeight = (v1 - v0) / (sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale)); + v1 += (yDelta + bottomPadding / field.scale) * pixelHeight; } return super.batchQuad(gameObject, x0, y0, x1, y1, x2, y2, x3, y3, u0, v0, u1, v1, tintTL, tintTR, tintBL, tintBR, tintEffect, texture, unit); diff --git a/src/pokemon.ts b/src/pokemon.ts index 688159cc8b5..0cace02c03c 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -399,6 +399,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteScale(): number { + if (this.species.speciesId === Species.ETERNATUS && this.formIndex) + return 1.5; return 1; } @@ -1565,8 +1567,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!this.maskEnabled) { this.maskSprite = this.getTintSprite(); this.maskSprite.setVisible(true); - this.maskSprite.setPosition(this.x * 6, this.y * 6); - this.maskSprite.setScale(6 * this.getSpriteScale()); + this.maskSprite.setPosition(this.x * this.parentContainer.scale + this.parentContainer.x, + this.y * this.parentContainer.scale + this.parentContainer.y); + this.maskSprite.setScale(this.getSpriteScale() * this.parentContainer.scale); this.maskEnabled = true; } }