Compare commits
3 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f632f816a4 | ||
| 62f9afc011 | |||
| e460c758fd |
4 changed files with 170 additions and 48 deletions
|
|
@ -132,22 +132,24 @@ export default function AgentSprite({ agent, hovered, onHover }) {
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
{/* Agent name label */}
|
{/* Agent name label — hover only (per design-critique-1 Issue 3, P1) */}
|
||||||
<Text
|
{hovered && (
|
||||||
position={
|
<Text
|
||||||
targetPos
|
position={
|
||||||
? [targetPos[0], 0.7, targetPos[2]]
|
targetPos
|
||||||
: [0, 0.7, 0]
|
? [targetPos[0], 0.7, targetPos[2]]
|
||||||
}
|
: [0, 0.7, 0]
|
||||||
fontSize={0.15}
|
}
|
||||||
color={agent.activity === 'offline' ? '#888888' : '#ffffff'}
|
fontSize={0.15}
|
||||||
anchorX="center"
|
color={agent.activity === 'offline' ? '#888888' : '#ffffff'}
|
||||||
anchorY="middle"
|
anchorX="center"
|
||||||
outlineWidth={0.01}
|
anchorY="middle"
|
||||||
outlineColor="#000000"
|
outlineWidth={0.01}
|
||||||
>
|
outlineColor="#000000"
|
||||||
{agent.name}
|
>
|
||||||
</Text>
|
{agent.name}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Hover tooltip */}
|
{/* Hover tooltip */}
|
||||||
{hovered && (
|
{hovered && (
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { useRef, useCallback } from 'react';
|
||||||
import { useStore } from './store';
|
import { useStore } from './store';
|
||||||
import { rooms, agentDesks } from './officeMap';
|
import { rooms, agentDesks } from './officeMap';
|
||||||
import { TILESET_URL, TILE_INDEX, ROOM_TILE_STYLES } from './src/tileLoader';
|
import { TILESET_URL, TILE_INDEX, ROOM_TILE_STYLES } from './src/tileLoader';
|
||||||
|
import { getAgentSpriteUrl } from './src/agentSprites';
|
||||||
|
|
||||||
const TILE = 32;
|
const TILE = 32;
|
||||||
const SCALE = 2; // 2x = 64px per tile
|
const SCALE = 2; // 2x = 64px per tile
|
||||||
|
|
@ -53,13 +54,16 @@ function TileSprite({ tileCol, tileRow, x, y }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render a single furniture item: an <image> element clipped to one tile
|
// Render a single furniture item: an <image> element clipped to one tile
|
||||||
function FurnitureItem({ tileName, tx, ty }) {
|
function FurnitureItem({ tileName, tx, ty, w, h }) {
|
||||||
const t = TILE_INDEX[tileName];
|
const t = TILE_INDEX[tileName];
|
||||||
if (!t) return null;
|
if (!t) return null;
|
||||||
|
const tw = w || 1;
|
||||||
|
const th = h || 1;
|
||||||
|
const clipId = `clip-f-${tx}-${ty}-${tw}-${th}`;
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<clipPath id={`clip-f-${tx}-${ty}`}>
|
<clipPath id={clipId}>
|
||||||
<rect x={px(tx)} y={py(ty)} width={px(1)} height={py(1)} />
|
<rect x={px(tx)} y={py(ty)} width={px(tw)} height={px(th)} />
|
||||||
</clipPath>
|
</clipPath>
|
||||||
<image
|
<image
|
||||||
href={TILESET_URL}
|
href={TILESET_URL}
|
||||||
|
|
@ -68,7 +72,7 @@ function FurnitureItem({ tileName, tx, ty }) {
|
||||||
width={TILESET_W * SCALE}
|
width={TILESET_W * SCALE}
|
||||||
height={TILESET_H * SCALE}
|
height={TILESET_H * SCALE}
|
||||||
style={{ imageRendering: 'pixelated' }}
|
style={{ imageRendering: 'pixelated' }}
|
||||||
clipPath={`url(#clip-f-${tx}-${ty})`}
|
clipPath={`url(#${clipId})`}
|
||||||
/>
|
/>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
|
@ -243,7 +247,7 @@ export default function FarmCanvas() {
|
||||||
|
|
||||||
{/* Furniture from tile set */}
|
{/* Furniture from tile set */}
|
||||||
{tileStyle.furniture.map((f, fi) => (
|
{tileStyle.furniture.map((f, fi) => (
|
||||||
<FurnitureItem key={`furn-${fi}`} tileName={f.tile} tx={room.x + f.tx} ty={room.y + f.ty} />
|
<FurnitureItem key={`furn-${fi}`} tileName={f.tile} tx={room.x + f.tx} ty={room.y + f.ty} w={f.w} h={f.h} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Doorways */}
|
{/* Doorways */}
|
||||||
|
|
@ -312,7 +316,7 @@ export default function FarmCanvas() {
|
||||||
}} />
|
}} />
|
||||||
{/* Sprite at 48x48, centered in 56x56 container */}
|
{/* Sprite at 48x48, centered in 56x56 container */}
|
||||||
<img
|
<img
|
||||||
src={`/office/sprites/agent-${agent.id}.png`}
|
src={getAgentSpriteUrl(agent.id)}
|
||||||
alt={agent.name}
|
alt={agent.name}
|
||||||
style={{
|
style={{
|
||||||
width: 48,
|
width: 48,
|
||||||
|
|
|
||||||
30
src/frontend/src/agentSprites.js
Normal file
30
src/frontend/src/agentSprites.js
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// agentSprites.js — Per-agent character sprite assignments
|
||||||
|
// Uses LimeZu character sprites from public/agents/ directory
|
||||||
|
// 4 base characters (Amelia, Adam, Alex, Bob) with idle/run/sit variations
|
||||||
|
// Each agent gets a unique character for distinct silhouettes.
|
||||||
|
|
||||||
|
// Character base URL
|
||||||
|
export const SPRITES_BASE = '/agents/';
|
||||||
|
|
||||||
|
// Per-agent character assignments
|
||||||
|
// character: which NPC base to use
|
||||||
|
// pose: idle, run, or sit (defaults to idle)
|
||||||
|
// Each agent gets a DIFFERENT character so they're distinguishable by silhouette
|
||||||
|
export const AGENT_SPRITES = {
|
||||||
|
cos: { character: 'Alex', pose: 'idle' },
|
||||||
|
engineering: { character: 'Adam', pose: 'idle' },
|
||||||
|
'senior-eng': { character: 'Bob', pose: 'idle' },
|
||||||
|
trust: { character: 'Amelia', pose: 'idle' },
|
||||||
|
market: { character: 'Adam', pose: 'run' },
|
||||||
|
health: { character: 'Amelia', pose: 'sit' },
|
||||||
|
content: { character: 'Alex', pose: 'sit' },
|
||||||
|
presentations: { character: 'Bob', pose: 'run' },
|
||||||
|
vault: { character: 'Bob', pose: 'sit' },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the sprite URL for an agent
|
||||||
|
export function getAgentSpriteUrl(agentId) {
|
||||||
|
const sprite = AGENT_SPRITES[agentId];
|
||||||
|
if (!sprite) return `${SPRITES_BASE}Amelia_idle.png`;
|
||||||
|
return `${SPRITES_BASE}${sprite.character}_${sprite.pose}.png`;
|
||||||
|
}
|
||||||
|
|
@ -38,68 +38,154 @@ export const TILE_INDEX = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Per-room tile style assignments
|
// Per-room tile style assignments
|
||||||
// furniture: array of { tile: TILE_INDEX key, tx: room-relative x, ty: room-relative y }
|
// furniture: array of { tile: TILE_INDEX key, tx: room-relative x, ty: room-relative y, w?: width in tiles, h?: height in tiles }
|
||||||
|
// Room dimensions (from officeMap.js): briefing 7x5, commons 7x5, warroom 7x5, engineering 7x5, quiet 7x5, ops 7x5
|
||||||
|
// All rooms are 7 wide x 5 tall in tile coordinates
|
||||||
|
|
||||||
export const ROOM_TILE_STYLES = {
|
export const ROOM_TILE_STYLES = {
|
||||||
briefing: {
|
briefing: {
|
||||||
floor: 'floor_light_wood',
|
floor: 'floor_light_wood',
|
||||||
wall: 'wall_inner',
|
wall: 'wall_inner',
|
||||||
|
// Conference room layout: central table, chairs around it, AV setup at front
|
||||||
furniture: [
|
furniture: [
|
||||||
{ tile: 'conf_table', tx: 2.5, ty: 2 },
|
// Central conference table (spans 3 tiles wide)
|
||||||
{ tile: 'chair_down', tx: 1.5, ty: 2.5 },
|
{ tile: 'conf_table', tx: 2, ty: 1.5, w: 3, h: 1 },
|
||||||
{ tile: 'chair_up', tx: 5.7, ty: 2.5 },
|
{ tile: 'conf_table', tx: 2, ty: 2.5, w: 3, h: 1 },
|
||||||
|
// Chairs around the table
|
||||||
|
{ tile: 'chair_down', tx: 1.5, ty: 1.5 }, // left side
|
||||||
|
{ tile: 'chair_up', tx: 1.5, ty: 2.5 },
|
||||||
|
{ tile: 'chair_down', tx: 5.5, ty: 1.5 }, // right side
|
||||||
|
{ tile: 'chair_up', tx: 5.5, ty: 2.5 },
|
||||||
|
// Monitor/screen at front
|
||||||
|
{ tile: 'monitor', tx: 3.5, ty: 0.5 },
|
||||||
|
// Plants for ambiance
|
||||||
{ tile: 'plant', tx: 0.5, ty: 0.5 },
|
{ tile: 'plant', tx: 0.5, ty: 0.5 },
|
||||||
|
{ tile: 'plant', tx: 6.5, ty: 0.5 },
|
||||||
|
// Whiteboard
|
||||||
|
{ tile: 'whiteboard', tx: 6.5, ty: 3.5 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
commons: {
|
commons: {
|
||||||
floor: 'floor_modern',
|
floor: 'floor_modern',
|
||||||
wall: 'wall_inner',
|
wall: 'wall_inner',
|
||||||
|
// Lounge layout: seating area, coffee station, bulletin board
|
||||||
furniture: [
|
furniture: [
|
||||||
{ tile: 'sofa', tx: 1, ty: 1.5 },
|
// Sofa cluster (two facing sofas)
|
||||||
{ tile: 'sofa', tx: 1, ty: 2.5 },
|
{ tile: 'sofa', tx: 1, ty: 1.5 },
|
||||||
{ tile: 'desk', tx: 4, ty: 2.5 },
|
{ tile: 'sofa', tx: 2.5, ty: 1.5 },
|
||||||
{ tile: 'plant', tx: 0.5, ty: 0.5 },
|
// Coffee table between sofas
|
||||||
{ tile: 'whiteboard', tx: 5, ty: 0.5 },
|
{ tile: 'conf_table', tx: 1.5, ty: 2.5, w: 2, h: 1 },
|
||||||
|
// Standing desk / counter area
|
||||||
|
{ tile: 'desk', tx: 5, ty: 1.5, w: 2, h: 1 },
|
||||||
|
{ tile: 'desk', tx: 5, ty: 2.5, w: 2, h: 1 },
|
||||||
|
// Decoration
|
||||||
|
{ tile: 'plant', tx: 0.5, ty: 3.5 },
|
||||||
|
{ tile: 'whiteboard', tx: 6.5, ty: 0.5 },
|
||||||
|
// Reading nook
|
||||||
|
{ tile: 'bookshelf', tx: 6.5, ty: 3.5 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
warroom: {
|
warroom: {
|
||||||
floor: 'floor_dark_wood',
|
floor: 'floor_dark_wood',
|
||||||
wall: 'wall_inner',
|
wall: 'wall_inner',
|
||||||
|
// Command center: large central table, wall screens, command chairs
|
||||||
furniture: [
|
furniture: [
|
||||||
{ tile: 'conf_table', tx: 2.5, ty: 2 },
|
// Central war table (large, spans 4 tiles)
|
||||||
{ tile: 'chair_down', tx: 1.5, ty: 3 },
|
{ tile: 'conf_table', tx: 1.5, ty: 1.5, w: 4, h: 1 },
|
||||||
{ tile: 'chair_up', tx: 5.5, ty: 3 },
|
{ tile: 'conf_table', tx: 1.5, ty: 2.5, w: 4, h: 1 },
|
||||||
{ tile: 'monitor', tx: 0.5, ty: 1.5 },
|
// Command chairs
|
||||||
|
{ tile: 'chair_down', tx: 1, ty: 1.5 },
|
||||||
|
{ tile: 'chair_down', tx: 5.5, ty: 1.5 },
|
||||||
|
{ tile: 'chair_up', tx: 1, ty: 2.5 },
|
||||||
|
{ tile: 'chair_up', tx: 5.5, ty: 2.5 },
|
||||||
|
// Wall-mounted monitors
|
||||||
|
{ tile: 'monitor', tx: 0.5, ty: 0.5 },
|
||||||
|
{ tile: 'monitor', tx: 3.5, ty: 0.5 },
|
||||||
|
{ tile: 'monitor', tx: 6.5, ty: 0.5 },
|
||||||
|
// Equipment rack
|
||||||
|
{ tile: 'cabinet', tx: 6.5, ty: 3.5 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
engineering: {
|
engineering: {
|
||||||
floor: 'floor_tile',
|
floor: 'floor_tile',
|
||||||
wall: 'wall_inner',
|
wall: 'wall_inner',
|
||||||
|
// Dev workspace: rows of desks with monitors, gear, books
|
||||||
furniture: [
|
furniture: [
|
||||||
{ tile: 'computer_desk', tx: 2.5, ty: 2.5 },
|
// Workstation row 1
|
||||||
{ tile: 'monitor', tx: 3.5, ty: 2 },
|
{ tile: 'computer_desk', tx: 1.5, ty: 1.5 },
|
||||||
{ tile: 'chair_down', tx: 3, ty: 3.5 },
|
{ tile: 'computer_desk', tx: 3.5, ty: 1.5 },
|
||||||
{ tile: 'bookshelf', tx: 0.5, ty: 0.5 },
|
{ tile: 'computer_desk', tx: 5.5, ty: 1.5 },
|
||||||
|
// Monitors above desks
|
||||||
|
{ tile: 'monitor', tx: 1.5, ty: 0.5 },
|
||||||
|
{ tile: 'monitor', tx: 3.5, ty: 0.5 },
|
||||||
|
{ tile: 'monitor', tx: 5.5, ty: 0.5 },
|
||||||
|
// Workstation row 2
|
||||||
|
{ tile: 'computer_desk', tx: 1.5, ty: 3 },
|
||||||
|
{ tile: 'computer_desk', tx: 3.5, ty: 3 },
|
||||||
|
{ tile: 'computer_desk', tx: 5.5, ty: 3 },
|
||||||
|
// Chairs
|
||||||
|
{ tile: 'chair_down', tx: 2, ty: 2.5 },
|
||||||
|
{ tile: 'chair_down', tx: 4, ty: 2.5 },
|
||||||
|
{ tile: 'chair_down', tx: 6, ty: 2.5 },
|
||||||
|
{ tile: 'chair_up', tx: 2, ty: 3.5 },
|
||||||
|
{ tile: 'chair_up', tx: 4, ty: 3.5 },
|
||||||
|
{ tile: 'chair_up', tx: 6, ty: 3.5 },
|
||||||
|
// Reference books and gear
|
||||||
|
{ tile: 'bookshelf', tx: 0.5, ty: 0.5 },
|
||||||
|
{ tile: 'bookshelf', tx: 0.5, ty: 3.5 },
|
||||||
|
{ tile: 'plant', tx: 6.5, ty: 4.5 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
quiet: {
|
quiet: {
|
||||||
floor: 'floor_rug',
|
floor: 'floor_rug',
|
||||||
wall: 'wall_inner',
|
wall: 'wall_inner',
|
||||||
|
// Reading/relaxation zone: comfortable seating, books, soft lighting
|
||||||
furniture: [
|
furniture: [
|
||||||
{ tile: 'sofa', tx: 2.5, ty: 2.5 },
|
// Central sofa / reading area
|
||||||
{ tile: 'bookshelf', tx: 0.5, ty: 0.5 },
|
{ tile: 'sofa', tx: 2.5, ty: 1.5, w: 2, h: 1 },
|
||||||
{ tile: 'plant', tx: 5, ty: 0.5 },
|
// Side table / reading desk
|
||||||
|
{ tile: 'desk', tx: 2, ty: 2.5 },
|
||||||
|
{ tile: 'chair_up', tx: 2, ty: 3.5 },
|
||||||
|
// Bookshelves lining walls
|
||||||
|
{ tile: 'bookshelf', tx: 0.5, ty: 0.5 },
|
||||||
|
{ tile: 'bookshelf', tx: 6.5, ty: 0.5 },
|
||||||
|
{ tile: 'bookshelf', tx: 0.5, ty: 2.5 },
|
||||||
|
{ tile: 'bookshelf', tx: 6.5, ty: 2.5 },
|
||||||
|
// Plants and ambiance
|
||||||
|
{ tile: 'plant', tx: 4.5, ty: 0.5 },
|
||||||
|
{ tile: 'plant', tx: 0.5, ty: 4.5 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
ops: {
|
ops: {
|
||||||
floor: 'floor_tile',
|
floor: 'floor_tile',
|
||||||
wall: 'wall_inner',
|
wall: 'wall_inner',
|
||||||
|
// Operations center: multi-console setup with central visibility
|
||||||
furniture: [
|
furniture: [
|
||||||
{ tile: 'desk', tx: 2, ty: 2 },
|
// Main ops desk row
|
||||||
{ tile: 'desk', tx: 2, ty: 3 },
|
{ tile: 'desk', tx: 1, ty: 1 },
|
||||||
{ tile: 'desk', tx: 4, ty: 2 },
|
{ tile: 'desk', tx: 1, ty: 2 },
|
||||||
{ tile: 'desk', tx: 4, ty: 3 },
|
{ tile: 'desk', tx: 1, ty: 3 },
|
||||||
{ tile: 'monitor', tx: 2.5, ty: 1.5 },
|
// Secondary ops row
|
||||||
{ tile: 'chair_down', tx: 3, ty: 2.5 },
|
{ tile: 'desk', tx: 3, ty: 2 },
|
||||||
|
{ tile: 'desk', tx: 3, ty: 3 },
|
||||||
|
// Monitoring station
|
||||||
|
{ tile: 'computer_desk', tx: 5, ty: 2 },
|
||||||
|
{ tile: 'computer_desk', tx: 5, ty: 3 },
|
||||||
|
// Wall monitors
|
||||||
|
{ tile: 'monitor', tx: 0.5, ty: 0.5 },
|
||||||
|
{ tile: 'monitor', tx: 3, ty: 0.5 },
|
||||||
|
{ tile: 'monitor', tx: 5.5, ty: 0.5 },
|
||||||
|
// Chairs at consoles
|
||||||
|
{ tile: 'chair_down', tx: 2, ty: 1.5 },
|
||||||
|
{ tile: 'chair_down', tx: 2, ty: 2.5 },
|
||||||
|
{ tile: 'chair_down', tx: 2, ty: 3.5 },
|
||||||
|
{ tile: 'chair_down', tx: 4, ty: 2.5 },
|
||||||
|
{ tile: 'chair_down', tx: 4, ty: 3.5 },
|
||||||
|
{ tile: 'chair_up', tx: 6, ty: 2.5 },
|
||||||
|
{ tile: 'chair_up', tx: 6, ty: 3.5 },
|
||||||
|
// Infrastructure
|
||||||
|
{ tile: 'cabinet', tx: 6.5, ty: 0.5 },
|
||||||
|
{ tile: 'cabinet', tx: 6.5, ty: 1.5 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue