Add catfish species and update fish artwork

This commit is contained in:
Alexander Sellite 2026-07-30 23:46:53 -04:00
parent 969ed8f10f
commit 51ce214a25
25 changed files with 576 additions and 16 deletions

View file

@ -0,0 +1,153 @@
extends SceneTree
const FishCatchType = preload("res://fish/fish_catch.gd")
const CollectionLogType = preload("res://collection/collection_log.gd")
const FishDataType = preload("res://fish/fish_data.gd")
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
const FishPoolType = preload("res://fish/fish_pool.gd")
const FishSelectorType = preload("res://fish/fish_selector.gd")
const FishingContextType = preload("res://fishing/fishing_context.gd")
const NetworkSaleServiceType = preload(
"res://network/network_sale_service.gd"
)
const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres")
const PondPool: FishPoolType = preload(
"res://fish/pools/starter_pond_pool.tres"
)
const OceanPool: FishPoolType = preload(
"res://fish/pools/test_water_pool.tres"
)
const PelicanBuyer = preload("res://economy/buyers/pelicans.tres")
const ORIGINAL_IDS: Array[StringName] = [
&"bluegill", &"bass", &"carp", &"sunfish",
]
const CATFISH_IDS: Array[StringName] = [
&"catfish_blue",
&"catfish_channel",
&"catfish_flathead",
&"catfish_white",
]
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_validate_catalog_and_pools()
_validate_catches_and_authoritative_sale()
print("Fish catalog content validation: PASS")
quit()
func _validate_catalog_and_pools() -> void:
assert(Catalog.candidates.size() == 8)
assert(PondPool.candidates.size() == 8)
assert(OceanPool.candidates.size() == 4)
for fish_id: StringName in ORIGINAL_IDS:
var original_fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(original_fish != null)
assert(original_fish.is_selectable())
assert(PondPool.get_fish_by_id(fish_id) != null)
assert(OceanPool.get_fish_by_id(fish_id) != null)
for fish_id: StringName in CATFISH_IDS:
var fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(fish != null)
assert(fish.is_selectable())
assert(PondPool.get_fish_by_id(fish_id) == fish)
assert(OceanPool.get_fish_by_id(fish_id) == null)
assert(fish.availability.allowed_location_tags == [&"starter_pond"])
assert(LogbookCatalog.category_for(fish) == (
LogbookCatalog.Category.FRESH_WATER
))
var expected_values: Dictionary[StringName, Array] = {
&"catfish_blue": [1, 1.25, 3.0, 12.0, 6, 9],
&"catfish_channel": [0, 2.0, 1.5, 7.0, 4, 7],
&"catfish_flathead": [1, 1.0, 3.0, 14.0, 7, 10],
&"catfish_white": [0, 2.0, 1.0, 5.0, 4, 6],
}
for fish_id: StringName in CATFISH_IDS:
var fish: FishDataType = Catalog.get_fish_by_id(fish_id)
var values: Array = expected_values[fish_id]
assert(int(fish.rarity) == int(values[0]))
assert(is_equal_approx(fish.base_catch_weight, float(values[1])))
assert(is_equal_approx(fish.weight_min_lb, float(values[2])))
assert(is_equal_approx(fish.weight_max_lb, float(values[3])))
assert(fish.sell_value_min == int(values[4]))
assert(fish.sell_value_max == int(values[5]))
var pond_context := FishingContextType.new()
pond_context.location_tags = [&"starter_pond"]
var ocean_context := FishingContextType.new()
ocean_context.location_tags = [&"coast", &"ocean"]
for fish_id: StringName in CATFISH_IDS:
var fish: FishDataType = Catalog.get_fish_by_id(fish_id)
assert(fish.availability.is_available(pond_context))
assert(not fish.availability.is_available(ocean_context))
var single_species_pool := FishPoolType.new()
single_species_pool.candidates = [fish]
var collection := CollectionLogType.new()
var selector := FishSelectorType.new()
selector.use_deterministic_test_seed = true
selector.begin_roll()
assert(
selector.select_fish(
single_species_pool, pond_context, collection
) == fish
)
collection.free()
func _validate_catches_and_authoritative_sale() -> void:
var inventory := FishInventoryType.new()
root.add_child(inventory)
var selector := FishSelectorType.new()
selector.use_deterministic_test_seed = true
selector.begin_roll()
var sale_service := NetworkSaleServiceType.new()
var session := NetworkSession.new()
root.add_child(session)
root.add_child(sale_service)
sale_service.set("_session", session)
sale_service.set("_fish_catalog", Catalog)
sale_service.set("_buyer", PelicanBuyer)
for index: int in Catalog.candidates.size():
var fish: FishDataType = Catalog.candidates[index]
var fish_catch: FishCatch = selector.create_catch(fish)
assert(fish_catch != null)
fish_catch.catch_sequence = index + 1
assert(fish_catch.fish.display_texture == fish.display_texture)
var loaded: FishCatch = FishCatchType.from_save_dict(
fish_catch.to_save_dict(), Catalog.get_fish_by_id(fish.id)
)
assert(loaded != null)
assert(loaded.fish_id == fish.id)
assert(loaded.fish == fish)
var replicated: FishCatch = FishCatchType.from_network_dict(
fish_catch.to_network_dict(), Catalog.get_fish_by_id(fish.id)
)
assert(replicated != null)
assert(replicated.fish_id == fish.id)
assert(replicated.fish.display_texture == fish.display_texture)
inventory.add_catch(loaded)
assert(inventory.contains_catch_id(loaded.catch_id))
var sale_result: Dictionary = sale_service.call(
"_build_authoritative_result",
1,
"catalog_sale_%d" % index,
[loaded.to_network_dict()],
)
assert(bool(sale_result.get("accepted", false)))
assert(int(sale_result.get("base_value", -1)) == loaded.sale_value)
assert((sale_result.get("catch_ids", []) as Array).size() == 1)
inventory.queue_free()
sale_service.queue_free()
session.queue_free()

