diff --git a/packages/frontend/src/pages/rooms/room.core.vue b/packages/frontend/src/pages/rooms/room.core.vue index 3243963f4f..7050d4980c 100644 --- a/packages/frontend/src/pages/rooms/room.core.vue +++ b/packages/frontend/src/pages/rooms/room.core.vue @@ -132,6 +132,18 @@ SPDX-License-Identifier: AGPL-3.0-only + + + + +
+ + + +
+
@@ -147,6 +159,7 @@ import XEnvOptions from './room.env-options.vue'; import XOverlayPanel from './OverlayPanel.vue'; import type { RoomControllerOptions } from '@/world/room/controller.js'; import type { RoomState, RoomAttachments } from 'misskey-world/src/room/type.js'; +import type { PlayerProfile } from 'misskey-world-engine/src/PlayerContainer.js'; import MkWorldMonoOptionsForm from '@/components/MkWorldMonoOptionsForm.vue'; import { i18n } from '@/i18n.js'; import MkButton from '@/components/MkButton.vue'; @@ -177,13 +190,14 @@ const canvas = useTemplateRef('canvas'); const isMyRoom = computed(() => props.room.userId === $i?.id); const isNarrow = deviceKind === 'smartphone'; - const isModified = ref(false); +const pointedPlayerInfo = ref(null); const isMenuShowing = ref(!isNarrow); const isRoomSettingsOpen = ref(false); const isRoomInfoOpen = ref(false); const isFurnitureSettingsOpen = ref(false); +const isPlayerInfoOpen = ref(false); watch(isFurnitureSettingsOpen, () => { if (isFurnitureSettingsOpen.value) { @@ -385,6 +399,11 @@ watch([graphicsQuality, fps, resolution, antialias], () => { refresh(); }); +controller.addListener('playerPointed', ({ playerId }) => { + pointedPlayerInfo.value = multiplayer.playerProfiles.value[playerId] ?? null; + isPlayerInfoOpen.value = true; +}); + function resize() { controller.resize(); } diff --git a/packages/frontend/src/world/engineControllerBase.ts b/packages/frontend/src/world/engineControllerBase.ts index 32ed1e87de..631b74409d 100644 --- a/packages/frontend/src/world/engineControllerBase.ts +++ b/packages/frontend/src/world/engineControllerBase.ts @@ -19,9 +19,11 @@ export type EngineControllerBaseOptions = { type EngineEventsOf = T extends EngineBase ? X : EngineBaseEvents; +type ControllerEvents = EventEmitter.ValidEventTypes; + // UIとエンジンの間に挟まり抽象化を行うレイヤー。 // UIからは、エンジンが直で動いててもワーカーで動いてても同じように操作できるように見える -export abstract class EngineControllerBase> { +export abstract class EngineControllerBase, E extends EventEmitter.ValidEventTypes = EventEmitter.ValidEventTypes> extends EventEmitter { private worker: Worker | null = null; private engine: T | null = null; private canvas: HTMLCanvasElement | null = null; @@ -34,6 +36,7 @@ export abstract class EngineControllerBase { +export class RoomController extends EngineControllerBase { public isSitting = ref(false); public isEditMode = ref(false); public isRoomLightOn = ref(true); @@ -115,6 +117,10 @@ export class RoomController extends EngineControllerBase { engineEvents.on('playSfxUrl', ({ url, options }) => { sound.playUrl(url, options); }); + + engineEvents.on('playerPointed', ({ playerId }) => { + this.emit('playerPointed', { playerId }); + }); } public async reset(canvas: HTMLCanvasElement, attachments: RoomAttachments, roomState?: RoomState | null, options?: RoomControllerOptions | null) { diff --git a/packages/frontend/src/world/room/multiplayer.ts b/packages/frontend/src/world/room/multiplayer.ts index a02a98bbd9..dd94f938d8 100644 --- a/packages/frontend/src/world/room/multiplayer.ts +++ b/packages/frontend/src/world/room/multiplayer.ts @@ -18,7 +18,7 @@ export class Multiplayer { private controller: RoomController; private connection: Misskey.IChannelConnection | null = null; private roomId: string; - private playerProfiles: Record = {}; + public playerProfiles: Record = {}; constructor(roomId: string, controller: RoomController) { this.roomId = roomId;