diff --git a/client/src/network/SocketClient.ts b/client/src/network/SocketClient.ts index 7c38b6d..51d5acc 100644 --- a/client/src/network/SocketClient.ts +++ b/client/src/network/SocketClient.ts @@ -56,6 +56,10 @@ export class SocketClient { this.socket.emit('spawn-npc', { x, y }); } + followNpc(entityId: number | null): void { + this.socket.emit('follow-npc', { entityId }); + } + disconnect(): void { this.socket.disconnect(); } diff --git a/client/src/scenes/GameScene.ts b/client/src/scenes/GameScene.ts index 038f581..fea229a 100644 --- a/client/src/scenes/GameScene.ts +++ b/client/src/scenes/GameScene.ts @@ -118,10 +118,15 @@ export class GameScene extends Phaser.Scene { // Panel visibility if (this.mode === 'follow') { + const npcIds = this.getNpcIds(); + if (npcIds.length > 0) { + this.client.followNpc(npcIds[this.followTargetIndex]); + } this.showFollowPanel(); this.commandPanel.hide(); this.followCommandPanel.show(); } else if (prevMode === 'follow') { + this.client.followNpc(null); this.npcInfoPanel.hide(); this.followCommandPanel.hide(); this.clearHighlight(); @@ -203,6 +208,7 @@ export class GameScene extends Phaser.Scene { if (idx === -1) return; this.followTargetIndex = idx; + this.client.followNpc(entityId); if (this.mode !== 'follow') { this.mode = 'follow'; this.showFollowPanel(); @@ -222,6 +228,7 @@ export class GameScene extends Phaser.Scene { if (idx === -1) return; this.followTargetIndex = idx; + this.client.followNpc(entityId); if (this.mode !== 'follow') { this.mode = 'follow'; this.showFollowPanel(); @@ -689,6 +696,7 @@ export class GameScene extends Phaser.Scene { if (left) { this.followTargetIndex = (this.followTargetIndex - 1 + npcIds.length) % npcIds.length; this.followThrottle = 150; + this.client.followNpc(npcIds[this.followTargetIndex]); if (this.highlightEnabled) { this.applyHighlight(npcIds[this.followTargetIndex]); } @@ -697,6 +705,7 @@ export class GameScene extends Phaser.Scene { } else if (right) { this.followTargetIndex = (this.followTargetIndex + 1) % npcIds.length; this.followThrottle = 150; + this.client.followNpc(npcIds[this.followTargetIndex]); if (this.highlightEnabled) { this.applyHighlight(npcIds[this.followTargetIndex]); } diff --git a/server/src/network/SocketServer.ts b/server/src/network/SocketServer.ts index 7c6f179..507e4ae 100644 --- a/server/src/network/SocketServer.ts +++ b/server/src/network/SocketServer.ts @@ -1,6 +1,6 @@ import { Server } from 'socket.io'; import type http from 'http'; -import type { ClientEvents, ServerEvents, PlayerInput, Position, Movement, PlayerControlled, Appearance } from '@dflike/shared'; +import type { ClientEvents, ServerEvents, PlayerInput, Position, Movement, PlayerControlled, Appearance, EntityId } from '@dflike/shared'; import { Direction, MAX_NPC_COUNT } from '@dflike/shared'; import { generateRandomAppearance } from '../spawner/appearanceGenerator.js'; import type { GameLoop } from '../game/GameLoop.js'; @@ -10,6 +10,7 @@ import { serializeWorldState, serializeStateUpdate } from './stateSerializer.js' export class SocketServer { private io: Server; private gameLoop: GameLoop; + private followedEntities: Map = new Map(); constructor(httpServer: http.Server, gameLoop: GameLoop) { this.io = new Server(httpServer, { @@ -82,15 +83,30 @@ export class SocketServer { spawnNPC(world, map, target); }); + // Handle follow-npc + socket.on('follow-npc', (data: { entityId: EntityId | null }) => { + this.followedEntities.set(socket.id, data.entityId); + this.rebuildFollowedEntityIds(); + }); + // Handle disconnect socket.on('disconnect', () => { console.log(`Player disconnected: ${playerId}`); + this.followedEntities.delete(socket.id); + this.rebuildFollowedEntityIds(); world.removeEntity(entity); this.io.emit('player-left', { playerId }); }); }); } + private rebuildFollowedEntityIds(): void { + this.gameLoop.followedEntityIds.clear(); + for (const [, entityId] of this.followedEntities) { + if (entityId !== null) this.gameLoop.followedEntityIds.add(entityId); + } + } + private handleInput(playerId: string, entityId: number, input: PlayerInput): void { const world = this.gameLoop.world; const pc = world.getComponent(entityId, 'playerControlled'); diff --git a/shared/src/types.ts b/shared/src/types.ts index 6974338..a5a661d 100644 --- a/shared/src/types.ts +++ b/shared/src/types.ts @@ -181,4 +181,5 @@ export interface ServerEvents { export interface ClientEvents { 'player-input': (data: PlayerInput) => void; 'spawn-npc': (data: { x: number; y: number }) => void; + 'follow-npc': (data: { entityId: EntityId | null }) => void; }