refactor(agent-office): wire real AgentSprite import, remove Phase 1 stub
This commit is contained in:
@@ -4,86 +4,10 @@ import mapData from '../assets/office-map.json';
|
|||||||
import { TileMap } from './TileMap.js';
|
import { TileMap } from './TileMap.js';
|
||||||
import { FurnitureRenderer } from './FurnitureRenderer.js';
|
import { FurnitureRenderer } from './FurnitureRenderer.js';
|
||||||
import { Pathfinder } from './Pathfinder.js';
|
import { Pathfinder } from './Pathfinder.js';
|
||||||
|
import { AgentSprite } from './AgentSprite.js';
|
||||||
import { OverlayRenderer } from './OverlayRenderer.js';
|
import { OverlayRenderer } from './OverlayRenderer.js';
|
||||||
import { getTheme } from './themes.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 = {
|
const AGENT_META = {
|
||||||
stock: { displayName: '주식 트레이더', emoji: '📈', color: '#4488cc' },
|
stock: { displayName: '주식 트레이더', emoji: '📈', color: '#4488cc' },
|
||||||
music: { displayName: '음악 프로듀서', emoji: '🎵', color: '#44aa88' },
|
music: { displayName: '음악 프로듀서', emoji: '🎵', color: '#44aa88' },
|
||||||
@@ -246,12 +170,7 @@ export class OfficeRenderer {
|
|||||||
updateAgentState(agentId, state, detail) {
|
updateAgentState(agentId, state, detail) {
|
||||||
const sprite = this.agents.get(agentId);
|
const sprite = this.agents.get(agentId);
|
||||||
if (!sprite) return;
|
if (!sprite) return;
|
||||||
if (typeof sprite.onStateChange === 'function') {
|
sprite.onStateChange(state, detail, mapData.waypoints);
|
||||||
sprite.onStateChange(state, detail, mapData.waypoints);
|
|
||||||
} else {
|
|
||||||
sprite.state = state;
|
|
||||||
sprite.detail = detail || '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 에이전트 알림 배지 설정 */
|
/** 에이전트 알림 배지 설정 */
|
||||||
@@ -314,9 +233,7 @@ export class OfficeRenderer {
|
|||||||
|
|
||||||
_update(dt) {
|
_update(dt) {
|
||||||
for (const sprite of this.agents.values()) {
|
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()) {
|
for (const sprite of this.agents.values()) {
|
||||||
if (typeof sprite.draw === 'function') {
|
renderables.push({
|
||||||
renderables.push({
|
zY: sprite.y,
|
||||||
zY: sprite.y,
|
draw: (ctx2) => sprite.draw(ctx2, this.zoom, this.panX, this.panY, mapData.tileSize)
|
||||||
draw: (ctx2) => sprite.draw(ctx2, this.zoom, this.panX, this.panY, mapData.tileSize)
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y좌표 정렬
|
// Y좌표 정렬
|
||||||
@@ -368,9 +283,7 @@ export class OfficeRenderer {
|
|||||||
|
|
||||||
// 3. 오버레이 (항상 최상위)
|
// 3. 오버레이 (항상 최상위)
|
||||||
for (const sprite of this.agents.values()) {
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user