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;