Avatar loader & customization system: merge onto main + fix regressions, add smoke coverage #53
@@ -28,20 +28,20 @@ var skin_tone: Color = Color.TRANSPARENT
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Inner class: SlotItem
|
||||
# Inner classes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Represents a single equipped slot item in a loadout.
|
||||
## Used by AvatarVisualizer to access slot data.
|
||||
## Represents a single equipped item in a slot.
|
||||
## Used by AvatarVisualizer to iterate over equipped items.
|
||||
class SlotItem:
|
||||
## The asset ID of the equipped item.
|
||||
var asset_id: String = ""
|
||||
## The slot key this item belongs to.
|
||||
var slot_key: String = ""
|
||||
## The slot this item belongs to.
|
||||
var slot_id: String
|
||||
## The catalog asset ID of the equipped item.
|
||||
var asset_id: String
|
||||
|
||||
func _init(p_asset_id: String = "", p_slot_key: String = "") -> void:
|
||||
func _init(p_slot_id: String, p_asset_id: String) -> void:
|
||||
slot_id = p_slot_id
|
||||
asset_id = p_asset_id
|
||||
slot_key = p_slot_key
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -52,6 +52,10 @@ func _init() -> void:
|
||||
validation_errors = []
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Serialization
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Create a loadout from a JSON string.
|
||||
static func from_json(json_string: String) -> AvatarLoadout:
|
||||
var loadout := AvatarLoadout.new()
|
||||
@@ -119,6 +123,10 @@ func to_json() -> String:
|
||||
return JSON.stringify(data, " ")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Validate this loadout against the slot registry.
|
||||
## Returns true if valid; populates validation_errors if not.
|
||||
func validate() -> bool:
|
||||
@@ -182,6 +190,10 @@ func is_valid() -> bool:
|
||||
return validate()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Accessors
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Check if a specific slot is equipped with a non-null item.
|
||||
func is_equipped(slot_id: String) -> bool:
|
||||
if not AvatarSlotRegistry.has_slot(slot_id):
|
||||
@@ -254,6 +266,32 @@ func remove_accessory(asset_id: String) -> bool:
|
||||
return false
|
||||
|
||||
|
||||
## Iterate over equipped items as SlotItem objects.
|
||||
## Returns an array of SlotItem for each equipped slot.
|
||||
func get_equipped_items() -> Array[SlotItem]:
|
||||
var items: Array[SlotItem] = []
|
||||
|
||||
for slot_id in AvatarSlotRegistry.SLOT_IDS:
|
||||
var value = slots.get(slot_id)
|
||||
if value == null:
|
||||
continue
|
||||
|
||||
if typeof(value) == TYPE_STRING:
|
||||
var asset_id = value as String
|
||||
if not asset_id.is_empty():
|
||||
items.append(SlotItem.new(slot_id, asset_id))
|
||||
elif typeof(value) == TYPE_ARRAY:
|
||||
for item_id in value:
|
||||
if item_id is String and not item_id.is_empty():
|
||||
items.append(SlotItem.new(slot_id, item_id))
|
||||
|
||||
return items
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hashing
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
## Compute a hash of this loadout for change detection.
|
||||
## Mirrors the server's loadout_hash concept.
|
||||
func compute_hash() -> int:
|
||||
|
||||
Reference in New Issue
Block a user