Expand fish and equipment catalogs

This commit is contained in:
Alexander Sellite 2026-08-14 16:31:28 -04:00
parent 63e50e285d
commit f55663c105
380 changed files with 9430 additions and 575 deletions

View file

@ -48,7 +48,8 @@ func _run() -> void:
as PlayerAssetReservationService
)
assert(player != null)
assert(catalog != null and catalog.candidates.size() == 53)
assert(catalog != null and catalog.candidates.size() == 313)
assert(LogbookCatalog.ordered_species(catalog.candidates).size() == 53)
assert(sale_service != null)
assert(shop_service != null)
assert(session != null and session.is_host())
@ -72,6 +73,7 @@ func _run() -> void:
assert(session.set_host_open(false))
await _test_host_shop_purchase(main, player, shop_service)
await _test_host_rod_purchase(player, shop_service)
await _test_host_art_shop_purchase(main, player, shop_service)
await _test_fishing_shop_sale_ui(
main, player, catalog, sale_service, reservations
@ -160,6 +162,8 @@ func _run_multiplayer_client() -> void:
shop_service.local_purchase_finished.connect(_on_shop_finished)
var catch_ids: Array[StringName] = []
for fish: FishData in catalog.candidates:
if not fish.active:
continue
var fish_catch := _make_catch(fish)
player.inventory.add_catch(fish_catch)
catch_ids.append(fish_catch.catch_id)
@ -260,6 +264,8 @@ func _test_each_species(
sale_service: NetworkSaleService,
) -> void:
for fish: FishData in catalog.candidates:
if not fish.active:
continue
assert(fish != null)
var fish_catch := _make_catch(fish)
player.inventory.add_catch(fish_catch)
@ -372,6 +378,8 @@ func _test_host_shop_sale(
var species_ids: Array[StringName] = []
var expected_payout: int = 0
for fish: FishData in catalog.candidates:
if not fish.active:
continue
var fish_catch := _make_catch(fish)
player.inventory.add_catch(fish_catch)
species_ids.append(fish_catch.catch_id)
@ -528,6 +536,18 @@ func _test_host_art_shop_purchase(
assert(not shop_service.is_local_purchase_pending())
func _test_host_rod_purchase(
player: Player,
shop_service: NetworkShopService,
) -> void:
assert(player.bag.remove_item(&"basic_fishing_rod", 1))
_shop_result.clear()
assert(not shop_service.request_rod(&"basic_fishing_rod").is_empty())
assert(not _shop_result.is_empty() and bool(_shop_result[1]))
assert(player.bag.owns_item(&"basic_fishing_rod"))
assert(not shop_service.is_local_purchase_pending())
func _test_fishing_shop_sale_ui(
main: Node,
player: Player,
@ -616,6 +636,27 @@ func _test_fishing_shop_sale_ui(
assert(art_supplies_tab != null and art_supplies_tab.text == "Art Supplies")
var sell_mode := shop_tabs[5] as Button
assert(sell_mode != null and sell_mode.text == "Sell Fish")
var equipment_tab := shop_tabs[3] as Button
assert(equipment_tab != null and equipment_tab.text == "Equipment")
await _activate_pointer_control(equipment_tab, ui_viewport)
await process_frame
var equipment_sections: Array[String] = []
for child: Node in shop.get_node("%SuppliesList").get_children():
if child is Label:
equipment_sections.append((child as Label).text)
assert(equipment_sections == ["rods", "equipment"])
var supplies_list := shop.get_node("%SuppliesList") as VBoxContainer
var rod_carousel := supplies_list.get_node("RodCarousel") as HBoxContainer
assert(rod_carousel != null)
var rod_cards := rod_carousel.get_node("RodScroll/RodCards") as HBoxContainer
assert(rod_cards != null and rod_cards.get_child_count() == 1)
var basic_rod_button := rod_cards.find_child(
"Rod_basic_fishing_rod", true, false
) as Button
assert(basic_rod_button != null)
assert(basic_rod_button.custom_minimum_size == Vector2(144.0, 144.0))
assert(basic_rod_button.icon != null and basic_rod_button.disabled)
assert(rod_cards.find_child("Rod_aurora_rod", true, false) == null)
await _activate_pointer_control(art_supplies_tab, ui_viewport)
await process_frame
var stock_sections: Array[String] = []

View file

@ -0,0 +1,49 @@
extends SceneTree
const FishingShopStockType = preload("res://economy/fishing_shop_stock.gd")
const FishingShopType = preload("res://ui/fishing_shop.gd")
const ItemCatalogType = preload("res://items/item_catalog.gd")
const ItemDataType = preload("res://items/item_data.gd")
const PlayerBagType = preload("res://inventory/player_bag.gd")
const PlayerWalletType = preload("res://economy/player_wallet.gd")
const ItemCatalogResource: ItemCatalogType = preload(
"res://items/catalog/item_catalog.tres"
)
func _initialize() -> void:
var shop := FishingShopType.new()
assert(shop.call("_unlock_status", false) == "locked")
assert(shop.call("_unlock_status", true) == "unlocked")
shop.free()
var magnet: ItemDataType = ItemCatalogResource.get_available_item_by_id(
&"magnet"
)
assert(magnet != null)
assert(magnet.category == ItemDataType.Category.TOOL)
assert(magnet.icon == null)
assert(not magnet.usable)
assert(not magnet.equippable)
assert(not magnet.hotbar_allowed)
assert(FishingShopStockType.get_price(&"magnet") == 250)
assert(FishingShopStockType.get_stock_item_ids().has(&"magnet"))
assert(FishingShopStockType.is_permanent_unlock(&"magnet", magnet))
var wallet := PlayerWalletType.new()
wallet.current_balance = 250
var bag := PlayerBagType.new()
bag.setup(ItemCatalogResource)
assert(FishingShopStockType.purchase_one(
&"magnet", wallet, bag, ItemCatalogResource
))
assert(bag.owns_item(&"magnet"))
assert(wallet.get_balance() == 0)
assert(not FishingShopStockType.purchase_one(
&"magnet", wallet, bag, ItemCatalogResource
))
bag.free()
wallet.free()
print("Equipment catalog validation: PASS")
quit()

View file

@ -0,0 +1 @@
uid://1f2k3yrth7qn

View file

@ -89,6 +89,8 @@ func _run() -> void:
func _validate_weight_based_display_scale() -> void:
var previous_scale: float = 0.0
for fish: FishDataType in Catalog.candidates:
if not fish.active:
continue
assert(fish.is_selectable())
assert(fish.weight_min_lb > 0.0)
assert(fish.weight_max_lb <= 1000.0)
@ -119,9 +121,48 @@ func _validate_weight_based_display_scale() -> void:
func _validate_catalog_and_pools() -> void:
assert(Catalog.candidates.size() == 53)
assert(Catalog.candidates.size() == 313)
assert(PondPool.candidates.size() == 19)
assert(OceanPool.candidates.size() == 34)
var active_count: int = 0
var inactive_count: int = 0
var catalog_numbers: Dictionary[int, bool] = {}
for fish: FishDataType in Catalog.candidates:
assert(fish != null and not fish.id.is_empty())
assert(fish.catalog_number > 0)
assert(not fish.collection_group.is_empty())
assert(not catalog_numbers.has(fish.catalog_number))
catalog_numbers[fish.catalog_number] = true
assert(not fish.logbook_fact.strip_edges().is_empty())
if fish.active:
active_count += 1
assert(fish.is_selectable())
assert(fish.display_texture != null)
else:
inactive_count += 1
assert(not fish.is_selectable())
assert(fish.display_texture == null)
assert(active_count == 53)
assert(inactive_count == 260)
var inactive_fish: FishDataType = Catalog.get_fish_by_id(&"bowfin")
assert(inactive_fish != null and not inactive_fish.active)
var inactive_pool := FishPoolType.new()
inactive_pool.candidates = [inactive_fish]
var inactive_context := FishingContextType.new()
inactive_context.water_type = WaterType.Type.FRESH_WATER
var inactive_collection := CollectionLogType.new()
var inactive_selector := FishSelectorType.new()
inactive_selector.use_deterministic_test_seed = true
inactive_selector.begin_roll()
assert(
inactive_selector.select_fish(
inactive_pool,
inactive_context,
inactive_collection,
) == null
)
assert(inactive_selector.create_catch(inactive_fish) == null)
inactive_collection.free()
for fish_id: StringName in ORIGINAL_IDS:
var original_fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(original_fish != null)
@ -277,21 +318,33 @@ func _validate_authoritative_water_filter() -> void:
stale_fresh_pool_region.location_tags = [&"coast", &"ocean"]
var evidence := {"discovered_fish_ids": []}
assert(
service.call("_select_authoritative_fish", pond_region, evidence, null)
!= null
)
assert(
service.call("_select_authoritative_fish", ocean_region, evidence, null)
service.call(
"_select_authoritative_fish", pond_region, evidence, null, null
)
!= null
)
assert(
service.call(
"_select_authoritative_fish", stale_salt_pool_region, evidence, null
"_select_authoritative_fish", ocean_region, evidence, null, null
)
!= null
)
assert(
service.call(
"_select_authoritative_fish",
stale_salt_pool_region,
evidence,
null,
null,
) == null
)
assert(
service.call(
"_select_authoritative_fish", stale_fresh_pool_region, evidence, null
"_select_authoritative_fish",
stale_fresh_pool_region,
evidence,
null,
null,
) == null
)
service.free()
@ -347,11 +400,14 @@ func _validate_catches_and_authoritative_sale() -> void:
}
sale_service.set("_buyers", sale_buyers)
for index: int in Catalog.candidates.size():
var fish: FishDataType = Catalog.candidates[index]
var catch_sequence: int = 0
for fish: FishDataType in Catalog.candidates:
if not fish.active:
continue
catch_sequence += 1
var fish_catch: FishCatch = selector.create_catch(fish)
assert(fish_catch != null)
fish_catch.catch_sequence = index + 1
fish_catch.catch_sequence = catch_sequence
assert(fish_catch.fish.display_texture == fish.display_texture)
var loaded: FishCatch = FishCatchType.from_save_dict(
@ -385,7 +441,7 @@ func _validate_catches_and_authoritative_sale() -> void:
var sale_result: Dictionary = sale_service.call(
"_build_authoritative_result",
1,
"catalog_sale_%d" % index,
"catalog_sale_%d" % catch_sequence,
[loaded.to_network_dict()],
PelicanBuyer,
)

View file

@ -108,9 +108,11 @@ func _validate_save_round_trip(
var player := main.get("_player") as Player
var catalog := main.get("fish_catalog") as FishPool
assert(catalog != null)
assert(catalog.candidates.size() == 53)
assert(catalog.candidates.size() == 313)
var active_species := LogbookCatalog.ordered_species(catalog.candidates)
assert(active_species.size() == 53)
for index: int in 4:
_add_test_catch(player, catalog.candidates[index])
_add_test_catch(player, active_species[index])
assert(save_manager.save_now())
var no_catches: Array[FishCatch] = []
var no_discoveries: Array[StringName] = []
@ -119,18 +121,18 @@ func _validate_save_round_trip(
assert(save_manager.load_player_data())
assert(player.inventory.get_all_catches().size() == 4)
for index: int in 4:
var original_fish: FishData = catalog.candidates[index]
var original_fish: FishData = active_species[index]
assert(player.inventory.get_count(original_fish.id) == 1)
assert(player.collection_log.has_discovered(original_fish.id))
for index: int in range(4, catalog.candidates.size()):
_add_test_catch(player, catalog.candidates[index])
for index: int in range(4, active_species.size()):
_add_test_catch(player, active_species[index])
assert(save_manager.save_now())
assert(player.inventory.replace_all_catches(no_catches, 1))
assert(player.collection_log.replace_discovered_ids(no_discoveries))
assert(save_manager.load_player_data())
assert(player.inventory.get_all_catches().size() == 53)
for fish: FishData in catalog.candidates:
for fish: FishData in active_species:
assert(player.inventory.get_count(fish.id) == 1)
assert(player.collection_log.has_discovered(fish.id))
for fish_id: StringName in [

View file

@ -40,119 +40,27 @@ func _run() -> void:
func _validate_catalog() -> void:
var expected_ids: 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",
]
assert(LogbookCatalog.CATALOG_ORDER == expected_ids)
for index: int in expected_ids.size():
var fish := CatalogResource.get_fish_by_id(expected_ids[index])
assert(CatalogResource.candidates.size() == 313)
var ordered := LogbookCatalog.ordered_species(CatalogResource.candidates)
assert(ordered.size() == 53)
var previous_number: int = 0
var catalog_numbers: Dictionary[int, bool] = {}
for fish: FishDataType in CatalogResource.candidates:
assert(fish != null)
assert(not LogbookCatalog.facts_for(fish.id).is_empty())
assert(fish.catalog_number > 0)
assert(not catalog_numbers.has(fish.catalog_number))
catalog_numbers[fish.catalog_number] = true
assert(not LogbookCatalog.facts_for(fish).is_empty())
assert(
LogbookCatalog.facts_for(fish.id)
== LogbookCatalog.facts_for(fish.id).to_lower()
LogbookCatalog.facts_for(fish)
== LogbookCatalog.facts_for(fish).to_lower()
)
if LogbookCatalog.FISH_FACTS.has(fish.id):
assert(LogbookCatalog.facts_for(fish.id) != "unknown")
else:
assert(LogbookCatalog.facts_for(fish.id) == "unknown")
var expected_category: WaterType.Type = (
WaterType.Type.SALT_WATER
if expected_ids[index] in [
&"bass",
&"sunfish",
&"tuna_albacore",
&"tuna_bigeye",
&"tuna_bluefin",
&"tuna_skipjack",
&"tuna_yellowfin",
&"salmon_atlantic",
&"salmon_chum",
&"salmon_coho",
&"salmon_pink",
&"salmon_sockeye",
&"anchovy_european",
&"anchovy_northern",
&"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",
&"snapper_lane",
&"snapper_mangrove",
&"snapper_mutton",
&"snapper_red",
&"swordfish",
]
else WaterType.Type.FRESH_WATER
)
assert(
LogbookCatalog.category_for(fish)
== expected_category
)
assert(LogbookCatalog.catalog_number(fish.id) == index + 1)
assert(LogbookCatalog.facts_for(fish) != "unknown")
for fish: FishDataType in ordered:
assert(fish.active)
assert(fish.catalog_number > previous_number)
assert(LogbookCatalog.catalog_number(fish) == fish.catalog_number)
previous_number = fish.catalog_number
assert(
LogbookCatalog.empty_state(WaterType.Type.SALT_WATER)
== "No saltwater catches cataloged yet."
@ -195,7 +103,7 @@ func _validate_page() -> void:
if LogbookCatalog.category_for(fish) != category:
continue
var unknown_key := StringName(
"unknown_%d" % LogbookCatalog.catalog_number(fish.id)
"unknown_%d" % LogbookCatalog.catalog_number(fish)
)
var entry := entries.get(unknown_key) as Button
assert(entry != null)
@ -251,10 +159,13 @@ func _validate_page() -> void:
await create_timer(0.25).timeout
var bluegill = CatalogResource.get_fish_by_id(&"bluegill")
page.call("_select_entry", &"unknown_1", StringName())
var bluegill_unknown := StringName(
"unknown_%d" % bluegill.catalog_number
)
page.call("_select_entry", bluegill_unknown, StringName())
assert(
(page.get("_entry_buttons") as Dictionary)
.get(&"unknown_1") != null
.get(bluegill_unknown) != null
)
collection.mark_discovered(&"bluegill")
await process_frame
@ -290,9 +201,13 @@ func _validate_page() -> void:
assert(_detail_text(page).contains("number owned\n1"))
assert(_detail_text(page).contains("number caught\nunknown"))
assert(_detail_text(page).contains("body of water\nfresh water"))
assert(_detail_text(page).contains("catalog number\n#001"))
assert(
_detail_text(page).contains(
"catalog number\n#%03d" % bluegill.catalog_number
)
)
assert(_detail_text(page).contains("fish facts\n"))
assert(_detail_text(page).contains(LogbookCatalog.facts_for(&"bluegill")))
assert(_detail_text(page).contains(LogbookCatalog.facts_for(bluegill)))
_validate_detail_field_fonts(page)
_validate_handwritten_numeric_scale(page)
var portrait_button := page.get("_detail_portrait_button") as Button
@ -413,7 +328,7 @@ func _validate_handwritten_numeric_scale(page: LogbookPage) -> void:
var label := node as Label
if label.text == "common":
text_value = label
elif label.text == "#001":
elif label.text.begins_with("#"):
numeric_value = label
assert(text_value != null)
assert(numeric_value != null)

View file

@ -0,0 +1,109 @@
extends SceneTree
const FishingRodDataType = preload("res://items/fishing_rod_data.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 OwnedItemType = preload("res://items/owned_item.gd")
const PlayerBagType = preload("res://inventory/player_bag.gd")
const PlayerWalletType = preload("res://economy/player_wallet.gd")
const ItemCatalogResource: ItemCatalogType = preload(
"res://items/catalog/item_catalog.tres"
)
const RodAttachmentScene = preload(
"res://player/fishing_rod_attachment.tscn"
)
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_validate_catalog_activation()
_validate_shop_stock()
_validate_inactive_save_compatibility()
_validate_fallback_attachment()
print("Rod catalog validation: PASS")
quit()
func _validate_catalog_activation() -> void:
var rods: Array[FishingRodDataType] = []
for item: ItemDataType in ItemCatalogResource.get_valid_items():
var rod := item as FishingRodDataType
if rod == null:
continue
rods.append(rod)
assert(rod.is_valid())
if rod.icon == null:
assert(not rod.active)
assert(rods.size() == 21)
var available_rods: Array[FishingRodDataType] = []
for rod: FishingRodDataType in rods:
if rod.is_available():
available_rods.append(rod)
assert(available_rods.size() == 1)
var basic: FishingRodDataType = available_rods.front()
assert(basic.item_id == &"basic_fishing_rod")
assert(basic.icon != null)
assert(basic.rod_model_scene == null)
assert(
ItemCatalogResource.get_available_item_by_id(&"aurora_rod") == null
)
assert(ItemCatalogResource.get_item_by_id(&"aurora_rod") != null)
func _validate_shop_stock() -> void:
var stock: Array[FishingRodDataType] = (
FishingShopStockType.get_rod_stock(ItemCatalogResource)
)
assert(stock.size() == 1)
assert(stock.front().item_id == &"basic_fishing_rod")
var wallet := PlayerWalletType.new()
wallet.current_balance = 100
var bag := PlayerBagType.new()
bag.setup(ItemCatalogResource)
assert(
FishingShopStockType.purchase_one(
&"basic_fishing_rod",
wallet,
bag,
ItemCatalogResource,
)
)
assert(bag.owns_item(&"basic_fishing_rod"))
assert(wallet.get_balance() == 100)
assert(
not FishingShopStockType.purchase_one(
&"aurora_rod",
wallet,
bag,
ItemCatalogResource,
)
)
bag.free()
wallet.free()
func _validate_inactive_save_compatibility() -> void:
var bag := PlayerBagType.new()
bag.setup(ItemCatalogResource)
assert(not bag.can_add_item(&"aurora_rod", 1))
assert(not bag.add_item(&"aurora_rod", 1))
var legacy_record := OwnedItemType.new()
legacy_record.item_id = &"aurora_rod"
legacy_record.quantity = 1
var legacy_items: Array[OwnedItemType] = [legacy_record]
assert(bag.replace_all_items(legacy_items))
assert(bag.owns_item(&"aurora_rod"))
bag.free()
func _validate_fallback_attachment() -> void:
var attachment := RodAttachmentScene.instantiate()
assert(attachment.get_node_or_null("FishingRod/FallbackRodMesh") != null)
assert(attachment.get_node_or_null("FishingRod/ModelMount") != null)
assert(attachment.get_node_or_null("FishingRod/FishingRodTip") != null)
attachment.free()

View file

@ -0,0 +1 @@
uid://bjbgbahx5iuyy