netfishing/progression/character_customization_catalog.gd

138 lines
3.9 KiB
GDScript3
Raw Normal View History

class_name CharacterCustomizationCatalog
extends RefCounted
const CATEGORY_IDS: PackedStringArray = [
"species",
"fur_pattern",
"ears",
"eyes",
"nose",
"mouth",
"tail",
]
const CATEGORY_LABELS: Dictionary = {
2026-08-04 22:34:43 -04:00
"species": "head",
"fur_pattern": "fur color",
"ears": "ears",
"eyes": "eyes",
"nose": "nose",
"mouth": "mouth",
"tail": "tail",
}
const OPTIONS: Dictionary = {
2026-08-04 22:34:43 -04:00
"species": [
{"id": "round", "label": "round"},
{"id": "pointy", "label": "pointy"},
],
# The serialized field remains `fur_pattern` for save and network
# compatibility. It holds the initial solid fur-color selection until the
# authored pattern layer is ready.
"fur_pattern": [
{"id": "white", "label": "white", "color": Color("f2f0e8")},
{"id": "gray", "label": "gray", "color": Color("819398")},
{"id": "charcoal", "label": "charcoal", "color": Color("34444a")},
{"id": "brown", "label": "brown", "color": Color("7b4a32")},
{"id": "orange", "label": "orange", "color": Color("c86c36")},
{"id": "yellow", "label": "yellow", "color": Color("d8c545")},
{"id": "green", "label": "green", "color": Color("6f913c")},
{"id": "teal", "label": "teal", "color": Color("3a8790")},
],
"ears": [
{"id": "none", "label": "none"},
{"id": "pointy_long", "label": "long"},
{"id": "pointy_short", "label": "short"},
{"id": "pointy_wide", "label": "wide"},
],
"eyes": [
{"id": "simple_shine", "label": "simple shine"},
{
"id": "simple_shine_eyebrows",
"label": "simple shine + eyebrows",
},
],
"nose": [{"id": "dog_round", "label": "round"}],
"mouth": [{"id": "three", "label": "three"}],
"tail": [{"id": "none", "label": "none"}],
}
2026-08-04 22:34:43 -04:00
const LEGACY_OPTION_ALIASES: Dictionary = {
"species": {"default": "round"},
"fur_pattern": {"solid": "white"},
"ears": {"default": "none"},
"eyes": {"default": "simple_shine"},
"nose": {"default": "dog_round"},
"mouth": {"default": "three"},
}
static func default_snapshot() -> Dictionary:
return {
2026-08-04 22:34:43 -04:00
"species": "round",
"fur_pattern": "white",
"ears": "none",
"eyes": "simple_shine",
"nose": "dog_round",
"mouth": "three",
"tail": "none",
}
static func options_for(category_id: String) -> Array:
return OPTIONS.get(category_id, [])
static func category_label(category_id: String) -> String:
return str(CATEGORY_LABELS.get(category_id, category_id))
static func is_valid_option(category_id: String, option_id: String) -> bool:
2026-08-04 22:34:43 -04:00
option_id = canonical_option_id(category_id, option_id)
for option: Dictionary in options_for(category_id):
if str(option.get("id", "")) == option_id:
return true
return false
2026-08-04 22:34:43 -04:00
static func canonical_option_id(category_id: String, option_id: String) -> String:
var aliases: Dictionary = LEGACY_OPTION_ALIASES.get(category_id, {})
return str(aliases.get(option_id, option_id))
static func option_color(category_id: String, option_id: String) -> Color:
var canonical_id: String = canonical_option_id(category_id, option_id)
for option: Dictionary in options_for(category_id):
if str(option.get("id", "")) == canonical_id:
return option.get("color", Color.WHITE) as Color
return Color.WHITE
static func validate_snapshot(value: Variant) -> bool:
if typeof(value) != TYPE_DICTIONARY:
return false
var snapshot: Dictionary = value
if snapshot.size() != CATEGORY_IDS.size():
return false
for category_id: String in CATEGORY_IDS:
if (
typeof(snapshot.get(category_id)) != TYPE_STRING
or not is_valid_option(category_id, str(snapshot[category_id]))
):
return false
return true
static func sanitized_snapshot(value: Variant) -> Dictionary:
var result := default_snapshot()
if typeof(value) != TYPE_DICTIONARY:
return result
var snapshot: Dictionary = value
for category_id: String in CATEGORY_IDS:
2026-08-04 22:34:43 -04:00
var option_id: String = canonical_option_id(
category_id, str(snapshot.get(category_id, ""))
)
if is_valid_option(category_id, option_id):
result[category_id] = option_id
return result