Skip to content
KCKingdoms Connected Scripting APIScripting guides and API
GuideMaintainer authored

Player appearance (face, hair, body)

Give players their own face, hair, beard and skin from the game's catalog, and handle gender and per-face beards.

On a fresh server everyone is Henry. player.setAppearance changes that, and the Appearances catalog lists what you can choose from.

src/server/index.ts
Events.on("playerSpawned", (player) => {
player.setAppearance(Appearances.random());
});

Call it from playerSpawned, not playerConnect, when their client has a body to apply it to. Appearances.random() is uniform over the catalog, not over what looks good together, so the result looks recognisably random.

Player.outfitPresets(gender?) lists the game’s clothing presets. player.setOutfit(nameOrGuid) equips a preset for the player’s gender:

const presets = Player.outfitPresets(player.appearance.gender);
const preset = presets[0];
if (preset && !player.setOutfit(preset)) {
console.log("The outfit was refused. Check inventory space and gender.");
}

The old clothes are unequipped and stay in the inventory. New clothes are added and worn; weapons and other items stay as they were. Repeated calls can add more clothing. False means the preset is unknown, the gender does not match, the inventory is unavailable or there is no room. The player’s game dresses them after the server inventory changes.

The sample game mode provides /outfit; its presets do not change the player’s face or body.

An Appearance is a gender plus four of the game’s character-component names:

const look: Appearance = player.appearance;
// { gender: "male", head: "", hair: "", beard: "", body: "" }
Part What it is
head The face.
hair The hairstyle, colour included.
beard The beard. Male only.
body The skin: complexion and build.

An empty string means “leave it to the game”. A body nobody has chosen for reads as all parts empty.

setAppearance takes a partial. An empty string hands that part back:

player.setAppearance({ hair: "m_hair_barber_07" });
player.setAppearance({ beard: "" }); // clean-shaven again

It returns true when the request went out; player.appearance changes with their next update. It returns false for a name not in the catalog, a name from the other gender’s catalog, a beard that face cannot wear, or no connection. See Server vs client authority.

Appearances.options("hair"); // male by default
Appearances.options("head", "female");
Appearances.find("hair", "m_hair_barber_07"); // AppearanceOption or null
Appearances.beards("m_head_012"); // beards that face can grow
Appearances.random("female"); // a complete Appearance

Every name is also listed in Faces, hair and skins and Beards.

Tree Faces Hairstyles Beards Skins
male 212 262 51 47
female 61 71 0 41

The game ships each hairstyle once per colour. Each AppearanceOption has a group naming its style, so grouping turns 262 entries into a readable list:

const styles = new Map<string, string[]>();
for (const option of Appearances.options("hair")) {
const style = option.group ?? option.name;
styles.set(style, [...(styles.get(style) ?? []), option.name]);
}

Check names from players with Appearances.find first, so you can give a useful error instead of a refused request.

A beard is modelled against particular heads. Generic faces carry 24 each, Henry’s face the 15 the barber offers, and 14 faces carry none. A face and beard pair that was never modelled is refused whole, face included.

Build beard choices from Appearances.beards(head), not options("beard"), and when changing the face, check the current beard fits. The default gamemode’s /appearance set head shaves rather than refuses:

function setHead(player: Player, head: string): boolean {
const { gender, beard } = player.appearance;
const fits = beard === "" || Appearances.beards(head, gender).some((option) => option.name === beard);
return player.setAppearance(fits ? { head } : { head, beard: "" });
}

Appearances.random() draws the beard after the face, so its result always fits.

Gender lives on the body’s archetype and decides which half of the catalog applies. The halves never share names, so clear the parts with it:

player.setAppearance({ gender: "female", head: "", hair: "", beard: "", body: "" });

The default gamemode’s /lookalike reads one player’s published look and asks another client to wear it:

const PARTS = ["head", "hair", "beard", "body"] as const;
function copyLook(player: Player, model: Player): string {
const wanted = model.appearance;
if (PARTS.every((part) => wanted[part] === "")) {
return `${model.nickname} has not chosen a body yet.`;
}
if (!player.setAppearance(wanted)) {
return "Refused. Your own client decides what it can wear.";
}
return `Asked to look like ${model.nickname}.`;
}

Clothing is separate: it is inventory, listed in player.equipment. Changing hair does not disturb a hat. See Items.