223 lines
5.9 KiB
TypeScript
223 lines
5.9 KiB
TypeScript
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;
|
|
}
|
|
|
|
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()
|
|
});
|
|
}
|
|
}
|