Files
last-hearth/docs/character-creator.md
T
2026-11-05 09:57:11 -05:00

5.7 KiB
Executable File
Raw Blame History

Woodland traveler character creator

Use /character at any time while connected, or open Character appearance from the inventory. Creation is opt-in: joining restores the saved appearance without opening the creator. Existing location selection and field-note join forms retain priority.

A quiet traveler, not a costume catalogue

All choices are inclusive and independent, with no gender, identity or stat fields. Compact, lean, balanced, broad and sturdy silhouettes have identical mechanics, height, camera, collision, reach, movement and held-tool anchors. Hair, layered field clothes, cloaks, belt gear and packs use original low-poly geometry with hand-drawn, muted woodland pixel art.

Four tabs keep the 14 × 11 form manageable:

Tab Choices
Face 5 body silhouettes, 6 skin tones, 5 face-line patterns, 5 eye colors
Hair 10 styles, 6 shared hair/facial-hair colors, 7 facial-hair styles
Clothing 7 garments, 8 cloth colors, 4 trousers, 3 boot leathers
Finishing 9 accents, 8 accent colors, 5 face-detail patterns

Dropdowns use readable names, not gender categories or free-form colors. Cloth and accent palettes are sage, teal, slate, mauve, ochre, rust, linen and charcoal. Transparent none layers and colors under absent hair/accents mean the raw selection combinations are not all visually distinct; no uniqueness count is claimed.

The form contains a real Luanti model[] preview, not a screenshot, external browser or URL. It uses one composited 64 × 64 texture and the selected B3D. The preview turns continuously and supports mouse dragging; Front, Profile and Back reset its camera angle. These controls never rotate the actual player.

Changing a dropdown, switching tabs, Randomize and Reset draft affect only a private session draft. Randomize chooses from every fixed allowlist using bounded math.random; it is not security randomness and introduces no secrets. Reset copies the defaults rather than modifying shared state. Cancel or Escape discards the draft, preserving both the real appearance and saved bytes. Only Save / Apply commits. No need, inventory, identity or physics values are changed. Drafts disappear when the session ends.

Version-1 storage and recovery

The only new persistent value is the engine-owned player metadata string hearth:appearance. It is independent of the existing global save schema and world/mod storage. A complete flat JSON object contains exactly version:1 and these string selections:

body, skin, face, eyes, hair, hairColor, beard, garment, cloth, trousers, boots, accent, accentColor, detail.

Defaults match the authoritative art contract: balanced, sand, calm, hazel, sidepart, chestnut, none, tunic, teal, earth, walnut, scarf, sage, none. An absent value uses defaults without writing metadata. Invalid, unknown-version, missing/extra-field, malformed, nested or over-2048-byte values show safe defaults and a fixed warning. Original corrupt bytes are not overwritten, and appearance errors never kick a player. Only an explicit successful Apply replaces that value. Back up the world before manual recovery.

Encoding and decoding use core.write_json / core.parse_json(raw,nil,true); parser errors do not log player payloads. There is no executable deserialization, custom path, uploaded texture or free-form hexadecimal color. Every mesh and layer filename is built solely from fixed IDs. Compositing order is skin, face, eyes, garment, trousers, boots, hair, beard, accent, detail; accents layer above hair so a selected hood frames and covers it. The model includes an original draped mantle, belt, pack, projecting nose and ears. Face overlays are skin-neutral; there is no global color tint. The held-tool material remains the existing compatibility texture.

Apply serializes and checks the roundtrip, then writes metadata before publishing mesh and textures. A thrown/false metadata write or serialization failure keeps the previous published appearance and saved session selection, leaving the draft available to retry. Engine metadata durability still follows normal engine player saving; this is not a new disk transaction service.

Integration and validation boundary

The factory receives (hearth, api), registers its own command/receive-fields callback, and exports apply, on_join, show, handle, defaults, options, validate, decode, encode, preview and snapshot-only state. Bootstrap instantiates it before the interface and players; the player join handler calls it after base property initialization. Only the current engine-supplied connected player object and its own session can edit an open draft. Target names, arbitrary fields, hidden-tab selections and invalid dropdown indices are rejected as a whole event. All displayed data is formspec-escaped; text and input sizes are bounded.

CodeGuard input/storage guidance was applied: allowlisted keys and selections, bounded parsing, session ownership, preserved corrupt data and sanitized errors. No credentials, certificate handling, cryptography, network calls or dependencies are introduced. No authentication or database infrastructure is changed.

Focused offline Lua harnesses cover character behavior, player join integration, inventory navigation and bootstrap order. Mocks model native JSON through registered exact fixture strings, not executable Lua. Native engine rendering, mouse behavior and visual fit still require client review; no full-engine or live-world test is claimed by this change.