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

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.

Loading NPC Templates

The system first loads static NPC definitions from the game configuration cache. At line 74 of include/system.func.php, the engine executes:

include_once config('npc',$gamecfg);

This imports the $npcinfo array from 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:

    $npc = array_merge($npcinit,$npcs);
  3. Instance metadata assignment – Lines 87-90 assign runtime identifiers:

    $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:

$npc = player_format_with_db_structure($npc);

This helper function (defined in 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:

$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. This enables ad-hoc instantiation without rebooting the game instance.

Function Signature and Merging Logic

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:

$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:

$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:

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 – Contains the core instantiation logic, including the bulk initialization loop and the addnpc() function for dynamic spawning.
  • gamedata/cache/npc.php – Stores the $npcinfo array with all NPC templates and sub-variant definitions.
  • include/init.func.php – Invokes the bulk NPC creation routines when initializing a new game round.
  • 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, 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. 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, 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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →