feat: implement modular character skinning system with procedural and imported model support

This commit is contained in:
DottsGit
2026-06-11 23:05:36 -04:00
parent 3bdf8f413b
commit 523fcc5226
14 changed files with 587 additions and 15 deletions
+117
View File
@@ -5,6 +5,7 @@ var main_vbox: VBoxContainer
var resume_btn: Button
var respawn_btn: Button
var loadouts_btn: Button
var character_btn: Button
var settings_btn: Button
var return_btn: Button
var exit_btn: Button
@@ -51,6 +52,12 @@ var rebind_overlay: ColorRect
var rebind_label: Label
var waiting_for_input_action: String = ""
# Character Skin UI
var character_editor: Control
var character_back_btn: Button
var skin_grid: GridContainer
var skin_preview_label: Label
var quit_dialog: ConfirmationDialog
var return_dialog: ConfirmationDialog
@@ -146,6 +153,11 @@ func _build_ui() -> void:
loadouts_btn.custom_minimum_size = Vector2(200, 50)
main_vbox.add_child(loadouts_btn)
character_btn = Button.new()
character_btn.text = "Character"
character_btn.custom_minimum_size = Vector2(200, 50)
main_vbox.add_child(character_btn)
settings_btn = Button.new()
settings_btn.text = "Settings"
settings_btn.custom_minimum_size = Vector2(200, 50)
@@ -667,6 +679,48 @@ func _build_ui() -> void:
rebind_label.add_theme_font_size_override("font_size", 32)
rebind_center.add_child(rebind_label)
# ── Character Editor Root ──
character_editor = Control.new()
character_editor.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.add_child(character_editor)
var char_center = CenterContainer.new()
char_center.set_anchors_preset(Control.PRESET_FULL_RECT)
character_editor.add_child(char_center)
var char_vbox = VBoxContainer.new()
char_vbox.custom_minimum_size = Vector2(600, 500)
char_vbox.add_theme_constant_override("separation", 15)
char_center.add_child(char_vbox)
var char_title = Label.new()
char_title.text = "CHARACTER SKIN"
char_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
char_title.add_theme_font_size_override("font_size", 32)
char_vbox.add_child(char_title)
skin_preview_label = Label.new()
skin_preview_label.text = "Selected: Default Blue"
skin_preview_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
skin_preview_label.add_theme_font_size_override("font_size", 18)
char_vbox.add_child(skin_preview_label)
var skin_scroll = ScrollContainer.new()
skin_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
char_vbox.add_child(skin_scroll)
skin_grid = GridContainer.new()
skin_grid.columns = 4
skin_grid.add_theme_constant_override("h_separation", 12)
skin_grid.add_theme_constant_override("v_separation", 12)
skin_grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL
skin_scroll.add_child(skin_grid)
character_back_btn = Button.new()
character_back_btn.text = "Back to Menu"
character_back_btn.custom_minimum_size = Vector2(0, 40)
char_vbox.add_child(character_back_btn)
# ── Quit Dialog ──
quit_dialog = ConfirmationDialog.new()
@@ -734,6 +788,8 @@ func _connect_signals() -> void:
save_loadout_btn.pressed.connect(_save_current_loadout)
quit_dialog.confirmed.connect(_quit_game)
return_dialog.confirmed.connect(_return_to_main_menu)
character_btn.pressed.connect(_show_character)
character_back_btn.pressed.connect(_show_main_menu)
func _show_return_dialog() -> void:
return_dialog.popup_centered()
@@ -753,14 +809,75 @@ func _show_main_menu() -> void:
main_vbox.visible = true
loadout_editor.visible = false
settings_editor.visible = false
character_editor.visible = false
func _show_loadouts() -> void:
main_vbox.visible = false
settings_editor.visible = false
character_editor.visible = false
loadout_editor.visible = true
edit_panel.visible = false
_populate_loadout_list()
func _show_character() -> void:
main_vbox.visible = false
settings_editor.visible = false
loadout_editor.visible = false
character_editor.visible = true
_populate_skin_grid()
func _populate_skin_grid() -> void:
for c in skin_grid.get_children():
c.queue_free()
var skin_mgr = get_node_or_null("/root/SkinManager")
if not skin_mgr: return
var current_id = skin_mgr.selected_skin_id
var all_ids = skin_mgr.get_all_skin_ids()
for id in all_ids:
var skin = skin_mgr.get_skin(id)
var panel = PanelContainer.new()
var style = StyleBoxFlat.new()
style.bg_color = skin.primary_color
style.corner_radius_top_left = 8
style.corner_radius_top_right = 8
style.corner_radius_bottom_left = 8
style.corner_radius_bottom_right = 8
if id == current_id:
style.border_width_left = 4
style.border_width_right = 4
style.border_width_top = 4
style.border_width_bottom = 4
style.border_color = Color.WHITE
skin_preview_label.text = "Selected: " + skin.display_name
panel.add_theme_stylebox_override("panel", style)
panel.custom_minimum_size = Vector2(120, 120)
var btn = Button.new()
btn.text = skin.display_name
btn.set_anchors_preset(Control.PRESET_FULL_RECT)
var btn_style = StyleBoxEmpty.new()
btn.add_theme_stylebox_override("normal", btn_style)
btn.add_theme_stylebox_override("hover", btn_style)
btn.add_theme_stylebox_override("pressed", btn_style)
btn.add_theme_stylebox_override("focus", btn_style)
# Text shadow for readability
btn.add_theme_color_override("font_color", Color.WHITE)
btn.add_theme_color_override("font_outline_color", Color.BLACK)
btn.add_theme_constant_override("outline_size", 4)
btn.pressed.connect(func():
skin_mgr.set_player_skin(id)
_populate_skin_grid() # Refresh borders
)
panel.add_child(btn)
skin_grid.add_child(panel)
func _populate_video_ui() -> void:
if _display_mode_opt:
for i in range(_display_mode_opt.item_count):