diff --git a/src/systems/VillagerSystem.ts b/src/systems/VillagerSystem.ts index da53fa9..7e21b6c 100644 --- a/src/systems/VillagerSystem.ts +++ b/src/systems/VillagerSystem.ts @@ -15,9 +15,6 @@ const WORK_LOG_MAX = 20 interface VillagerRuntime { 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 energyBar: Phaser.GameObjects.Graphics jobIcon: Phaser.GameObjects.Text @@ -121,15 +118,8 @@ export class VillagerSystem { case 'sleeping':this.tickSleeping(v, rt, delta); break } - // Sync sprite to state position; depth is Y-based so Nisse sort correctly with world objects - const worldDepth = Math.floor(v.y / TILE_SIZE) + 5 - 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) + // Nisse always render above world objects + rt.sprite.setPosition(v.x, v.y) rt.nameLabel.setPosition(v.x, v.y - 22) rt.energyBar.setPosition(0, 0) @@ -586,18 +576,9 @@ export class VillagerSystem { * @param v - Villager state to create sprites for */ private spawnSprite(v: VillagerState): void { - // Silhouette: same texture, white fill, fixed high depth so it shows through - // 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. + // Nisse always render above trees, buildings and other world objects. 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, { fontSize: '8px', color: '#ffffff', fontFamily: 'monospace', @@ -610,7 +591,7 @@ export class VillagerSystem { sprite.setInteractive() 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 } - // ─── 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 ─────────────────────────────────────────────────────────── /** @@ -726,10 +683,14 @@ export class VillagerSystem { * Destroys all Nisse sprites and clears the runtime map. * 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 { for (const rt of this.runtime.values()) { - rt.sprite.destroy(); rt.outlineSprite.destroy() - rt.nameLabel.destroy(); rt.energyBar.destroy(); rt.jobIcon.destroy() + rt.sprite.destroy(); rt.nameLabel.destroy() + rt.energyBar.destroy(); rt.jobIcon.destroy() } this.runtime.clear() }