ani
This commit is contained in:
@@ -3,7 +3,8 @@ extends Node
|
||||
## Autoload: manages all available player skins.
|
||||
##
|
||||
## Skins come from two places:
|
||||
## 1. Built-in color tints for the procedural model (registered below).
|
||||
## 1. Built-in signature characters (registered below). These are the
|
||||
## authored Sakura/Akiba defaults and may layer equipment over a GLB rig.
|
||||
## 2. assets/characters/skins/skins.json — written automatically by
|
||||
## tools/pipeline.py for every imported character. No code changes needed
|
||||
## to add a new model: run the pipeline, restart the game, it's there.
|
||||
@@ -14,6 +15,7 @@ extends Node
|
||||
signal skin_changed(skin_id: String)
|
||||
|
||||
const SKINS_JSON := "res://assets/characters/skins/skins.json"
|
||||
const LOCAL_SKINS_JSON := "res://assets/characters/skins/skins.local.json"
|
||||
const SELECTION_CFG := "user://skin_selection.cfg"
|
||||
|
||||
var skins: Dictionary = {}
|
||||
@@ -23,14 +25,46 @@ var active_skin_id: String = "default"
|
||||
func _ready() -> void:
|
||||
_register_builtin_skins()
|
||||
_load_skins_json()
|
||||
_load_skin_registry(LOCAL_SKINS_JSON, false)
|
||||
_load_selection()
|
||||
# Fold the old global character choice into legacy loadouts, then derive the
|
||||
# live skin from the active loadout from this point onward.
|
||||
var loadout_manager := get_node_or_null("/root/LoadoutManager")
|
||||
if loadout_manager:
|
||||
loadout_manager.migrate_legacy_skin(active_skin_id)
|
||||
var loadout_skin: String = loadout_manager.get_active_skin_id()
|
||||
if skins.has(loadout_skin) and skins[loadout_skin].is_unlocked:
|
||||
active_skin_id = loadout_skin
|
||||
print("SkinManager: %d skins available" % skins.size())
|
||||
|
||||
|
||||
func _register_builtin_skins() -> void:
|
||||
_add_color_skin("default", "Default", "Standard issue", Color(0.2, 0.4, 0.8))
|
||||
_add_color_skin("red_team", "Red Team", "Red team colors", Color(0.8, 0.2, 0.2))
|
||||
_add_color_skin("forest", "Forest", "Forest camouflage", Color(0.2, 0.6, 0.2))
|
||||
_add_mecha_skin("default", "Sakura Vanguard",
|
||||
"HD anime shrine-runner in a petal-armored vector-thrust frame",
|
||||
"res://assets/characters/skins/sakura_pilot_hd.glb", "sakura",
|
||||
Color("efe7e8"), Color("e86f98"), Color("343a61"), Color("ffabc8"))
|
||||
_add_mecha_skin("akiba_pulse", "Akiba Pulse",
|
||||
"HD anime district pilot in a graphite neon cyber-mecha frame",
|
||||
"res://assets/characters/skins/akiba_pilot_hd.glb", "akiba",
|
||||
Color("272c3e"), Color("20c9d8"), Color("171a28"), Color("ff4eb8"))
|
||||
|
||||
|
||||
func _add_mecha_skin(id: String, display_name: String, description: String,
|
||||
model: String, theme: String, sleeve: Color, accent: Color,
|
||||
glove: Color, nail: Color) -> void:
|
||||
var skin := PlayerSkin.new()
|
||||
skin.skin_name = display_name
|
||||
skin.description = description
|
||||
skin.model_path = model
|
||||
skin.set("mecha_theme", theme)
|
||||
skin.viewmodel_sleeve_color = sleeve
|
||||
skin.viewmodel_accent_color = accent
|
||||
skin.viewmodel_glove_color = glove
|
||||
skin.viewmodel_nail_color = nail
|
||||
skin.viewmodel_hand_style = "fingerless"
|
||||
skin.viewmodel_sleeve_ratio = 0.72
|
||||
skin.is_unlocked = true
|
||||
skins[id] = skin
|
||||
|
||||
|
||||
func _add_color_skin(id: String, display_name: String, description: String, tint: Color) -> void:
|
||||
@@ -48,15 +82,20 @@ func _add_color_skin(id: String, display_name: String, description: String, tint
|
||||
|
||||
|
||||
func _load_skins_json() -> void:
|
||||
if not FileAccess.file_exists(SKINS_JSON):
|
||||
_load_skin_registry(SKINS_JSON, true)
|
||||
|
||||
|
||||
func _load_skin_registry(path: String, warn_if_malformed: bool) -> void:
|
||||
if not FileAccess.file_exists(path):
|
||||
return
|
||||
var file := FileAccess.open(SKINS_JSON, FileAccess.READ)
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if not file:
|
||||
return
|
||||
var data = JSON.parse_string(file.get_as_text())
|
||||
file.close()
|
||||
if not data is Dictionary or not data.has("skins"):
|
||||
push_warning("SkinManager: skins.json is malformed")
|
||||
if warn_if_malformed:
|
||||
push_warning("SkinManager: %s is malformed" % path)
|
||||
return
|
||||
for entry in data["skins"]:
|
||||
if not entry is Dictionary or not entry.has("id"):
|
||||
@@ -65,6 +104,7 @@ func _load_skins_json() -> void:
|
||||
skin.skin_name = entry.get("name", entry["id"])
|
||||
skin.description = entry.get("description", "")
|
||||
skin.model_path = entry.get("model", "")
|
||||
skin.set("mecha_theme", entry.get("mecha_theme", ""))
|
||||
skin.is_unlocked = entry.get("unlocked", true)
|
||||
var viewmodel = entry.get("viewmodel", {})
|
||||
if viewmodel is Dictionary:
|
||||
|
||||
Reference in New Issue
Block a user