Avatar customization: skin tone, head swap, and hair now actually apply
checkpoint / checkpoint (push) Successful in 1m45s

Three dead controls on the customization screen:

- Skin tone slider did nothing: neither the visualizer nor AvatarSystem
  ever read loadout.skin_tone. Added AvatarLoader.apply_skin_tone() -- it
  retints the base body's skin surfaces (matched by the placeholder skin
  albedo, i.e. head + arms) -- and both build paths call it. Slider now
  maps across a deep-to-pale skin range.

- "Cyber Head" did nothing: its catalog entry was type "body_part", which
  load_attachment() rejects, and the placeholder body is one mesh so there
  was nothing to hide either. Retyped it as an attachment -> it mounts on
  the Head bone as a cyber visor.

- Mohawk / ponytail sat inside the head: the Head and HeadTop bones were
  well below the visual head (bone at 1.58, head crown at ~2.03). Moved
  Head to the head's centre and HeadTop just above the crown (mesh is
  rigidly bound, so it doesn't move), and retuned the hair/visor/glasses
  meshes and offsets to match.

Verified: avatar smoke 14/14 (source + pack), fresh-clone, 2p multiplayer,
and drove the real customization screen in rendered screenshots (equip
head+mohawk+mid skin, then ponytail+glasses+pale skin).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
2026-09-06 16:10:46 +00:00
parent 181ba6ae02
commit 43a3b888da
11 changed files with 60 additions and 21 deletions
+6 -7
View File
@@ -489,15 +489,14 @@ func _refresh_preview() -> void:
func _on_skin_tone_changed(value: float) -> void:
if loadout == null:
return
# Map slider 0..1 to a skin tone color range.
# Map slider 0..1 across a deep-to-pale skin range.
var t := value
var skin := Color(
0.6 + 0.3 * t, # R
0.4 + 0.2 * t, # G
0.3 + 0.15 * t, # B
1.0
loadout.skin_tone = Color(
0.35 + 0.60 * t, # R
0.22 + 0.55 * t, # G
0.16 + 0.50 * t, # B
1.0,
)
loadout.skin_tone = skin
_refresh_preview()
loadout_changed.emit(loadout)