diff --git a/src/pages/agent-office/canvas/OfficeRenderer.js b/src/pages/agent-office/canvas/OfficeRenderer.js index 7cc0370..c63197e 100644 --- a/src/pages/agent-office/canvas/OfficeRenderer.js +++ b/src/pages/agent-office/canvas/OfficeRenderer.js @@ -4,86 +4,10 @@ import mapData from '../assets/office-map.json'; import { TileMap } from './TileMap.js'; import { FurnitureRenderer } from './FurnitureRenderer.js'; import { Pathfinder } from './Pathfinder.js'; +import { AgentSprite } from './AgentSprite.js'; import { OverlayRenderer } from './OverlayRenderer.js'; import { getTheme } from './themes.js'; -// AgentSprite is implemented in Phase 2. -// Until then, use a minimal stub that satisfies the interface expected by OfficeRenderer. -let AgentSprite; -try { - // Dynamic import fallback — wrapping in try/catch for bundler compatibility - AgentSprite = null; // will be set below -} catch (_) {} - -/** Phase 2 stub for AgentSprite */ -class _AgentSpriteStub { - constructor(id, meta, col, row /*, pathfinder */) { - this.id = id; - this.meta = meta; - this.x = col; - this.y = row; - this.deskCol = col; - this.deskRow = row; - this.state = 'idle'; - this.detail = ''; - this.notificationCount = 0; - this._animTimer = 0; - this._bobOffset = 0; - } - - onStateChange(state, detail /*, waypoints */) { - this.state = state; - this.detail = detail || ''; - } - - update(dt) { - this._animTimer += dt; - this._bobOffset = Math.sin(this._animTimer * 2) * 2; - } - - draw(ctx, scale, offsetX, offsetY, tileSize) { - const ts = tileSize * scale; - const cx = this.x * ts + offsetX + ts / 2; - const cy = this.y * ts + offsetY + ts / 2 + this._bobOffset * scale; - const r = ts * 0.35; - - // Simple circle avatar - ctx.save(); - ctx.beginPath(); - ctx.arc(cx, cy, r, 0, Math.PI * 2); - ctx.fillStyle = this.meta.color || '#8b5cf6'; - ctx.fill(); - ctx.strokeStyle = 'rgba(255,255,255,0.4)'; - ctx.lineWidth = scale; - ctx.stroke(); - - // Emoji label - ctx.font = `${Math.max(10, ts * 0.4)}px serif`; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(this.meta.emoji || '?', cx, cy); - - // State indicator dot - const stateColors = { - idle: '#6b7280', - working: '#22c55e', - waiting: '#f59e0b', - reporting: '#3b82f6', - break: '#f97316' - }; - const dotColor = stateColors[this.state] || '#6b7280'; - ctx.beginPath(); - ctx.arc(cx + r * 0.7, cy - r * 0.7, r * 0.25, 0, Math.PI * 2); - ctx.fillStyle = dotColor; - ctx.fill(); - - ctx.restore(); - } -} - -// Use the stub until Phase 2 provides a real AgentSprite -AgentSprite = _AgentSpriteStub; - const AGENT_META = { stock: { displayName: '주식 트레이더', emoji: '📈', color: '#4488cc' }, music: { displayName: '음악 프로듀서', emoji: '🎵', color: '#44aa88' }, @@ -246,12 +170,7 @@ export class OfficeRenderer { updateAgentState(agentId, state, detail) { const sprite = this.agents.get(agentId); if (!sprite) return; - if (typeof sprite.onStateChange === 'function') { - sprite.onStateChange(state, detail, mapData.waypoints); - } else { - sprite.state = state; - sprite.detail = detail || ''; - } + sprite.onStateChange(state, detail, mapData.waypoints); } /** 에이전트 알림 배지 설정 */ @@ -314,9 +233,7 @@ export class OfficeRenderer { _update(dt) { for (const sprite of this.agents.values()) { - if (typeof sprite.update === 'function') { - sprite.update(dt); - } + sprite.update(dt); } } @@ -352,12 +269,10 @@ export class OfficeRenderer { // 에이전트 for (const sprite of this.agents.values()) { - if (typeof sprite.draw === 'function') { - renderables.push({ - zY: sprite.y, - draw: (ctx2) => sprite.draw(ctx2, this.zoom, this.panX, this.panY, mapData.tileSize) - }); - } + renderables.push({ + zY: sprite.y, + draw: (ctx2) => sprite.draw(ctx2, this.zoom, this.panX, this.panY, mapData.tileSize) + }); } // Y좌표 정렬 @@ -368,9 +283,7 @@ export class OfficeRenderer { // 3. 오버레이 (항상 최상위) for (const sprite of this.agents.values()) { - if (this.overlayRenderer && typeof this.overlayRenderer.draw === 'function') { - this.overlayRenderer.draw(ctx, sprite, this.theme, this.zoom, this.panX, this.panY, mapData.tileSize); - } + this.overlayRenderer.draw(ctx, sprite, this.theme, this.zoom, this.panX, this.panY, mapData.tileSize); } }