fix(v2): player zoom + dot blink + version + sidebar links + dungeon radar + rare emojis
1. Player click → zoom: clicking a player in sidebar or on map dot zooms to their position at 3x zoom, centered on screen. Click again to deselect. Uses direct DOM transform (no React state). 2. Selected dot blink: selected player dot gets 10px size + blink animation (0.6s step-end infinite) matching v1's .dot.highlight. 3. Version display: fetches /api-version on mount, shows "vX.Y.Z" in small text positioned just right of sidebar (fixed, top: 6px). 4. Missing sidebar buttons: added Combat Stats (⚔️) alongside existing Issues (📋) and Vital Sharing (🤝) in SidebarWindowButtons. 5. Rare notification: added 🎆 emojis to "LEGENDARY RARE!" title matching v1's notification text. 6. Dungeon map in radar — verbatim port from v1 lines 3596-3930: - loadDungeonTiles(): fetches dungeon_tiles.json, processes each tile image (color remap: UB source colors → display colors, white → transparent, black → semi-transparent) - cellRotation(): maps rotation values to radians (v1's exact logic) - Dungeon rendering: sorts z_levels (current floor on top at 85% opacity, others at 12%), draws each cell with per-cell rotation, uses processed tile canvases or colored rectangle fallback - Requests dungeon map via WebSocket when radar detects dungeon - Caches dungeon maps on window.__dungeonMapCache - Overworld map: fixed srcSize calculation to use range * pixPerCoord Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
76baec33e7
commit
a59296867d
13 changed files with 219 additions and 71 deletions
|
|
@ -54,13 +54,27 @@ export function useLiveData(): DashboardState {
|
|||
} else if (msg.type === 'rare') {
|
||||
const r = msg as RareMessage;
|
||||
setRecentRares(prev => [r, ...prev].slice(0, 50));
|
||||
} else if (msg.type === 'dungeon_map') {
|
||||
// Cache dungeon map data for radar rendering (stored on window for canvas access)
|
||||
const dm = msg as unknown as { landblock: string; z_levels: any[] };
|
||||
if (dm.landblock) {
|
||||
if (!(window as any).__dungeonMapCache) (window as any).__dungeonMapCache = {};
|
||||
(window as any).__dungeonMapCache[dm.landblock] = dm;
|
||||
}
|
||||
} else if (msg.type === 'nearby_objects') {
|
||||
const no = msg as unknown as { character_name: string; objects: any[] };
|
||||
const no = msg as unknown as { character_name: string; objects: any[]; is_dungeon?: boolean; landblock?: number };
|
||||
setNearbyObjects(prev => {
|
||||
const next = new Map(prev);
|
||||
next.set(no.character_name, no);
|
||||
return next;
|
||||
});
|
||||
// Request dungeon map if in dungeon and not cached
|
||||
if (no.is_dungeon && no.landblock && !(window as any).__dungeonMapCache?.[no.landblock]) {
|
||||
const ws = socketRef.current;
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'request_dungeon_map', landblock: no.landblock }));
|
||||
}
|
||||
}
|
||||
} else if (msg.type === 'chat') {
|
||||
const m = msg as unknown as { character_name: string; text: string; color?: number; timestamp: string };
|
||||
setChatMessages(prev => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue