|
|
|
@ -77,7 +77,8 @@ export class GameRenderer {
|
|
|
|
// Reusable Graphics objects (avoid GC in hot path)
|
|
|
|
// Reusable Graphics objects (avoid GC in hot path)
|
|
|
|
private _groundGfx: Graphics | null = null;
|
|
|
|
private _groundGfx: Graphics | null = null;
|
|
|
|
private _heroGfx: Graphics | null = null;
|
|
|
|
private _heroGfx: Graphics | null = null;
|
|
|
|
private _campfireGfx: Graphics | null = null;
|
|
|
|
/** Tent + campfire while resting in the wild phase (roadside / adventure inline). */
|
|
|
|
|
|
|
|
private _restCampGfx: Graphics | null = null;
|
|
|
|
private _enemyGfx: Graphics | null = null;
|
|
|
|
private _enemyGfx: Graphics | null = null;
|
|
|
|
private _thoughtGfx: Graphics | null = null;
|
|
|
|
private _thoughtGfx: Graphics | null = null;
|
|
|
|
private _thoughtText: Text | null = null;
|
|
|
|
private _thoughtText: Text | null = null;
|
|
|
|
@ -317,8 +318,8 @@ export class GameRenderer {
|
|
|
|
this._heroGfx = new Graphics();
|
|
|
|
this._heroGfx = new Graphics();
|
|
|
|
this.entityLayer.addChild(this._heroGfx);
|
|
|
|
this.entityLayer.addChild(this._heroGfx);
|
|
|
|
|
|
|
|
|
|
|
|
this._campfireGfx = new Graphics();
|
|
|
|
this._restCampGfx = new Graphics();
|
|
|
|
this.entityLayer.addChild(this._campfireGfx);
|
|
|
|
this.entityLayer.addChild(this._restCampGfx);
|
|
|
|
|
|
|
|
|
|
|
|
this._enemyGfx = new Graphics();
|
|
|
|
this._enemyGfx = new Graphics();
|
|
|
|
this.entityLayer.addChild(this._enemyGfx);
|
|
|
|
this.entityLayer.addChild(this._enemyGfx);
|
|
|
|
@ -560,30 +561,58 @@ export class GameRenderer {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Draw a small campfire near hero while roadside-resting. */
|
|
|
|
/**
|
|
|
|
drawCampfire(wx: number, wy: number, now: number): void {
|
|
|
|
* Draw a small camp (A-frame tent + campfire) near the hero during wilderness rest (wild phase).
|
|
|
|
const gfx = this._campfireGfx;
|
|
|
|
* Placed slightly behind the hero in screen space for a “bivouac” read.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
drawRestCamp(wx: number, wy: number, now: number): void {
|
|
|
|
|
|
|
|
const gfx = this._restCampGfx;
|
|
|
|
if (!gfx) return;
|
|
|
|
if (!gfx) return;
|
|
|
|
gfx.clear();
|
|
|
|
gfx.clear();
|
|
|
|
|
|
|
|
|
|
|
|
const iso = worldToScreen(wx, wy);
|
|
|
|
const iso = worldToScreen(wx, wy);
|
|
|
|
const bob = Math.sin(now * 0.005) * 1.2;
|
|
|
|
const bob = Math.sin(now * 0.004) * 1.0;
|
|
|
|
const cx = iso.x + 18;
|
|
|
|
|
|
|
|
const cy = iso.y + 9 + bob;
|
|
|
|
// --- Tent (screen-left of hero, reads “behind” in iso) ---
|
|
|
|
|
|
|
|
const tx = iso.x - 26;
|
|
|
|
|
|
|
|
const ty = iso.y - 4 + bob * 0.4;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Ground shadow under tent
|
|
|
|
|
|
|
|
gfx.ellipse(tx, ty + 14, 22, 7);
|
|
|
|
|
|
|
|
gfx.fill({ color: 0x000000, alpha: 0.18 });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Tent body (trapezoid wall + triangle roof)
|
|
|
|
|
|
|
|
gfx.poly([tx - 18, ty + 12, tx + 18, ty + 12, tx + 14, ty - 8, tx - 14, ty - 8]);
|
|
|
|
|
|
|
|
gfx.fill({ color: 0x8b6914, alpha: 0.92 });
|
|
|
|
|
|
|
|
gfx.poly([tx - 14, ty - 8, tx, ty - 22, tx + 14, ty - 8]);
|
|
|
|
|
|
|
|
gfx.fill({ color: 0xc4a574, alpha: 0.96 });
|
|
|
|
|
|
|
|
gfx.poly([tx - 14, ty - 8, tx, ty - 22, tx + 14, ty - 8]);
|
|
|
|
|
|
|
|
gfx.stroke({ color: 0x5c4030, width: 1.2, alpha: 0.85 });
|
|
|
|
|
|
|
|
gfx.rect(tx - 5, ty + 2, 10, 10);
|
|
|
|
|
|
|
|
gfx.fill({ color: 0x1a1510, alpha: 0.55 });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Guy lines / pegs (tiny)
|
|
|
|
|
|
|
|
gfx.moveTo(tx - 18, ty + 12);
|
|
|
|
|
|
|
|
gfx.lineTo(tx - 26, ty + 16);
|
|
|
|
|
|
|
|
gfx.stroke({ color: 0x4a3a2a, width: 1, alpha: 0.5 });
|
|
|
|
|
|
|
|
gfx.moveTo(tx + 18, ty + 12);
|
|
|
|
|
|
|
|
gfx.lineTo(tx + 26, ty + 16);
|
|
|
|
|
|
|
|
gfx.stroke({ color: 0x4a3a2a, width: 1, alpha: 0.5 });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- Campfire (near tent / hero) ---
|
|
|
|
|
|
|
|
const cx = iso.x + 16;
|
|
|
|
|
|
|
|
const cy = iso.y + 10 + bob;
|
|
|
|
|
|
|
|
|
|
|
|
// Ground shadow / ember glow
|
|
|
|
|
|
|
|
gfx.ellipse(cx, cy + 6, 12, 4);
|
|
|
|
gfx.ellipse(cx, cy + 6, 12, 4);
|
|
|
|
gfx.fill({ color: 0x000000, alpha: 0.22 });
|
|
|
|
gfx.fill({ color: 0x000000, alpha: 0.22 });
|
|
|
|
gfx.ellipse(cx, cy + 3, 10, 3.2);
|
|
|
|
gfx.ellipse(cx, cy + 3, 10, 3.2);
|
|
|
|
gfx.fill({ color: 0xff7a1a, alpha: 0.2 });
|
|
|
|
gfx.fill({ color: 0xff7a1a, alpha: 0.22 });
|
|
|
|
|
|
|
|
|
|
|
|
// Logs
|
|
|
|
|
|
|
|
gfx.roundRect(cx - 9, cy + 1, 18, 3, 1.5);
|
|
|
|
gfx.roundRect(cx - 9, cy + 1, 18, 3, 1.5);
|
|
|
|
gfx.fill({ color: 0x5a3a24, alpha: 0.95 });
|
|
|
|
gfx.fill({ color: 0x5a3a24, alpha: 0.95 });
|
|
|
|
gfx.roundRect(cx - 8, cy - 1, 16, 3, 1.5);
|
|
|
|
gfx.roundRect(cx - 8, cy - 1, 16, 3, 1.5);
|
|
|
|
gfx.fill({ color: 0x6b4428, alpha: 0.9 });
|
|
|
|
gfx.fill({ color: 0x6b4428, alpha: 0.9 });
|
|
|
|
|
|
|
|
|
|
|
|
// Flame (layered circles for lightweight VFX)
|
|
|
|
|
|
|
|
const pulse = 0.9 + 0.2 * Math.sin(now * 0.012);
|
|
|
|
const pulse = 0.9 + 0.2 * Math.sin(now * 0.012);
|
|
|
|
gfx.circle(cx, cy - 6, 5.2 * pulse);
|
|
|
|
gfx.circle(cx, cy - 6, 5.2 * pulse);
|
|
|
|
gfx.fill({ color: 0xff8a2a, alpha: 0.8 });
|
|
|
|
gfx.fill({ color: 0xff8a2a, alpha: 0.8 });
|
|
|
|
@ -592,11 +621,11 @@ export class GameRenderer {
|
|
|
|
gfx.circle(cx, cy - 8, 1.6 * pulse);
|
|
|
|
gfx.circle(cx, cy - 8, 1.6 * pulse);
|
|
|
|
gfx.fill({ color: 0xfff3b0, alpha: 0.95 });
|
|
|
|
gfx.fill({ color: 0xfff3b0, alpha: 0.95 });
|
|
|
|
|
|
|
|
|
|
|
|
gfx.zIndex = cy + 96;
|
|
|
|
gfx.zIndex = Math.max(ty, cy) + 94;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
clearCampfire(): void {
|
|
|
|
clearRestCamp(): void {
|
|
|
|
if (this._campfireGfx) this._campfireGfx.clear();
|
|
|
|
if (this._restCampGfx) this._restCampGfx.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
|