View file

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

View file

@ -1,6 +1,7 @@
extends SceneTree
const MainScene = preload("res://main/main.tscn")
const FishCatchType = preload("res://fish/fish_catch.gd")
func _initialize() -> void:
@ -19,6 +20,7 @@ func _run() -> void:
for _frame: int in 8:
await process_frame
assert(bool(main.get("_gameplay_started")))
_validate_save_round_trip(main, save_manager)
var game_ui := main.get_node("%GameUI") as GameUI
var player_menu := game_ui.get_node("%PlayerMenu") as PlayerMenu
@ -44,7 +46,7 @@ func _run() -> void:
"_select_category", LogbookCatalog.Category.FRESH_WATER
)
await create_timer(0.25).timeout
assert((logbook.get("_entry_buttons") as Dictionary).is_empty())
assert((logbook.get("_entry_buttons") as Dictionary).size() == 4)
await _capture_if_requested("-fresh")
logbook.call("_select_category", LogbookCatalog.Category.SALT_WATER)
await create_timer(0.25).timeout
@ -84,6 +86,71 @@ func _run() -> void:
quit()
func _validate_save_round_trip(
main: Node,
save_manager: PlayerSaveManager,
) -> void:
var player := main.get("_player") as Player
var catalog := main.get("fish_catalog") as FishPool
assert(catalog != null)
assert(catalog.candidates.size() == 8)
for index: int in 4:
_add_test_catch(player, catalog.candidates[index])
assert(save_manager.save_now())
var no_catches: Array[FishCatch] = []
var no_discoveries: Array[StringName] = []
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() == 4)
for index: int in 4:
var original_fish: FishData = catalog.candidates[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])
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() == 8)
for fish: FishData in catalog.candidates:
assert(player.inventory.get_count(fish.id) == 1)
assert(player.collection_log.has_discovered(fish.id))
for fish_id: StringName in [
&"catfish_blue",
&"catfish_channel",
&"catfish_flathead",
&"catfish_white",
]:
var fish_catch: FishCatch = (
player.inventory.get_catches_by_fish_id(fish_id).front()
)
player.begin_catch_showcase(fish_catch)
var catch_sprite := player.get_node(
"%CatchSprite"
) as Sprite3D
assert(catch_sprite.texture == fish_catch.fish.display_texture)
player.end_catch_showcase(Callable(), true)
func _add_test_catch(player: Player, fish: FishData) -> void:
var fish_catch := FishCatchType.new()
fish_catch.fish = fish
fish_catch.fish_id = fish.id
fish_catch.weight_lb = fish.get_minimum_weight()
fish_catch.display_scale = fish.get_display_scale_for_weight(
fish_catch.weight_lb
)
fish_catch.sale_value = fish.get_sale_value_for_weight(
fish_catch.weight_lb
)
fish_catch.ensure_identity()
player.inventory.add_catch(fish_catch)
player.collection_log.mark_discovered(fish.id)
func _capture_if_requested(suffix: String) -> void:
if not OS.has_environment("NETFISHING_LOGBOOK_CAPTURE"):
return

View file

@ -6,7 +6,7 @@ const FishPoolType = preload("res://fish/fish_pool.gd")
const CollectionLogType = preload("res://collection/collection_log.gd")
const LogbookPageScene = preload("res://ui/logbook_page.tscn")
const CatalogResource: FishPoolType = preload(
"res://fish/pools/test_water_pool.tres"
"res://fish/pools/fish_catalog.tres"
)
@ -34,21 +34,29 @@ func _run() -> void:
func _validate_catalog() -> void:
var expected_ids: Array[StringName] = [
&"bluegill", &"bass", &"carp", &"sunfish",
&"bluegill",
&"bass",
&"carp",
&"sunfish",
&"catfish_blue",
&"catfish_channel",
&"catfish_flathead",
&"catfish_white",
]
assert(LogbookCatalog.CATALOG_ORDER == expected_ids)
for index: int in expected_ids.size():
var fish := CatalogResource.get_fish_by_id(expected_ids[index])
assert(fish != null)
var expected_category: LogbookCatalog.Category = (
LogbookCatalog.Category.OTHER
if index < 4
else LogbookCatalog.Category.FRESH_WATER
)
assert(
LogbookCatalog.category_for(fish)
== LogbookCatalog.Category.OTHER
== expected_category
)
assert(LogbookCatalog.catalog_number(fish.id) == index + 1)
assert(
LogbookCatalog.empty_state(LogbookCatalog.Category.FRESH_WATER)
== "No freshwater catches cataloged yet."
)
assert(
LogbookCatalog.empty_state(LogbookCatalog.Category.SALT_WATER)
== "No saltwater catches cataloged yet."
@ -83,11 +91,14 @@ func _validate_page() -> void:
"_select_category", LogbookCatalog.Category.FRESH_WATER
)
await create_timer(0.25).timeout
assert((page.get("_entry_buttons") as Dictionary).is_empty())
assert(
(page.get("_empty_state") as Label).text
== "No freshwater catches cataloged yet."
)
assert((page.get("_entry_buttons") as Dictionary).size() == 4)
for entry_value: Variant in (
page.get("_entry_buttons") as Dictionary
).values():
var unknown_catfish := entry_value as Button
assert(unknown_catfish != null)
assert(unknown_catfish.text.contains("???"))
assert(unknown_catfish.icon == null)
page.call("_select_category", LogbookCatalog.Category.SALT_WATER)
await create_timer(0.25).timeout
assert((page.get("_entry_buttons") as Dictionary).is_empty())