npm package: @starweave/roblox
Description: Vedic astrology-driven NPC behavior SDK for Roblox
Author: Whimco Studio
Latest version: 0.1.4

# @starweave/roblox

Vedic astrology-driven NPC behavior SDK for Roblox. Give your NPCs unique personalities, moods, relationships, and daily routines — all computed from birth charts.

## Install

**roblox-ts** (TypeScript):
```bash
npm install @starweave/roblox
```

**Luau** (native):
Download `Starweave.rbxm` from [Releases](https://github.com/Whimco-Studio/Starweave/releases) and insert it into `ReplicatedStorage`.

## Quick Start

```typescript
import { NPCSpawner, DEFAULT_SPAWNER_CONFIG } from "@starweave/roblox";

// 1. Create a spawner with your API credentials
const spawner = new NPCSpawner({
  ...DEFAULT_SPAWNER_CONFIG,
  api_base_url: "https://starweave.whimco.com",
  api_key: "your-api-key",
});

// 2. Spawn an NPC — birth_time determines their entire personality
await spawner.spawnNPC({
  npc_id: "merchant_01",
  birth_time: "1985-06-15T14:30:00Z",
  model: workspace.NPCs.Merchant,
  spawn_position: new Vector3(10, 5, 20),
});

// 3. Start the behavior loop
spawner.startUpdateLoop();

// 4. Read NPC state anytime
const behavior = spawner.getNPCBehavior("merchant_01");
// → {
//     current_activity: "trading",
//     mood_expression: "content",
//     movement_style: "walk",
//     social_disposition: "friendly",
//     is_busy: false,
//   }
```

## How It Works

Each NPC gets a **birth time** (ISO 8601 string). The Starweave engine computes a Vedic birth chart and derives:

- **Static Profile** (permanent): base personality traits, behavioral flags, preferred activities, daily schedule variance
- **Dynamic State** (changes over time): current dasha period, active planetary transits, mood, motivation, temporary trait shifts
- **Relational State** (per relationship): trust, familiarity, social disposition toward players and other NPCs

The SDK handles all API calls, caching, and state management. You just read the results.

## API

### NPCSpawner

The main entry point. Manages NPC lifecycle, behavior updates, and proximity detection.

```typescript
const spawner = new NPCSpawner(config: SpawnerConfig);
```

#### SpawnerConfig

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `api_base_url` | `string` | required | Your Starweave API URL |
| `api_key` | `string` | required | API key for authentication |
| `update_interval` | `number` | `5` | Seconds between behavior recalculations |
| `dynamic_refresh` | `number` | `300` | Seconds between dynamic state API refreshes |
| `proximity_radius` | `number` | `20` | Studs for player/NPC detection |
| `proximity_check_interval` | `number` | `1` | Seconds between proximity scans |
| `cache_ttl_seconds` | `number` | `300` | Seconds to cache API responses |

Use `DEFAULT_SPAWNER_CONFIG` to start with sensible defaults and override what you need:

```typescript
const spawner = new NPCSpawner({
  ...DEFAULT_SPAWNER_CONFIG,
  api_base_url: "https://starweave.whimco.com",
  api_key: "your-api-key",
  proximity_radius: 50, // wider awareness range
});
```

#### Methods

```typescript
// Spawn a single NPC
await spawner.spawnNPC({
  npc_id: "guard_01",
  birth_time: "1972-03-22T08:15:00Z",
  model: workspace.NPCs.Guard,
  spawn_position: new Vector3(0, 5, 0), // optional
}): Promise<boolean>

// Spawn multiple NPCs
await spawner.spawnBatch(definitions): Promise<number> // returns count spawned

// Start the global update loop (call once after spawning)
spawner.startUpdateLoop(): void

// Query NPC state
spawner.getNPCBehavior(npcId): BehaviorState | undefined
spawner.getNPCProfile(npcId): StaticProfile | undefined
spawner.getController(npcId): NPCBehaviorController | undefined

// Cleanup
spawner.removeNPC(npcId): void
spawner.destroy(): void
```

### NPCBehaviorController

Lower-level controller for a single NPC. Use this if you want manual control instead of NPCSpawner.

```typescript
const controller = new NPCBehaviorController(config, vedicService);
await controller.initialize();

// Called every frame (or on a timer)
controller.update(dt);

// Proximity events
await controller.onPlayerProximity(player);
await controller.onNPCProximity(otherNpcId, otherProfile);

// Record interactions (trades, combat, conversations)
controller.recordInteraction(targetId, "traded_sword");

// Read state
controller.getBehavior(): BehaviorState
controller.getStaticProfile(): StaticProfile | undefined
controller.getDynamicState(): DynamicState | undefined
controller.getRelationship(targetId): RelationalState | undefined
```

### BehaviorState

What you read from an NPC at any moment:

```typescript
interface BehaviorState {
  current_activity: string;       // "idle", "trading", "patrolling", "meditating", ...
  target_entity?: string;         // player/NPC they're focused on
  mood_expression: string;        // "joyful", "content", "neutral", "anxious", "angry"
  movement_style: "idle" | "walk" | "run" | "sneak" | "patrol";
  social_disposition: "friendly" | "neutral" | "wary" | "hostile";
  is_busy: boolean;
}
```

### VedicService

Direct API client if you want to make raw calls:

```typescript
const service = new VedicService({
  baseUrl: "https://starweave.whimco.com",
  apiKey: "your-api-key",
  cacheTtlSeconds: 300,
});

const profile = await service.getStaticProfile({
  npc_id: "npc_01",
  birth_time: "1990-04-15T06:30:00Z",
});

const dynamic = await service.getDynamicState({
  npc_id: "npc_01",
  current_time: os.date("!%Y-%m-%dT%H:%M:%SZ"),
  static_profile: profile,
});

service.clearCache();
service.clearCacheForNPC("npc_01");
```

## Birth Time Guide

Birth time is the only input that matters. It determines everything about an NPC's personality through Vedic astrology:

- **Lagna (Ascendant)**: Core personality archetype (12 types)
- **Moon Nakshatra**: Emotional texture and behavioral nuance (27 types)
- **Planetary Aspects**: Behavioral flags like `territorial`, `merchant`, `healer`
- **Dasha Period**: Current life phase and motivation (changes over time)

**Tips for variety:**
- Spread birth times across a 120-year window (`1920-01-01` to `2040-01-01`) — NPCs born in the same decade will share similar dasha phases
- Different birth times on the same day still produce different lagnas (ascendant changes every ~2 hours)
- Store birth times in your NPC data — they never change and the static profile is computed once

```typescript
// Good: diverse birth times
const npcs = [
  { npc_id: "elder",    birth_time: "1935-11-02T04:00:00Z", model: ... },
  { npc_id: "merchant", birth_time: "1978-07-19T16:45:00Z", model: ... },
  { npc_id: "guard",    birth_time: "2001-01-30T09:20:00Z", model: ... },
];
```

## Configuration Tuning

| Scenario | update_interval | dynamic_refresh | proximity_radius | cache_ttl |
|----------|----------------|----------------|-----------------|-----------|
| Active NPCs (shops, quest givers) | 2-3s | 300s | 20 studs | 300s |
| Background NPCs (crowds, ambient) | 10-30s | 600s | 50 studs | 600s |
| Large scenes (50+ NPCs) | 5s | 900s | 30 studs | 900s |

**Key rule**: `dynamic_refresh` should equal `cache_ttl_seconds`. Refreshing faster than the cache wastes API calls; refreshing slower causes stale state.

## Types

All Vedic types are exported for TypeScript users:

```typescript
import type {
  StaticProfile,
  DynamicState,
  RelationalState,
  TraitBlock,
  BehavioralFlag,
  PlanetaryPosition,
  DashaPeriod,
  Transit,
  ZodiacSign,
  Nakshatra,
  Planet,
} from "@starweave/roblox";
```

### TraitBlock

The five core personality dimensions (0.0 to 1.0):

```typescript
interface TraitBlock {
  openness: number;            // curiosity, willingness to explore
  loyalty: number;             // faithfulness, attachment strength
  aggression: number;          // combativeness, territorial instinct
  fear: number;                // anxiety, caution level
  deception_tendency: number;  // cunning, willingness to deceive
}
```

## License

MIT — [Whimco Studio](https://whimco.com)