feat: display NPC desires in info panel

Shows category-colored diamond bullets for active desires and
placeholder slots in the Status tab, between backstory and thoughts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-10 00:07:14 +00:00
parent f3591e68d7
commit 7a16872bb9
+43 -1
View File
@@ -1,4 +1,4 @@
import type { EntityState, NarrationEvent, MemoryEvent, Stats, StatName } from '@dflike/shared';
import type { EntityState, NarrationEvent, MemoryEvent, Stats, StatName, DesireCategory } from '@dflike/shared';
import { attachTooltip } from './tooltip.js';
const ACTIVITY_LABELS: Record<string, string> = {
@@ -32,6 +32,18 @@ const EB = {
shine: 'rgba(200,200,255,0.08)', // Subtle window shine
};
function desireCategoryColor(category: string): string {
switch (category) {
case 'material': return '#d4a574';
case 'shelter': return '#c4956a';
case 'comfort': return '#e8b87a';
case 'social': return '#7ab8e8';
case 'community': return '#7ae8a0';
case 'creative': return '#c87ae8';
default: return '#9898d0';
}
}
const STAT_FULL_NAMES: Record<string, string> = {
STR: 'Strength', DEX: 'Dexterity', CON: 'Constitution', INT: 'Intelligence', PER: 'Perception',
SOC: 'Sociability', COU: 'Courage', CUR: 'Curiosity', EMP: 'Empathy', TMP: 'Temperament',
@@ -48,6 +60,7 @@ export class NpcInfoPanel {
private needsBarsContainer: HTMLDivElement;
private needsBars: Map<string, { wrapper: HTMLDivElement; fill: HTMLDivElement; valueEl: HTMLDivElement }> = new Map();
private backstoryEl: HTMLDivElement;
private desiresEl: HTMLDivElement;
private statsContainer: HTMLDivElement;
private statElements: Map<string, HTMLDivElement> = new Map();
private statusTab!: HTMLDivElement;
@@ -265,6 +278,16 @@ export class NpcInfoPanel {
`;
this.statusContent.appendChild(this.backstoryEl);
// Desires section
this.desiresEl = document.createElement('div');
this.desiresEl.style.cssText = `
padding: 4px 8px;
font-family: 'Press Start 2P', monospace;
font-size: 10px;
line-height: 1.8;
`;
this.statusContent.appendChild(this.desiresEl);
// Inner thought (italic first-person, shown above recent events)
this.thoughtEl = document.createElement('div');
this.thoughtEl.style.cssText = `
@@ -441,6 +464,25 @@ export class NpcInfoPanel {
this.backstoryEl.style.minHeight = '0';
}
// Update desires
const desires = entity.desires;
if (desires && desires.length > 0) {
let html = `<div style="color: ${EB.textMuted}; font-size: 9px; margin-bottom: 2px;">Desires</div>`;
for (const desire of desires) {
const color = desireCategoryColor(desire.category);
html += `<div style="color: ${color}; padding: 1px 0;">&#9670; ${desire.description}</div>`;
}
const empty = 3 - desires.length;
for (let i = 0; i < empty; i++) {
html += `<div style="color: ${EB.textMuted}; font-style: italic; padding: 1px 0;">&#9675; (daydreaming...)</div>`;
}
this.desiresEl.innerHTML = html;
this.desiresEl.style.display = 'block';
} else {
this.desiresEl.innerHTML = `<div style="color: ${EB.textMuted}; font-size: 9px;">Content for now.</div>`;
this.desiresEl.style.display = 'block';
}
if (entity.needs) {
this.updateNeeds(entity.needs);
}