# How PHPDTS Manages NPCs and Instantiates Non-Player Characters: A Deep Dive into the NPC Management System

> Discover how PHPDTS manages NPCs by treating them as player records. Learn about their instantiation process and how non-player characters are persistently stored.

- Repository: [Nemo Ma/phpdts](https://github.com/amarillonmc/phpdts)
- Tags: deep-dive
- Published: 2026-02-24

---

**PHPDTS treats NPCs as player records stored in the same database table, instantiating them by merging template configurations with a base structure and persisting via `player_format_with_db_structure()` and `array_insert()`.**

The `amarillonmc/phpdts` project implements a unified architecture where non-player characters (NPCs) are technically indistinguishable from human players at the database level. This design allows the NPC management system to leverage the same persistence layer and attribute schema for both entity types, simplifying state management and combat resolution.

## Unified Player-NPC Architecture

In PHPDTS, there is no separate `npcs` table. Instead, the system stores all entities—human and computer-controlled—in the **`players`** table (prefixed as `{$tablepre}players`). The distinction between a user and an NPC is determined by the **`type`** field and the presence of specific initialization flags. This architecture ensures that combat, movement, and status effects work identically regardless of whether the target is a human opponent or an AI-controlled character.

## Bulk NPC Initialization at Game Start

When a new game instance begins, the engine populates the world with predefined NPCs through a bulk creation process defined in [`include/system.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/system.func.php).

### Loading NPC Templates

The system first loads static NPC definitions from the game configuration cache. At line 74 of [`include/system.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/system.func.php), the engine executes:

```php
include_once config('npc',$gamecfg);

```

This imports the **`$npcinfo`** array from [`gamedata/cache/npc.php`](https://github.com/amarillonmc/phpdts/blob/main/gamedata/cache/npc.php), which contains associative definitions for every NPC type, including base statistics, possible sub-variants, spawn quantities, and equipment loadouts.

### Merging and Instantiating

For each NPC type defined in `$npcinfo`, the engine loops through the required spawn count (`$npcs['num']`) and constructs individual records. The instantiation follows a strict merging protocol:

1. **Base structure initialization** – The system retrieves `$npcinit`, a default player structure containing neutral values for all required database columns.
2. **Template merging** – At line 85, the engine overlays type-specific data:
   ```php
   $npc = array_merge($npcinit,$npcs);
   ```

3. **Instance metadata assignment** – Lines 87-90 assign runtime identifiers:
   ```php
   $npc['type']   = $i;        // NPC type identifier
   $npc['endtime']= $now;      // Creation timestamp
   $npc['sNo']    = $j;        // Sequential spawn number
   ```

After merging, the system randomizes gender, location coordinates, club affiliation, and weapon selections based on the template’s allowed ranges.

### Persistence to Database

Before insertion, the associative array must conform to the database schema. Line 155 invokes:

```php
$npc = player_format_with_db_structure($npc);

```

This helper function (defined in [`include/global.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/global.func.php)) maps the raw NPC array to the exact column structure expected by the `players` table. Finally, line 156 persists the record:

```php
$db->array_insert("{$tablepre}players", $npc);

```

## Dynamic NPC Spawning with addnpc()

For event-driven scenarios—such as summoning a boss after a player action—the system provides the **`addnpc()`** function at line 725 of [`include/system.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/system.func.php). This enables ad-hoc instantiation without rebooting the game instance.

### Function Signature and Merging Logic

```php
function addnpc($type,$sub,$num,$time = 0,$anpcdata = NULL) {
    $npcinit = get_npcinit();                                 // line 735
    $anpcinfo = get_addnpcinfo();                             // line 736
    $anpc = array_merge($npcinit,$anpcinfo[$type]);           // line 738
    $anpc = array_merge($anpc,$anpc['sub'][$sub]);            // line 739

```

The function accepts five parameters:
- **`$type`**: The primary NPC classification from the template system.
- **`$sub`**: The sub-variant index for differentiating specific instances (e.g., elite vs. standard).
- **`$num`**: Quantity to spawn.
- **`$time`**: Optional expiration timestamp (0 for permanent).
- **`$anpcdata`**: Optional associative array for overriding specific attributes (name, level, health).

### Finalizing Dynamic Instances

Lines 746-748 finalize the NPC record:

```php
$npc = $anpc;
$npc['type']   = $type;
$npc['endtime']= $time;

```

The function calculates derived statistics such as experience points based on level and global base rates, then applies the same persistence pipeline as bulk creation. Lines 824-826 format and insert the final record:

```php
$npc = player_format_with_db_structure($npc);            // line 824
$db->array_insert("{$tablepre}players", $npc);         // line 826

```

### Practical Example: Spawning a Boss

To summon a custom boss during an event, developers call:

```php
addnpc(99, 1, 1, $now + 300, [
    'name'   => 'The Dark Overlord',
    'club'   => 'evil',
    'lvl'    => 99,
    'mhp'    => 5000,
    'msp'    => 2000,
    'wep'    => '巨剑',
    'wepk'   => 'w',
]);

```

This creates a single instance of type 99, sub-variant 1, lasting five minutes, with custom health and weapon attributes.

## Key Files in the NPC Management System

The following files define the complete lifecycle of non-player characters in the codebase:

- **[`include/system.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/system.func.php)** – Contains the core instantiation logic, including the bulk initialization loop and the `addnpc()` function for dynamic spawning.
- **[`gamedata/cache/npc.php`](https://github.com/amarillonmc/phpdts/blob/main/gamedata/cache/npc.php)** – Stores the `$npcinfo` array with all NPC templates and sub-variant definitions.
- **[`include/init.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/init.func.php)** – Invokes the bulk NPC creation routines when initializing a new game round.
- **[`include/global.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/global.func.php)** – Provides `player_format_with_db_structure()`, which normalizes NPC arrays for database insertion.

## Summary

- **PHPDTS unifies entities** by storing NPCs and players in the same `players` table, distinguishing them only through the `type` field and initialization metadata.
- **Template-driven instantiation** loads definitions from `config('npc')`, merges them with `$npcinit`, and randomizes location, gender, and equipment before persistence.
- **Bulk creation** occurs at game start through the initialization loop in [`include/system.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/system.func.php), handling multiple instances per NPC type.
- **Dynamic spawning** uses `addnpc()` to inject NPCs mid-game, supporting custom attribute overrides via the `$anpcdata` parameter.
- **Schema normalization** via `player_format_with_db_structure()` ensures all NPC arrays match the database column structure before insertion.

## Frequently Asked Questions

### How does PHPDTS store NPC data compared to regular players?

PHPDTS stores NPCs and human players in the identical `players` table using the same schema. The system differentiates them through the `type` column and initialization flags, allowing combat and movement systems to treat both entity types uniformly without conditional logic for database access.

### What is the difference between bulk NPC creation and the addnpc() function?

Bulk creation runs once during game initialization to populate the world with standard NPCs according to predefined quotas in [`npc.php`](https://github.com/amarillonmc/phpdts/blob/main/npc.php). The `addnpc()` function enables runtime spawning during active gameplay, accepting parameters for custom attributes, expiration timers, and specific sub-variants—ideal for event-driven boss encounters or temporary modifiers.

### Where are NPC templates defined in the codebase?

NPC templates reside in [`gamedata/cache/npc.php`](https://github.com/amarillonmc/phpdts/blob/main/gamedata/cache/npc.php), which exports the `$npcinfo` array. This file is generated by the installer and defines all base statistics, possible locations, equipment sets, and sub-variant configurations for every NPC type in the game.

### How are NPC attributes like location and gender randomized during instantiation?

During the merging phase in [`include/system.func.php`](https://github.com/amarillonmc/phpdts/blob/main/include/system.func.php), the system applies randomization functions to select values from the template’s allowed ranges. Specifically, after the `array_merge($npcinit, $npcs)` operation at line 85, the code shuffles sub-variants if they exceed the spawn count, then assigns random gender, club affiliation, map coordinates, and weapon selections before calculating derived stats like HP and SP.