PC3 Phase 1.4-1.6: avatars wired into the game end to end
- api_client.gd: get_avatar_loadout() / save_avatar_loadout() - main_menu.gd: on successful auth (fresh or session-restore), load the catalog + fetch the saved loadout into AvatarSystem so the in-world player renders as the player's avatar. Best-effort: a slow/failed prime never gates getting in (UI goes authenticated first, prime runs after). New "Customize Avatar" button (enabled only when signed in). - avatar_system.gd: local-player convenience API -- ensure_catalog(), set_loadout_from_dict() (from the backend's GET shape), has_loadout(), build_current_avatar(). - player.gd: swaps the placeholder capsule for the assembled avatar (feet-aligned to the capsule bottom) when a loadout is primed; silent fallback to the capsule otherwise. Camera stays first-person for now (third-person is a noted follow-up). - avatar_visualizer.gd: rewrote it -- the old version referenced AvatarLoader.SlotResult / load_slot() which never existed, so it didn't compile and took avatar_customization_ui.gd down with it. Now a thin wrapper over AvatarLoader for the preview. - avatar_customization_ui.gd: seeds from the live loadout, saves through to AvatarSystem + PUT /api/avatar/loadout (local file kept as fallback), fixed a Camera3D.look_at() before-in-tree crash. - scenes/avatar_customization.tscn rebuilt (old one used the Godot-3 `ViewportContainer` node type) + avatar_customization_screen.gd wrapper that returns to the menu on close. - smoke_test_avatar_in_world.gd: primed loadout -> game.tscn -> player shows the assembled avatar, capsule hidden, rig + bone mounts present. Verified: cold-cache smokes green (player 15/15, avatar_system 14/14, avatar_in_world 10/10, main_menu_ui 13/13); against a live backend main_menu_full_flow 12/12, main_menu_back 20/20, world_list 4/4, enter_world 9/9, e2e 4/4; plus a manual register->default-loadout-> customize->save->re-fetch-persisted round trip. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
This commit is contained in:
@@ -230,3 +230,46 @@ func get_assets_for_slot(slot_id: String) -> Array[String]:
|
||||
func get_asset_display_name(asset_id: String) -> String:
|
||||
var asset := catalog.resolve(asset_id)
|
||||
return asset.get("display_name", asset_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Local-player convenience API
|
||||
#
|
||||
# The rest of the game (the main menu, the in-world player) only needs three
|
||||
# things: load the catalog once, hand us the player's loadout (as the dict the
|
||||
# backend returns), and ask for an assembled avatar node to drop into a scene.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## True once a loadout has been handed to us (from the backend or a default).
|
||||
func has_loadout() -> bool:
|
||||
return current_loadout != null
|
||||
|
||||
|
||||
## Ensure the catalog is loaded (idempotent). Safe to await repeatedly.
|
||||
func ensure_catalog() -> bool:
|
||||
if is_catalog_loaded:
|
||||
return true
|
||||
return load_catalog() == OK
|
||||
|
||||
|
||||
## Set the current player's loadout from the dict shape the backend's
|
||||
## GET /api/avatar/loadout returns (i.e. AvatarLoadout.to_json() parsed).
|
||||
## Returns true if it validated and was stored.
|
||||
func set_loadout_from_dict(loadout_dict: Dictionary) -> bool:
|
||||
var lo := AvatarLoadout.from_json(JSON.stringify(loadout_dict))
|
||||
if not lo.validate():
|
||||
push_warning("AvatarSystem: rejecting invalid loadout dict: %s"
|
||||
% str(lo.validation_errors))
|
||||
return false
|
||||
current_loadout = lo
|
||||
loadout_changed.emit(current_loadout)
|
||||
return true
|
||||
|
||||
|
||||
## Assemble a fresh avatar Node3D from the current loadout, ready to parent
|
||||
## into a scene (e.g. onto the in-world player). Returns null if there's no
|
||||
## loadout or the catalog isn't loaded or assembly failed.
|
||||
func build_current_avatar() -> Node3D:
|
||||
if current_loadout == null or not is_catalog_loaded:
|
||||
return null
|
||||
return load_avatar(current_loadout)
|
||||
|
||||
Reference in New Issue
Block a user