Add piano item type with realtime play mode and remote notes

This commit is contained in:
Jage9
2026-02-22 23:42:17 -05:00
parent 81c6af6399
commit 1319c044dd
23 changed files with 1014 additions and 23 deletions

View File

@@ -46,9 +46,23 @@ type MessageHandlerDeps = {
sanitizeName: (value: string) => string;
randomFootstepUrl: () => string;
playRemoteSpatialStepOrTeleport: (url: string, peerX: number, peerY: number) => void;
playRemotePianoNote: (note: {
itemId: string;
senderId: string;
keyId: string;
midi: number;
instrument: string;
attack: number;
decay: number;
x: number;
y: number;
emitRange: number;
}) => void;
stopRemotePianoNote: (senderId: string, keyId: string) => void;
stopAllRemotePianoNotesForSender: (senderId: string) => void;
TELEPORT_SOUND_URL: string;
TELEPORT_START_SOUND_URL: string;
getAudioLayers: () => { world: boolean };
getAudioLayers: () => { world: boolean; item: boolean };
pushChatMessage: (message: string) => void;
classifySystemMessageSound: (message: string) => 'logon' | 'logout' | 'notify' | null;
SYSTEM_SOUND_URLS: { logon: string; logout: string; notify: string };
@@ -159,6 +173,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
if (peer) {
deps.updateStatus(`${peer.nickname} has left.`);
}
deps.stopAllRemotePianoNotesForSender(message.id);
deps.state.peers.delete(message.id);
deps.peerManager.removePeer(message.id);
break;
@@ -226,7 +241,7 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
if (message.action === 'use') {
deps.pushChatMessage(message.message);
const item = message.itemId ? deps.getItemById(message.itemId) : null;
if (!item?.useSound && item) {
if (!item?.useSound && item && item.type !== 'piano') {
deps.playLocateToneAt(item.x, item.y);
}
} else if (message.action !== 'update') {
@@ -248,6 +263,27 @@ export function createOnMessageHandler(deps: MessageHandlerDeps): (message: Inco
}
break;
}
case 'item_piano_note': {
if (!deps.getAudioLayers().item) break;
if (message.on) {
deps.playRemotePianoNote({
itemId: message.itemId,
senderId: message.senderId,
keyId: message.keyId,
midi: message.midi,
instrument: message.instrument,
attack: message.attack,
decay: message.decay,
x: message.x,
y: message.y,
emitRange: message.emitRange,
});
} else {
deps.stopRemotePianoNote(message.senderId, message.keyId);
}
break;
}
}
};
}

View File

@@ -2,7 +2,7 @@ import { z } from 'zod';
export const itemSchema = z.object({
id: z.string(),
type: z.enum(['radio_station', 'dice', 'wheel', 'clock', 'widget']),
type: z.enum(['radio_station', 'dice', 'wheel', 'clock', 'widget', 'piano']),
title: z.string(),
x: z.number().int(),
y: z.number().int(),
@@ -42,10 +42,10 @@ export const welcomeMessageSchema = z.object({
.optional(),
uiDefinitions: z
.object({
itemTypeOrder: z.array(z.enum(['radio_station', 'dice', 'wheel', 'clock', 'widget'])),
itemTypeOrder: z.array(z.enum(['radio_station', 'dice', 'wheel', 'clock', 'widget', 'piano'])),
itemTypes: z.array(
z.object({
type: z.enum(['radio_station', 'dice', 'wheel', 'clock', 'widget']),
type: z.enum(['radio_station', 'dice', 'wheel', 'clock', 'widget', 'piano']),
label: z.string().optional(),
tooltip: z.string().optional(),
editableProperties: z.array(z.string()),
@@ -150,6 +150,21 @@ export const itemUseSoundSchema = z.object({
y: z.number().int(),
});
export const itemPianoNoteSchema = z.object({
type: z.literal('item_piano_note'),
itemId: z.string(),
senderId: z.string(),
keyId: z.string(),
midi: z.number().int().min(0).max(127),
on: z.boolean(),
instrument: z.string(),
attack: z.number().int().min(0).max(100),
decay: z.number().int().min(0).max(100),
x: z.number().int(),
y: z.number().int(),
emitRange: z.number().int().min(1),
});
export const incomingMessageSchema = z.discriminatedUnion('type', [
welcomeMessageSchema,
signalMessageSchema,
@@ -163,6 +178,7 @@ export const incomingMessageSchema = z.discriminatedUnion('type', [
itemRemoveSchema,
itemActionResultSchema,
itemUseSoundSchema,
itemPianoNoteSchema,
]);
export type IncomingMessage = z.infer<typeof incomingMessageSchema>;
@@ -173,11 +189,12 @@ export type OutgoingMessage =
| { type: 'update_nickname'; nickname: string }
| { type: 'chat_message'; message: string }
| { type: 'ping'; clientSentAt: number }
| { type: 'item_add'; itemType: 'radio_station' | 'dice' | 'wheel' | 'clock' | 'widget' }
| { type: 'item_add'; itemType: 'radio_station' | 'dice' | 'wheel' | 'clock' | 'widget' | 'piano' }
| { type: 'item_pickup'; itemId: string }
| { type: 'item_drop'; itemId: string; x: number; y: number }
| { type: 'item_delete'; itemId: string }
| { type: 'item_use'; itemId: string }
| { type: 'item_piano_note'; itemId: string; keyId: string; midi: number; on: boolean }
| {
type: 'item_update';
itemId: string;