feat: character creation wizard (#108) #134
No reviewers
Labels
No labels
alpha:wave-0
alpha:wave-1
alpha:wave-2
alpha:wave-3
area:assets
area:combat
area:ecology
area:infra
area:render
area:scripting
area:ui
area:world
enhancement
epic
migration
post-alpha
roadmap
tech-debt
type:bug
type:chore
type:design
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
marco/IsoMmo!134
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/character-creation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Character creation (#108): a first-login, UO-style wizard to build the character that's mine —
pick a hair style, beard style, hair/beard/skin colour and a name, with a live
paperdoll preview (the same UO paperdoll the in-game character sheet draws — same frame, same
front orientation), a Randomize shortcut, and a live name-availability check. From then on
that look is what nearby players see in the world.
The important call this PR makes (a mid-design pivot): appearance is game state, not identity.
Auth stays identity-only (the name); the client sends a validated
SetAppearanceintent to theGameServer, which owns it — validates every choice against a server catalog, accepts it once
(a barber is a later feature), persists it in the save blob (
PlayerMobilev3), and reconciles itto observers via the existing appearance pass. This keeps the strict JWT-only Auth↔GameServer
contract clean instead of leaking "hair/beard" into the auth service.
Rendering uses a runtime per-layer tint on hue-neutral grey-ramp UO art (extracted from the
classic client): in-world, hair/beard are anim overlay layers over the body, each multiplied by its
colour (skin tints the body); the paperdoll/preview do the same with the hair/beard gumps via a
shared
PaperdollFigure— so the preview and the in-game paperdoll are identical by construction,and colours are a client-side table (
AppearancePalette), keeping the wire a stable semantickey (art-agnostic). Discovered UO art ids are recorded in
docs/uo-asset-map.mdso we don'tre-hunt them.
Name handling is hardened server-side (the name rides every AoI broadcast): max length,
letters+spaces whitelist, NFKC + zero-width/combining strip before the world-unique check, and a
reserved-word blocklist.
POST /me/characterreturns a typed reason (too-short / taken /invalid-chars / reserved / …) so the wizard shows a specific inline message, and a new
GET /me/character/availablepowers the live check.While building the two new screens we extracted a small reusable
IsoMmo.Client.UIwidget designsystem (Widget base with Visible/Enabled, Button, self-focusing TextInputWidget, Label, Panel,
OptionStepper, SwatchPicker, WidgetGroup). Screens compose widgets and stay thin coordinators
(Screen HARD GATE); login was refactored onto it, and the dedicated
CharacterCreationScreenisnot bolted onto login. Documented in
docs/ui-widgets.md.Follow-ups filed: #133 (TLS end-to-end before public exposure).
ProtocolVersionbumped 8 → 9.Known limitation: the male beard paperdoll gump set is sparse (~2), so a few beard styles reuse a
gump on the doll while staying distinct in-world — sourcing more is a follow-up.
Screenshots / recording
Piloted from a fresh database (wiped auth db +
world.sav) via the debug harness(
docs/debug-harness.md):wizard <user>→ drive withwiz …→screenshot, thenwiz confirm→
open doll, andlogoutto build the next character.The wizard (note "Enter world" stays disabled until the name is valid):
Consistency across three combinations — the wizard preview matches the in-game paperdoll,
same figure, same orientation (long-hair+goatee / pageboy / mohawk+mustache, different colours &
skin tones):
A created character's in-game paperdoll + the same character in the world:
Commands (abridged):
wizard gimli;wiz hair 1;wiz beard 3;wiz haircolor 3;wiz skin 2;wiz name Gimli;wiz confirm;open doll;logout;wizard legolas…How it was tested
CharacterAppearance/AppearanceCatalogvalidation, wireround-trip incl.
SetAppearance); GameServer (ChooseAppearancevalid / set-once-rejected /out-of-catalog-rejected / save-restore round-trip); Auth (name too-long / digits / reserved /
zero-width-collision + availability free/taken); Client.Core (
AppearancePalettedistinct +fallback,
AppearanceSelectioncycle/wrap/randomize).just lint(CSharpier + analyzers, zero warnings) andjust test(377 tests) green; wholesolution builds incl. client + tools.
combinations (screenshots above), verifying preview == in-game paperdoll each time.
Checklist
just lintpasses (CSharpier + analyzers, zero warnings)just testis green🤖 Generated with Claude Code