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:
@@ -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
|
||||
Reference in New Issue
Block a user