Add collectible fish quality tiers
This commit is contained in:
parent
7661b057bc
commit
35d00e8035
30 changed files with 834 additions and 73 deletions
|
|
@ -12,6 +12,7 @@ const PlayerBagType = preload("res://inventory/player_bag.gd")
|
|||
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
|
||||
const SELECTED_SCALE: float = 1.12
|
||||
const HOVER_SCALE: float = 1.025
|
||||
|
|
@ -179,7 +180,10 @@ func refresh() -> void:
|
|||
_quantity_label.visible = not quantity_text.is_empty()
|
||||
tooltip_text = (
|
||||
"%s · %.1f lb" % [
|
||||
fish_catch.fish.display_name,
|
||||
FishQualityType.qualified_name(
|
||||
fish_catch.fish.display_name,
|
||||
fish_catch.quality,
|
||||
),
|
||||
fish_catch.weight_lb,
|
||||
]
|
||||
if fish_catch != null
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ var _depth_scale: float = 1.0
|
|||
var _batch_selected: bool = false
|
||||
var _focused_catch: bool = false
|
||||
var _hovered: bool = false
|
||||
var _rarity_color := Color.WHITE
|
||||
var _quality_color := Color.WHITE
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
|
|
@ -40,7 +40,7 @@ func configure(
|
|||
texture: Texture2D,
|
||||
phase: float,
|
||||
depth_scale: float,
|
||||
rarity_color: Color,
|
||||
quality_color: Color,
|
||||
) -> void:
|
||||
catch_id = identity
|
||||
_display_name = display_name
|
||||
|
|
@ -48,7 +48,7 @@ func configure(
|
|||
_fish_shadow.texture = texture
|
||||
_motion_phase = phase
|
||||
_depth_scale = depth_scale
|
||||
_rarity_color = rarity_color
|
||||
_quality_color = quality_color
|
||||
_refresh_style()
|
||||
tooltip_text = "%s · drag to a hotbar slot" % _display_name
|
||||
|
||||
|
|
@ -136,14 +136,16 @@ func _update_visual_pivot() -> void:
|
|||
|
||||
func _refresh_style() -> void:
|
||||
var idle := StyleBoxEmpty.new()
|
||||
var normal := _make_rarity_style(_rarity_color, 2, 0.25)
|
||||
var hover := _make_rarity_style(_rarity_color.lightened(0.08), 4, 0.34)
|
||||
var selected_fill: Color = (
|
||||
_rarity_color.lightened(0.12)
|
||||
if _batch_selected
|
||||
else _rarity_color
|
||||
var normal := _make_quality_style(_quality_color, 2, 0.25)
|
||||
var hover := _make_quality_style(
|
||||
_quality_color.lightened(0.08), 4, 0.34
|
||||
)
|
||||
var selected := _make_rarity_style(
|
||||
var selected_fill: Color = (
|
||||
_quality_color.lightened(0.12)
|
||||
if _batch_selected
|
||||
else _quality_color
|
||||
)
|
||||
var selected := _make_quality_style(
|
||||
selected_fill,
|
||||
6 if _batch_selected else 4,
|
||||
0.46 if _batch_selected else 0.34,
|
||||
|
|
@ -169,7 +171,7 @@ func _refresh_style() -> void:
|
|||
add_theme_stylebox_override("pressed", selected)
|
||||
|
||||
|
||||
func _make_rarity_style(
|
||||
func _make_quality_style(
|
||||
fill: Color,
|
||||
shadow_size: int,
|
||||
shadow_alpha: float,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const CollectionLogType = preload("res://collection/collection_log.gd")
|
|||
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||
const PlayerMenuType = preload("res://ui/player_menu.gd")
|
||||
|
|
@ -569,6 +570,7 @@ func _on_showcase_changed(
|
|||
fish_name: String,
|
||||
rarity_name: String,
|
||||
weight_lb: float,
|
||||
quality: int,
|
||||
visible: bool,
|
||||
) -> void:
|
||||
_showcase_active = visible
|
||||
|
|
@ -583,11 +585,13 @@ func _on_showcase_changed(
|
|||
_barrier_health.visible = false
|
||||
_clear_barrier_markers()
|
||||
_showcase_details.text = (
|
||||
"%.1f lb • %s"
|
||||
% [weight_lb, rarity_name]
|
||||
"%s • %.1f lb"
|
||||
% [rarity_name.to_lower(), weight_lb]
|
||||
)
|
||||
_showcase_details.visible = true
|
||||
_status_label.text = "You caught a %s!" % fish_name
|
||||
_status_label.text = "You caught %s!" % (
|
||||
FishQualityType.qualified_name_with_article(fish_name, quality)
|
||||
)
|
||||
_status_label.visible = true
|
||||
_refresh_fishing_panel_visibility()
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const PlayerBagType = preload("res://inventory/player_bag.gd")
|
|||
const PlayerHotbarType = preload("res://inventory/player_hotbar.gd")
|
||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
const BubbleHotbarSlotType = preload(
|
||||
"res://ui/components/bubble_hotbar/bubble_hotbar_slot.gd"
|
||||
)
|
||||
|
|
@ -321,7 +322,10 @@ func _show_assignment_name(slot_index: int, identity: StringName) -> void:
|
|||
if not catch_id.is_empty() and _fish_inventory != null:
|
||||
var fish_catch: FishCatchType = _fish_inventory.get_catch_by_id(catch_id)
|
||||
if fish_catch != null:
|
||||
_selected_item_label.text = fish_catch.fish.display_name
|
||||
_selected_item_label.text = FishQualityType.qualified_name(
|
||||
fish_catch.fish.display_name,
|
||||
fish_catch.quality,
|
||||
)
|
||||
_selected_item_label.visible = true
|
||||
return
|
||||
var item = (
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ extends Control
|
|||
|
||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||
const SILHOUETTE_SHADER: Shader = preload(
|
||||
|
|
@ -559,6 +560,7 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
facts.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
facts.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
facts_column.add_child(facts)
|
||||
_detail_body.add_child(_build_quality_progress(fish.id))
|
||||
|
||||
var stats_columns := HBoxContainer.new()
|
||||
stats_columns.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
|
|
@ -594,8 +596,14 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
right_stats,
|
||||
"value range",
|
||||
"%d–%d fish coin" % [
|
||||
fish.sell_value_min,
|
||||
fish.sell_value_max,
|
||||
FishQualityType.apply_sale_value(
|
||||
fish.sell_value_min,
|
||||
FishQualityType.Tier.BORING,
|
||||
),
|
||||
FishQualityType.apply_sale_value(
|
||||
fish.sell_value_max,
|
||||
FishQualityType.Tier.SHINY,
|
||||
),
|
||||
],
|
||||
)
|
||||
_add_detail_row(
|
||||
|
|
@ -605,6 +613,56 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
)
|
||||
|
||||
|
||||
func _build_quality_progress(fish_id: StringName) -> VBoxContainer:
|
||||
var quality_section := VBoxContainer.new()
|
||||
quality_section.add_theme_constant_override("separation", 3)
|
||||
var discovered_count: int = 0
|
||||
for quality: int in FishQualityType.TIER_COUNT:
|
||||
if _collection_log.has_discovered_quality(fish_id, quality):
|
||||
discovered_count += 1
|
||||
var heading := _field_label(
|
||||
"quality collection • %d / %d" % [
|
||||
discovered_count,
|
||||
FishQualityType.TIER_COUNT,
|
||||
],
|
||||
14,
|
||||
)
|
||||
heading.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
heading.add_theme_color_override("font_color", MUTED_INK)
|
||||
quality_section.add_child(heading)
|
||||
var tiers := HBoxContainer.new()
|
||||
tiers.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
tiers.add_theme_constant_override("separation", 10)
|
||||
quality_section.add_child(tiers)
|
||||
for quality: int in FishQualityType.TIER_COUNT:
|
||||
var discovered: bool = _collection_log.has_discovered_quality(
|
||||
fish_id,
|
||||
quality,
|
||||
)
|
||||
var tier := HBoxContainer.new()
|
||||
tier.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
tier.add_theme_constant_override("separation", 3)
|
||||
var quality_color: Color = UIPalette.get_quality_color(quality)
|
||||
var dot := _label("●" if discovered else "○", 13)
|
||||
dot.add_theme_color_override("font_color", quality_color)
|
||||
dot.modulate.a = 1.0 if discovered else 0.48
|
||||
tier.add_child(dot)
|
||||
var tier_label := _label(FishQualityType.display_name(quality), 13)
|
||||
tier_label.add_theme_color_override(
|
||||
"font_color",
|
||||
INK if discovered else MUTED_INK,
|
||||
)
|
||||
tier_label.modulate.a = 1.0 if discovered else 0.58
|
||||
tier.tooltip_text = (
|
||||
"%s quality collected"
|
||||
if discovered
|
||||
else "%s quality not yet collected"
|
||||
) % FishQualityType.display_name(quality)
|
||||
tier.add_child(tier_label)
|
||||
tiers.add_child(tier)
|
||||
return quality_section
|
||||
|
||||
|
||||
func _show_no_selection() -> void:
|
||||
_clear_details()
|
||||
var instruction := _label("Select an entry to read its catch record.", 21)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const SALUTATION_LABELS := {
|
|||
"salutations": "Salutations",
|
||||
"good_luck_have_fun": "Good Luck Have Fun",
|
||||
}
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
|
||||
var _service: NetworkMailService
|
||||
var _reservations: PlayerAssetReservationService
|
||||
|
|
@ -507,7 +508,11 @@ func _refresh_attachment_choices(_index: int) -> void:
|
|||
):
|
||||
_attachment_choice.add_item(
|
||||
"%s — %.1f lb" % [
|
||||
fish_catch.fish.display_name, fish_catch.weight_lb,
|
||||
FishQualityType.qualified_name(
|
||||
fish_catch.fish.display_name,
|
||||
fish_catch.quality,
|
||||
),
|
||||
fish_catch.weight_lb,
|
||||
]
|
||||
)
|
||||
_attachment_choice.set_item_metadata(
|
||||
|
|
@ -719,11 +724,18 @@ func _attachment_text(attachment: Dictionary) -> String:
|
|||
)
|
||||
if fish_catch != null:
|
||||
return "%s — %.1f lb" % [
|
||||
fish_catch.fish.display_name, fish_catch.weight_lb,
|
||||
FishQualityType.qualified_name(
|
||||
fish_catch.fish.display_name,
|
||||
fish_catch.quality,
|
||||
),
|
||||
fish_catch.weight_lb,
|
||||
]
|
||||
var data: Dictionary = attachment.get("catch", {})
|
||||
return "%s — %.1f lb" % [
|
||||
str(data.get("fish_id", "fish")).capitalize(),
|
||||
FishQualityType.qualified_name(
|
||||
str(data.get("fish_id", "fish")).capitalize(),
|
||||
int(data.get("quality", FishQualityType.Tier.BORING)),
|
||||
),
|
||||
float(data.get("weight_lb", 0.0)),
|
||||
]
|
||||
PlayerAssetReservationService.AttachmentType.CONSUMABLE:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const INPUT_OWNER: StringName = &"player_menu"
|
|||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
const FishQualityType = preload("res://fish/fish_quality.gd")
|
||||
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
|
||||
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||
|
|
@ -74,11 +75,6 @@ const NAVIGATION_PRESENTATION_SCALE: float = 0.60
|
|||
const NAVIGATION_CANONICAL_POSITION := Vector2(424.0, 44.0)
|
||||
const NAVIGATION_SELECTED_SCALE: float = 1.02
|
||||
const INVENTORY_TAB_LEFT_INSET: float = 96.0
|
||||
const COOLER_RARITY_COMMON := Color("e8eef0")
|
||||
const COOLER_RARITY_UNCOMMON := Color("64c87c")
|
||||
const COOLER_RARITY_RARE := Color("6098dd")
|
||||
const COOLER_RARITY_EPIC := Color("a979cf")
|
||||
const COOLER_RARITY_LEGENDARY := Color("db78a7")
|
||||
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
|
||||
|
||||
signal menu_visibility_changed(is_open: bool)
|
||||
|
|
@ -2609,11 +2605,14 @@ func _sync_cooler_fish_nodes(catches: Array[FishCatchType]) -> void:
|
|||
var identity_hash: int = absi(String(fish_catch.catch_id).hash())
|
||||
fish_node.configure(
|
||||
fish_catch.catch_id,
|
||||
fish_catch.fish.display_name,
|
||||
FishQualityType.qualified_name(
|
||||
fish_catch.fish.display_name,
|
||||
fish_catch.quality,
|
||||
),
|
||||
fish_catch.fish.display_texture,
|
||||
float(identity_hash % 628) / 100.0,
|
||||
0.96 + float(identity_hash % 9) * 0.01,
|
||||
_get_cooler_rarity_color(fish_catch.fish.rarity),
|
||||
UIPalette.get_quality_color(fish_catch.quality),
|
||||
)
|
||||
fish_node.set_item_state(
|
||||
_fish_selection.is_selected(fish_catch.catch_id),
|
||||
|
|
@ -2631,20 +2630,6 @@ func _sync_cooler_fish_nodes(catches: Array[FishCatchType]) -> void:
|
|||
_configure_cooler_fish_focus()
|
||||
|
||||
|
||||
func _get_cooler_rarity_color(rarity: int) -> Color:
|
||||
match rarity:
|
||||
FishDataType.Rarity.UNCOMMON:
|
||||
return COOLER_RARITY_UNCOMMON
|
||||
FishDataType.Rarity.RARE:
|
||||
return COOLER_RARITY_RARE
|
||||
FishDataType.Rarity.EPIC:
|
||||
return COOLER_RARITY_EPIC
|
||||
FishDataType.Rarity.LEGENDARY:
|
||||
return COOLER_RARITY_LEGENDARY
|
||||
_:
|
||||
return COOLER_RARITY_COMMON
|
||||
|
||||
|
||||
func _layout_cooler_fish(animate: bool = true) -> void:
|
||||
if not is_node_ready():
|
||||
return
|
||||
|
|
@ -2926,7 +2911,10 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
|||
and _reservations.is_fish_reserved(fish_catch.catch_id)
|
||||
):
|
||||
_cooler_detail_name.text += " • reserved in mail"
|
||||
_cooler_weight_unit.text = "lb • %s" % fish_catch.fish.get_rarity_name()
|
||||
_cooler_weight_unit.text = "lb • %s • %s" % [
|
||||
FishQualityType.display_name(fish_catch.quality),
|
||||
fish_catch.fish.get_rarity_name(),
|
||||
]
|
||||
if buyer_offer >= 0 and active_buyer != null:
|
||||
_cooler_offer_label.text = _get_offer_label(active_buyer)
|
||||
_cooler_offer_value.text = "$%d" % buyer_offer
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@ const RARITY_RARE := Color("5596f6")
|
|||
const RARITY_EPIC := Color("b176e8")
|
||||
const RARITY_LEGENDARY := Color("f276b3")
|
||||
|
||||
const QUALITY_BORING := Color("e8eef0")
|
||||
const QUALITY_AVERAGE := Color("64c87c")
|
||||
const QUALITY_IMPRESSIVE := Color("6098dd")
|
||||
const QUALITY_EXCEPTIONAL := Color("a979cf")
|
||||
const QUALITY_SHINY := Color("db78a7")
|
||||
|
||||
|
||||
static func get_rarity_color(rarity: int) -> Color:
|
||||
match rarity:
|
||||
|
|
@ -31,3 +37,17 @@ static func get_rarity_color(rarity: int) -> Color:
|
|||
return RARITY_LEGENDARY
|
||||
_:
|
||||
return RARITY_COMMON
|
||||
|
||||
|
||||
static func get_quality_color(quality: int) -> Color:
|
||||
match quality:
|
||||
1:
|
||||
return QUALITY_AVERAGE
|
||||
2:
|
||||
return QUALITY_IMPRESSIVE
|
||||
3:
|
||||
return QUALITY_EXCEPTIONAL
|
||||
4:
|
||||
return QUALITY_SHINY
|
||||
_:
|
||||
return QUALITY_BORING
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue