feat: expand character customization
This commit is contained in:
parent
c85f709851
commit
9d0591c68d
9 changed files with 286 additions and 52 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -3,6 +3,22 @@ extends RefCounted
|
||||||
|
|
||||||
const PlayerScene: PackedScene = preload("res://player/player.tscn")
|
const PlayerScene: PackedScene = preload("res://player/player.tscn")
|
||||||
const RUNTIME_MATERIAL_META: StringName = &"netfishing_runtime_material"
|
const RUNTIME_MATERIAL_META: StringName = &"netfishing_runtime_material"
|
||||||
|
const EAR_MESH_NAMES: Array[String] = [
|
||||||
|
"ears_antlers_round",
|
||||||
|
"ears_bear",
|
||||||
|
"ears_bunny",
|
||||||
|
"ears_pointy_long",
|
||||||
|
"ears_pointy_short",
|
||||||
|
"ears_pointy_wide",
|
||||||
|
]
|
||||||
|
const TAIL_MESH_NAMES: Array[String] = [
|
||||||
|
"tails_bear",
|
||||||
|
"tails_bunny",
|
||||||
|
"tails_cat",
|
||||||
|
"tails_fox",
|
||||||
|
"tails_gator",
|
||||||
|
"tails_pointy",
|
||||||
|
]
|
||||||
|
|
||||||
static var _feature_uv_aspects: Dictionary = {}
|
static var _feature_uv_aspects: Dictionary = {}
|
||||||
|
|
||||||
|
|
@ -113,15 +129,17 @@ static func apply_appearance(
|
||||||
var selected_ears: String = CharacterCustomizationCatalog.canonical_option_id(
|
var selected_ears: String = CharacterCustomizationCatalog.canonical_option_id(
|
||||||
"ears", str(snapshot.get("ears", "none"))
|
"ears", str(snapshot.get("ears", "none"))
|
||||||
)
|
)
|
||||||
var ear_names: Array[String] = [
|
for ear_name: String in EAR_MESH_NAMES:
|
||||||
"ears_pointy_long",
|
|
||||||
"ears_pointy_short",
|
|
||||||
"ears_pointy_wide",
|
|
||||||
]
|
|
||||||
for ear_name: String in ear_names:
|
|
||||||
var ears: Node3D = skeleton.get_node_or_null(ear_name) as Node3D
|
var ears: Node3D = skeleton.get_node_or_null(ear_name) as Node3D
|
||||||
if ears != null:
|
if ears != null:
|
||||||
ears.visible = selected_ears == ear_name.trim_prefix("ears_")
|
ears.visible = selected_ears == ear_name.trim_prefix("ears_")
|
||||||
|
var selected_tail: String = CharacterCustomizationCatalog.canonical_option_id(
|
||||||
|
"tail", str(snapshot.get("tail", "none"))
|
||||||
|
)
|
||||||
|
for tail_name: String in TAIL_MESH_NAMES:
|
||||||
|
var tail: Node3D = skeleton.get_node_or_null(tail_name) as Node3D
|
||||||
|
if tail != null:
|
||||||
|
tail.visible = selected_tail == tail_name.trim_prefix("tails_")
|
||||||
|
|
||||||
var fur_color: Color = CharacterCustomizationCatalog.option_color(
|
var fur_color: Color = CharacterCustomizationCatalog.option_color(
|
||||||
"fur_pattern", str(snapshot.get("fur_pattern", "white"))
|
"fur_pattern", str(snapshot.get("fur_pattern", "white"))
|
||||||
|
|
@ -130,8 +148,10 @@ static func apply_appearance(
|
||||||
_set_mesh_color(skeleton, "body_main", fur_color)
|
_set_mesh_color(skeleton, "body_main", fur_color)
|
||||||
_set_mesh_color(skeleton, "head_pointy", fur_color)
|
_set_mesh_color(skeleton, "head_pointy", fur_color)
|
||||||
_set_mesh_color(skeleton, "head_round", fur_color)
|
_set_mesh_color(skeleton, "head_round", fur_color)
|
||||||
for ear_name: String in ear_names:
|
for ear_name: String in EAR_MESH_NAMES:
|
||||||
_set_mesh_color(skeleton, ear_name, fur_color)
|
_set_mesh_color(skeleton, ear_name, fur_color)
|
||||||
|
for tail_name: String in TAIL_MESH_NAMES:
|
||||||
|
_set_mesh_color(skeleton, tail_name, fur_color)
|
||||||
|
|
||||||
var eye_id: String = CharacterCustomizationCatalog.canonical_option_id(
|
var eye_id: String = CharacterCustomizationCatalog.canonical_option_id(
|
||||||
"eyes", str(snapshot.get("eyes", "simple_shine"))
|
"eyes", str(snapshot.get("eyes", "simple_shine"))
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@ const CATEGORY_LABELS: Dictionary = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const SCALE_CATEGORY_ID: String = "scale"
|
const SCALE_CATEGORY_ID: String = "scale"
|
||||||
const MIN_CHARACTER_SCALE: float = 0.75
|
const MIN_CHARACTER_SCALE: float = 0.5
|
||||||
const MAX_CHARACTER_SCALE: float = 1.25
|
const MAX_CHARACTER_SCALE: float = 1.5
|
||||||
const DEFAULT_CHARACTER_SCALE: float = 1.0
|
const DEFAULT_CHARACTER_SCALE: float = 1.0
|
||||||
const CHARACTER_SCALE_STEP: float = 0.05
|
const CHARACTER_SCALE_STEP: float = 0.05
|
||||||
|
|
||||||
|
|
@ -51,16 +51,24 @@ const OPTIONS: Dictionary = {
|
||||||
# authored pattern layer is ready.
|
# authored pattern layer is ready.
|
||||||
"fur_pattern": [
|
"fur_pattern": [
|
||||||
{"id": "white", "label": "white", "color": Color("f2f0e8")},
|
{"id": "white", "label": "white", "color": Color("f2f0e8")},
|
||||||
|
{"id": "cream", "label": "cream", "color": Color("e6d39b")},
|
||||||
{"id": "gray", "label": "gray", "color": Color("819398")},
|
{"id": "gray", "label": "gray", "color": Color("819398")},
|
||||||
{"id": "charcoal", "label": "charcoal", "color": Color("34444a")},
|
{"id": "charcoal", "label": "charcoal", "color": Color("34444a")},
|
||||||
{"id": "brown", "label": "brown", "color": Color("7b4a32")},
|
{"id": "brown", "label": "brown", "color": Color("7b4a32")},
|
||||||
{"id": "orange", "label": "orange", "color": Color("c86c36")},
|
{"id": "orange", "label": "orange", "color": Color("c86c36")},
|
||||||
|
{"id": "red", "label": "red", "color": Color("a9433f")},
|
||||||
|
{"id": "pink", "label": "pink", "color": Color("d8899e")},
|
||||||
{"id": "yellow", "label": "yellow", "color": Color("d8c545")},
|
{"id": "yellow", "label": "yellow", "color": Color("d8c545")},
|
||||||
{"id": "green", "label": "green", "color": Color("6f913c")},
|
{"id": "green", "label": "green", "color": Color("6f913c")},
|
||||||
{"id": "teal", "label": "teal", "color": Color("3a8790")},
|
{"id": "teal", "label": "teal", "color": Color("3a8790")},
|
||||||
|
{"id": "blue", "label": "blue", "color": Color("4d76a8")},
|
||||||
|
{"id": "purple", "label": "purple", "color": Color("76558f")},
|
||||||
],
|
],
|
||||||
"ears": [
|
"ears": [
|
||||||
{"id": "none", "label": "none"},
|
{"id": "none", "label": "none"},
|
||||||
|
{"id": "antlers_round", "label": "antlers"},
|
||||||
|
{"id": "bear", "label": "bear"},
|
||||||
|
{"id": "bunny", "label": "bunny"},
|
||||||
{"id": "pointy_long", "label": "long"},
|
{"id": "pointy_long", "label": "long"},
|
||||||
{"id": "pointy_short", "label": "short"},
|
{"id": "pointy_short", "label": "short"},
|
||||||
{"id": "pointy_wide", "label": "wide"},
|
{"id": "pointy_wide", "label": "wide"},
|
||||||
|
|
@ -74,13 +82,22 @@ const OPTIONS: Dictionary = {
|
||||||
],
|
],
|
||||||
"nose": [{"id": "dog_round", "label": "round"}],
|
"nose": [{"id": "dog_round", "label": "round"}],
|
||||||
"mouth": [{"id": "three", "label": "three"}],
|
"mouth": [{"id": "three", "label": "three"}],
|
||||||
"tail": [{"id": "none", "label": "none"}],
|
"tail": [
|
||||||
|
{"id": "none", "label": "none"},
|
||||||
|
{"id": "bear", "label": "bear"},
|
||||||
|
{"id": "bunny", "label": "bunny"},
|
||||||
|
{"id": "cat", "label": "cat"},
|
||||||
|
{"id": "fox", "label": "fox"},
|
||||||
|
{"id": "gator", "label": "gator"},
|
||||||
|
{"id": "pointy", "label": "pointy"},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
const LEGACY_OPTION_ALIASES: Dictionary = {
|
const LEGACY_OPTION_ALIASES: Dictionary = {
|
||||||
"species": {"default": "round"},
|
"species": {"default": "round"},
|
||||||
"fur_pattern": {"solid": "white"},
|
"fur_pattern": {"solid": "white"},
|
||||||
"ears": {"default": "none"},
|
"ears": {"default": "none"},
|
||||||
|
"tail": {"default": "none"},
|
||||||
"eyes": {"default": "simple_shine"},
|
"eyes": {"default": "simple_shine"},
|
||||||
"nose": {"default": "dog_round"},
|
"nose": {"default": "dog_round"},
|
||||||
"mouth": {"default": "three"},
|
"mouth": {"default": "three"},
|
||||||
|
|
@ -127,6 +144,82 @@ static func options_for(category_id: String) -> Array:
|
||||||
return OPTIONS.get(category_id, [])
|
return OPTIONS.get(category_id, [])
|
||||||
|
|
||||||
|
|
||||||
|
static func feature_option_groups(category_id: String) -> Array:
|
||||||
|
if category_id not in FEATURE_CATEGORIES:
|
||||||
|
return []
|
||||||
|
var options: Array = options_for(category_id)
|
||||||
|
var option_ids: Array[String] = []
|
||||||
|
for option: Dictionary in options:
|
||||||
|
option_ids.append(str(option.get("id", "")))
|
||||||
|
var grouped_options: Dictionary = {}
|
||||||
|
var group_order: Array[String] = []
|
||||||
|
for option: Dictionary in options:
|
||||||
|
var option_id := str(option.get("id", ""))
|
||||||
|
var group_id := _feature_variant_root(option_id, option_ids)
|
||||||
|
if not grouped_options.has(group_id):
|
||||||
|
grouped_options[group_id] = []
|
||||||
|
group_order.append(group_id)
|
||||||
|
var group: Array = grouped_options[group_id] as Array
|
||||||
|
group.append(option)
|
||||||
|
grouped_options[group_id] = group
|
||||||
|
var result: Array = []
|
||||||
|
for group_id: String in group_order:
|
||||||
|
result.append({
|
||||||
|
"id": group_id,
|
||||||
|
"options": grouped_options[group_id],
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
static func _feature_variant_root(
|
||||||
|
option_id: String,
|
||||||
|
option_ids: Array[String],
|
||||||
|
) -> String:
|
||||||
|
if option_id == "none":
|
||||||
|
return option_id
|
||||||
|
# Prefer the shortest authored base option. This keeps nested names such as
|
||||||
|
# `beady_extra_shine` in the `beady` drawer rather than creating drawers
|
||||||
|
# inside drawers.
|
||||||
|
var authored_root := ""
|
||||||
|
for candidate_id: String in option_ids:
|
||||||
|
if candidate_id == "none":
|
||||||
|
continue
|
||||||
|
if (
|
||||||
|
option_id != candidate_id
|
||||||
|
and not option_id.begins_with(candidate_id + "_")
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
var has_descendant := false
|
||||||
|
for descendant_id: String in option_ids:
|
||||||
|
if descendant_id.begins_with(candidate_id + "_"):
|
||||||
|
has_descendant = true
|
||||||
|
break
|
||||||
|
if (
|
||||||
|
has_descendant
|
||||||
|
and (
|
||||||
|
authored_root.is_empty()
|
||||||
|
or candidate_id.length() < authored_root.length()
|
||||||
|
)
|
||||||
|
):
|
||||||
|
authored_root = candidate_id
|
||||||
|
if not authored_root.is_empty():
|
||||||
|
return authored_root
|
||||||
|
|
||||||
|
# Some families have no unqualified base asset. Group those siblings by
|
||||||
|
# their leading name segment, making the sorted first asset representative.
|
||||||
|
var separator_index := option_id.find("_")
|
||||||
|
if separator_index <= 0:
|
||||||
|
return option_id
|
||||||
|
var leading_segment := option_id.substr(0, separator_index)
|
||||||
|
var sibling_count := 0
|
||||||
|
for candidate_id: String in option_ids:
|
||||||
|
if candidate_id.begins_with(leading_segment + "_"):
|
||||||
|
sibling_count += 1
|
||||||
|
if sibling_count >= 2:
|
||||||
|
return leading_segment
|
||||||
|
return option_id
|
||||||
|
|
||||||
|
|
||||||
static func texture_for(category_id: String, option_id: String) -> Texture2D:
|
static func texture_for(category_id: String, option_id: String) -> Texture2D:
|
||||||
if category_id not in FEATURE_CATEGORIES:
|
if category_id not in FEATURE_CATEGORIES:
|
||||||
return null
|
return null
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ extends Control
|
||||||
|
|
||||||
const CHECK_DEBOUNCE_SECONDS: float = 0.4
|
const CHECK_DEBOUNCE_SECONDS: float = 0.4
|
||||||
const OPTION_GRID_COLUMNS: int = 6
|
const OPTION_GRID_COLUMNS: int = 6
|
||||||
|
const FEATURE_DRAWER_ANIMATION_SECONDS: float = 0.16
|
||||||
const ControllerMappingManagerType = preload(
|
const ControllerMappingManagerType = preload(
|
||||||
"res://settings/controller_mapping_manager.gd"
|
"res://settings/controller_mapping_manager.gd"
|
||||||
)
|
)
|
||||||
|
|
@ -45,6 +46,8 @@ var _experience_level: Label
|
||||||
var _experience_progress: ProgressBar
|
var _experience_progress: ProgressBar
|
||||||
var _experience_value: Label
|
var _experience_value: Label
|
||||||
var _feature_preview_cache: Dictionary = {}
|
var _feature_preview_cache: Dictionary = {}
|
||||||
|
var _expanded_feature_drawers: Dictionary = {}
|
||||||
|
var _feature_drawer_animation_key: String = ""
|
||||||
var _scale_value_label: Label
|
var _scale_value_label: Label
|
||||||
var _voice_preview: AnimaleseVoiceType
|
var _voice_preview: AnimaleseVoiceType
|
||||||
var _voice_preview_tween: Tween
|
var _voice_preview_tween: Tween
|
||||||
|
|
@ -630,7 +633,7 @@ func _build_scale_option() -> void:
|
||||||
_option_list.add_child(gameplay_note)
|
_option_list.add_child(gameplay_note)
|
||||||
|
|
||||||
|
|
||||||
func _build_feature_preview_options(options: Array) -> void:
|
func _build_feature_preview_options(_options: Array) -> void:
|
||||||
var scroll := ScrollContainer.new()
|
var scroll := ScrollContainer.new()
|
||||||
scroll.custom_minimum_size = Vector2(190.0, 0.0)
|
scroll.custom_minimum_size = Vector2(190.0, 0.0)
|
||||||
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
|
@ -646,7 +649,70 @@ func _build_feature_preview_options(options: Array) -> void:
|
||||||
var selected_id := CharacterCustomizationCatalog.canonical_option_id(
|
var selected_id := CharacterCustomizationCatalog.canonical_option_id(
|
||||||
_category_id, str(_draft_appearance.get(_category_id, ""))
|
_category_id, str(_draft_appearance.get(_category_id, ""))
|
||||||
)
|
)
|
||||||
for option: Dictionary in options:
|
var groups: Array = CharacterCustomizationCatalog.feature_option_groups(
|
||||||
|
_category_id
|
||||||
|
)
|
||||||
|
var expanded_drawer_id := str(
|
||||||
|
_expanded_feature_drawers.get(_category_id, "")
|
||||||
|
)
|
||||||
|
if expanded_drawer_id.is_empty():
|
||||||
|
for group: Dictionary in groups:
|
||||||
|
var group_options: Array = group.get("options", []) as Array
|
||||||
|
if group_options.size() <= 1:
|
||||||
|
continue
|
||||||
|
var representative_id := str(
|
||||||
|
(group_options[0] as Dictionary).get("id", "")
|
||||||
|
)
|
||||||
|
for option: Dictionary in group_options:
|
||||||
|
if (
|
||||||
|
str(option.get("id", "")) == selected_id
|
||||||
|
and selected_id != representative_id
|
||||||
|
):
|
||||||
|
expanded_drawer_id = str(group.get("id", ""))
|
||||||
|
_expanded_feature_drawers[_category_id] = (
|
||||||
|
expanded_drawer_id
|
||||||
|
)
|
||||||
|
break
|
||||||
|
if not expanded_drawer_id.is_empty():
|
||||||
|
break
|
||||||
|
|
||||||
|
var animation_key := _feature_drawer_animation_key
|
||||||
|
for group: Dictionary in groups:
|
||||||
|
var group_id := str(group.get("id", ""))
|
||||||
|
var group_options: Array = group.get("options", []) as Array
|
||||||
|
if group_options.is_empty():
|
||||||
|
continue
|
||||||
|
var has_variants := group_options.size() > 1
|
||||||
|
var is_expanded := has_variants and group_id == expanded_drawer_id
|
||||||
|
for option_index: int in range(group_options.size()):
|
||||||
|
if option_index > 0 and not is_expanded:
|
||||||
|
continue
|
||||||
|
var option: Dictionary = group_options[option_index] as Dictionary
|
||||||
|
var button := _build_feature_option_button(
|
||||||
|
option,
|
||||||
|
selected_id,
|
||||||
|
group_id,
|
||||||
|
option_index == 0,
|
||||||
|
has_variants,
|
||||||
|
is_expanded,
|
||||||
|
)
|
||||||
|
grid.add_child(button)
|
||||||
|
if (
|
||||||
|
option_index > 0
|
||||||
|
and animation_key == "%s:%s" % [_category_id, group_id]
|
||||||
|
):
|
||||||
|
_animate_feature_drawer_button(button)
|
||||||
|
_feature_drawer_animation_key = ""
|
||||||
|
|
||||||
|
|
||||||
|
func _build_feature_option_button(
|
||||||
|
option: Dictionary,
|
||||||
|
selected_id: String,
|
||||||
|
drawer_id: String,
|
||||||
|
is_representative: bool,
|
||||||
|
has_variants: bool,
|
||||||
|
is_expanded: bool,
|
||||||
|
) -> Button:
|
||||||
var option_id := str(option.get("id", ""))
|
var option_id := str(option.get("id", ""))
|
||||||
var button := Button.new()
|
var button := Button.new()
|
||||||
var is_none := option_id == "none"
|
var is_none := option_id == "none"
|
||||||
|
|
@ -654,11 +720,20 @@ func _build_feature_preview_options(options: Array) -> void:
|
||||||
button.tooltip_text = "none" if is_none else str(
|
button.tooltip_text = "none" if is_none else str(
|
||||||
option.get("label", option_id)
|
option.get("label", option_id)
|
||||||
)
|
)
|
||||||
|
if is_representative and has_variants:
|
||||||
|
button.tooltip_text += (
|
||||||
|
" (hide variants)" if is_expanded else " (show variants)"
|
||||||
|
)
|
||||||
button.toggle_mode = true
|
button.toggle_mode = true
|
||||||
button.button_pressed = selected_id == option_id
|
button.button_pressed = selected_id == option_id
|
||||||
button.custom_minimum_size = Vector2(84.0, 64.0)
|
button.custom_minimum_size = Vector2(84.0, 64.0)
|
||||||
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
button.clip_contents = true
|
button.clip_contents = true
|
||||||
|
if is_representative and has_variants:
|
||||||
|
button.pressed.connect(
|
||||||
|
_select_feature_drawer.bind(_category_id, drawer_id, option_id)
|
||||||
|
)
|
||||||
|
else:
|
||||||
button.pressed.connect(_select_option.bind(_category_id, option_id))
|
button.pressed.connect(_select_option.bind(_category_id, option_id))
|
||||||
UtilityPageStyle.apply_compact_ocean_button(button)
|
UtilityPageStyle.apply_compact_ocean_button(button)
|
||||||
button.custom_minimum_size = Vector2(84.0, 64.0)
|
button.custom_minimum_size = Vector2(84.0, 64.0)
|
||||||
|
|
@ -666,17 +741,13 @@ func _build_feature_preview_options(options: Array) -> void:
|
||||||
button.add_theme_font_size_override("font_size", 28)
|
button.add_theme_font_size_override("font_size", 28)
|
||||||
else:
|
else:
|
||||||
var preview := TextureRect.new()
|
var preview := TextureRect.new()
|
||||||
preview.texture = _feature_preview_texture(
|
preview.texture = _feature_preview_texture(_category_id, option_id)
|
||||||
_category_id, option_id
|
|
||||||
)
|
|
||||||
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
preview.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
preview.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
var preview_height := 50.0
|
var preview_height := 50.0
|
||||||
var preview_width := clampf(
|
var preview_width := clampf(
|
||||||
preview_height * PlayerVisualPresenter.feature_uv_aspect(
|
preview_height * PlayerVisualPresenter.feature_uv_aspect(_category_id),
|
||||||
_category_id
|
|
||||||
),
|
|
||||||
32.0,
|
32.0,
|
||||||
74.0,
|
74.0,
|
||||||
)
|
)
|
||||||
|
|
@ -684,7 +755,47 @@ func _build_feature_preview_options(options: Array) -> void:
|
||||||
preview.size = Vector2(preview_width, preview_height)
|
preview.size = Vector2(preview_width, preview_height)
|
||||||
preview.position = Vector2((84.0 - preview_width) * 0.5, 7.0)
|
preview.position = Vector2((84.0 - preview_width) * 0.5, 7.0)
|
||||||
button.add_child(preview)
|
button.add_child(preview)
|
||||||
grid.add_child(button)
|
if is_representative and has_variants:
|
||||||
|
var indicator := Label.new()
|
||||||
|
indicator.text = "<" if is_expanded else ">"
|
||||||
|
indicator.position = Vector2(66.0, 1.0)
|
||||||
|
indicator.size = Vector2(14.0, 16.0)
|
||||||
|
indicator.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
indicator.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
indicator.add_theme_font_size_override("font_size", 13)
|
||||||
|
indicator.add_theme_color_override(
|
||||||
|
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||||
|
)
|
||||||
|
button.add_child(indicator)
|
||||||
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
func _select_feature_drawer(
|
||||||
|
category_id: String,
|
||||||
|
drawer_id: String,
|
||||||
|
option_id: String,
|
||||||
|
) -> void:
|
||||||
|
if str(_expanded_feature_drawers.get(category_id, "")) == drawer_id:
|
||||||
|
_expanded_feature_drawers.erase(category_id)
|
||||||
|
else:
|
||||||
|
_expanded_feature_drawers[category_id] = drawer_id
|
||||||
|
_feature_drawer_animation_key = "%s:%s" % [category_id, drawer_id]
|
||||||
|
_select_option(category_id, option_id)
|
||||||
|
|
||||||
|
|
||||||
|
func _animate_feature_drawer_button(button: Button) -> void:
|
||||||
|
button.scale = Vector2(0.05, 1.0)
|
||||||
|
var faded := button.modulate
|
||||||
|
faded.a = 0.0
|
||||||
|
button.modulate = faded
|
||||||
|
var tween := button.create_tween()
|
||||||
|
tween.set_parallel(true)
|
||||||
|
tween.tween_property(
|
||||||
|
button, "scale", Vector2.ONE, FEATURE_DRAWER_ANIMATION_SECONDS
|
||||||
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
|
tween.tween_property(
|
||||||
|
button, "modulate:a", 1.0, FEATURE_DRAWER_ANIMATION_SECONDS
|
||||||
|
).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||||
|
|
||||||
|
|
||||||
func _feature_preview_texture(
|
func _feature_preview_texture(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ const UIReferencePresentationType = preload(
|
||||||
)
|
)
|
||||||
const FRONT_FACING_YAW: float = PI
|
const FRONT_FACING_YAW: float = PI
|
||||||
const DEFAULT_CAMERA_DISTANCE: float = 1.9
|
const DEFAULT_CAMERA_DISTANCE: float = 1.9
|
||||||
|
const DEFAULT_CAMERA_PITCH: float = 0.12
|
||||||
const MIN_CAMERA_DISTANCE: float = 1.05
|
const MIN_CAMERA_DISTANCE: float = 1.05
|
||||||
const MAX_CAMERA_DISTANCE: float = 2.8
|
const MAX_CAMERA_DISTANCE: float = 2.8
|
||||||
const CAMERA_ZOOM_STEP: float = 0.14
|
const CAMERA_ZOOM_STEP: float = 0.14
|
||||||
|
|
@ -25,7 +26,7 @@ const CAMERA_TARGET_HEIGHT: float = 0.95
|
||||||
|
|
||||||
var _dragging: bool = false
|
var _dragging: bool = false
|
||||||
var _camera_yaw: float = 0.0
|
var _camera_yaw: float = 0.0
|
||||||
var _camera_pitch: float = 0.0
|
var _camera_pitch: float = DEFAULT_CAMERA_PITCH
|
||||||
var _camera_distance: float = DEFAULT_CAMERA_DISTANCE
|
var _camera_distance: float = DEFAULT_CAMERA_DISTANCE
|
||||||
var _visuals: Node3D
|
var _visuals: Node3D
|
||||||
var _controller_mapping_manager: ControllerMappingManagerType
|
var _controller_mapping_manager: ControllerMappingManagerType
|
||||||
|
|
@ -85,11 +86,9 @@ func apply_appearance_profile(profile: Dictionary) -> void:
|
||||||
func reset_view() -> void:
|
func reset_view() -> void:
|
||||||
_preview_root.rotation.y = FRONT_FACING_YAW
|
_preview_root.rotation.y = FRONT_FACING_YAW
|
||||||
_camera_yaw = 0.0
|
_camera_yaw = 0.0
|
||||||
_camera_pitch = 0.0
|
_camera_pitch = DEFAULT_CAMERA_PITCH
|
||||||
_camera_distance = DEFAULT_CAMERA_DISTANCE
|
_camera_distance = DEFAULT_CAMERA_DISTANCE
|
||||||
_apply_camera_orbit()
|
_apply_camera_orbit()
|
||||||
if _preview_camera != null:
|
|
||||||
_preview_camera.position.z = DEFAULT_CAMERA_DISTANCE
|
|
||||||
|
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ script = ExtResource("3_display")
|
||||||
species_id = "pointy"
|
species_id = "pointy"
|
||||||
fur_color_id = "white"
|
fur_color_id = "white"
|
||||||
ears_id = "pointy_wide"
|
ears_id = "pointy_wide"
|
||||||
|
tail_id = "fox"
|
||||||
eyes_id = "punk_purple"
|
eyes_id = "punk_purple"
|
||||||
nose_id = "triangle_small"
|
nose_id = "triangle_small"
|
||||||
mouth_id = "slanted"
|
mouth_id = "slanted"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,15 @@ const PlayerVisualPresenterType = preload(
|
||||||
"pointy_short",
|
"pointy_short",
|
||||||
"pointy_wide",
|
"pointy_wide",
|
||||||
) var ears_id: String = "none"
|
) var ears_id: String = "none"
|
||||||
|
@export_enum(
|
||||||
|
"none",
|
||||||
|
"bear",
|
||||||
|
"bunny",
|
||||||
|
"cat",
|
||||||
|
"fox",
|
||||||
|
"gator",
|
||||||
|
"pointy",
|
||||||
|
) var tail_id: String = "none"
|
||||||
@export var eyes_id: String = "simple_shine"
|
@export var eyes_id: String = "simple_shine"
|
||||||
@export var nose_id: String = "dog_round"
|
@export var nose_id: String = "dog_round"
|
||||||
@export var mouth_id: String = "three"
|
@export var mouth_id: String = "three"
|
||||||
|
|
@ -48,6 +57,7 @@ func _ready() -> void:
|
||||||
appearance["species"] = species_id
|
appearance["species"] = species_id
|
||||||
appearance["fur_pattern"] = fur_color_id
|
appearance["fur_pattern"] = fur_color_id
|
||||||
appearance["ears"] = ears_id
|
appearance["ears"] = ears_id
|
||||||
|
appearance["tail"] = tail_id
|
||||||
appearance["eyes"] = eyes_id
|
appearance["eyes"] = eyes_id
|
||||||
appearance["nose"] = nose_id
|
appearance["nose"] = nose_id
|
||||||
appearance["mouth"] = mouth_id
|
appearance["mouth"] = mouth_id
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue