foitin/engine/core/character_data.gd
m3ultra 8e58b88262 Blender render pipeline + first real character from asset library
- pipeline/render_moves.py: headless Blender 5.x renderer — imports rigged
  FBX/GLB, remaps Mixamo bone namespaces (mixamorigN:), fixes FBX axis/scale,
  normalises height, centres ground pivot, renders transparent ortho sprites
- engine: sprite_scale in manifest (scales sprite + all boxes), --p1/--p2
  roster picks for testing
- characters/trainer: woman_athleisure_01 (Mixamo, web-ok) rendered through
  the full pipeline as integration proof — idle/jab/hit from NPC clips,
  silhouette auto-hurtboxes, verified in-game vs alpha

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 19:02:39 +10:00

60 lines
2.2 KiB
GDScript

class_name CharacterData
## Loads one character folder (see characters/_spec/CHARACTER_SPEC.md).
## The roster is whatever folders exist under characters/ — adding a
## fighter is dropping a folder in, no code changes.
var id := ""
var display_name := ""
var health := 1000
var walk_speed := 3.0
var back_speed := 2.5
var jump_velocity := -13.5
var color := Color.WHITE
var sprite_scale := 1.0 # draw scale; also scales all box coords
var normals := {} # "P" -> move name, "6P" -> forward+P, "K" -> ...
var command_moves: Array = [] # [{motion:[2,3,6], button:"P", move:"rush_palm"}]
var moves := {} # name -> MoveData
var portrait: Texture2D = null
static func load_dir(dir_path: String) -> CharacterData:
var c := CharacterData.new()
var manifest_text := FileAccess.get_file_as_string(dir_path + "/manifest.json")
if manifest_text.is_empty():
push_error("CharacterData: missing manifest.json in " + dir_path)
return c
var d = JSON.parse_string(manifest_text)
c.id = d.get("id", dir_path.get_file())
c.display_name = d.get("name", c.id.to_upper())
c.health = int(d.get("health", 1000))
c.walk_speed = float(d.get("walk_speed", 3.0))
c.back_speed = float(d.get("back_speed", 2.5))
c.jump_velocity = float(d.get("jump_velocity", -13.5))
c.color = Color(d.get("color", "#ffffff"))
c.sprite_scale = float(d.get("sprite_scale", 1.0))
c.normals = d.get("normals", {})
c.command_moves = d.get("command_moves", [])
var moves_global := ProjectSettings.globalize_path(dir_path + "/moves")
for move_dir in DirAccess.get_directories_at(moves_global):
c.moves[move_dir] = MoveData.load_dir(dir_path + "/moves/" + move_dir)
var portrait_path := ProjectSettings.globalize_path(dir_path + "/portrait.png")
if FileAccess.file_exists(portrait_path):
var img := Image.load_from_file(portrait_path)
if img:
c.portrait = ImageTexture.create_from_image(img)
return c
static func scan_roster(root := "res://characters") -> Array:
var out: Array = []
var global := ProjectSettings.globalize_path(root)
var dirs := Array(DirAccess.get_directories_at(global))
dirs.sort()
for d in dirs:
if d.begins_with("_") or d.begins_with("."):
continue
out.append(root + "/" + d)
return out