netfishing/tests/fur_pattern_validation.gd
Voyager 84695c473b Expand per-part fur customization
Load authored pattern masks by body component, add custom fur colors and the themed picker UI, and advance the signed profile protocol to version 6.
2026-08-15 14:27:53 -04:00

404 lines
13 KiB
GDScript

extends SceneTree
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
CharacterCustomizationCatalog.refresh_fur_pattern_assets()
var legacy_snapshot := CharacterCustomizationCatalog.default_snapshot()
legacy_snapshot[CharacterCustomizationCatalog.FUR_STYLE_ID] = "spots_bengal"
for added_id: String in [
CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID,
CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID,
CharacterCustomizationCatalog.FUR_STYLE_EARS_ID,
CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID,
]:
legacy_snapshot.erase(added_id)
var migrated := CharacterCustomizationCatalog.sanitized_snapshot(
legacy_snapshot
)
assert(CharacterCustomizationCatalog.validate_snapshot(migrated))
for style_field: String in CharacterCustomizationCatalog.FUR_STYLE_IDS:
assert(migrated[style_field] == "bengal")
var fur_colors := CharacterCustomizationCatalog.options_for("fur_pattern")
assert(fur_colors.size() == 30)
for natural_color_id: String in [
"ivory",
"sand",
"tan",
"taupe",
"black",
"dark_brown",
"chocolate",
"chestnut",
"ginger",
"sage",
"forest",
]:
assert(CharacterCustomizationCatalog.is_valid_option(
"fur_pattern",
natural_color_id,
))
var custom_color := Color("2b91c7")
var custom_color_id := CharacterCustomizationCatalog.custom_fur_color_id(
custom_color
)
assert(custom_color_id == "custom_2b91c7")
assert(
CharacterCustomizationCatalog.custom_fur_color_id(
Color(custom_color.r, custom_color.g, custom_color.b, 0.2)
)
== custom_color_id
)
for color_field: String in CharacterCustomizationCatalog.FUR_COLOR_IDS:
assert(CharacterCustomizationCatalog.is_valid_option(
color_field,
custom_color_id,
))
assert(
CharacterCustomizationCatalog.option_color(
color_field, custom_color_id
).is_equal_approx(custom_color)
)
assert(not CharacterCustomizationCatalog.is_valid_option(
"fur_pattern", "custom_2b91cg"
))
assert(not CharacterCustomizationCatalog.is_valid_option(
"fur_pattern", "custom_2b91c7ff"
))
var pattern_ids: Array[String] = []
for option: Dictionary in CharacterCustomizationCatalog.options_for(
CharacterCustomizationCatalog.FUR_STYLE_ID
):
pattern_ids.append(str(option.get("id", "")))
assert(pattern_ids == [
"solid",
"bengal",
"calico",
"fox",
"paws",
"stripes",
"tiger",
"tummy",
])
var patterned := CharacterCustomizationCatalog.default_snapshot()
patterned["species"] = "round"
patterned["ears"] = "pointy_short"
patterned["tail"] = "fox"
patterned["fur_pattern"] = "orange"
patterned[CharacterCustomizationCatalog.FUR_STYLE_ID] = "calico"
patterned[CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID] = "paws"
patterned[CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID] = "tiger"
patterned[CharacterCustomizationCatalog.FUR_STYLE_EARS_ID] = "fox"
patterned[CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID] = "fox"
patterned["fur_color_2"] = "cream"
patterned["fur_color_3"] = "brown"
patterned["fur_color_4"] = "red"
assert(CharacterCustomizationCatalog.validate_snapshot(patterned))
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_ID, patterned
),
["solid", "bengal", "calico", "fox", "paws", "stripes", "tiger", "tummy"],
)
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID, patterned
),
["solid", "bengal", "fox", "paws"],
)
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID, patterned
),
["solid", "bengal", "fox", "tiger"],
)
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_EARS_ID, patterned
),
["solid", "fox"],
)
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID, patterned
),
["solid", "fox"],
)
var pointy_snapshot := patterned.duplicate(true)
pointy_snapshot["species"] = "pointy"
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_HEAD_ID, pointy_snapshot
),
["solid", "fox"],
)
var no_accessories := patterned.duplicate(true)
no_accessories["ears"] = "none"
no_accessories["tail"] = "none"
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_EARS_ID, no_accessories
),
["solid"],
)
_assert_option_ids(
CharacterCustomizationCatalog.fur_pattern_options_for_field(
CharacterCustomizationCatalog.FUR_STYLE_TAIL_ID, no_accessories
),
["solid"],
)
var body_texture := _assert_pattern_texture("calico", "body_main")
var arms_texture := _assert_pattern_texture("paws", "body_arms")
var head_texture := _assert_pattern_texture("tiger", "head_round")
var ears_texture := _assert_pattern_texture("fox", "ears_pointy_short")
var tail_texture := _assert_pattern_texture("fox", "tails_fox")
_assert_pattern_texture("tummy", "body_main")
_assert_pattern_texture("stripes", "body_main")
_assert_pattern_texture("fox", "head_pointy")
var visuals := PlayerVisualPresenter.instantiate_visuals()
root.add_child(visuals)
PlayerVisualPresenter.apply_appearance(visuals, patterned)
var skeleton := visuals.find_child("Skeleton3D", true, false) as Skeleton3D
assert(skeleton != null)
var body := skeleton.get_node_or_null("body_main") as MeshInstance3D
assert(body != null)
var body_arrays := body.mesh.surface_get_arrays(0)
var body_uvs := body_arrays[Mesh.ARRAY_TEX_UV] as PackedVector2Array
assert(not body_uvs.is_empty())
var uv_min := Vector2(INF, INF)
var uv_max := Vector2(-INF, -INF)
for uv: Vector2 in body_uvs:
uv_min = uv_min.min(uv)
uv_max = uv_max.max(uv)
assert(uv_min.x < 0.05 and uv_min.y < 0.05)
assert(uv_max.x > 0.95 and uv_max.y > 0.95)
var body_material := _assert_mesh_pattern(
skeleton, "body_main", body_texture
)
var arms_material := _assert_mesh_pattern(
skeleton, "body_arms", arms_texture
)
var head_material := _assert_mesh_pattern(
skeleton, "head_round", head_texture
)
var ears_material := _assert_mesh_pattern(
skeleton, "ears_pointy_short", ears_texture
)
var tail_material := _assert_mesh_pattern(
skeleton, "tails_fox", tail_texture
)
for material: ShaderMaterial in [
body_material,
arms_material,
head_material,
ears_material,
tail_material,
]:
assert(material.shader == preload("res://player/fur_pattern.gdshader"))
assert(material.get_shader_parameter("base_color") == Color("c86c36"))
assert(material.get_shader_parameter("red_zone_color") == Color("e6d39b"))
assert(material.get_shader_parameter("green_zone_color") == Color("7b4a32"))
assert(material.get_shader_parameter("blue_zone_color") == Color("a9433f"))
var recolored := patterned.duplicate(true)
recolored["fur_color_3"] = "teal"
PlayerVisualPresenter.apply_appearance(visuals, recolored)
for material: ShaderMaterial in [
body_material,
arms_material,
head_material,
ears_material,
tail_material,
]:
assert(material.get_shader_parameter("green_zone_color") == Color("3a8790"))
assert(
NetworkProfileProtocol.signature_fields({"appearance": patterned})
!= NetworkProfileProtocol.signature_fields({"appearance": recolored})
)
var other_arms := patterned.duplicate(true)
other_arms[CharacterCustomizationCatalog.FUR_STYLE_ARMS_ID] = "fox"
assert(
NetworkProfileProtocol.signature_fields({"appearance": patterned})
!= NetworkProfileProtocol.signature_fields({"appearance": other_arms})
)
var custom_snapshot := patterned.duplicate(true)
custom_snapshot["fur_pattern"] = custom_color_id
assert(CharacterCustomizationCatalog.validate_snapshot(custom_snapshot))
PlayerVisualPresenter.apply_appearance(visuals, custom_snapshot)
for material: ShaderMaterial in [
body_material,
arms_material,
head_material,
ears_material,
tail_material,
]:
assert(
(material.get_shader_parameter("base_color") as Color).is_equal_approx(
custom_color
)
)
assert(
NetworkProfileProtocol.signature_fields({"appearance": patterned})
!= NetworkProfileProtocol.signature_fields({"appearance": custom_snapshot})
)
var solid := CharacterCustomizationCatalog.default_snapshot()
PlayerVisualPresenter.apply_appearance(visuals, solid)
assert(body.material_override is StandardMaterial3D)
await _validate_fur_color_ui(patterned)
print("Fur pattern validation: PASS")
visuals.queue_free()
await process_frame
quit()
func _validate_fur_color_ui(snapshot: Dictionary) -> void:
var profile_page := ProfilePage.new()
profile_page.size = Vector2(1280.0, 720.0)
root.add_child(profile_page)
await process_frame
profile_page.set("_draft_appearance", snapshot.duplicate(true))
profile_page.call("_select_category", "fur_pattern")
profile_page.call("_select_fur_section", "colors")
await process_frame
await process_frame
var color_panel := profile_page.find_child(
"FurColorOptionsPanel", true, false
) as PanelContainer
var channel_grid := profile_page.find_child(
"FurColorChannelGrid", true, false
) as GridContainer
var palette_grid := profile_page.find_child(
"FurPaletteGrid", true, false
) as GridContainer
var picker := profile_page.find_child(
"FurCustomColorPicker", true, false
) as ColorPickerButton
var picker_popup := picker.get_popup()
var picker_control := picker.get_picker()
assert(color_panel != null and color_panel.size.x > 360.0)
assert(channel_grid != null and channel_grid.columns == 4)
assert(channel_grid.get_child_count() == 4)
assert(palette_grid != null and palette_grid.columns == 10)
assert(palette_grid.get_child_count() == 30)
for option_button: Button in palette_grid.get_children():
assert(
option_button.custom_minimum_size
== Vector2.ONE * ProfilePage.FUR_PALETTE_SWATCH_SIZE
)
for color_index: int in CharacterCustomizationCatalog.FUR_COLOR_IDS.size():
var category_id: String = (
CharacterCustomizationCatalog.FUR_COLOR_IDS[color_index]
)
var channel_button := profile_page.find_child(
"FurColorSlot%d" % (color_index + 1), true, false
) as Button
assert(channel_button != null)
var swatch := channel_button.get_node(
"ChannelContents/ActiveColorSwatch"
) as Panel
var channel_label := channel_button.get_node(
"ChannelContents/ChannelNumber"
) as Label
assert(swatch.size.is_equal_approx(
Vector2.ONE * ProfilePage.FUR_CHANNEL_SWATCH_SIZE
))
assert(swatch.size.y <= channel_label.size.y)
var swatch_style := swatch.get_theme_stylebox(
"panel"
) as StyleBoxFlat
assert(swatch_style != null)
assert(
swatch_style.corner_radius_top_left
== roundi(ProfilePage.FUR_CHANNEL_SWATCH_SIZE * 0.5)
)
assert(swatch_style.bg_color.is_equal_approx(
CharacterCustomizationCatalog.option_color(
category_id,
str(snapshot[category_id]),
)
))
var picker_style := picker.get_theme_stylebox("normal") as StyleBoxFlat
assert(picker_style != null and picker_style.corner_radius_top_left == 14)
assert(picker_popup.min_size == ProfilePage.FUR_COLOR_PICKER_POPUP_SIZE)
assert(picker_popup.theme.default_font == UtilityPageStyle.TuffyFont)
var popup_style := picker_popup.get_theme_stylebox(
"panel"
) as StyleBoxFlat
assert(popup_style != null)
assert(popup_style.bg_color.a == 1.0)
assert(popup_style.corner_radius_top_left == 20)
assert(
picker_control.get_theme_constant("sv_width")
== ProfilePage.FUR_COLOR_PICKER_SV_SIZE.x
)
assert(
picker_control.get_theme_constant("sv_height")
== ProfilePage.FUR_COLOR_PICKER_SV_SIZE.y
)
assert(not picker_control.edit_alpha)
assert(not picker_control.edit_intensity)
var picker_display := picker.get_node("FurCustomColorDisplay") as Panel
var display_style := picker_display.get_theme_stylebox(
"panel"
) as StyleBoxFlat
assert(display_style != null and display_style.corner_radius_top_left == 11)
var live_custom_color := Color("2b91c7")
profile_page.call(
"_select_custom_fur_color",
live_custom_color,
CharacterCustomizationCatalog.FUR_COLOR_IDS[0],
)
var first_swatch := profile_page.find_child(
"FurColorSlot1", true, false
).get_node("ChannelContents/ActiveColorSwatch") as Panel
var first_swatch_style := first_swatch.get_theme_stylebox(
"panel"
) as StyleBoxFlat
assert(first_swatch_style.bg_color.is_equal_approx(live_custom_color))
profile_page.free()
await process_frame
func _assert_option_ids(options: Array, expected: Array[String]) -> void:
var actual: Array[String] = []
for option: Dictionary in options:
actual.append(str(option.get("id", "")))
assert(actual == expected)
func _assert_pattern_texture(
pattern_id: String,
component_id: String,
) -> Texture2D:
var texture := CharacterCustomizationCatalog.fur_pattern_texture(
pattern_id, component_id
)
assert(texture != null)
assert(texture.get_width() == 1024)
assert(texture.get_height() == 1024)
return texture
func _assert_mesh_pattern(
skeleton: Skeleton3D,
mesh_name: String,
expected_texture: Texture2D,
) -> ShaderMaterial:
var mesh := skeleton.get_node_or_null(mesh_name) as MeshInstance3D
assert(mesh != null)
var material := mesh.material_override as ShaderMaterial
assert(material != null)
assert(material.get_shader_parameter("pattern_texture") == expected_texture)
return material