diff --git a/client/src/scenes/GameScene.ts b/client/src/scenes/GameScene.ts index 0a72afc..78a8928 100644 --- a/client/src/scenes/GameScene.ts +++ b/client/src/scenes/GameScene.ts @@ -1,6 +1,8 @@ import Phaser from 'phaser'; import type { SocketClient } from '../network/SocketClient.js'; import { CharacterCompositor } from '../sprites/CharacterCompositor.js'; +import { PortraitCompositor } from '../sprites/PortraitCompositor.js'; +import { NpcInfoPanel } from '../ui/NpcInfoPanel.js'; import type { WorldState, StateUpdate, EntityState, Appearance } from '@dflike/shared'; import { TILE_SIZE, SPRITE_FRAME_WIDTH, SPRITE_FRAME_HEIGHT, SPRITE_COLS, Direction } from '@dflike/shared'; @@ -21,6 +23,8 @@ export class GameScene extends Phaser.Scene { private moveThrottle = 0; private followTargetIndex = 0; private followThrottle = 0; + private portraitCompositor!: PortraitCompositor; + private npcInfoPanel!: NpcInfoPanel; constructor() { super({ key: 'GameScene' }); @@ -33,6 +37,8 @@ export class GameScene extends Phaser.Scene { create(): void { this.compositor = new CharacterCompositor(this); + this.portraitCompositor = new PortraitCompositor(); + this.npcInfoPanel = new NpcInfoPanel(); // Draw tile grid this.drawWorld(); @@ -53,6 +59,7 @@ export class GameScene extends Phaser.Scene { // Tab to toggle mode this.input.keyboard!.addKey('TAB').on('down', () => { + const prevMode = this.mode; if (this.mode === 'camera') { this.mode = 'avatar'; } else if (this.mode === 'avatar') { @@ -62,6 +69,13 @@ export class GameScene extends Phaser.Scene { } this.client.sendInput({ type: 'toggle-mode' }); this.updateModeText(); + + // Panel visibility + if (this.mode === 'follow') { + this.showFollowPanel(); + } else if (prevMode === 'follow') { + this.npcInfoPanel.hide(); + } }); // UI text @@ -221,6 +235,19 @@ export class GameScene extends Phaser.Scene { this.entitySprites.delete(id); } } + + // Live-update the follow panel if visible + if (this.mode === 'follow' && this.npcInfoPanel.isVisible()) { + const npcIds = this.getNpcIds(); + const followedId = npcIds[this.followTargetIndex]; + const followed = this.entitySprites.get(followedId); + if (followed) { + this.npcInfoPanel.updateInfo(followed.lastState); + if (followed.lastState.needs) { + this.npcInfoPanel.updateNeeds(followed.lastState.needs); + } + } + } } update(_time: number, delta: number): void { @@ -285,10 +312,12 @@ export class GameScene extends Phaser.Scene { this.followTargetIndex = (this.followTargetIndex - 1 + npcIds.length) % npcIds.length; this.followThrottle = 150; this.updateModeText(); + this.updateFollowPanel(); } else if (right) { this.followTargetIndex = (this.followTargetIndex + 1) % npcIds.length; this.followThrottle = 150; this.updateModeText(); + this.updateFollowPanel(); } } @@ -301,6 +330,29 @@ export class GameScene extends Phaser.Scene { } } + private async showFollowPanel(): Promise { + const npcIds = this.getNpcIds(); + if (npcIds.length === 0) return; + const targetId = npcIds[this.followTargetIndex]; + const es = this.entitySprites.get(targetId); + if (!es) return; + + const portraitUrl = await this.portraitCompositor.compositePortrait(es.lastState.appearance); + this.npcInfoPanel.show(es.lastState, portraitUrl); + } + + private async updateFollowPanel(): Promise { + const npcIds = this.getNpcIds(); + if (npcIds.length === 0) return; + const targetId = npcIds[this.followTargetIndex]; + const es = this.entitySprites.get(targetId); + if (!es) return; + + const portraitUrl = await this.portraitCompositor.compositePortrait(es.lastState.appearance); + this.npcInfoPanel.updatePortrait(portraitUrl); + this.npcInfoPanel.updateInfo(es.lastState); + } + private getNpcIds(): number[] { return [...this.entitySprites.keys()].sort((a, b) => a - b); }