Equipment
The server holds what a player wears, as part of their inventory. Each row (InventoryRow) has equipped, the number of units the body
wears or holds. To dress or undress a player, change equipped through kcds.players.Inventory.set. Their game then dresses the body with
its own equipment code and reports what is really worn.
Slots
You do not name a slot. The game chooses from the class's armour type, which Inventory.classes() gives as slot. It is a name from the table
below, or null for things the body does not wear, such as weapons, food, glasses, sword sheaths and horse gear.
const slotOf = new Map(kcds.players.Inventory.classes().map((entry) => [entry.name, entry.slot])); slotOf.get('HoseJoined01_m03_C'); // "leg_trousers" slotOf.get('TunicShort05_m09_D'); // "body_cloth" slotOf.get('longSwordDuel'); // null
Most class names start with their armour type: HoseJoined01_m03_C is HoseJoined, and BascinetVisor04_m02_A5 is BascinetVisor. Belts,
rings and necklaces have lower-case names, such as belt_4slot, ring_golden_cross and necklace_silver_cross_insignia.
Body slots
Slots sharing a place, such as Cloth 1 and Cloth 2, take one piece at a time.
| Slot | Place | Armour types | Needs |
|---|---|---|---|
head_coif | Coif 1 | CoifCap | |
head_coif_padded | Coif 2 | CoifSmall, CoifLarge, CoifMail | |
head_cap | Cap 1 | Cap, LeatherCap, F_Hood, F_Bonnet, F_CapAndWimple, F_Hat, F_HoodOpen, F_Veil, F_VeilAndWimple | head_coif_padded empty |
head_helmet | Cap 2 | KettleHat, SkullCap, BascinetOpen, BascinetVisor | head_coif_padded worn |
head_hood | Hood | Hood | |
collar | Collar | CollarPadded, CollarMail | |
body_cloth | Cloth 1 | TunicShort, TunicLong, F_SimpleDress, F_Smock, LeatherApron | |
body_cloth_padded | Cloth 2 | GambesonShort, GambesonLong, Caftan, Pourpoint | |
body_chainmail | Chainmail | MailShort, MailLong | body_cloth_padded worn |
body_plate | Plate | Brigandine, Cuirass | body_cloth_padded worn |
body_coat | Coat | Coat, Waffenrock, Habit | |
sleeves | Sleeves | ArmBrigandine, ArmPlate | body_cloth_padded worn |
gloves | Gloves | Gloves, Gauntlets | |
leg_trousers | Trousers 1 | HoseJoined, HoseLoose, HoseSeparate | |
leg_trousers_padded | Trousers 2 | LegsPadded, LegsChain | |
leg_armor | LegArmor | LegsBrigandine, LegsPlate | leg_trousers_padded worn |
boot | Boot | Shoes, BootsAnkle, BootsKnee, F_Shoes | |
spur | Spur | Spurs | boot worn |
belt | Belt | Belt | |
pouch | Pouch | Pouch | |
ring | Ring | Ring | |
necklace | Necklace | Necklace |
The body wears one belt and one pouch. A belt with quick slots (belt_4slot) uses the belt slot, and a pouch (Pouch_test_4slot) uses
pouch. The apprentice and master badges, Cap01_mApprentice and Cap01_mBlacksmith, also use belt.
Horses have their own slots: horse_head, horse_saddle, horse_torso and horse_shoe. See Horses and dogs.
A complete harness
The game can dress all of this in one Inventory.set: LegsPadded01_m07_C3, GambesonLong01_m09_C3, CollarPadded01_m08_C1,
CoifSmall02_m07_E1, MailLong02_m01_C2, Cuirass07_m03_A4, ArmPlate03_m02_D1, LegsPlate02_m01_B4, Gauntlets05_m01_A5,
BascinetVisor04_m02_A5, Waffenrock08_mPisek_B, Spurs01_m01_C, belt_4slot, ring_golden_cross and
necklace_silver_cross_insignia.
Pieces that clash
The game would keep only some of a set of clashing pieces. So the server refuses such an Inventory.set with invalidEquipment before
anything changes. The answer does not say which rows clash.
Only rows with equipped > 0 count, and each unit is a piece, so equipped: 2 is two pieces. Weapons, ammunition and classes with slot
null are not counted.
One piece per place:
- two slots sharing a place: a shirt or a gambeson, hose or padded legs, a coif or a padded coif, a cap or a helmet
- two pieces in one slot, such as two shirts or two rings
Other conflicts:
- A cap is not worn over a padded coif.
- Plate legs with iron shoes, such as
LegsPlate02_m01_B4, cover the feet, so shoes cannot go with them, though spurs can.
Pieces needing another beneath:
| Piece | Needs |
|---|---|
| a helmet | a padded coif |
| mail, plate or sleeves | a gambeson |
| plate legs | padded legs |
| spurs | shoes, or plate legs with iron shoes |
Put both in the same Inventory.set. The game dresses in two passes, so row order does not matter. When taking off a lower piece, take off
what depends on it too, such as a helmet with its padded coif.
Swapping: to put a piece on an occupied place, change both in one Inventory.set, using equipped: 0 for the old and equipped: 1 for the
new.
Weapons and quick slots
Weapons and ammunition use equipped too. quickslots in Inventory.set decides which weapon sits in which quick slot. A belt adds weapon
slots and a pouch adds item slots.
Dressing or undressing one piece
// Puts one row on or takes it off, leaving the other rows as they are. function setWorn(player, rowId, worn) { const { Inventory } = kcds.players; const current = Inventory.get(player); if (!current) { return false; } const items = current.items.map((row) => { let equipped = row.equipped; if (row.id === rowId) { equipped = worn ? 1 : 0; } return { id: row.id, item: row.item, amount: row.amount, metadata: row.metadata, equipped }; }); const answer = Inventory.set(player, { items, quickslots: current.quickslots, revision: current.revision, }); return answer.ok; }
- The game moves only rows whose
equippedchanged, and leaves whatever the player changed in the meantime. revisionrefuses the change withstaleRevisionif the inventory has changed, so read it again.equippedis only for equipment and weapons, and can be at most the row'samount. Otherwise you getinvalidEquipment, as with clashing pieces, sosetWornon an occupied place returnsfalse.
ok: true does not yet mean the body wears it, only that the server accepted the state. When the game has done what it can,
playerInventoryChanged fires with the reason equipment, and the rows show what is really worn:
kcds.core.Events.on('playerInventoryChanged', (player, change) => { if (change.reason !== 'equipment') { return; } const rows = kcds.players.Inventory.get(player)?.items ?? []; const worn = rows.filter((row) => row.equipped > 0); console.log(`${player.nickname} wears ${worn.map((row) => row.name).join(', ')}`); });
Reading what someone wears
Inventory.get(player).itemswithequipped > 0gives the rows worn or held, with quality and condition.player.equipmentgives the classes the body wears, as 32 hex digits.player.rightHandItemandleftHandItemgive the class in each hand, or''.
Client scripts see the same for each player their game shows.
Restoring what a player wore
Save the inventory as Inventory.get gives it on disconnect, and return it with Inventory.set when they come back. Their game dresses them by
equipped and restores the quick slots. A player's joining name comes with their client, so wait for playerReady.
const { Inventory } = kcds.players; const { Events } = kcds.core; const savedOutfits = new Map(); Events.on('playerDisconnect', (player) => { const current = Inventory.get(player); if (!current) { return; } const items = current.items.map((row) => ({ id: row.id, item: row.item, amount: row.amount, metadata: row.metadata, equipped: row.equipped, })); savedOutfits.set(player.nickname, { items, quickslots: current.quickslots }); }); Events.on('playerReady', (player) => { const outfit = savedOutfits.get(player.nickname); if (outfit) { Inventory.set(player, outfit); } });
This example only keeps it in memory. A real server should save it in a database.
Reference: Inventory.