From 4dc87235ec3094c634d5c2d402d7521218736401 Mon Sep 17 00:00:00 2001 From: Voyager Date: Mon, 24 Aug 2026 13:50:57 -0400 Subject: [PATCH] Restore catch quality inventory styling --- tests/unified_inventory_validation.gd | 54 +++++++++++++++++++++ ui/components/general_inventory_slot.gd | 62 +++++++++++++++++++++---- ui/components/shop_sale_tray_slot.gd | 26 ++++++++++- ui/shop_sell_inventory.gd | 10 +++- 4 files changed, 141 insertions(+), 11 deletions(-) diff --git a/tests/unified_inventory_validation.gd b/tests/unified_inventory_validation.gd index dacc5a0..e9a0d85 100644 --- a/tests/unified_inventory_validation.gd +++ b/tests/unified_inventory_validation.gd @@ -62,6 +62,7 @@ func _run() -> void: fish_catch.sale_value = Bluegill.get_sale_value_for_weight( fish_catch.weight_lb ) + fish_catch.quality = FishQuality.Tier.IMPRESSIVE assert(catches.add_catch(fish_catch)) assert(layout.get_inventory_count() == 2) assert(layout.move_entry_to_first_free( @@ -106,8 +107,53 @@ func _run() -> void: == Color(UtilityPageStyle.OCEAN_SELECTED, 0.92) ) staged_slot.set_staged(false) + var quality_slot := GeneralInventorySlot.new() + root.add_child(quality_slot) + quality_slot.set_presentation_size(Vector2(78.0, 78.0)) + quality_slot.configure( + 0, + PlayerInventoryLayout.InventoryContainer.STORAGE, + false, + layout, + bag, + catches, + hotbar, + ItemCatalogResource, + ) + assert(quality_slot.entry_identity == fish_catch.catch_id) + assert( + (quality_slot.get_theme_stylebox("normal") as StyleBoxFlat).bg_color + == GeneralInventorySlot.quality_background_color( + FishQuality.Tier.IMPRESSIVE, + GeneralInventorySlot.QualityEmphasis.NORMAL, + ) + ) + quality_slot.set_staged(true) + assert( + (quality_slot.get_theme_stylebox("normal") as StyleBoxFlat).bg_color + == GeneralInventorySlot.quality_background_color( + FishQuality.Tier.IMPRESSIVE, + GeneralInventorySlot.QualityEmphasis.SELECTED, + ) + ) + var distinct_quality_colors: Dictionary[Color, bool] = {} + for quality: int in FishQuality.TIER_COUNT: + var quality_color := GeneralInventorySlot.quality_background_color( + quality, + GeneralInventorySlot.QualityEmphasis.NORMAL, + ) + assert(not distinct_quality_colors.has(quality_color)) + distinct_quality_colors[quality_color] = true + assert(distinct_quality_colors.size() == FishQuality.TIER_COUNT) var sale_tray_slot := ShopSaleTraySlot.new() root.add_child(sale_tray_slot) + sale_tray_slot.configure( + "catch:%s" % fish_catch.catch_id, + fish_catch.fish.display_texture, + fish_catch.fish.display_name, + 1, + fish_catch.quality, + ) assert( sale_tray_slot.custom_minimum_size == GeneralInventoryGrid.DEFAULT_SLOT_SIZE @@ -117,6 +163,14 @@ func _run() -> void: ) as StyleBoxFlat assert(sale_tray_style != null) assert(sale_tray_style.corner_radius_top_left == 26) + var expected_tray_quality_color := ( + GeneralInventorySlot.quality_background_color( + FishQuality.Tier.IMPRESSIVE, + GeneralInventorySlot.QualityEmphasis.NORMAL, + ) + ) + expected_tray_quality_color.a = 0.96 + assert(sale_tray_style.bg_color == expected_tray_quality_color) var wallet := PlayerWallet.new() root.add_child(wallet) assert(wallet.restore_balance(15000)) diff --git a/ui/components/general_inventory_slot.gd b/ui/components/general_inventory_slot.gd index 9204d0a..cf9ccc4 100644 --- a/ui/components/general_inventory_slot.gd +++ b/ui/components/general_inventory_slot.gd @@ -11,6 +11,12 @@ const LockedContentPresentationType = preload( "res://ui/components/locked_content_presentation.gd" ) +enum QualityEmphasis { + NORMAL, + HOVER, + SELECTED, +} + var slot_index: int = -1 var container: int = -1 var entry_kind: int = -1 @@ -29,6 +35,7 @@ var _context_text: String = "" var _context_hovered: bool = false var _context_focused: bool = false var _staged: bool = false +var _quality_tier: int = -1 func _ready() -> void: @@ -101,10 +108,12 @@ func refresh() -> void: entry_kind = -1 entry_identity = StringName() _context_text = "" + _quality_tier = -1 _icon.texture = null _quantity.text = "" disabled = _locked _apply_icon_geometry() + _apply_style() if _locked: _icon.texture = LockedContentPresentationType.ICON _icon.modulate = LockedContentPresentationType.icon_modulate() @@ -152,12 +161,14 @@ func refresh() -> void: ) if fish_catch == null: return + _quality_tier = fish_catch.quality _icon.texture = fish_catch.fish.display_texture _context_text = _catch_context_text(fish_catch) var catch_name: String = FishQuality.qualified_name( fish_catch.fish.display_name, fish_catch.quality ) accessibility_name = "%s, slot %d" % [catch_name, slot_index + 1] + _apply_style() func _on_pressed() -> void: @@ -306,27 +317,62 @@ func _drop_data(_at_position: Vector2, data: Variant) -> void: func _apply_style() -> void: var radius: int = roundi(minf(_presentation_size.x, _presentation_size.y) * 0.5) + var normal_color: Color = quality_background_color( + _quality_tier, + QualityEmphasis.SELECTED if _staged else QualityEmphasis.NORMAL, + ) var normal := UtilityPageStyle.rounded_style( - Color( - UtilityPageStyle.OCEAN_SELECTED - if _staged else UtilityPageStyle.OCEAN_FIELD, - 0.92 if _staged else 0.88, - ), + normal_color, radius, ) var hover := UtilityPageStyle.rounded_style( - Color(UtilityPageStyle.OCEAN_SELECTED, 0.92), radius + quality_background_color(_quality_tier, QualityEmphasis.HOVER), + radius, + ) + var selected := UtilityPageStyle.rounded_style( + quality_background_color(_quality_tier, QualityEmphasis.SELECTED), + radius, ) var locked := UtilityPageStyle.rounded_style( LockedContentPresentationType.disabled_background_color(), radius ) - for state: StringName in [&"normal", &"pressed"]: - add_theme_stylebox_override(state, normal) + add_theme_stylebox_override("normal", normal) + add_theme_stylebox_override( + "pressed", selected if FishQuality.is_valid(_quality_tier) else normal + ) add_theme_stylebox_override("disabled", locked) for state: StringName in [&"hover", &"focus"]: add_theme_stylebox_override(state, hover) +static func quality_background_color( + quality: int, + emphasis: QualityEmphasis, +) -> Color: + var base_color: Color + var alpha: float + var quality_mix: float + match emphasis: + QualityEmphasis.HOVER: + base_color = UtilityPageStyle.OCEAN_SELECTED + alpha = 0.92 + quality_mix = 0.64 + QualityEmphasis.SELECTED: + base_color = UtilityPageStyle.OCEAN_SELECTED + alpha = 0.92 + quality_mix = 0.82 + _: + base_color = UtilityPageStyle.OCEAN_FIELD + alpha = 0.88 + quality_mix = 0.46 + if not FishQuality.is_valid(quality): + return Color(base_color, alpha) + return Color( + base_color.lerp(UIPalette.get_quality_color(quality), quality_mix), + alpha, + ) + + func _apply_presentation() -> void: _apply_icon_geometry() var is_large: bool = _presentation_size.x >= 70.0 diff --git a/ui/components/shop_sale_tray_slot.gd b/ui/components/shop_sale_tray_slot.gd index 5deb62f..0ac87bf 100644 --- a/ui/components/shop_sale_tray_slot.gd +++ b/ui/components/shop_sale_tray_slot.gd @@ -7,6 +7,7 @@ signal drop_requested(payload: Dictionary) var entry_key: String = "" var _icon: TextureRect var _quantity: Label +var _quality_tier: int = -1 func _ready() -> void: @@ -32,11 +33,29 @@ func _ready() -> void: _quantity.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT _quantity.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(_quantity) + _apply_style() + + +func _apply_style() -> void: + var normal_color := GeneralInventorySlot.quality_background_color( + _quality_tier, + GeneralInventorySlot.QualityEmphasis.NORMAL, + ) + var hover_color := GeneralInventorySlot.quality_background_color( + _quality_tier, + GeneralInventorySlot.QualityEmphasis.SELECTED, + ) + # Preserve the tray's established opacity while sharing the inventory's + # quality colors. Ordinary items and the empty drop target stay unchanged. + normal_color.a = 0.96 + hover_color.a = 0.96 var normal := UtilityPageStyle.rounded_style( - Color(UtilityPageStyle.OCEAN_FIELD, 0.96), 26 + normal_color, + 26, ) var hover := UtilityPageStyle.rounded_style( - Color(UtilityPageStyle.OCEAN_SELECTED, 0.96), 26 + hover_color, + 26, ) for state: StringName in [&"normal", &"pressed", &"disabled"]: add_theme_stylebox_override(state, normal) @@ -49,12 +68,15 @@ func configure( icon: Texture2D, label: String, quantity: int = 1, + quality: int = -1, ) -> void: entry_key = key + _quality_tier = quality _icon.texture = icon _quantity.text = "×%d" % quantity if quantity > 1 else "" tooltip_text = "%s · select to remove" % label accessibility_name = tooltip_text + _apply_style() func _can_drop_data(_at_position: Vector2, data: Variant) -> bool: diff --git a/ui/shop_sell_inventory.gd b/ui/shop_sell_inventory.gd index f4e9a43..9e39850 100644 --- a/ui/shop_sell_inventory.gd +++ b/ui/shop_sell_inventory.gd @@ -205,12 +205,14 @@ func _refresh_tray() -> void: var identity := StringName(str(record["identity"])) var icon: Texture2D var label: String + var quality: int = -1 if int(record["kind"]) == PlayerInventoryLayout.EntryKind.CATCH: var fish_catch := _fish_inventory.get_catch_by_id(identity) if fish_catch == null: continue icon = fish_catch.fish.display_texture label = fish_catch.fish.display_name + quality = fish_catch.quality else: var item := _item_catalog.get_item_by_id(identity) if item == null: @@ -219,7 +221,13 @@ func _refresh_tray() -> void: label = item.display_name var slot := ShopSaleTraySlot.new() _tray_grid.add_child(slot) - slot.configure(key, icon, label, int(record.get("quantity", 1))) + slot.configure( + key, + icon, + label, + int(record.get("quantity", 1)), + quality, + ) slot.remove_requested.connect(_on_remove_requested) slot.drop_requested.connect(_on_drop_payload) var drop_slot := ShopSaleTraySlot.new()