feat(v2): 13 improvements — functional, visual, UX, backend
Functional:
1. Chat: "▼ New messages below" indicator when scrolled up, click to jump
2. Combat stats: "Clear Session" button (red, with confirm dialog)
3. Inventory: live updates via inventory_delta WS (re-fetches on change)
4. Inventory: real mana time from equipment_cantrip_state WS (live
countdown with state dot: green=active, red=inactive, yellow=unknown)
Visual:
5. Thin separator line between tool links and sort buttons
6. Selected player row highlighted with darker background (#2a3344)
7. Scroll-to-top button (▲) appears when scrolled past 200px in player list
UX:
8. Double-click player dot on map opens their chat window
9. Right-click player dot shows context menu (Chat/Stats/Inv/Char/Combat/Radar)
10. Ctrl+D keyboard shortcut toggles between map and dashboard views
11. Sound notification on rare drops (880Hz sine beep via Web Audio API)
Backend:
12. Deep-merge lifetime offense/defense per element — accumulates
total_attacks, failed_attacks, crits, damage per AttackType×Element
instead of overwriting with latest session data
13. Startup cleanup: deletes stale combat_stats records from before
the lifetime fix (pre-2026-04-14T09:00Z)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0b64c6ccff
commit
0112c59514
41 changed files with 404 additions and 112 deletions
|
|
@ -14,6 +14,8 @@ export interface DashboardState {
|
|||
recentRares: RareMessage[];
|
||||
chatMessages: Map<string, Array<{ text: string; color?: number; timestamp: string }>>;
|
||||
nearbyObjects: Map<string, any>;
|
||||
inventoryVersion: number;
|
||||
equipmentCantrips: Map<string, any>;
|
||||
socketRef: React.RefObject<WebSocket | null>;
|
||||
}
|
||||
|
||||
|
|
@ -23,10 +25,11 @@ export function useLiveData(): DashboardState {
|
|||
const [totalRares, setTotalRares] = useState(0);
|
||||
const [totalKills, setTotalKills] = useState(0);
|
||||
const [recentRares, setRecentRares] = useState<RareMessage[]>([]);
|
||||
// Chat messages stored in ref to avoid re-renders on every message.
|
||||
// A counter state triggers re-render only when needed.
|
||||
const chatMessagesRef = useRef(new Map<string, Array<{ text: string; color?: number; timestamp: string }>>());
|
||||
const [chatVersion, setChatVersion] = useState(0);
|
||||
const [inventoryVersion, setInventoryVersion] = useState(0);
|
||||
const equipmentCantripRef = useRef(new Map<string, any>());
|
||||
const [equipCantripVersion, setEquipCantripVersion] = useState(0);
|
||||
const [nearbyObjects, setNearbyObjects] = useState<Map<string, any>>(new Map());
|
||||
const charsRef = useRef(characters);
|
||||
charsRef.current = characters;
|
||||
|
|
@ -57,6 +60,14 @@ 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 === 'inventory_delta') {
|
||||
const d = msg as unknown as { character_name: string };
|
||||
// Bump inventory version so open inventory windows can re-fetch
|
||||
setInventoryVersion(v => v + 1);
|
||||
} else if (msg.type === 'equipment_cantrip_state') {
|
||||
const ecs = msg as unknown as { character_name: string; items: any[]; timestamp: string };
|
||||
equipmentCantripRef.current.set(ecs.character_name, ecs);
|
||||
setEquipCantripVersion(v => v + 1);
|
||||
} 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[] };
|
||||
|
|
@ -169,5 +180,8 @@ export function useLiveData(): DashboardState {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const chatMessages = useMemo(() => chatMessagesRef.current, [chatVersion]);
|
||||
|
||||
return { characters, serverHealth, totalRares, totalKills, recentRares, chatMessages, nearbyObjects, socketRef };
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const equipmentCantrips = useMemo(() => equipmentCantripRef.current, [equipCantripVersion]);
|
||||
|
||||
return { characters, serverHealth, totalRares, totalKills, recentRares, chatMessages, nearbyObjects, inventoryVersion, equipmentCantrips, socketRef };
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue