import Phaser from "phaser"; import { type EntityId, type ActorType } from "../core/types"; import { TILE_SIZE } from "../core/constants"; import { GAME_CONFIG } from "../core/config/GameConfig"; export class FxRenderer { private scene: Phaser.Scene; private corpseSprites: Phaser.GameObjects.Sprite[] = []; constructor(scene: Phaser.Scene) { this.scene = scene; } showFloatingText(x: number, y: number, message: string, color: string) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; const text = this.scene.add.text(screenX, screenY, message, { fontSize: "14px", color: color, stroke: "#000", strokeThickness: 2, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(200); this.scene.tweens.add({ targets: text, y: screenY - 30, alpha: 0, duration: 1000, ease: "Power1", onComplete: () => text.destroy() }); } clearCorpses() { for (const sprite of this.corpseSprites) { sprite.destroy(); } this.corpseSprites = []; } showDamage(x: number, y: number, amount: number, isCrit = false, isBlock = false) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; let textStr = amount.toString(); let color = "#ff3333"; let fontSize = "16px"; if (isCrit) { textStr += "!"; color = "#ffff00"; fontSize = "22px"; } const text = this.scene.add.text(screenX, screenY, textStr, { fontSize, color, stroke: "#000", strokeThickness: 2, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(200); if (isBlock) { const blockText = this.scene.add.text(screenX + 10, screenY - 10, "Blocked", { fontSize: "10px", color: "#888888", fontStyle: "bold" }).setOrigin(0, 1).setDepth(200); this.scene.tweens.add({ targets: blockText, y: screenY - 34, alpha: 0, duration: 800, onComplete: () => blockText.destroy() }); } this.scene.tweens.add({ targets: text, y: screenY - 24, alpha: 0, duration: isCrit ? 1200 : 800, ease: isCrit ? "Bounce.out" : "Power1", onComplete: () => text.destroy() }); } showDodge(x: number, y: number) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; const text = this.scene.add.text(screenX, screenY, "Dodge", { fontSize: "14px", color: "#ffffff", stroke: "#000", strokeThickness: 2, fontStyle: "italic" }).setOrigin(0.5, 1).setDepth(200); this.scene.tweens.add({ targets: text, x: screenX + (Math.random() > 0.5 ? 20 : -20), y: screenY - 20, alpha: 0, duration: 600, onComplete: () => text.destroy() }); } showHeal(x: number, y: number, amount: number) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; const text = this.scene.add.text(screenX, screenY, `+${amount}`, { fontSize: "16px", color: "#33ff33", stroke: "#000", strokeThickness: 2, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(200); this.scene.tweens.add({ targets: text, y: screenY - 30, alpha: 0, duration: 1000, onComplete: () => text.destroy() }); } spawnCorpse(x: number, y: number, type: ActorType) { const textureKey = type === "player" ? "warrior" : type; const corpse = this.scene.add.sprite( x * TILE_SIZE + TILE_SIZE / 2, y * TILE_SIZE + TILE_SIZE / 2, textureKey, 0 ); corpse.setDepth(50); corpse.play(`${textureKey}-die`); this.corpseSprites.push(corpse); } showWait(x: number, y: number) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; const text = this.scene.add.text(screenX, screenY, "zZz", { fontSize: "14px", color: "#aaaaff", stroke: "#000", strokeThickness: 2, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(200); this.scene.tweens.add({ targets: text, y: screenY - 20, alpha: 0, duration: 600, ease: "Power1", onComplete: () => text.destroy() }); } collectOrb(_actorId: EntityId, amount: number, x: number, y: number) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; const text = this.scene.add.text(screenX, screenY, `+${amount} EXP`, { fontSize: "14px", color: "#" + GAME_CONFIG.rendering.expTextColor.toString(16), stroke: "#000", strokeThickness: 2, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(200); this.scene.tweens.add({ targets: text, y: screenY - 32, alpha: 0, duration: 1000, ease: "Power1", onComplete: () => text.destroy() }); } showLevelUp(x: number, y: number) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE; const text = this.scene.add.text(screenX, screenY - 16, "+1 LVL", { fontSize: "20px", color: "#" + GAME_CONFIG.rendering.levelUpColor.toString(16), stroke: "#000", strokeThickness: 3, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(210); this.scene.tweens.add({ targets: text, y: screenY - 60, alpha: 0, duration: 1500, ease: "Cubic.out", onComplete: () => text.destroy() }); } showAlert(x: number, y: number) { const screenX = x * TILE_SIZE + TILE_SIZE / 2; const screenY = y * TILE_SIZE - 8; const text = this.scene.add.text(screenX, screenY, "!", { fontSize: "24px", color: "#ffaa00", stroke: "#000", strokeThickness: 3, fontStyle: "bold" }).setOrigin(0.5, 1).setDepth(210); // Exclamation mark stays visible for alert duration this.scene.tweens.add({ targets: text, y: screenY - 8, duration: 200, yoyo: true, repeat: 3, // Bounce a few times ease: "Sine.inOut" }); this.scene.tweens.add({ targets: text, alpha: 0, delay: 900, // Start fading out near end of alert period duration: 300, onComplete: () => text.destroy() }); } }