Custom NPCs
The S1API provides a comprehensive system for creating custom NPCs that integrate seamlessly with the base game's systems. This guide covers creating physical NPCs with full functionality including schedules, dialogue, customer, dealer, and supplier behavior, relationships, and appearance customization.
Overview
Custom NPCs in S1API are built on a modular architecture that allows you to create both physical NPCs (visible in the world) and non-physical NPCs (contacts, informants, etc.). The system provides:
- Physical NPCs: Visible in the game world with 3D models, movement, and direct interaction
- Non-Physical NPCs: Invisible contacts for messaging and phone interactions
- Modular Components: Appearance, Dialogue, Schedule, Customer, Dealer, Supplier, and Relationship systems
- Save/Load Integration: Full persistence support with the game's save system
- Network Compatibility: Works in both single-player and multiplayer environments
- Cross-branch Compatibility: Works in both Mono and Il2Cpp builds
Documentation Structure
The Custom NPC system is documented across multiple focused pages:
Core Concepts
- Basic NPC Creation - Fundamental concepts and getting started
- Prefab Configuration - Setting up NPC components and behavior
- Runtime Management - NPC lifecycle and runtime properties
Systems & Features
- Appearance Customization - Visual appearance and avatar system
- Scheduling System - NPC schedules and movement patterns
- Dialogue System - Interactive conversations and dialogue trees
- Customer Behavior - NPCs as business customers
- Dealer System - NPCs that distribute products for the player
- Supplier NPCs - Native supplier shops, dead drops, meetings, and deliveries
- Deliveries - Read-only active-delivery and receipt wrappers
- Relationship Management - NPC relationships and connections
API Reference
- S1API - Detailed API documentation
Example Repository
For complete, production-ready NPC implementations, see the S1API NPC Example Repository. This repository contains four fully-featured example NPCs covering all major use cases:
- ExamplePhysicalNPC1 - Customer with dialogue, inventory, and complex scheduling
- ExamplePhysicalNPC2 - Customer events and dealer recommendations
- ExamplePhysicalDealerNPC - Complete dealer implementation
- CharacterCustomizerNPC - UI integration example
Using AI Assistance?
If you use an AI coding agent to create or review custom NPC mods, point it at the schedule-one-custom-npcs agents skill. The skill summarizes the S1API two-phase NPC model, physical versus non-physical NPC choices, customer/dealer setup, appearance constraints, schedules, dialogue, and lifecycle checks that custom NPC implementations should follow.
Quick Start
Here's a minimal example to get you started:
public sealed class MyFirstNPC : NPC
{
public override bool IsPhysical => true;
public override bool IsCustomer => true;
protected override void ConfigurePrefab(NPCPrefabBuilder builder)
{
builder.WithIdentity(
id: "my_first_npc",
firstName: "John",
lastName: "Doe")
.WithSpawnPosition(new Vector3(0, 0, 0))
.WithCustomerDefaults(cd => {
cd.WithSpending(100f, 500f)
.WithOrdersPerWeek(1, 3);
});
}
public MyFirstNPC() : base()
{
}
protected override void OnCreated()
{
base.OnCreated();
// Set up appearance
Appearance
.Set<CustomizationFields.Gender>(0.5f)
.Set<CustomizationFields.Height>(1.0f)
.Build();
// Enable systems
Schedule.Enable();
Schedule.InitializeActions();
}
}
Messaging and read objectives
Use Messaging when quest or tutorial logic needs to know whether the player opened an NPC conversation. The read state is conversation-level: Schedule One does not expose per-message read receipts for NPC conversations.
protected override void OnCreated()
{
base.OnCreated();
Messaging.OnConversationOpened += HandleConversationOpened;
Messaging.SendTextMessage("Open this conversation to continue.");
}
protected override void OnDestroyed()
{
Messaging.OnConversationOpened -= HandleConversationOpened;
base.OnDestroyed();
}
private void HandleConversationOpened()
{
if (!Messaging.HasUnreadMessages)
{
// Complete the related quest entry here.
}
}
Available state:
Messaging.IsRead: whether the native conversation is marked as read. A conversation that does not exist yet is treated as read.Messaging.HasUnreadMessages: the inverse conversation-level unread state.Messaging.IsOpen: whether this conversation is currently open in the phone's Messages app.Messaging.OnConversationOpened: raised whenever the conversation is opened, including later reopenings.
NPC.SendTextMessage(...) remains available for compatibility. Messaging.SendTextMessage(...) forwards to the same implementation so state checks, events, and sending can live under one API surface.
Selecting a voice
Configure a custom NPC's voice in ConfigurePrefab. Use the typed catalog when possible; the string overload accepts the same case-insensitive identifiers.
using S1API.Entities.Voices;
protected override void ConfigurePrefab(NPCPrefabBuilder builder)
{
builder
.WithIdentity("my-mod:dispatcher", "Dispatch", "")
.WithVoice(NPCVoiceCatalog.Tyler, pitch: 0.92f);
}
Supported identifiers are cold, crackhead, female-1, female-2, goblin, hippie, joel, monotone, redneck, timid, and tyler.
These identifiers name reusable voice databases, not individual NPCs. For example, Ray's native configuration combines the tyler database with a character-specific pitch; use the pitch overload when reproducing that kind of voice profile.
The pitch overload accepts values from 0.1 through 4.0. Omitting the pitch preserves the selected base prefab's inherited pitch. Omitting WithVoice(...) entirely preserves both the inherited voice database and pitch.
Call WithVoice(...) when the NPC should keep a specific voice across game updates. Inherited voices depend on S1API's current donor prefab and may change when the game changes that prefab. A missing donor voice also leaves the NPC silent. Invalid identifiers, unavailable databases, and out-of-range pitch values throw an actionable configuration error.
Voice selection controls which clips normal NPC dialogue and reactions play. It does not play an individual voice line.
What To Read First
- Start here: Basic NPC Creation
- Then: Prefab Configuration (identity, relationships, schedules, customer/dealer defaults)
- As needed: Dialogue System, Scheduling System, Customer Behavior, Dealer System, Supplier NPCs
Getting Help
- Review the S1API for detailed method documentation
- Look at the example projects in the repository for real-world usage patterns
- When working with an AI coding agent, share the schedule-one-custom-npcs agents skill so generated NPC code follows the same S1API patterns as these docs.
Next Steps
- Copy an example NPC and get it spawning: S1API NPC Example Repository
- Make it walk somewhere: Scheduling System
- Add interaction: Dialogue System