Add clock item type with timezone/time-format and emit sound
This commit is contained in:
@@ -105,14 +105,21 @@ type ChangelogData = {
|
||||
|
||||
const APP_VERSION = String(window.CHGRID_WEB_VERSION ?? '').trim();
|
||||
const DISPLAY_TIME_ZONE = resolveDisplayTimeZone();
|
||||
const CLOCK_TIME_ZONE_OPTIONS = [
|
||||
'America/Detroit',
|
||||
'America/New_York',
|
||||
'America/Indiana/Indianapolis',
|
||||
'America/Kentucky/Louisville',
|
||||
] as const;
|
||||
dom.appVersion.textContent = APP_VERSION
|
||||
? `Another AI experiment with Jage. Version ${APP_VERSION}`
|
||||
: 'Another AI experiment with Jage. Version unknown';
|
||||
const ITEM_TYPE_SEQUENCE: ItemType[] = ['radio_station', 'dice', 'wheel'];
|
||||
const ITEM_TYPE_SEQUENCE: ItemType[] = ['radio_station', 'dice', 'wheel', 'clock'];
|
||||
const ITEM_TYPE_GLOBAL_PROPERTIES: Record<ItemType, Record<string, string | number | boolean>> = {
|
||||
radio_station: { useCooldownMs: 1000 },
|
||||
dice: { useCooldownMs: 1000 },
|
||||
wheel: { useCooldownMs: 4000 },
|
||||
radio_station: { emitSound: 'none', useCooldownMs: 1000 },
|
||||
dice: { emitSound: 'sounds/roll.ogg', useCooldownMs: 1000 },
|
||||
wheel: { emitSound: 'sounds/spin.ogg', useCooldownMs: 4000 },
|
||||
clock: { emitSound: 'sounds/clock.ogg', useCooldownMs: 1000 },
|
||||
};
|
||||
const EDITABLE_ITEM_PROPERTY_KEYS = new Set([
|
||||
'title',
|
||||
@@ -125,10 +132,14 @@ const EDITABLE_ITEM_PROPERTY_KEYS = new Set([
|
||||
'spaces',
|
||||
'sides',
|
||||
'number',
|
||||
'timeZone',
|
||||
'use24Hour',
|
||||
]);
|
||||
const OPTION_ITEM_PROPERTY_VALUES: Partial<Record<string, string[]>> = {
|
||||
effect: EFFECT_SEQUENCE.map((effect) => effect.id),
|
||||
channel: [...RADIO_CHANNEL_OPTIONS],
|
||||
timeZone: [...CLOCK_TIME_ZONE_OPTIONS],
|
||||
use24Hour: ['off', 'on'],
|
||||
};
|
||||
const APP_BASE_URL = import.meta.env.BASE_URL || '/';
|
||||
function withBase(path: string): string {
|
||||
@@ -452,6 +463,8 @@ function getEditableItemPropertyKeys(item: WorldItem): string[] {
|
||||
keys.push('sides', 'number');
|
||||
} else if (item.type === 'wheel') {
|
||||
keys.push('spaces');
|
||||
} else if (item.type === 'clock') {
|
||||
keys.push('timeZone', 'use24Hour');
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
@@ -461,7 +474,7 @@ function getInspectItemPropertyKeys(item: WorldItem): string[] {
|
||||
const seen = new Set(editableKeys);
|
||||
const allKeys: string[] = [...editableKeys];
|
||||
|
||||
const baseKeys = ['type', 'x', 'y', 'carrierId', 'version', 'createdBy', 'createdAt', 'updatedAt', 'capabilities', 'useSound'];
|
||||
const baseKeys = ['type', 'x', 'y', 'carrierId', 'version', 'createdBy', 'createdAt', 'updatedAt', 'capabilities', 'emitSound'];
|
||||
for (const key of baseKeys) {
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
@@ -617,8 +630,10 @@ function getItemPropertyValue(item: WorldItem, key: string): string {
|
||||
if (key === 'createdAt') return formatTimestampMs(item.createdAt);
|
||||
if (key === 'updatedAt') return formatTimestampMs(item.updatedAt);
|
||||
if (key === 'capabilities') return item.capabilities.join(', ') || 'none';
|
||||
if (key === 'useSound') return item.useSound ?? 'none';
|
||||
if (key === 'emitSound') return item.emitSound ?? 'none';
|
||||
if (key === 'enabled') return item.params.enabled === false ? 'off' : 'on';
|
||||
if (key === 'timeZone') return String(item.params.timeZone ?? CLOCK_TIME_ZONE_OPTIONS[0]);
|
||||
if (key === 'use24Hour') return item.params.use24Hour === true ? 'on' : 'off';
|
||||
if (key === 'channel') return normalizeRadioChannel(item.params.channel);
|
||||
if (key === 'effect') return normalizeRadioEffect(item.params.effect);
|
||||
if (key === 'effectValue') return String(normalizeRadioEffectValue(item.params.effectValue));
|
||||
@@ -1028,7 +1043,7 @@ async function onMessage(message: IncomingMessage): Promise<void> {
|
||||
if (message.action === 'use') {
|
||||
pushChatMessage(message.message);
|
||||
const item = message.itemId ? state.items.get(message.itemId) : null;
|
||||
if (!item?.useSound && item) {
|
||||
if (!item?.emitSound && item) {
|
||||
audio.sfxLocate({ x: item.x - state.player.x, y: item.y - state.player.y });
|
||||
}
|
||||
} else if (message.action !== 'update') {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
||||
|
||||
export const itemSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(['radio_station', 'dice', 'wheel']),
|
||||
type: z.enum(['radio_station', 'dice', 'wheel', 'clock']),
|
||||
title: z.string(),
|
||||
x: z.number().int(),
|
||||
y: z.number().int(),
|
||||
@@ -11,7 +11,7 @@ export const itemSchema = z.object({
|
||||
updatedAt: z.number().int(),
|
||||
version: z.number().int(),
|
||||
capabilities: z.array(z.string()),
|
||||
useSound: z.string().optional(),
|
||||
emitSound: z.string().optional(),
|
||||
params: z.record(z.string(), z.unknown()),
|
||||
carrierId: z.string().nullable().optional(),
|
||||
});
|
||||
@@ -129,7 +129,7 @@ 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' }
|
||||
| { type: 'item_add'; itemType: 'radio_station' | 'dice' | 'wheel' | 'clock' }
|
||||
| { type: 'item_pickup'; itemId: string }
|
||||
| { type: 'item_drop'; itemId: string; x: number; y: number }
|
||||
| { type: 'item_delete'; itemId: string }
|
||||
|
||||
@@ -76,11 +76,22 @@ export class CanvasRenderer {
|
||||
private drawItem(item: WorldItem): void {
|
||||
const drawX = item.x * this.squarePixelSize;
|
||||
const drawY = this.canvas.height - (item.y * this.squarePixelSize) - this.squarePixelSize;
|
||||
this.ctx.fillStyle = item.type === 'radio_station' ? '#fbbf24' : item.type === 'wheel' ? '#f97316' : '#60a5fa';
|
||||
this.ctx.fillStyle =
|
||||
item.type === 'radio_station'
|
||||
? '#fbbf24'
|
||||
: item.type === 'wheel'
|
||||
? '#f97316'
|
||||
: item.type === 'clock'
|
||||
? '#86efac'
|
||||
: '#60a5fa';
|
||||
this.ctx.fillRect(drawX, drawY, this.squarePixelSize, this.squarePixelSize);
|
||||
this.ctx.fillStyle = '#111827';
|
||||
this.ctx.font = 'bold 12px Courier New';
|
||||
this.ctx.textAlign = 'center';
|
||||
this.ctx.fillText(item.type === 'radio_station' ? 'R' : item.type === 'wheel' ? 'W' : 'D', drawX + this.squarePixelSize / 2, drawY + 13);
|
||||
this.ctx.fillText(
|
||||
item.type === 'radio_station' ? 'R' : item.type === 'wheel' ? 'W' : item.type === 'clock' ? 'C' : 'D',
|
||||
drawX + this.squarePixelSize / 2,
|
||||
drawY + 13,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ export const GRID_SIZE = 41;
|
||||
export const HEARING_RADIUS = 15;
|
||||
export const MOVE_COOLDOWN_MS = 200;
|
||||
|
||||
export type ItemType = 'radio_station' | 'dice' | 'wheel';
|
||||
export type ItemType = 'radio_station' | 'dice' | 'wheel' | 'clock';
|
||||
|
||||
export type WorldItem = {
|
||||
id: string;
|
||||
@@ -15,7 +15,7 @@ export type WorldItem = {
|
||||
updatedAt: number;
|
||||
version: number;
|
||||
capabilities: string[];
|
||||
useSound?: string;
|
||||
emitSound?: string;
|
||||
params: Record<string, unknown>;
|
||||
carrierId?: string | null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user