Expand fish and equipment catalogs
This commit is contained in:
parent
63e50e285d
commit
f55663c105
380 changed files with 9430 additions and 575 deletions
|
|
@ -377,7 +377,7 @@ func _can_drop_data(_at_position: Vector2, data: Variant) -> bool:
|
|||
if _bag == null or not _bag.owns_item(item_id) or _catalog == null:
|
||||
return false
|
||||
var item: ItemDataType = _catalog.get_item_by_id(item_id)
|
||||
return item != null and item.is_valid() and item.hotbar_allowed
|
||||
return item != null and item.is_available() and item.hotbar_allowed
|
||||
if kind == "cooler_fish":
|
||||
var catch_id: StringName = StringName(str(payload.get("catch_id", "")))
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
|||
const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
|
||||
const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||
const ItemDataType = preload("res://items/item_data.gd")
|
||||
const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
|
||||
const PlayerBagType = preload("res://inventory/player_bag.gd")
|
||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||
const PlayerFishingUpgradesType = preload(
|
||||
|
|
@ -82,6 +83,15 @@ const SUPPLY_PRICE_HEIGHT: float = 24.0
|
|||
const SUPPLY_PRICE_HORIZONTAL_PADDING: float = 12.0
|
||||
const SUPPLY_PRICE_ICON_SIZE: float = 18.0
|
||||
const SUPPLY_PRICE_ICON_GAP: float = 3.0
|
||||
const ROD_CARD_TILE_SIZE := Vector2(144.0, 144.0)
|
||||
const ROD_CARD_HOST_SIZE := Vector2(152.0, 198.0)
|
||||
const ROD_CAROUSEL_HEIGHT: float = 206.0
|
||||
const ROD_CAROUSEL_SEPARATION: int = 12
|
||||
const ROD_CAROUSEL_STEP: float = 328.0
|
||||
const ROD_PRICE_Y: float = 128.0
|
||||
const ROD_PRICE_HEIGHT: float = 30.0
|
||||
const ROD_PRICE_FONT_SIZE: int = 18
|
||||
const ROD_PRICE_ICON_SIZE: float = 22.0
|
||||
|
||||
@onready var _wallet_label: Label = %WalletLabel
|
||||
@onready var _shop_panel: PanelContainer = %ShopPanel
|
||||
|
|
@ -650,7 +660,12 @@ func _refresh_supplies() -> void:
|
|||
stock_item_ids = grouped_item_ids
|
||||
var current_stock_group: StringName = StringName()
|
||||
var current_stock_grid: GridContainer
|
||||
if _shop_section in [ShopSection.SNACKS, ShopSection.EQUIPMENT]:
|
||||
if _shop_section == ShopSection.SNACKS:
|
||||
current_stock_grid = _add_stock_icon_grid()
|
||||
elif _shop_section == ShopSection.EQUIPMENT:
|
||||
_add_stock_section("rods")
|
||||
_add_rod_carousel()
|
||||
_add_stock_section("equipment")
|
||||
current_stock_grid = _add_stock_icon_grid()
|
||||
for item_id: StringName in stock_item_ids:
|
||||
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
|
||||
|
|
@ -671,6 +686,10 @@ func _refresh_supplies() -> void:
|
|||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
var owned: int = _bag.get_quantity(item_id)
|
||||
var bait_topoff: bool = FishingShopStockType.is_bait_topoff(item_id)
|
||||
var permanent_unlock: bool = (
|
||||
FishingShopStockType.is_permanent_unlock(item_id, item)
|
||||
)
|
||||
var unlock_status: String = _unlock_status(owned > 0)
|
||||
var bait_unlocked: bool = (
|
||||
not bait_topoff or _bag.is_bait_unlocked(item_id)
|
||||
)
|
||||
|
|
@ -683,10 +702,11 @@ func _refresh_supplies() -> void:
|
|||
var use_icon_tile: bool = (
|
||||
current_stock_grid != null
|
||||
)
|
||||
button.text = "%s\nowned %d" % [
|
||||
item.display_name,
|
||||
owned,
|
||||
]
|
||||
button.text = (
|
||||
"%s\n%s" % [item.display_name, unlock_status]
|
||||
if permanent_unlock
|
||||
else "%s\nowned %d" % [item.display_name, owned]
|
||||
)
|
||||
var item_tooltip_text: String = item.description
|
||||
if bait_topoff:
|
||||
if use_icon_tile:
|
||||
|
|
@ -724,11 +744,19 @@ func _refresh_supplies() -> void:
|
|||
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
button.text = ""
|
||||
item_tooltip_text = "%s\nowned %d\n%s" % [
|
||||
item.display_name,
|
||||
owned,
|
||||
item.description,
|
||||
]
|
||||
item_tooltip_text = (
|
||||
"%s\n%s\n%s" % [
|
||||
item.display_name,
|
||||
unlock_status,
|
||||
item.description,
|
||||
]
|
||||
if permanent_unlock
|
||||
else "%s\nowned %d\n%s" % [
|
||||
item.display_name,
|
||||
owned,
|
||||
item.description,
|
||||
]
|
||||
)
|
||||
button.disabled = (
|
||||
_transaction_in_progress
|
||||
or _closing
|
||||
|
|
@ -777,6 +805,8 @@ func _refresh_supplies() -> void:
|
|||
|
||||
|
||||
func _item_belongs_in_current_section(item: ItemDataType) -> bool:
|
||||
if not item.is_available():
|
||||
return false
|
||||
match _shop_section:
|
||||
ShopSection.BAIT:
|
||||
return item.is_bait() or item.is_lure()
|
||||
|
|
@ -787,6 +817,10 @@ func _item_belongs_in_current_section(item: ItemDataType) -> bool:
|
|||
return false
|
||||
|
||||
|
||||
func _unlock_status(unlocked: bool) -> String:
|
||||
return "unlocked" if unlocked else "locked"
|
||||
|
||||
|
||||
func _add_stock_section(title: String) -> void:
|
||||
var label := Label.new()
|
||||
label.text = title
|
||||
|
|
@ -811,21 +845,156 @@ func _add_stock_icon_grid() -> GridContainer:
|
|||
return grid
|
||||
|
||||
|
||||
func _add_rod_carousel() -> void:
|
||||
var shell := HBoxContainer.new()
|
||||
shell.name = "RodCarousel"
|
||||
shell.custom_minimum_size.y = ROD_CAROUSEL_HEIGHT
|
||||
shell.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
shell.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
shell.add_theme_constant_override("separation", 8)
|
||||
_supplies_list.add_child(shell)
|
||||
var previous := Button.new()
|
||||
previous.name = "PreviousRod"
|
||||
previous.text = "‹"
|
||||
previous.tooltip_text = "Previous rods"
|
||||
previous.custom_minimum_size = Vector2(42.0, 72.0)
|
||||
previous.focus_mode = Control.FOCUS_ALL
|
||||
UtilityPageStyleType.apply_ocean_button(previous)
|
||||
shell.add_child(previous)
|
||||
var scroll := ScrollContainer.new()
|
||||
scroll.name = "RodScroll"
|
||||
scroll.custom_minimum_size.y = ROD_CAROUSEL_HEIGHT
|
||||
scroll.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO
|
||||
scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
scroll.follow_focus = true
|
||||
shell.add_child(scroll)
|
||||
var row := HBoxContainer.new()
|
||||
row.name = "RodCards"
|
||||
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
row.add_theme_constant_override(
|
||||
"separation", ROD_CAROUSEL_SEPARATION
|
||||
)
|
||||
scroll.add_child(row)
|
||||
var rods: Array[FishingRodDataType] = (
|
||||
FishingShopStockType.get_rod_stock(_item_catalog)
|
||||
)
|
||||
for rod: FishingRodDataType in rods:
|
||||
_add_rod_card(row, rod)
|
||||
var next := Button.new()
|
||||
next.name = "NextRod"
|
||||
next.text = "›"
|
||||
next.tooltip_text = "Next rods"
|
||||
next.custom_minimum_size = Vector2(42.0, 72.0)
|
||||
next.focus_mode = Control.FOCUS_ALL
|
||||
UtilityPageStyleType.apply_ocean_button(next)
|
||||
shell.add_child(next)
|
||||
previous.disabled = rods.size() <= 1
|
||||
next.disabled = rods.size() <= 1
|
||||
previous.pressed.connect(
|
||||
_scroll_rod_carousel.bind(scroll, -ROD_CAROUSEL_STEP)
|
||||
)
|
||||
next.pressed.connect(
|
||||
_scroll_rod_carousel.bind(scroll, ROD_CAROUSEL_STEP)
|
||||
)
|
||||
|
||||
|
||||
func _add_rod_card(parent: HBoxContainer, rod: FishingRodDataType) -> void:
|
||||
var owned: bool = _bag.owns_item(rod.item_id)
|
||||
var level: int = _player.experience.get_level()
|
||||
var level_locked: bool = level < rod.unlock_level
|
||||
var tooltip := "%s\n%s" % [rod.display_name, rod.description]
|
||||
if not rod.effect_summary.strip_edges().is_empty():
|
||||
tooltip += "\n%s" % rod.effect_summary
|
||||
if not rod.tradeoff.strip_edges().is_empty():
|
||||
tooltip += "\ntradeoff: %s" % rod.tradeoff
|
||||
tooltip += "\nlevel %d" % rod.unlock_level
|
||||
tooltip += "\n%s" % _unlock_status(owned)
|
||||
var button := _make_stock_button("", tooltip)
|
||||
button.name = "Rod_%s" % str(rod.item_id)
|
||||
button.custom_minimum_size = ROD_CARD_TILE_SIZE
|
||||
button.size = ROD_CARD_TILE_SIZE
|
||||
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
button.icon = rod.icon
|
||||
button.expand_icon = true
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
button.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
button.disabled = (
|
||||
owned
|
||||
or level_locked
|
||||
or _transaction_in_progress
|
||||
or _closing
|
||||
or _network_shop == null
|
||||
or not _network_shop.can_request_purchase()
|
||||
or not _wallet.can_afford(rod.shop_price)
|
||||
or not _bag.can_add_item(rod.item_id, 1)
|
||||
)
|
||||
button.pressed.connect(_purchase_rod.bind(rod.item_id))
|
||||
var host: Control = _add_supply_icon_tile(
|
||||
parent,
|
||||
button,
|
||||
rod.shop_price,
|
||||
ROD_CARD_TILE_SIZE,
|
||||
ROD_CARD_HOST_SIZE,
|
||||
ROD_PRICE_Y,
|
||||
ROD_PRICE_HEIGHT,
|
||||
ROD_PRICE_FONT_SIZE,
|
||||
ROD_PRICE_ICON_SIZE,
|
||||
)
|
||||
var name_label := Label.new()
|
||||
name_label.name = "RodName"
|
||||
name_label.position = Vector2(0.0, 162.0)
|
||||
name_label.size = Vector2(ROD_CARD_HOST_SIZE.x, 30.0)
|
||||
name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
name_label.text = rod.display_name
|
||||
name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
name_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
name_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
name_label.add_theme_font_override("font", UtilityPageStyleType.TuffyFont)
|
||||
name_label.add_theme_font_size_override("font_size", 17)
|
||||
name_label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyleType.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
host.add_child(name_label)
|
||||
|
||||
|
||||
func _scroll_rod_carousel(scroll: ScrollContainer, amount: float) -> void:
|
||||
var target: float = clampf(
|
||||
float(scroll.scroll_horizontal) + amount,
|
||||
0.0,
|
||||
maxf(
|
||||
scroll.get_h_scroll_bar().max_value
|
||||
- scroll.get_h_scroll_bar().page,
|
||||
0.0,
|
||||
),
|
||||
)
|
||||
var tween := create_tween()
|
||||
tween.set_trans(Tween.TRANS_QUAD)
|
||||
tween.set_ease(Tween.EASE_OUT)
|
||||
tween.tween_method(
|
||||
func(value: float) -> void:
|
||||
scroll.scroll_horizontal = roundi(value),
|
||||
float(scroll.scroll_horizontal),
|
||||
target,
|
||||
0.15,
|
||||
)
|
||||
|
||||
|
||||
func _add_art_kit_button(parent: GridContainer) -> void:
|
||||
var item: ItemDataType = _item_catalog.get_item_by_id(
|
||||
ArtShopStockType.ART_KIT_ITEM_ID
|
||||
)
|
||||
if item == null:
|
||||
if item == null or not item.is_available():
|
||||
return
|
||||
var owned: bool = _bag.get_quantity(ArtShopStockType.ART_KIT_ITEM_ID) > 0
|
||||
var button: Button = _make_stock_button(
|
||||
"%s\n%s" % [
|
||||
item.display_name,
|
||||
"owned" if owned else "not owned",
|
||||
_unlock_status(owned),
|
||||
],
|
||||
"%s\n%s\n%s" % [
|
||||
item.display_name,
|
||||
"owned" if owned else "not owned",
|
||||
_unlock_status(owned),
|
||||
item.description,
|
||||
],
|
||||
)
|
||||
|
|
@ -942,29 +1111,35 @@ func _configure_marker_icon_tile(
|
|||
|
||||
|
||||
func _add_supply_icon_tile(
|
||||
parent: GridContainer,
|
||||
parent: Container,
|
||||
button: Button,
|
||||
price: int,
|
||||
) -> void:
|
||||
tile_size: Vector2 = SUPPLY_ICON_TILE_SIZE,
|
||||
host_size: Vector2 = SUPPLY_ICON_HOST_SIZE,
|
||||
price_y: float = SUPPLY_PRICE_Y,
|
||||
price_height: float = SUPPLY_PRICE_HEIGHT,
|
||||
price_font_size: int = SUPPLY_PRICE_FONT_SIZE,
|
||||
price_icon_size: float = SUPPLY_PRICE_ICON_SIZE,
|
||||
) -> Control:
|
||||
var tile_host := Control.new()
|
||||
tile_host.custom_minimum_size = SUPPLY_ICON_HOST_SIZE
|
||||
tile_host.custom_minimum_size = host_size
|
||||
tile_host.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
parent.add_child(tile_host)
|
||||
button.position = Vector2.ZERO
|
||||
button.size = SUPPLY_ICON_TILE_SIZE
|
||||
button.size = tile_size
|
||||
tile_host.add_child(button)
|
||||
var price_text := str(price)
|
||||
var price_text_width: float = UtilityPageStyleType.TuffyFont.get_string_size(
|
||||
price_text,
|
||||
HORIZONTAL_ALIGNMENT_LEFT,
|
||||
-1.0,
|
||||
SUPPLY_PRICE_FONT_SIZE,
|
||||
price_font_size,
|
||||
).x
|
||||
var price_width: float = minf(
|
||||
SUPPLY_ICON_TILE_SIZE.x,
|
||||
tile_size.x,
|
||||
ceilf(
|
||||
price_text_width
|
||||
+ SUPPLY_PRICE_ICON_SIZE
|
||||
+ price_icon_size
|
||||
+ SUPPLY_PRICE_ICON_GAP
|
||||
+ SUPPLY_PRICE_HORIZONTAL_PADDING
|
||||
),
|
||||
|
|
@ -972,10 +1147,10 @@ func _add_supply_icon_tile(
|
|||
var price_bubble := PanelContainer.new()
|
||||
price_bubble.name = "PriceBubble"
|
||||
price_bubble.position = Vector2(
|
||||
(SUPPLY_ICON_TILE_SIZE.x - price_width) * 0.5,
|
||||
SUPPLY_PRICE_Y,
|
||||
(tile_size.x - price_width) * 0.5,
|
||||
price_y,
|
||||
)
|
||||
price_bubble.size = Vector2(price_width, SUPPLY_PRICE_HEIGHT)
|
||||
price_bubble.size = Vector2(price_width, price_height)
|
||||
price_bubble.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
price_bubble.z_index = 2
|
||||
var price_style := UtilityPageStyleType.rounded_style(
|
||||
|
|
@ -998,7 +1173,7 @@ func _add_supply_icon_tile(
|
|||
price_bubble.add_child(price_row)
|
||||
var price_icon := TextureRect.new()
|
||||
price_icon.name = "CurrencyIcon"
|
||||
price_icon.custom_minimum_size = Vector2.ONE * SUPPLY_PRICE_ICON_SIZE
|
||||
price_icon.custom_minimum_size = Vector2.ONE * price_icon_size
|
||||
price_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
price_icon.texture = CURRENCY_ICON
|
||||
price_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
|
|
@ -1015,13 +1190,14 @@ func _add_supply_icon_tile(
|
|||
"font", UtilityPageStyleType.TuffyFont
|
||||
)
|
||||
price_label.add_theme_font_size_override(
|
||||
"font_size", SUPPLY_PRICE_FONT_SIZE
|
||||
"font_size", price_font_size
|
||||
)
|
||||
price_label.add_theme_color_override(
|
||||
"font_color", SUPPLY_PRICE_COLOR
|
||||
)
|
||||
price_label.add_theme_constant_override("outline_size", 0)
|
||||
price_row.add_child(price_label)
|
||||
return tile_host
|
||||
|
||||
|
||||
func _refresh_cooler_capacity() -> void:
|
||||
|
|
@ -1073,7 +1249,7 @@ func _purchase_supply(item_id: StringName) -> void:
|
|||
return
|
||||
var owned: int = _bag.get_quantity(item_id)
|
||||
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
|
||||
if item == null:
|
||||
if item == null or not item.is_available():
|
||||
_set_feedback("Purchase could not be completed.")
|
||||
return
|
||||
var quantity: int = FishingShopStockType.get_purchase_quantity(
|
||||
|
|
@ -1094,6 +1270,23 @@ func _purchase_supply(item_id: StringName) -> void:
|
|||
_network_shop.request_supply(item_id)
|
||||
|
||||
|
||||
func _purchase_rod(item_id: StringName) -> void:
|
||||
if _network_shop == null or not _is_transaction_context_valid():
|
||||
_set_feedback("Purchase could not be completed.")
|
||||
return
|
||||
var rod := _item_catalog.get_item_by_id(item_id) as FishingRodDataType
|
||||
if (
|
||||
rod == null
|
||||
or not rod.is_shop_available()
|
||||
or _bag.owns_item(item_id)
|
||||
or not _wallet.can_afford(rod.shop_price)
|
||||
or not _bag.can_add_item(item_id, 1)
|
||||
):
|
||||
_set_feedback("Purchase could not be completed.")
|
||||
return
|
||||
_network_shop.request_rod(item_id)
|
||||
|
||||
|
||||
func _purchase_art_kit() -> void:
|
||||
if _network_shop == null or not _is_transaction_context_valid():
|
||||
_set_feedback("Purchase could not be completed.")
|
||||
|
|
|
|||
|
|
@ -3,281 +3,6 @@ extends RefCounted
|
|||
|
||||
const FishDataType = preload("res://fish/fish_data.gd")
|
||||
|
||||
# Catalog numbers are a presentation contract. Add future species deliberately;
|
||||
# filtering and display order must not redefine an existing number.
|
||||
const CATALOG_ORDER: Array[StringName] = [
|
||||
&"bluegill",
|
||||
&"bass",
|
||||
&"carp",
|
||||
&"sunfish",
|
||||
&"catfish_blue",
|
||||
&"catfish_channel",
|
||||
&"catfish_flathead",
|
||||
&"catfish_white",
|
||||
&"tuna_albacore",
|
||||
&"tuna_bigeye",
|
||||
&"tuna_bluefin",
|
||||
&"tuna_skipjack",
|
||||
&"tuna_yellowfin",
|
||||
&"goby_round",
|
||||
&"salmon_atlantic",
|
||||
&"salmon_chum",
|
||||
&"salmon_coho",
|
||||
&"salmon_pink",
|
||||
&"salmon_sockeye",
|
||||
&"anchovy_european",
|
||||
&"anchovy_northern",
|
||||
&"chub_european",
|
||||
&"chub_flame",
|
||||
&"chub_lake",
|
||||
&"goldfish",
|
||||
&"goldfish_bubbleeye",
|
||||
&"grouper_gulf",
|
||||
&"grouper_red",
|
||||
&"mackerel_atlantic",
|
||||
&"mackerel_cero",
|
||||
&"mackerel_chub",
|
||||
&"mackerel_king",
|
||||
&"mackerel_spanish",
|
||||
&"marlin_black",
|
||||
&"marlin_blue",
|
||||
&"marlin_white",
|
||||
&"pomfret_black",
|
||||
&"pomfret_chinese",
|
||||
&"pomfret_golden",
|
||||
&"pomfret_white",
|
||||
&"sailfish",
|
||||
&"sauger",
|
||||
&"saugeye",
|
||||
&"snapper_lane",
|
||||
&"snapper_mangrove",
|
||||
&"snapper_mutton",
|
||||
&"snapper_red",
|
||||
&"swordfish",
|
||||
&"trout_cutthroat",
|
||||
&"trout_golden",
|
||||
&"trout_rainbow",
|
||||
&"trout_steelhead",
|
||||
&"walleye",
|
||||
]
|
||||
|
||||
# Short presentation notes are kept here with the catalog contract rather
|
||||
# than mixed into gameplay balance resources. Add facts deliberately when a
|
||||
# species joins the catalog.
|
||||
const FISH_FACTS: Dictionary[StringName, String] = {
|
||||
&"bluegill": (
|
||||
"bluegill fathers sweep shallow bowls into the bottom for nests, "
|
||||
+ "then stand guard. whole neighborhoods of nests can crowd together."
|
||||
),
|
||||
&"bass": (
|
||||
"striped bass split their lives between salt and fresh water, "
|
||||
+ "returning upriver to spawn. a long-lived striper may see three decades."
|
||||
),
|
||||
&"carp": (
|
||||
"whisker-like barbels help a common carp investigate the bottom. "
|
||||
+ "nosing through sediment can cloud the water and loosen plants."
|
||||
),
|
||||
&"sunfish": (
|
||||
"ocean sunfish are built more like enormous swimming heads than "
|
||||
+ "ordinary fish. they visit the surface and turn sideways to warm up."
|
||||
),
|
||||
&"catfish_blue": (
|
||||
"a deep fork in the tail inspired the blue catfish's scientific "
|
||||
+ "name. it rests deep by daylight and becomes more active after dark."
|
||||
),
|
||||
&"catfish_channel": (
|
||||
"a channel catfish can taste through skin all over its body, "
|
||||
+ "especially near its gills and whiskers. the male watches the eggs."
|
||||
),
|
||||
&"catfish_flathead": (
|
||||
"flatheads favor hideouts made by logs, rocks, ledges, and other "
|
||||
+ "underwater structure. as they grow, fish dominate their menu."
|
||||
),
|
||||
&"catfish_white": (
|
||||
"white catfish began along the atlantic coast between new york "
|
||||
+ "and florida. people later carried them far beyond that home range."
|
||||
),
|
||||
&"tuna_albacore": (
|
||||
"albacore have unusually long pectoral fins. their warm muscles help "
|
||||
+ "them cruise across wide stretches of open ocean."
|
||||
),
|
||||
&"tuna_bigeye": (
|
||||
"bigeye tuna make daily trips between deep, cool water and warmer "
|
||||
+ "surface layers. their large eyes suit those dim depths."
|
||||
),
|
||||
&"tuna_bluefin": (
|
||||
"bluefin tuna are powerful ocean travelers. heat retained in their "
|
||||
+ "swimming muscles lets them thrive in surprisingly cold seas."
|
||||
),
|
||||
&"tuna_skipjack": (
|
||||
"skipjack travel in fast-moving schools and have dark stripes along "
|
||||
+ "their lower sides. they rarely stop swimming."
|
||||
),
|
||||
&"tuna_yellowfin": (
|
||||
"yellowfin are named for their bright yellow fins and finlets. large "
|
||||
+ "schools often gather with other open-ocean hunters."
|
||||
),
|
||||
&"goby_round": (
|
||||
"round gobies use fused pelvic fins like a suction cup to hold onto "
|
||||
+ "rocks. they are small fish with a famously large appetite."
|
||||
),
|
||||
&"salmon_atlantic": (
|
||||
"atlantic salmon can return from the ocean to the river where they "
|
||||
+ "hatched. unlike many salmon, some survive to make the trip again."
|
||||
),
|
||||
&"salmon_chum": (
|
||||
"chum salmon travel immense distances and develop striking bars when "
|
||||
+ "they return to fresh water to spawn."
|
||||
),
|
||||
&"salmon_coho": (
|
||||
"coho salmon are agile hunters known for powerful leaps. young coho "
|
||||
+ "may spend more than a year growing in streams."
|
||||
),
|
||||
&"salmon_pink": (
|
||||
"pink salmon usually follow a strict two-year life cycle. spawning "
|
||||
+ "males develop the hump that inspired their nickname."
|
||||
),
|
||||
&"salmon_sockeye": (
|
||||
"sockeye salmon feed heavily on plankton at sea. adults turn vivid red "
|
||||
+ "as they return inland to spawn."
|
||||
),
|
||||
&"anchovy_european": (
|
||||
"european anchovies form dense schools and feed by filtering tiny "
|
||||
+ "plankton from coastal water."
|
||||
),
|
||||
&"anchovy_northern": (
|
||||
"northern anchovies gather in huge schools along the pacific coast "
|
||||
+ "and filter plankton with their fine gill rakers."
|
||||
),
|
||||
&"chub_european": (
|
||||
"european chub are adaptable river fish. adults often patrol "
|
||||
+ "shallows and feed on insects, small fish, and fruit."
|
||||
),
|
||||
&"chub_flame": (
|
||||
"flame chubs prefer cool, clear streams. their bright breeding colors "
|
||||
+ "make a small fish easy to spot in spring water."
|
||||
),
|
||||
&"chub_lake": (
|
||||
"lake chubs school in cold northern water and use their small mouths "
|
||||
+ "to pick insects and other tiny prey from the current."
|
||||
),
|
||||
&"goldfish": (
|
||||
"goldfish can tolerate a surprisingly wide range of conditions. "
|
||||
+ "their wild relatives often live in slow, weedy water."
|
||||
),
|
||||
&"goldfish_bubbleeye": (
|
||||
"bubble-eye goldfish have delicate fluid-filled sacs beneath their "
|
||||
+ "eyes, making careful handling especially important."
|
||||
),
|
||||
&"grouper_gulf": (
|
||||
"gulf grouper are ambush predators that use a sudden gulp to pull "
|
||||
+ "prey into their cavernous mouths."
|
||||
),
|
||||
&"grouper_red": (
|
||||
"red grouper excavate shelters in reef rubble. the hollows they make "
|
||||
+ "can become hiding places for many smaller animals."
|
||||
),
|
||||
&"mackerel_atlantic": (
|
||||
"atlantic mackerel travel in fast schools across the north atlantic "
|
||||
+ "and feed on plankton and small schooling fish."
|
||||
),
|
||||
&"mackerel_cero": (
|
||||
"cero mackerel are streamlined coastal hunters. sharp teeth help "
|
||||
+ "them slash through schools of smaller fish."
|
||||
),
|
||||
&"mackerel_chub": (
|
||||
"chub mackerel school near the surface and follow plankton blooms. "
|
||||
+ "their dark wavy bars help identify them at a glance."
|
||||
),
|
||||
&"mackerel_king": (
|
||||
"king mackerel are swift coastal predators. their pointed teeth and "
|
||||
+ "long body are built for sudden bursts of speed."
|
||||
),
|
||||
&"mackerel_spanish": (
|
||||
"spanish mackerel hunt in warm coastal waters and often travel in "
|
||||
+ "loose schools while chasing baitfish."
|
||||
),
|
||||
&"marlin_black": (
|
||||
"black marlin are powerful billfish that spend much of their lives "
|
||||
+ "in warm open ocean and can make blistering runs."
|
||||
),
|
||||
&"marlin_blue": (
|
||||
"blue marlin are highly migratory open-ocean hunters. their long bill "
|
||||
+ "helps them stun prey before turning back to feed."
|
||||
),
|
||||
&"marlin_white": (
|
||||
"white marlin favor warm offshore water and use their rounded dorsal "
|
||||
+ "fin and bill to weave through schools of prey."
|
||||
),
|
||||
&"pomfret_black": (
|
||||
"black pomfret have a deep, laterally compressed body that lets them "
|
||||
+ "maneuver neatly through open water."
|
||||
),
|
||||
&"pomfret_chinese": (
|
||||
"chinese pomfret are silvery schooling fish with a tall, flattened "
|
||||
+ "body and long sickle-shaped fins."
|
||||
),
|
||||
&"pomfret_golden": (
|
||||
"golden pomfret are warm-water swimmers whose golden sheen is most "
|
||||
+ "noticeable along the fins and flanks."
|
||||
),
|
||||
&"pomfret_white": (
|
||||
"white pomfret gather over coastal grounds and use their compact, "
|
||||
+ "deep bodies to turn quickly while feeding."
|
||||
),
|
||||
&"sailfish": (
|
||||
"sailfish raise their enormous dorsal fin while herding baitfish. "
|
||||
+ "they are among the fastest fish in the sea."
|
||||
),
|
||||
&"sauger": (
|
||||
"sauger favor turbid rivers and reservoirs. their mottled backs "
|
||||
+ "blend into the dim, shifting light near the bottom."
|
||||
),
|
||||
&"saugeye": (
|
||||
"saugeye are a fertile hybrid of sauger and walleye. they combine "
|
||||
+ "traits from both parent species and often grow quickly."
|
||||
),
|
||||
&"snapper_lane": (
|
||||
"lane snapper use reef structure for cover and hunt small crustaceans "
|
||||
+ "and fish when the light begins to fade."
|
||||
),
|
||||
&"snapper_mangrove": (
|
||||
"mangrove snapper shelter among roots and docks when young, then "
|
||||
+ "move toward reefs as they grow."
|
||||
),
|
||||
&"snapper_mutton": (
|
||||
"mutton snapper have canine teeth that help them pick crabs, shrimp, "
|
||||
+ "and other hard-shelled prey from the reef."
|
||||
),
|
||||
&"snapper_red": (
|
||||
"red snapper gather around reefs, wrecks, and ledges. their diet "
|
||||
+ "includes small fish, shrimp, and squid."
|
||||
),
|
||||
&"swordfish": (
|
||||
"swordfish are fast, wide-ranging predators with a flattened bill. "
|
||||
+ "they can cross deep water and warm surface layers in one day."
|
||||
),
|
||||
&"trout_cutthroat": (
|
||||
"cutthroat trout take their name from the red slash beneath the jaw. "
|
||||
+ "many populations rely on cold, well-oxygenated streams."
|
||||
),
|
||||
&"trout_golden": (
|
||||
"golden trout evolved in clear, high-elevation california streams. "
|
||||
+ "their bright flanks stand out against rocky alpine water."
|
||||
),
|
||||
&"trout_rainbow": (
|
||||
"rainbow trout are adaptable stream hunters. the pink lateral stripe "
|
||||
+ "is especially vivid when the fish is in breeding condition."
|
||||
),
|
||||
&"trout_steelhead": (
|
||||
"steelhead are the ocean-going form of rainbow trout. adults can "
|
||||
+ "leave fresh water and later return to spawn."
|
||||
),
|
||||
&"walleye": (
|
||||
"walleye have light-sensitive eyes that help them hunt in murky water "
|
||||
+ "and around dusk. their reflective layer gives the eyes a pale glow."
|
||||
),
|
||||
}
|
||||
|
||||
static func category_for(fish: FishDataType) -> WaterType.Type:
|
||||
if fish == null:
|
||||
|
|
@ -299,24 +24,27 @@ static func empty_state(category: WaterType.Type) -> String:
|
|||
return "No entries available."
|
||||
|
||||
|
||||
static func catalog_number(fish_id: StringName) -> int:
|
||||
var index: int = CATALOG_ORDER.find(fish_id)
|
||||
return index + 1 if index >= 0 else 0
|
||||
static func catalog_number(fish: FishDataType) -> int:
|
||||
return fish.catalog_number if fish != null else 0
|
||||
|
||||
|
||||
static func facts_for(fish_id: StringName) -> String:
|
||||
return str(FISH_FACTS.get(fish_id, "unknown"))
|
||||
static func facts_for(fish: FishDataType) -> String:
|
||||
if fish == null or fish.logbook_fact.strip_edges().is_empty():
|
||||
return "unknown"
|
||||
return fish.logbook_fact
|
||||
|
||||
|
||||
static func ordered_species(
|
||||
candidates: Array[FishDataType],
|
||||
) -> Array[FishDataType]:
|
||||
var by_id: Dictionary[StringName, FishDataType] = {}
|
||||
for fish: FishDataType in candidates:
|
||||
if fish != null and not fish.id.is_empty():
|
||||
by_id[fish.id] = fish
|
||||
var ordered: Array[FishDataType] = []
|
||||
for fish_id: StringName in CATALOG_ORDER:
|
||||
if by_id.has(fish_id):
|
||||
ordered.append(by_id[fish_id])
|
||||
for fish: FishDataType in candidates:
|
||||
if fish != null and fish.active and not fish.id.is_empty():
|
||||
ordered.append(fish)
|
||||
ordered.sort_custom(
|
||||
func(a: FishDataType, b: FishDataType) -> bool:
|
||||
if a.catalog_number == b.catalog_number:
|
||||
return String(a.id) < String(b.id)
|
||||
return a.catalog_number < b.catalog_number
|
||||
)
|
||||
return ordered
|
||||
|
|
|
|||
|
|
@ -654,7 +654,7 @@ func _refresh_details(animate: bool) -> void:
|
|||
|
||||
func _build_known_details(fish: FishDataType) -> void:
|
||||
_clear_details()
|
||||
var catalog_number: int = LogbookCatalog.catalog_number(fish.id)
|
||||
var catalog_number: int = LogbookCatalog.catalog_number(fish)
|
||||
var summary_margin := MarginContainer.new()
|
||||
summary_margin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_set_margins(summary_margin, 40, 0, 40, 0)
|
||||
|
|
@ -719,7 +719,7 @@ func _build_known_details(fish: FishDataType) -> void:
|
|||
summary_columns.add_child(facts_column)
|
||||
var facts_heading := _field_label("fish facts", 16)
|
||||
facts_column.add_child(facts_heading)
|
||||
var facts := _label(LogbookCatalog.facts_for(fish.id), 16)
|
||||
var facts := _label(LogbookCatalog.facts_for(fish), 16)
|
||||
facts.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
facts.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
facts_column.add_child(facts)
|
||||
|
|
@ -999,7 +999,7 @@ func _on_inventory_changed() -> void:
|
|||
|
||||
|
||||
func _unknown_selection_key(fish: FishDataType) -> StringName:
|
||||
var number: int = LogbookCatalog.catalog_number(fish.id)
|
||||
var number: int = LogbookCatalog.catalog_number(fish)
|
||||
return StringName("unknown_%d" % number)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4334,7 +4334,7 @@ func _refresh_logbook() -> void:
|
|||
var valid_species: Array[FishDataType] = []
|
||||
if _catalog != null:
|
||||
for fish: FishDataType in _catalog.candidates:
|
||||
if fish != null and not fish.id.is_empty():
|
||||
if fish != null and fish.active and not fish.id.is_empty():
|
||||
valid_species.append(fish)
|
||||
_logbook_species = valid_species
|
||||
_logbook_empty.visible = valid_species.is_empty()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue