forked from woofmeow/straywild
feat: expand seasonal creature collection
This commit is contained in:
parent
925bbb79e5
commit
95c6b8b053
415 changed files with 1688 additions and 389 deletions
|
|
@ -49,7 +49,7 @@ func _run() -> void:
|
|||
)
|
||||
assert(player != null)
|
||||
assert(catalog != null and catalog.candidates.size() == 316)
|
||||
assert(LogbookCatalog.ordered_species(catalog.candidates).size() == 56)
|
||||
assert(LogbookCatalog.ordered_species(catalog.candidates).size() == 63)
|
||||
assert(sale_service != null)
|
||||
assert(shop_service != null)
|
||||
assert(session != null and session.is_host())
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@ func _initialize() -> void:
|
|||
assert(FishingShopStockType.get_price(&"standard_shovel") == 75)
|
||||
assert(FishingShopStockType.get_stock_item_ids().has(&"standard_shovel"))
|
||||
assert(FishingShopStockType.is_permanent_unlock(&"standard_shovel", shovel))
|
||||
assert(FishingShopStockType.is_gathering_tool(&"crab_net"))
|
||||
assert(FishingShopStockType.is_gathering_tool(&"standard_shovel"))
|
||||
assert(not FishingShopStockType.is_gathering_tool(&"basic_fishing_rod"))
|
||||
|
||||
var wallet := PlayerWalletType.new()
|
||||
wallet.current_balance = 250
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const NetworkFishingServiceType = preload(
|
|||
"res://network/network_fishing_service.gd"
|
||||
)
|
||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||
const CalendarSeasonType = preload("res://world/calendar_season.gd")
|
||||
|
||||
const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres")
|
||||
const PondPool: FishPoolType = preload(
|
||||
|
|
@ -69,6 +70,8 @@ const NEW_FRESH_WATER_IDS: Array[StringName] = [
|
|||
&"chub_european", &"chub_flame", &"chub_lake", &"goldfish",
|
||||
&"goldfish_bubbleeye", &"sauger", &"saugeye", &"trout_cutthroat",
|
||||
&"trout_golden", &"trout_rainbow", &"trout_steelhead", &"walleye",
|
||||
&"bowfin", &"sturgeon_lake", &"gar_longnose", &"paddlefish",
|
||||
&"sturgeon_shovelnose", &"gar_spotted", &"lungfish_west_african",
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -122,7 +125,7 @@ func _validate_weight_based_display_scale() -> void:
|
|||
|
||||
func _validate_catalog_and_pools() -> void:
|
||||
assert(Catalog.candidates.size() == 316)
|
||||
assert(PondPool.candidates.size() == 19)
|
||||
assert(PondPool.candidates.size() == 26)
|
||||
assert(OceanPool.candidates.size() == 34)
|
||||
var active_count: int = 0
|
||||
var inactive_count: int = 0
|
||||
|
|
@ -134,6 +137,8 @@ func _validate_catalog_and_pools() -> void:
|
|||
assert(not catalog_numbers.has(fish.catalog_number))
|
||||
catalog_numbers[fish.catalog_number] = true
|
||||
assert(not fish.logbook_fact.strip_edges().is_empty())
|
||||
assert(CalendarSeasonType.is_valid_mask(fish.available_seasons))
|
||||
assert(not fish.get_season_text().is_empty())
|
||||
if fish.active:
|
||||
active_count += 1
|
||||
assert(fish.is_selectable())
|
||||
|
|
@ -142,9 +147,46 @@ func _validate_catalog_and_pools() -> void:
|
|||
inactive_count += 1
|
||||
assert(not fish.is_selectable())
|
||||
assert(fish.display_texture == null)
|
||||
assert(active_count == 56)
|
||||
assert(inactive_count == 260)
|
||||
var inactive_fish: FishDataType = Catalog.get_fish_by_id(&"bowfin")
|
||||
assert(active_count == 63)
|
||||
assert(inactive_count == 253)
|
||||
var chum: FishDataType = Catalog.get_fish_by_id(&"salmon_chum")
|
||||
assert(chum != null)
|
||||
assert(chum.get_season_text() == "fall")
|
||||
assert(
|
||||
chum.is_available_in_season(CalendarSeasonType.Season.FALL)
|
||||
)
|
||||
assert(
|
||||
not chum.is_available_in_season(CalendarSeasonType.Season.SUMMER)
|
||||
)
|
||||
var chum_pool := FishPoolType.new()
|
||||
chum_pool.candidates = [chum]
|
||||
var seasonal_collection := CollectionLogType.new()
|
||||
var seasonal_selector := FishSelectorType.new()
|
||||
seasonal_selector.use_deterministic_test_seed = true
|
||||
var summer_context := FishingContextType.new()
|
||||
summer_context.water_type = WaterType.Type.SALT_WATER
|
||||
summer_context.season = CalendarSeasonType.Season.SUMMER
|
||||
seasonal_selector.begin_roll()
|
||||
assert(
|
||||
seasonal_selector.select_fish(
|
||||
chum_pool,
|
||||
summer_context,
|
||||
seasonal_collection,
|
||||
) == null
|
||||
)
|
||||
var fall_context := FishingContextType.new()
|
||||
fall_context.water_type = WaterType.Type.SALT_WATER
|
||||
fall_context.season = CalendarSeasonType.Season.FALL
|
||||
seasonal_selector.begin_roll()
|
||||
assert(
|
||||
seasonal_selector.select_fish(
|
||||
chum_pool,
|
||||
fall_context,
|
||||
seasonal_collection,
|
||||
) == chum
|
||||
)
|
||||
seasonal_collection.free()
|
||||
var inactive_fish: FishDataType = Catalog.get_fish_by_id(&"mudskipper_atlantic")
|
||||
assert(inactive_fish != null and not inactive_fish.active)
|
||||
var inactive_pool := FishPoolType.new()
|
||||
inactive_pool.candidates = [inactive_fish]
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ func _run() -> void:
|
|||
var invalid_state: Dictionary = valid_state.duplicate(true)
|
||||
invalid_state["display_scale"] = 1000.0
|
||||
assert(not NetworkFishShowcaseProtocol.validate_state(invalid_state))
|
||||
assert(NetworkProtocol.PROTOCOL_VERSION == 7)
|
||||
assert(NetworkProtocol.PROTOCOL_VERSION == 8)
|
||||
assert(NetworkProtocol.ENET_CHANNEL_COUNT == 10)
|
||||
assert(
|
||||
NetworkProtocol.FISH_QUALITY_CAPABILITY
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func _run() -> void:
|
|||
_validate_mail_round_trip()
|
||||
_validate_collection_mastery()
|
||||
_validate_version_four_migration()
|
||||
assert(NetworkProtocol.PROTOCOL_VERSION == 7)
|
||||
assert(NetworkProtocol.PROTOCOL_VERSION == 8)
|
||||
assert(NetworkProtocol.ENET_CHANNEL_COUNT == 10)
|
||||
assert(NetworkProtocol.FISH_QUALITY_CAPABILITY == "fish_quality_v1")
|
||||
print("Fish quality validation: PASS")
|
||||
|
|
|
|||
|
|
@ -49,20 +49,20 @@ func _run() -> void:
|
|||
assert(not hotbar.visible)
|
||||
|
||||
var entry_buttons: Dictionary = logbook.get("_entry_buttons")
|
||||
assert(entry_buttons.size() == 19)
|
||||
assert(entry_buttons.size() == 26)
|
||||
await _capture_if_requested("-unknown")
|
||||
logbook.call(
|
||||
"_select_category", WaterType.Type.FRESH_WATER
|
||||
)
|
||||
await create_timer(0.25).timeout
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).size() == 19)
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).size() == 26)
|
||||
await _capture_if_requested("-fresh")
|
||||
logbook.call("_select_category", WaterType.Type.SALT_WATER)
|
||||
await create_timer(0.25).timeout
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).size() == 34)
|
||||
logbook.call("_select_category", WaterType.Type.FRESH_WATER)
|
||||
await create_timer(0.25).timeout
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).size() == 19)
|
||||
assert((logbook.get("_entry_buttons") as Dictionary).size() == 26)
|
||||
player.collection_log.mark_discovered(&"bluegill")
|
||||
await process_frame
|
||||
logbook.call("_select_entry", &"bluegill", &"bluegill")
|
||||
|
|
@ -110,7 +110,21 @@ func _validate_save_round_trip(
|
|||
assert(catalog != null)
|
||||
assert(catalog.candidates.size() == 316)
|
||||
var active_species := LogbookCatalog.ordered_species(catalog.candidates)
|
||||
assert(active_species.size() == 56)
|
||||
assert(active_species.size() == 63)
|
||||
# This test intentionally round-trips one catch for every active species.
|
||||
# Give the fixture enough combined capacity, then bypass per-catch carried
|
||||
# inventory admission while constructing it. The inventory layout still
|
||||
# reconciles and persists the resulting placements.
|
||||
player.inventory_layout.restore_backpack_level(
|
||||
PlayerInventoryLayout.MAX_BACKPACK_LEVEL
|
||||
)
|
||||
player.cooler_capacity.restore_level(PlayerCoolerCapacity.MAX_LEVEL)
|
||||
assert(
|
||||
player.inventory_layout.get_backpack_level()
|
||||
== PlayerInventoryLayout.MAX_BACKPACK_LEVEL
|
||||
)
|
||||
assert(player.cooler_capacity.get_level() == PlayerCoolerCapacity.MAX_LEVEL)
|
||||
player.inventory.set_inventory_layout(null)
|
||||
for index: int in 4:
|
||||
_add_test_catch(player, active_species[index])
|
||||
assert(save_manager.save_now())
|
||||
|
|
@ -131,7 +145,7 @@ func _validate_save_round_trip(
|
|||
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() == 56)
|
||||
assert(player.inventory.get_all_catches().size() == 63)
|
||||
for fish: FishData in active_species:
|
||||
assert(player.inventory.get_count(fish.id) == 1)
|
||||
assert(player.collection_log.has_discovered(fish.id))
|
||||
|
|
@ -151,6 +165,7 @@ func _validate_save_round_trip(
|
|||
assert(catch_sprite != null)
|
||||
assert(catch_sprite.texture == fish_catch.fish.display_texture)
|
||||
player.end_catch_showcase(Callable(), true)
|
||||
player.inventory.set_inventory_layout(player.inventory_layout)
|
||||
|
||||
|
||||
func _add_test_catch(player: Player, fish: FishData) -> void:
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func _run() -> void:
|
|||
func _validate_catalog() -> void:
|
||||
assert(CatalogResource.candidates.size() == 316)
|
||||
var ordered := LogbookCatalog.ordered_species(CatalogResource.candidates)
|
||||
assert(ordered.size() == 56)
|
||||
assert(ordered.size() == 63)
|
||||
var previous_number: int = 0
|
||||
var catalog_numbers: Dictionary[int, bool] = {}
|
||||
for fish: FishDataType in CatalogResource.candidates:
|
||||
|
|
@ -152,7 +152,7 @@ func _validate_page() -> void:
|
|||
var entries: Dictionary = page.get("_entry_buttons")
|
||||
assert(
|
||||
entries.size()
|
||||
== (19 if category == LogbookCatalog.Category.FRESH_WATER else 34)
|
||||
== (26 if category == LogbookCatalog.Category.FRESH_WATER else 34)
|
||||
)
|
||||
for fish: FishDataType in LogbookCatalog.ordered_species(
|
||||
CatalogResource.candidates
|
||||
|
|
@ -203,7 +203,7 @@ func _validate_page() -> void:
|
|||
candidate.display_name
|
||||
)
|
||||
)
|
||||
assert(silhouette_count == 53)
|
||||
assert(silhouette_count == 60)
|
||||
|
||||
page.call("_select_category", LogbookCatalog.Category.SHELLFISH)
|
||||
await create_timer(0.25).timeout
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ const PlayerScene: PackedScene = preload("res://player/player.tscn")
|
|||
const CrabBrown: FishData = preload(
|
||||
"res://fish/species/crab_brown/crab_brown.tres"
|
||||
)
|
||||
const ClamManila: FishData = preload(
|
||||
"res://fish/species/clam_manila/clam_manila.tres"
|
||||
)
|
||||
const BeetleStagCommon: FishData = preload(
|
||||
"res://fish/species/beetle_stag_common/beetle_stag_common.tres"
|
||||
)
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
|
|
@ -15,6 +21,18 @@ func _run() -> void:
|
|||
var player := PlayerScene.instantiate() as Player
|
||||
root.add_child(player)
|
||||
await process_frame
|
||||
assert(is_equal_approx(
|
||||
Player.get_catch_texture_resolution_scale(CrabBrown.display_texture),
|
||||
1.0,
|
||||
))
|
||||
assert(is_equal_approx(
|
||||
Player.get_catch_texture_resolution_scale(ClamManila.display_texture),
|
||||
20.0,
|
||||
))
|
||||
assert(is_equal_approx(
|
||||
Player.get_catch_texture_resolution_scale(BeetleStagCommon.display_texture),
|
||||
20.0,
|
||||
))
|
||||
player.set_process(false)
|
||||
player.set_physics_process(false)
|
||||
var animation_player := player.get_node(
|
||||
|
|
|
|||
|
|
@ -157,12 +157,16 @@ func _run() -> void:
|
|||
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
|
||||
var chat_panel := chat_ui.get_node("ChatPanel") as Control
|
||||
var clock_panel := chat_ui.get_node("WorldClockPanel") as Control
|
||||
var calendar_panel := chat_ui.get_node(
|
||||
"WorldCalendarPanel"
|
||||
) as Control
|
||||
var status_effect_column := chat_ui.get_node(
|
||||
"StatusEffectColumn"
|
||||
) as Control
|
||||
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as Control
|
||||
assert(chat_panel != null)
|
||||
assert(clock_panel != null)
|
||||
assert(calendar_panel != null)
|
||||
assert(status_effect_column != null)
|
||||
assert(weather_icon != null)
|
||||
assert(chat_panel.visible)
|
||||
|
|
@ -175,15 +179,25 @@ func _run() -> void:
|
|||
ChatUI.CLOCK_EDGE_MARGIN,
|
||||
ChatUI.CLOCK_EDGE_MARGIN,
|
||||
)))
|
||||
assert(calendar_panel.position.is_equal_approx(Vector2(
|
||||
ChatUI.CLOCK_EDGE_MARGIN,
|
||||
ChatUI.CLOCK_EDGE_MARGIN
|
||||
+ ChatUI.CLOCK_SIZE.y
|
||||
+ ChatUI.CALENDAR_TOP_GAP,
|
||||
)))
|
||||
assert(calendar_panel.size.is_equal_approx(ChatUI.CALENDAR_SIZE))
|
||||
assert(is_equal_approx(
|
||||
status_effect_column.position.x,
|
||||
clock_panel.position.x
|
||||
+ (ChatUI.CLOCK_SIZE.x - ChatUI.STATUS_EFFECT_ICON_SIZE.x) * 0.5,
|
||||
calendar_panel.position.x
|
||||
+ (
|
||||
ChatUI.CALENDAR_SIZE.x
|
||||
- ChatUI.STATUS_EFFECT_ICON_SIZE.x
|
||||
) * 0.5,
|
||||
))
|
||||
assert(is_equal_approx(
|
||||
status_effect_column.position.y,
|
||||
clock_panel.position.y
|
||||
+ ChatUI.CLOCK_SIZE.y
|
||||
calendar_panel.position.y
|
||||
+ ChatUI.CALENDAR_SIZE.y
|
||||
+ ChatUI.STATUS_EFFECT_TOP_GAP,
|
||||
))
|
||||
assert(is_equal_approx(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ const EXPECTED_BY_TYPE: Dictionary = {
|
|||
&"clam_manila": 3,
|
||||
&"beetle_stag_common": 3,
|
||||
}
|
||||
const WINTER_POPULATION: int = 5
|
||||
const WINTER_BY_TYPE: Dictionary = {
|
||||
&"crab_brown": 2,
|
||||
&"clam_manila": 3,
|
||||
}
|
||||
const SUMMER_DATE_ID: String = "2026-08-18"
|
||||
const WINTER_DATE_ID: String = "2026-12-18"
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
|
|
@ -30,9 +37,12 @@ func _run_host() -> void:
|
|||
var main: Node = await _create_initialized_main()
|
||||
var session := main.get_node("%NetworkSession") as NetworkSession
|
||||
var service: Node = main.get_node("%NetworkWorldSpawnService")
|
||||
var world_time := main.get_node("%WorldTimeService") as WorldTimeService
|
||||
assert(session.start_dedicated_host(TEST_PORT, 8, "127.0.0.1"))
|
||||
assert(world_time.synchronize_calendar_time(12.0, SUMMER_DATE_ID))
|
||||
assert(world_time.set_authoritative_time(12.0))
|
||||
assert(session.set_host_open(true))
|
||||
await _wait_for_population(service)
|
||||
await _wait_for_population(service, EXPECTED_POPULATION)
|
||||
_freeze_timed_entries(service)
|
||||
_validate_population(service, true)
|
||||
|
||||
|
|
@ -49,6 +59,14 @@ func _run_host() -> void:
|
|||
remote_peer_id,
|
||||
NetworkProtocol.WORLD_SPAWN_CAPABILITY,
|
||||
))
|
||||
await create_timer(0.75).timeout
|
||||
assert(world_time.synchronize_calendar_time(12.0, WINTER_DATE_ID))
|
||||
await _wait_for_population(service, WINTER_POPULATION)
|
||||
_validate_population(service, true, WINTER_BY_TYPE)
|
||||
await create_timer(0.75).timeout
|
||||
assert(world_time.synchronize_calendar_time(12.0, SUMMER_DATE_ID))
|
||||
await _wait_for_population(service, EXPECTED_POPULATION)
|
||||
_validate_population(service, true)
|
||||
|
||||
var disconnect_deadline: int = Time.get_ticks_msec() + 12000
|
||||
while (
|
||||
|
|
@ -76,6 +94,7 @@ func _run_client() -> void:
|
|||
)
|
||||
var session := main.get_node("%NetworkSession") as NetworkSession
|
||||
var service: Node = main.get_node("%NetworkWorldSpawnService")
|
||||
var world_time := main.get_node("%WorldTimeService") as WorldTimeService
|
||||
var join_deadline: int = Time.get_ticks_msec() + 20000
|
||||
while Time.get_ticks_msec() < join_deadline:
|
||||
await process_frame
|
||||
|
|
@ -87,11 +106,14 @@ func _run_client() -> void:
|
|||
assert(session.supports_server_capability(
|
||||
NetworkProtocol.WORLD_SPAWN_CAPABILITY
|
||||
))
|
||||
await _wait_for_population(service)
|
||||
await _wait_for_population(service, EXPECTED_POPULATION)
|
||||
_validate_population(service, false)
|
||||
# Give the host process time to observe the authenticated peer before this
|
||||
# deliberately short client validation disconnects.
|
||||
await create_timer(0.75).timeout
|
||||
await _wait_for_population(service, WINTER_POPULATION)
|
||||
_validate_population(service, false, WINTER_BY_TYPE)
|
||||
assert(world_time.get_calendar_date_id() == WINTER_DATE_ID)
|
||||
await _wait_for_population(service, EXPECTED_POPULATION)
|
||||
_validate_population(service, false)
|
||||
assert(world_time.get_calendar_date_id() == SUMMER_DATE_ID)
|
||||
print("World spawn multiplayer client validation: PASS")
|
||||
session.disconnect_session("")
|
||||
main.queue_free()
|
||||
|
|
@ -101,20 +123,27 @@ func _run_client() -> void:
|
|||
quit()
|
||||
|
||||
|
||||
func _wait_for_population(service: Node) -> void:
|
||||
func _wait_for_population(service: Node, expected_population: int) -> void:
|
||||
var deadline: int = Time.get_ticks_msec() + 12000
|
||||
while Time.get_ticks_msec() < deadline:
|
||||
await process_frame
|
||||
if (service.get("_entities") as Dictionary).size() == EXPECTED_POPULATION:
|
||||
if (service.get("_entities") as Dictionary).size() == expected_population:
|
||||
return
|
||||
assert(false, "Timed out waiting for the world spawn population.")
|
||||
|
||||
|
||||
func _validate_population(service: Node, expect_authoritative_state: bool) -> void:
|
||||
func _validate_population(
|
||||
service: Node,
|
||||
expect_authoritative_state: bool,
|
||||
expected_by_type: Dictionary = EXPECTED_BY_TYPE,
|
||||
) -> void:
|
||||
var entities: Dictionary = service.get("_entities")
|
||||
var presentations: Dictionary = service.get("_presentations")
|
||||
assert(entities.size() == EXPECTED_POPULATION)
|
||||
assert(presentations.size() == EXPECTED_POPULATION)
|
||||
var expected_population: int = 0
|
||||
for amount: int in expected_by_type.values():
|
||||
expected_population += amount
|
||||
assert(entities.size() == expected_population)
|
||||
assert(presentations.size() == expected_population)
|
||||
var counts: Dictionary = {}
|
||||
for state: Dictionary in entities.values():
|
||||
var type_id := state.get("type_id") as StringName
|
||||
|
|
@ -137,7 +166,7 @@ func _validate_population(service: Node, expect_authoritative_state: bool) -> vo
|
|||
else:
|
||||
assert(entry.get_movement_speed_for_quality(quality) > 0.0)
|
||||
assert(entry.get_scare_radius_for_quality(quality) > 0.0)
|
||||
assert(counts == EXPECTED_BY_TYPE)
|
||||
assert(counts == expected_by_type)
|
||||
|
||||
|
||||
func _freeze_timed_entries(service: Node) -> void:
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ const WorldGatherableType = preload("res://gathering/world_gatherable.gd")
|
|||
const CRAB_PIXEL_SIZE: float = 0.0005
|
||||
const REDUCED_CATCH_RING_RADIUS: float = 0.175
|
||||
const NET_STRIKE_MARKER_DISTANCE: float = 0.85
|
||||
const CalendarSeasonType = preload("res://world/calendar_season.gd")
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
assert(NetworkProtocol.PROTOCOL_VERSION == 7)
|
||||
assert(NetworkProtocol.PROTOCOL_VERSION == 8)
|
||||
assert(
|
||||
NetworkWorldSpawnProtocol.CAPABILITY
|
||||
== NetworkProtocol.WORLD_SPAWN_CAPABILITY
|
||||
|
|
@ -67,6 +68,8 @@ func _validate_catalog_statuses() -> void:
|
|||
assert(not clam.catch_data.is_fishable())
|
||||
var beetle: GatherableData = Gatherables.get_entry(&"beetle_stag_common")
|
||||
assert(beetle != null and beetle.is_available())
|
||||
assert(beetle.is_available(CalendarSeasonType.Season.SUMMER))
|
||||
assert(not beetle.is_available(CalendarSeasonType.Season.WINTER))
|
||||
assert(beetle.catch_data.collection_method == FishData.CollectionMethod.NET)
|
||||
assert(beetle.required_tool_id == &"crab_net")
|
||||
assert(beetle.spawn_anchor_set_id == &"starter_reachable_tree_trunks")
|
||||
|
|
@ -104,6 +107,12 @@ func _validate_catalog_statuses() -> void:
|
|||
assert(available.has(brown))
|
||||
assert(available.has(clam))
|
||||
assert(available.has(beetle))
|
||||
var winter_available: Array[GatherableData] = (
|
||||
Gatherables.get_available_entries(CalendarSeasonType.Season.WINTER)
|
||||
)
|
||||
assert(winter_available.has(brown))
|
||||
assert(winter_available.has(clam))
|
||||
assert(not winter_available.has(beetle))
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = 24680
|
||||
var captured_delay: float = brown.get_respawn_delay(&"captured", rng)
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ func _run_host() -> void:
|
|||
remote_peer_id, NetworkProtocol.WORLD_WEATHER_CAPABILITY
|
||||
))
|
||||
assert(world_time.get_phase() == WorldTimeService.Phase.DUSK)
|
||||
assert(WorldTimeService.is_valid_calendar_date_id(
|
||||
world_time.get_calendar_date_id()
|
||||
))
|
||||
await create_timer(1.0).timeout
|
||||
assert(world_time.set_authoritative_time(UPDATED_HOST_TIME))
|
||||
assert(world_weather.set_authoritative_weather(
|
||||
|
|
@ -111,6 +114,10 @@ func _run_client() -> void:
|
|||
world_time.get_time_hours(), INITIAL_HOST_TIME
|
||||
) <= TIME_TOLERANCE_HOURS)
|
||||
assert(world_time.get_phase() == WorldTimeService.Phase.DUSK)
|
||||
var expected_date_id: String = WorldTimeService.date_id_from_datetime(
|
||||
Time.get_datetime_dict_from_system(false)
|
||||
)
|
||||
assert(world_time.get_calendar_date_id() == expected_date_id)
|
||||
var weather_deadline: int = Time.get_ticks_msec() + 8000
|
||||
while (
|
||||
Time.get_ticks_msec() < weather_deadline
|
||||
|
|
@ -122,15 +129,31 @@ func _run_client() -> void:
|
|||
var chat_ui := game_ui.get_node("%ChatUI") as Control
|
||||
var clock_panel := chat_ui.get_node("WorldClockPanel") as PanelContainer
|
||||
var clock_label := clock_panel.get_node("WorldClockLabel") as Label
|
||||
var calendar_panel := (
|
||||
chat_ui.get_node("WorldCalendarPanel") as PanelContainer
|
||||
)
|
||||
var calendar_label := (
|
||||
calendar_panel.get_node("WorldCalendarLabel") as Label
|
||||
)
|
||||
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as WeatherIcon
|
||||
var chat_panel := chat_ui.get_node("ChatPanel") as PanelContainer
|
||||
assert(clock_panel.visible)
|
||||
assert(clock_label.text == world_time.get_clock_text())
|
||||
assert(clock_label.text.ends_with(" pm"))
|
||||
assert(calendar_panel.visible)
|
||||
assert(calendar_label.text == world_time.get_calendar_text())
|
||||
assert(not calendar_label.text.is_empty())
|
||||
assert(weather_icon.visible)
|
||||
assert(weather_icon.get_weather() == WorldWeatherService.Weather.RAINY)
|
||||
assert(is_equal_approx(clock_panel.position.y, 10.0))
|
||||
assert(is_equal_approx(weather_icon.position.y, 10.0))
|
||||
assert(is_equal_approx(
|
||||
calendar_panel.position.y,
|
||||
clock_panel.position.y
|
||||
+ clock_panel.size.y
|
||||
+ ChatUI.CALENDAR_TOP_GAP,
|
||||
))
|
||||
assert(calendar_panel.size.is_equal_approx(ChatUI.CALENDAR_SIZE))
|
||||
assert(clock_panel.position.y + clock_panel.size.y < chat_panel.position.y)
|
||||
assert(not chat_ui.has_method("_handle_chat_command"))
|
||||
|
||||
|
|
@ -159,11 +182,17 @@ func _run_client() -> void:
|
|||
await process_frame
|
||||
assert(clock_panel.position.x > 1000.0)
|
||||
assert(weather_icon.position.x < clock_panel.position.x)
|
||||
assert(is_equal_approx(
|
||||
calendar_panel.position.x, weather_icon.position.x
|
||||
))
|
||||
assert(is_equal_approx(clock_panel.position.y, 10.0))
|
||||
chat_ui.call("set_dock_right", false)
|
||||
await process_frame
|
||||
assert(clock_panel.position.x < 20.0)
|
||||
assert(weather_icon.position.x > clock_panel.position.x)
|
||||
assert(is_equal_approx(
|
||||
calendar_panel.position.x, clock_panel.position.x
|
||||
))
|
||||
assert(is_equal_approx(clock_panel.position.y, 10.0))
|
||||
print("World time multiplayer client validation: PASS")
|
||||
session.disconnect_session("")
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ const FishableWaterRegionType = preload(
|
|||
"res://world/fishable_water_region.gd"
|
||||
)
|
||||
const WeatherIconType = preload("res://ui/weather_icon.gd")
|
||||
const CalendarSeasonType = preload("res://world/calendar_season.gd")
|
||||
|
||||
const Catalog: FishPoolType = preload("res://fish/pools/fish_catalog.tres")
|
||||
const SaltWaterMaterial: ShaderMaterial = preload(
|
||||
|
|
@ -57,6 +58,35 @@ func _validate_clock_boundaries_and_duration() -> void:
|
|||
"year": 2026, "month": 8, "day": 18,
|
||||
}) == "2026-08-18"
|
||||
)
|
||||
assert(WorldTimeServiceType.is_valid_calendar_date_id("2026-08-18"))
|
||||
assert(WorldTimeServiceType.is_valid_calendar_date_id("2028-02-29"))
|
||||
assert(not WorldTimeServiceType.is_valid_calendar_date_id("2026-02-29"))
|
||||
assert(not WorldTimeServiceType.is_valid_calendar_date_id("2026-13-01"))
|
||||
assert(not WorldTimeServiceType.is_valid_calendar_date_id("2026-8-18"))
|
||||
assert(
|
||||
WorldTimeServiceType.format_calendar_date("2026-08-18")
|
||||
== "tuesday, august 18"
|
||||
)
|
||||
assert(
|
||||
CalendarSeasonType.from_date_id("2026-03-01")
|
||||
== CalendarSeasonType.Season.SPRING
|
||||
)
|
||||
assert(
|
||||
CalendarSeasonType.from_date_id("2026-06-01")
|
||||
== CalendarSeasonType.Season.SUMMER
|
||||
)
|
||||
assert(
|
||||
CalendarSeasonType.from_date_id("2026-09-01")
|
||||
== CalendarSeasonType.Season.FALL
|
||||
)
|
||||
assert(
|
||||
CalendarSeasonType.from_date_id("2026-12-01")
|
||||
== CalendarSeasonType.Season.WINTER
|
||||
)
|
||||
assert(
|
||||
CalendarSeasonType.from_date_id("invalid")
|
||||
== CalendarSeasonType.UNKNOWN
|
||||
)
|
||||
assert(
|
||||
WorldTimeServiceType.calendar_cycle_id_from_datetime({
|
||||
"year": 2026, "month": 8, "day": 18,
|
||||
|
|
@ -99,7 +129,11 @@ func _validate_clock_boundaries_and_duration() -> void:
|
|||
|
||||
var clock := WorldTimeServiceType.new()
|
||||
root.add_child(clock)
|
||||
clock.begin_test_session(8.0)
|
||||
clock.begin_test_session(8.0, "2026-08-18")
|
||||
assert(clock.get_calendar_date_id() == "2026-08-18")
|
||||
assert(clock.get_calendar_text() == "tuesday, august 18")
|
||||
assert(clock.get_season() == CalendarSeasonType.Season.SUMMER)
|
||||
assert(clock.get_season_text() == "summer")
|
||||
clock.advance_time(12.0 * 60.0 * 60.0)
|
||||
assert(is_equal_approx(clock.get_time_hours(), 20.0))
|
||||
assert(clock.is_night_period())
|
||||
|
|
@ -214,7 +248,7 @@ func _validate_fishing_availability() -> void:
|
|||
|
||||
func _validate_fishing_spot_context() -> void:
|
||||
var clock := WorldTimeServiceType.new()
|
||||
clock.begin_test_session(20.25)
|
||||
clock.begin_test_session(20.25, "2026-12-18")
|
||||
var fishing_spot := FishingSpotType.new()
|
||||
fishing_spot.set("_world_time", clock)
|
||||
var region := FishableWaterRegionType.new()
|
||||
|
|
@ -223,6 +257,7 @@ func _validate_fishing_spot_context() -> void:
|
|||
var dusk_context: FishingContext = fishing_spot.build_network_context(region)
|
||||
assert(dusk_context.is_night)
|
||||
assert(dusk_context.is_day_night_transition)
|
||||
assert(dusk_context.season == CalendarSeasonType.Season.WINTER)
|
||||
clock.synchronize_time(14.0)
|
||||
var day_context: FishingContext = fishing_spot.build_network_context(region)
|
||||
assert(not day_context.is_night)
|
||||
|
|
@ -236,16 +271,25 @@ func _validate_network_snapshot_bounds() -> void:
|
|||
assert(NetworkWorldTimeServiceType.validate_snapshot({
|
||||
"session_id": "session",
|
||||
"time_hours": 8.25,
|
||||
"calendar_date_id": "2026-08-18",
|
||||
"sequence": 1,
|
||||
}))
|
||||
assert(not NetworkWorldTimeServiceType.validate_snapshot({
|
||||
"session_id": "session",
|
||||
"time_hours": 24.0,
|
||||
"calendar_date_id": "2026-08-18",
|
||||
"sequence": 1,
|
||||
}))
|
||||
assert(not NetworkWorldTimeServiceType.validate_snapshot({
|
||||
"session_id": "",
|
||||
"time_hours": 8.0,
|
||||
"calendar_date_id": "2026-08-18",
|
||||
"sequence": 1,
|
||||
}))
|
||||
assert(not NetworkWorldTimeServiceType.validate_snapshot({
|
||||
"session_id": "session",
|
||||
"time_hours": 8.0,
|
||||
"calendar_date_id": "2026-02-29",
|
||||
"sequence": 1,
|
||||
}))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue