NPCs
kcds.characters.Npc is a character the server spawns and owns. The server holds its health, inventory and orders. The game of
the nearest player within 150 m simulates its body, including walking, pathfinding and fighting, and all other players see what
that game reports. So everyone sees the same NPC.
const { Npc } = kcds.characters; const { Vector3 } = kcds.math; const guard = Npc.create({ soul: 'guard', name: 'Gate guard', nameplate: true, position: new Vector3(-2820, -1320, 104), }); guard.lookAt(player); guard.say('Halt! Who goes there?'); guard.moveTo(new Vector3(-2830, -1310, 104), { speed: 'walk' }); kcds.core.Events.on('npcDeath', (npc, killer) => { console.info(`${npc.name} died`); });
Spawning
Npc.create(options) requires position. The other options are:
- Body:
souland/orclass,appearance,outfit,wearing,faction - Identity:
name,nameplate - Place:
rotation,dimension,frozen,locomotion - Health:
health,invulnerable,lootable
soul is a role or a soul GUID. class is 'NPC', 'NPC_Female' or an animal class.
To find and remove NPCs, use Npc.all(dimension?), Npc.getById(id), Npc.removeAll(dimension?) and npc.remove().
Roles
Npc.roles() lists kinds of NPC by name: guard, guard_alt, knight, bandit, bandit_alt, cuman, townsman,
townsman_alt, townswoman and townswoman_alt. Each is one real soul from the game's tables, so the body already looks right,
and a woman's role has a woman's body. Pass the name as soul.
Nameplates
An NPC can have a player-style name and health bar over its head. Set nameplate: true, which is off by default. Then use
setNameplateText(text) for a title instead of the name, setNameplateColor(0xAARRGGBB) and setNameplateHealthVisible(false).
const captain = kcds.characters.Npc.create({ soul: 'knight', name: 'Hanush', nameplate: true, position, }); captain.setNameplateText('Captain of the guard'); captain.setNameplateColor(0xffffd27f);
Which game runs it
The nearest eligible player's game simulates the NPC. That game must have finished loading the world and started gameplay, so a connection that only receives world data cannot run NPC AI. If no eligible player holds the NPC, it stays at its last reported state until one is available.
npc.pin(player)keeps it on one player's game for a scene with them. If they walk away, it goes dormant rather than moving to someone else.npc.pin(null)releases it.npc.locomotion = 'kinematic'moves it along its orders at their pace on the ground, without walking animations.'native', the default, makes it walk.
Orders
Orders use the game's own movement and pathfinding.
| Order | Effect |
|---|---|
moveTo(position, { speed }) | walks there: walk (default), jog or run |
follow(target, { speed, radius }) | trails a player or NPC, 3 m behind by default |
flee(from, { speed, radius }) | runs away, 30 m by default |
lookAt(target) | turns towards a player, NPC or position |
attack(target, { weapon }) | fights a living player or NPC within 128 m |
patrol(route, options) | walks a route; see below |
hold() | drops the order and stays still |
teleport(position, rotation) | moves it at once |
intent shows what it was told, and status shows progress: idle, running, then done or failed. npcIntentDone fires when an
order ends. frozen = true holds it in place but keeps its order.
You can give an order right after Npc.create, and it waits for a player's game to simulate the NPC. A new body's game may refuse
a walk at first, so it is retried for a few seconds before npcIntentDone reports failure.
Patrol routes
const { PatrolRoute } = kcds.characters; const { Vector3 } = kcds.math; const wallWalk = PatrolRoute.create({ name: 'walls', points: [new Vector3(0, 0, 10), new Vector3(30, 0, 10), new Vector3(30, 30, 10)], speed: 'walk', waitSeconds: 5, }); guard.patrol(wallWalk); kcds.core.Events.on('patrolWaypoint', (npc, patrol) => { console.info(`${npc.name} at ${patrol.waypointIndex}`); });
An NPC copies the route when ordered, so later changes only affect new patrols. npc.routeState shows where it is on the route.
Health and fighting
Basics: health (0 to 100, where 0 kills), alive, invulnerable and fightsBack. Also damage(amount, attacker?),
kill(killer?), revive(health?), and setCombatSkill({ defense, damage }), which sets how well it parries and how hard it
hits. Events are npcDamage, npcDeath and npcRevive.
Three switches control behaviour. Each is true by default, and each is also an option of Npc.create. Animals have them too.
| Switch | When false |
|---|---|
brain | the game's brain does not run it; no thinking, perceiving, fighting or revenge (fightsBack reads false), attack is refused, and only your orders move it |
targetable | players cannot lock on to it |
attackable | players cannot fight it: no lock-on, and their blows, shots and horses' hits land nowhere, so it does not react and the game sees no attack to punish |
A shopkeeper or quest giver: use attackable: false, since the NPC still talks, walks and trades. Another NPC can still hit it,
so add invulnerable if that must not hurt it.
A mannequin: given to Npc.create, brain: false means the brain never starts, and all clients get the NPC with it off, so it
does not take a step or turn first. With interactable: false too, it is a mannequin from its first frame, suitable for a preview or
character-creation model. To make it unhurtable by blows, shots, falls and damage(), add invulnerable.
const statue = kcds.characters.Npc.create({ soul: 'townswoman', position: new kcds.math.Vector3(100, 200, 25), brain: false, targetable: false, invulnerable: true, }); statue.brain = true; // it thinks again
Looks, loot and interaction
setAppearance(...),setOutfit(preset)andwear(itemClasses)change its look.getInventory()shows what it carries. Players can search a dead,lootableNPC, wherenpcLootcan refuse andnpcLootedfollows.playAnimation(fragment, { tags })plays a Mannequin fragment, as for players, andplayAnimation('', { clip })plays a clip.say(text)speaks.- With
interactable, pressing the use key on it firesnpcInteract.
Animals
The server can populate the world with animals from the game's spawn points around players.
const { Npc } = kcds.characters; Npc.setAnimalPopulation({ enabled: true }); Npc.setAnimalSpecies('Wolf', 50); // half as many wolves Npc.setHarvestTable({ Boar: [{ item: 'boarMeat', amount: 2 }] });
The classes are Boar, CattleBull, CattleCow, Dog, Hare, Pig, Raven, RedDeerDoe, RedDeerStag, RoeDeerBuck,
RoeDeerHind, SheepEwe, SheepRam, WildDog and Wolf. Npc.animals() lists the spawned animals as Npc objects.
Carcasses
npcHarvest and npcHarvested follow a player skinning a carcass. A population animal is not lootable, so its carcass offers Gut
but not Search. Set npc.lootable = true on one players should search.
A dead animal with lootable = false and nothing to harvest offers neither prompt. That happens if its class is not in the table,
or it was already harvested. Npc.setHarvestTable({}) turns off the game's gutting for all animals. Any hints your server puts on
the carcass are then the only prompts there.
An npcHarvest handler can return its own yield for this harvest, as { items: [{ item, amount }] }. It can have up to 16 rows,
checked like the table's. The class must still be in the table, and [] is enough.
kcds.core.Events.on('npcHarvest', (player, npc) => { if (npc.actorClass !== 'Boar' || player.getStat('survival') < 10) { return; } return { items: [ { item: 'boarMeat', amount: 4 }, { item: 'boarKidney', amount: 1 }, ], }; });
The level's people
People the level places are server NPCs, the same for all players. They appear around players at their level positions and go when nobody is near. They walk a relaxed round of neighbouring places, a patrol that a script can replace with any order. They do not follow the level's daily schedules, and people a quest stages, such as an army or siege, are left out.
const { Npc } = kcds.characters; Npc.setPopulation({ maxPeople: 24, activationRadius: 60, despawnRadius: 90 }); Npc.setPopulation({ enabled: false }); // only your own NPCs
The same settings can go in people-population.json in the resources folder. Npc.placements() lists the level's people, and
Npc.adopt(guid, options) gives one to a script, after which it stays when players leave.
Bodies for one player
For a body only one player sees, such as a preview or a menu guide, see LocalNpc in Camera, aiming, pictures and local
figures.
Reference: Npc, PatrolRoute.