// Left team panel (research 06 §11.1, research 09 §3.1): one row per seat — avatar (band icon once picked), name, LP // tower, status glyph (… acting / ✓ ready / ⌛ deciding / ⚔ combat / door left / ✕ dead), AI badge, "you" marker, the // field being watched (eye badge), and emote bubbles. // Observing (client-side combat, `observe` prop — the official flow): tapping a teammate's avatar expands a mint // "前往查看" button under the row (when that teammate can be observed now; otherwise the reason is toasted through // onWatch); while observing, the own row shows a "返回战场" button. Without `observe` (server-run combat) a click // watches that player's field at once. import { useEffect, useState } from '../../vendor/hooks.module.js'; import { html, Icon, Tooltip } from './components.js'; import { PlayerAvatar, LpTower, GIcon, LocalSprite } from './gameComponents.js'; import { EmoteBubble } from './emotes.js'; import { STATUS_META, sortedPlayers } from './gameLogic.js'; import { localAsset } from '../data.js'; const cx = (...p) => p.flat().filter(Boolean).join(' '); /** Official status glyphs (ui/battle) per m.public players[].status; CSS glyph fallback. */ const STATUS_SPRITE = { ready: 'icon_ready', deciding: 'icon_waiting', done: 'icon_complete', dead: 'icon_dead' }; /** * @param {{ pub:any, myId:string, watching:string|null, bubbles: Map, onWatch:(p:any)=>void, * compact?: boolean, teamLp?: number|null, * observe?: null | { canObserve: (p:any) => { fieldId?: string, reason?: string|null, back?: boolean }, observing: boolean, onBack: () => void } }} props */ export function TeamPanel({ pub, myId, watching, bubbles, onWatch, compact = false, observe = null }) { const [openPid, setOpenPid] = useState(null); const phaseKey = `${pub?.phase}:${pub?.round}`; useEffect(() => { setOpenPid(null); }, [phaseKey, watching, observe?.observing]); const players = sortedPlayers(pub); if (!players.length) return null; const click = (p, self) => { if (!observe) { onWatch(p); return; } if (self) { if (observe.observing) observe.onBack(); setOpenPid(null); return; } const t = observe.canObserve(p) || {}; if (!t.fieldId) { setOpenPid(null); onWatch(p); return; } // the game screen toasts the reason setOpenPid((cur) => (cur === p.playerId ? null : p.playerId)); }; return html``; }