♻️ remove silhouette — Nisse always render above world objects
Depth fixed at 900; isOccluded() and outlineSprite removed. WorldSystem.hasResourceAt() stays as a useful utility.
This commit is contained in:
@@ -15,9 +15,6 @@ const WORK_LOG_MAX = 20
|
|||||||
|
|
||||||
interface VillagerRuntime {
|
interface VillagerRuntime {
|
||||||
sprite: Phaser.GameObjects.Image
|
sprite: Phaser.GameObjects.Image
|
||||||
/** White silhouette sprite rendered above all world objects so the Nisse is
|
|
||||||
* always locatable even when occluded by trees or buildings. */
|
|
||||||
outlineSprite: Phaser.GameObjects.Image
|
|
||||||
nameLabel: Phaser.GameObjects.Text
|
nameLabel: Phaser.GameObjects.Text
|
||||||
energyBar: Phaser.GameObjects.Graphics
|
energyBar: Phaser.GameObjects.Graphics
|
||||||
jobIcon: Phaser.GameObjects.Text
|
jobIcon: Phaser.GameObjects.Text
|
||||||
@@ -121,15 +118,8 @@ export class VillagerSystem {
|
|||||||
case 'sleeping':this.tickSleeping(v, rt, delta); break
|
case 'sleeping':this.tickSleeping(v, rt, delta); break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync sprite to state position; depth is Y-based so Nisse sort correctly with world objects
|
// Nisse always render above world objects
|
||||||
const worldDepth = Math.floor(v.y / TILE_SIZE) + 5
|
rt.sprite.setPosition(v.x, v.y)
|
||||||
rt.sprite.setPosition(v.x, v.y).setDepth(worldDepth)
|
|
||||||
|
|
||||||
// Show outline only when a world object below the Nisse would occlude them
|
|
||||||
const tileX = Math.floor(v.x / TILE_SIZE)
|
|
||||||
const tileY = Math.floor(v.y / TILE_SIZE)
|
|
||||||
const occluded = this.isOccluded(tileX, tileY)
|
|
||||||
rt.outlineSprite.setPosition(v.x, v.y).setFlipX(rt.sprite.flipX).setAngle(rt.sprite.angle).setVisible(occluded)
|
|
||||||
|
|
||||||
rt.nameLabel.setPosition(v.x, v.y - 22)
|
rt.nameLabel.setPosition(v.x, v.y - 22)
|
||||||
rt.energyBar.setPosition(0, 0)
|
rt.energyBar.setPosition(0, 0)
|
||||||
@@ -586,18 +576,9 @@ export class VillagerSystem {
|
|||||||
* @param v - Villager state to create sprites for
|
* @param v - Villager state to create sprites for
|
||||||
*/
|
*/
|
||||||
private spawnSprite(v: VillagerState): void {
|
private spawnSprite(v: VillagerState): void {
|
||||||
// Silhouette: same texture, white fill, fixed high depth so it shows through
|
// Nisse always render above trees, buildings and other world objects.
|
||||||
// trees and buildings. Visibility is toggled per frame by isOccluded().
|
|
||||||
const outlineSprite = this.scene.add.image(v.x, v.y, 'villager')
|
|
||||||
.setScale(1.15)
|
|
||||||
.setTintFill(0xaaddff)
|
|
||||||
.setAlpha(0.85)
|
|
||||||
.setDepth(900)
|
|
||||||
.setVisible(false)
|
|
||||||
|
|
||||||
// Main sprite depth is updated every frame based on Y position.
|
|
||||||
const sprite = this.scene.add.image(v.x, v.y, 'villager')
|
const sprite = this.scene.add.image(v.x, v.y, 'villager')
|
||||||
.setDepth(Math.floor(v.y / TILE_SIZE) + 5)
|
.setDepth(900)
|
||||||
|
|
||||||
const nameLabel = this.scene.add.text(v.x, v.y - 22, v.name, {
|
const nameLabel = this.scene.add.text(v.x, v.y - 22, v.name, {
|
||||||
fontSize: '8px', color: '#ffffff', fontFamily: 'monospace',
|
fontSize: '8px', color: '#ffffff', fontFamily: 'monospace',
|
||||||
@@ -610,7 +591,7 @@ export class VillagerSystem {
|
|||||||
sprite.setInteractive()
|
sprite.setInteractive()
|
||||||
sprite.on('pointerdown', () => this.onNisseClick?.(v.id))
|
sprite.on('pointerdown', () => this.onNisseClick?.(v.id))
|
||||||
|
|
||||||
this.runtime.set(v.id, { sprite, outlineSprite, nameLabel, energyBar, jobIcon, path: [], destination: null, workTimer: 0, idleScanTimer: 0, workLog: [] })
|
this.runtime.set(v.id, { sprite, nameLabel, energyBar, jobIcon, path: [], destination: null, workTimer: 0, idleScanTimer: 0, workLog: [] })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -644,30 +625,6 @@ export class VillagerSystem {
|
|||||||
if (rt.workLog.length > WORK_LOG_MAX) rt.workLog.length = WORK_LOG_MAX
|
if (rt.workLog.length > WORK_LOG_MAX) rt.workLog.length = WORK_LOG_MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Occlusion check ──────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if a world object (tree, rock, or building) with a higher tileY
|
|
||||||
* exists in the vicinity of the Nisse and would visually occlude them.
|
|
||||||
* Checks a 3-column × 4-row window below the Nisse's tile to account for
|
|
||||||
* wide tree canopies that extend above and to the sides of the trunk tile.
|
|
||||||
* @param tileX - Nisse's current tile column
|
|
||||||
* @param tileY - Nisse's current tile row
|
|
||||||
*/
|
|
||||||
private isOccluded(tileX: number, tileY: number): boolean {
|
|
||||||
const state = stateManager.getState()
|
|
||||||
const buildings = Object.values(state.world.buildings)
|
|
||||||
for (let dx = -1; dx <= 1; dx++) {
|
|
||||||
for (let dy = 1; dy <= 4; dy++) {
|
|
||||||
const cx = tileX + dx
|
|
||||||
const cy = tileY + dy
|
|
||||||
if (this.worldSystem.hasResourceAt(cx, cy)) return true
|
|
||||||
if (buildings.some(b => b.tileX === cx && b.tileY === cy && b.kind !== 'stockpile_zone')) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Public API ───────────────────────────────────────────────────────────
|
// ─── Public API ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -726,10 +683,14 @@ export class VillagerSystem {
|
|||||||
* Destroys all Nisse sprites and clears the runtime map.
|
* Destroys all Nisse sprites and clears the runtime map.
|
||||||
* Should be called when the scene shuts down.
|
* Should be called when the scene shuts down.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Destroys all Nisse sprites and clears the runtime map.
|
||||||
|
* Should be called when the scene shuts down.
|
||||||
|
*/
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
for (const rt of this.runtime.values()) {
|
for (const rt of this.runtime.values()) {
|
||||||
rt.sprite.destroy(); rt.outlineSprite.destroy()
|
rt.sprite.destroy(); rt.nameLabel.destroy()
|
||||||
rt.nameLabel.destroy(); rt.energyBar.destroy(); rt.jobIcon.destroy()
|
rt.energyBar.destroy(); rt.jobIcon.destroy()
|
||||||
}
|
}
|
||||||
this.runtime.clear()
|
this.runtime.clear()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user