From 716d8853a3636d4cde9213acad76cbe6c0206c4c Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Fri, 1 Mar 2024 00:27:46 -0500 Subject: [PATCH] Add damage numbers feature --- src/battle-scene.ts | 5 + src/data/move.ts | 2 +- src/field/damage-number-handler.ts | 164 ++++++++++++++++++++ src/field/pokemon-sprite-sparkle-handler.ts | 1 - src/field/pokemon.ts | 5 +- src/system/game-speed.ts | 18 +++ src/system/settings.ts | 6 + 7 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 src/field/damage-number-handler.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 0976c662d..efe6d3860 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -51,6 +51,7 @@ import { BattleSpec } from './enums/battle-spec'; import { getTypeRgb } from './data/type'; import PokemonSpriteSparkleHandler from './field/pokemon-sprite-sparkle-handler'; import CharSprite from './ui/char-sprite'; +import DamageNumberHandler from './field/damage-number-handler'; export const bypassLogin = false; export const startingLevel = 5; @@ -92,6 +93,7 @@ export default class BattleScene extends Phaser.Scene { public bgmVolume: number = 1; public seVolume: number = 1; public gameSpeed: integer = 1; + public damageNumbersMode: integer = 0; public showLevelUpStats: boolean = true; public enableTutorials: boolean = true; public windowType: integer = 1; @@ -141,6 +143,7 @@ export default class BattleScene extends Phaser.Scene { public seed: string; public waveSeed: string; + public damageNumberHandler: DamageNumberHandler private spriteSparkleHandler: PokemonSpriteSparkleHandler; public fieldSpritePipeline: FieldSpritePipeline; @@ -537,6 +540,8 @@ export default class BattleScene extends Phaser.Scene { this.updateUIPositions(); + this.damageNumberHandler = new DamageNumberHandler(); + this.spriteSparkleHandler = new PokemonSpriteSparkleHandler(); this.spriteSparkleHandler.setup(this); diff --git a/src/data/move.ts b/src/data/move.ts index 5bc92ce69..6a5cff9ea 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -15,7 +15,7 @@ import { ArenaTagType } from "./enums/arena-tag-type"; import { Abilities, BlockRecoilDamageAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs } from "./ability"; import { PokemonHeldItemModifier } from "../modifier/modifier"; import { BattlerIndex } from "../battle"; -import { Stat, getStatName } from "./pokemon-stat"; +import { Stat } from "./pokemon-stat"; export enum MoveCategory { PHYSICAL, diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts new file mode 100644 index 000000000..69e309317 --- /dev/null +++ b/src/field/damage-number-handler.ts @@ -0,0 +1,164 @@ +import { TextStyle, addTextObject } from "../ui/text"; +import Pokemon, { AttackMoveResult, HitResult } from "./pokemon"; +import * as Utils from "../utils"; +import { BattlerIndex } from "../battle"; + +export default class DamageNumberHandler { + private damageNumbers: Map; + + constructor() { + this.damageNumbers = new Map(); + } + + add(target: Pokemon, result: AttackMoveResult): void { + const scene = target.scene; + const battlerIndex = target.getBattlerIndex(); + const baseScale = target.getSpriteScale() / 6; + const damageNumber = addTextObject(scene, target.x, -(scene.game.canvas.height / 6) + target.y - target.getSprite().height / 2, Utils.formatStat(result.damage, true), TextStyle.SUMMARY); + damageNumber.setOrigin(0.5, 1); + damageNumber.setScale(baseScale); + + let [ textColor, shadowColor ] = [ null, null ]; + + switch (result.result) { + case HitResult.SUPER_EFFECTIVE: + [ textColor, shadowColor ] = [ '#f8d030', '#b8a038' ]; + break; + case HitResult.NOT_VERY_EFFECTIVE: + [ textColor, shadowColor ] = [ '#f08030', '#c03028' ]; + break; + case HitResult.ONE_HIT_KO: + [ textColor, shadowColor ] = [ '#a040a0', '#483850' ]; + break; + default: + [ textColor, shadowColor ] = [ '#ffffff', '#636363' ]; + break; + } + + if (textColor) + damageNumber.setColor(textColor); + if (shadowColor) { + if (result.critical) { + damageNumber.setShadowOffset(0, 0); + damageNumber.setStroke(shadowColor, 12); + } else + damageNumber.setShadowColor(shadowColor); + } + + scene.fieldUI.add(damageNumber); + + if (!this.damageNumbers.has(battlerIndex)) + this.damageNumbers.set(battlerIndex, []); + + const yOffset = this.damageNumbers.get(battlerIndex).length * -10; + if (yOffset) + damageNumber.y += yOffset; + + this.damageNumbers.get(battlerIndex).push(damageNumber); + + if (scene.damageNumbersMode === 1) { + scene.tweens.add({ + targets: damageNumber, + duration: Utils.fixedInt(750), + alpha: 1, + y: '-=32' + }); + scene.tweens.add({ + delay: 375, + targets: damageNumber, + duration: Utils.fixedInt(625), + alpha: 0, + ease: 'Sine.easeIn', + onComplete: () => { + this.damageNumbers.get(battlerIndex).splice(this.damageNumbers.get(battlerIndex).indexOf(damageNumber), 1); + damageNumber.destroy(true); + } + }); + return; + } + + damageNumber.setAlpha(0); + + scene.tweens.chain({ + targets: damageNumber, + tweens: [ + { + duration: Utils.fixedInt(250), + alpha: 1, + scaleX: 0.75 * baseScale, + scaleY: 1.25 * baseScale, + y: '-=16', + ease: 'Cubic.easeOut' + }, + { + duration: Utils.fixedInt(175), + alpha: 1, + scaleX: 0.875 * baseScale, + scaleY: 1.125 * baseScale, + y: '+=16', + ease: 'Cubic.easeIn' + }, + { + duration: Utils.fixedInt(100), + scaleX: 1.25 * baseScale, + scaleY: 0.75 * baseScale, + ease: 'Cubic.easeOut' + }, + { + duration: Utils.fixedInt(175), + scaleX: 0.875 * baseScale, + scaleY: 1.125 * baseScale, + y: '-=8', + ease: 'Cubic.easeOut' + }, + { + duration: Utils.fixedInt(50), + scaleX: 0.925 * baseScale, + scaleY: 1.075 * baseScale, + y: '+=8', + ease: 'Cubic.easeIn' + }, + { + duration: Utils.fixedInt(100), + scaleX: 1.125 * baseScale, + scaleY: 0.875 * baseScale, + ease: 'Cubic.easeOut' + }, + { + duration: Utils.fixedInt(175), + scaleX: 0.925 * baseScale, + scaleY: 1.075 * baseScale, + y: '-=4', + ease: 'Cubic.easeOut' + }, + { + duration: Utils.fixedInt(50), + scaleX: 0.975 * baseScale, + scaleY: 1.025 * baseScale, + y: '+=4', + ease: 'Cubic.easeIn' + }, + { + duration: Utils.fixedInt(100), + scaleX: 1.075 * baseScale, + scaleY: 0.925 * baseScale, + ease: 'Cubic.easeOut' + }, + { + duration: Utils.fixedInt(25), + scaleX: baseScale, + scaleY: baseScale, + ease: 'Cubic.easeOut' + }, + { + delay: Utils.fixedInt(500), + alpha: 0, + onComplete: () => { + this.damageNumbers.get(battlerIndex).splice(this.damageNumbers.get(battlerIndex).indexOf(damageNumber), 1); + damageNumber.destroy(true); + } + } + ] + }); + } +} \ No newline at end of file diff --git a/src/field/pokemon-sprite-sparkle-handler.ts b/src/field/pokemon-sprite-sparkle-handler.ts index c14aff6a2..5ae54f2d2 100644 --- a/src/field/pokemon-sprite-sparkle-handler.ts +++ b/src/field/pokemon-sprite-sparkle-handler.ts @@ -38,7 +38,6 @@ export default class PokemonSpriteSparkleHandler { const sparkle = (s.scene as BattleScene).addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), 'tera_sparkle'); sparkle.pipelineData['ignoreTimeTint'] = s.pipelineData['ignoreTimeTint']; sparkle.play('tera_sparkle'); - const teraColor = s.pipelineData['teraColor'] as number[]; parent.add(sparkle); s.scene.time.delayedCall(Utils.fixedInt(Math.floor((1000 / 12) * 13)), () => sparkle.destroy()); } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 3b33e8bea..9b97e5fe7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1135,7 +1135,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } source.turnData.damageDealt += damage.value; this.battleData.hitCount++; - this.turnData.attacksReceived.unshift({ move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id }); + const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id }; + this.turnData.attacksReceived.unshift(attackResult); + if (damage.value && this.scene.damageNumbersMode) + this.scene.damageNumberHandler.add(this, attackResult); if (source.isPlayer() && !this.isPlayer()) this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, damage) } diff --git a/src/system/game-speed.ts b/src/system/game-speed.ts index 437f85970..9d0d4c131 100644 --- a/src/system/game-speed.ts +++ b/src/system/game-speed.ts @@ -36,6 +36,24 @@ export function initGameSpeed() { config.hold = transformValue(config.hold); return originalTweensAdd.apply(this, [ config ]); }; + const originalTweensChain = this.tweens.chain; + this.tweens.chain = function (config: Phaser.Types.Tweens.TweenChainBuilderConfig): Phaser.Tweens.TweenChain { + if (config.tweens) { + config.tweens.forEach(t => { + if (t.duration) + t.duration = transformValue(t.duration); + if (t.delay) + t.delay = transformValue(t.delay); + if (t.repeatDelay) + t.repeatDelay = transformValue(t.repeatDelay); + if (t.loopDelay) + t.loopDelay = transformValue(t.loopDelay); + if (t.hold) + t.hold = transformValue(t.hold); + }); + } + return originalTweensChain.apply(this, [ config ]); + }; const originalAddCounter = this.tweens.addCounter; this.tweens.addCounter = function (config: Phaser.Types.Tweens.NumberTweenBuilderConfig) { if (config.duration) diff --git a/src/system/settings.ts b/src/system/settings.ts index 01f10a822..93b35d27f 100644 --- a/src/system/settings.ts +++ b/src/system/settings.ts @@ -7,6 +7,7 @@ export enum Setting { Master_Volume = "MASTER_VOLUME", BGM_Volume = "BGM_VOLUME", SE_Volume = "SE_VOLUME", + Damage_Numbers = "DAMAGE_NUMBERS", Show_Stats_on_Level_Up = "SHOW_LEVEL_UP_STATS", Window_Type = "WINDOW_TYPE", Tutorials = "TUTORIALS", @@ -29,6 +30,7 @@ export const settingOptions: SettingOptions = { [Setting.Master_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : 'Mute'), [Setting.BGM_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : 'Mute'), [Setting.SE_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : 'Mute'), + [Setting.Damage_Numbers]: [ 'Off', 'Simple', 'Fancy' ], [Setting.Show_Stats_on_Level_Up]: [ 'Off', 'On' ], [Setting.Window_Type]: new Array(4).fill(null).map((_, i) => (i + 1).toString()), [Setting.Tutorials]: [ 'Off', 'On' ], @@ -43,6 +45,7 @@ export const settingDefaults: SettingDefaults = { [Setting.Master_Volume]: 5, [Setting.BGM_Volume]: 10, [Setting.SE_Volume]: 10, + [Setting.Damage_Numbers]: 0, [Setting.Show_Stats_on_Level_Up]: 1, [Setting.Window_Type]: 0, [Setting.Tutorials]: 1, @@ -69,6 +72,9 @@ export function setSetting(scene: BattleScene, setting: Setting, value: integer) scene.seVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0; scene.updateSoundVolume(); break; + case Setting.Damage_Numbers: + scene.damageNumbersMode = value; + break; case Setting.Show_Stats_on_Level_Up: scene.showLevelUpStats = settingOptions[setting][value] === 'On'; break;