docs: add NPC social interactions design

Covers awareness zone, interaction state machine, cooldowns,
brain/movement guards, broadcast format, and client emoji overlay.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-07 12:52:31 +00:00
parent 50ccfd9c69
commit cc8fc7def7
@@ -0,0 +1,100 @@
# NPC Social Interactions Design
## Overview
Add NPC-to-NPC social interactions triggered by proximity. NPCs within a 5-tile awareness zone can initiate interactions that play out as a face-each-other → pause → emoji sequence. Outcomes are currently random but the system is designed to support future personality, relationships, and seek-out behavior.
## New ECS Component: SocialState
Every NPC receives a `SocialState` component at spawn time.
```typescript
type InteractionPhase = 'none' | 'facing' | 'pausing' | 'emoting';
interface SocialState {
phase: InteractionPhase;
partnerId: string | null;
phaseTimer: number; // ticks remaining in current phase
outcome: 'positive' | 'negative' | null;
globalCooldown: number; // ticks until this NPC can interact with anyone
pairCooldowns: Map<string, number>; // entityId -> ticks remaining
}
```
- `globalCooldown`: 50 ticks (5s)
- `pairCooldowns` entries: 300 ticks (30s)
- Cooldowns decrement each tick regardless of interaction state
## Social System
New `socialSystem` runs after `npcBrainSystem`, before `movementSystem`.
System order: needsDecay → npcBrain → **social** → movement.
### Phase 1: Update active interactions
For NPCs with `phase !== 'none'`, decrement `phaseTimer`. When timer hits 0, advance:
| Transition | Duration | Action |
|---|---|---|
| `facing``pausing` | 10 ticks (1s) | NPCs face each other, movement set to idle |
| `pausing``emoting` | 15 ticks (1.5s) | Roll outcome independently per NPC (50/50 positive/negative) |
| `emoting``none` | 20 ticks (2s) | Emoji visible; on exit set global + pair cooldowns |
### Phase 2: Detect new interactions
For NPCs with `phase === 'none'` and `globalCooldown === 0`:
1. Find all other NPCs within 5-tile Manhattan distance
2. Filter out those with active pair cooldowns or already interacting
3. Pick closest eligible NPC as partner
4. **Target rejection**: If target is not wandering AND their relevant need is below threshold (hunger < 30 or energy < 20), skip — they're too busy
5. If valid partner: set both NPCs to `facing` phase, update directions to face each other
Process in entity ID order to resolve conflicts — first initiator to claim an NPC wins.
### Cooldown decay
Decrement `globalCooldown` and all `pairCooldowns` each tick. Remove pair entries that hit 0.
## Integration: Brain & Movement Guards
- **npcBrainSystem**: If entity has `socialState.phase !== 'none'`, skip entirely
- **movementSystem**: Same check — skip entities in active social phase
- Path preservation: movement component's `target` and `path` are left untouched during interaction. When phase returns to `none`, movement resumes naturally (brain skips idle check if path exists, movement continues walking).
## Edge Cases
- **NPC despawn during interaction**: Reset remaining partner's phase to `none`
- **Simultaneous targeting**: Process in entity ID order; first claim wins
- **Player entities**: Filter out entities with `PlayerControlled` component
## Broadcast & Client Rendering
### Server: add to EntityState
```typescript
socialState?: {
phase: InteractionPhase;
partnerId: string | null;
outcome: 'positive' | 'negative' | null;
}
```
Only phase, partnerId, and outcome are serialized — no cooldowns or timers.
### Client: floating emoji overlay
- When `socialState.phase === 'emoting'` with non-null outcome, render HTML element above sprite
- Positive: happy emoji, Negative: angry emoji
- Float-up animation: translate Y upward ~20px over 2s, fade out in last 0.5s
- Clean up element when emoting phase ends or entity leaves view
- Track active elements to avoid duplicates
- Styling is placeholder (plain emoji text) — will be reworked to match game aesthetic later
## Future Extensions (not in scope now)
- NPC personality/likes/dislikes influencing outcome
- Persistent relationship tracking between NPC pairs
- NPCs seeking out liked/disliked NPCs for interactions
- Interaction outcomes affecting needs or mood