extends SceneTree const FishCatchType = preload("res://fish/fish_catch.gd") const FishQualityType = preload("res://fish/fish_quality.gd") const FishSelectorType = preload("res://fish/fish_selector.gd") const CollectionLogType = preload("res://collection/collection_log.gd") const NetworkSaleServiceType = preload( "res://network/network_sale_service.gd" ) const Catalog: FishPool = preload("res://fish/pools/fish_catalog.tres") const PelicanBuyer: FishBuyerProfile = preload( "res://economy/buyers/pelicans.tres" ) func _initialize() -> void: call_deferred("_run") func _run() -> void: _validate_tiers_and_distribution() _validate_catch_round_trip_and_sale() _validate_mail_round_trip() _validate_collection_mastery() _validate_version_four_migration() assert(NetworkProtocol.PROTOCOL_VERSION == 3) assert(NetworkProtocol.ENET_CHANNEL_COUNT == 10) assert(NetworkProtocol.FISH_QUALITY_CAPABILITY == "fish_quality_v1") print("Fish quality validation: PASS") quit() func _validate_tiers_and_distribution() -> void: assert(FishQualityType.TIER_COUNT == 5) assert(FishQualityType.display_name(0) == "boring") assert(FishQualityType.display_name(4) == "shiny") assert(UIPalette.get_quality_color(0) == UIPalette.QUALITY_BORING) assert(UIPalette.get_quality_color(4) == UIPalette.QUALITY_SHINY) assert( UIPalette.get_quality_color(0) != UIPalette.get_quality_color(1) ) assert(FishQualityType.apply_sale_value(3, 0) == 3) assert(FishQualityType.apply_sale_value(3, 1) == 4) assert(FishQualityType.apply_sale_value(3, 2) == 5) assert(FishQualityType.apply_sale_value(3, 3) == 6) assert(FishQualityType.apply_sale_value(3, 4) == 7) var previous_offer: int = -1 for quality: int in FishQualityType.TIER_COUNT: var offer: int = PelicanBuyer.get_quality_offer(3, quality) assert(offer > previous_offer) previous_offer = offer assert( FishQualityType.qualified_name_with_article("bluegill", 0) == "a boring bluegill" ) assert( FishQualityType.qualified_name_with_article("bluegill", 1) == "an average bluegill" ) var rng := RandomNumberGenerator.new() rng.seed = 727272 var counts: Array[int] = [0, 0, 0, 0, 0] const SAMPLE_COUNT: int = 50000 for _sample: int in SAMPLE_COUNT: counts[FishQualityType.roll(rng)] += 1 for quality: int in FishQualityType.TIER_COUNT: var observed: float = float(counts[quality]) / float(SAMPLE_COUNT) var expected: float = ( FishQualityType.BASE_ROLL_WEIGHTS[quality] / 100.0 ) assert(absf(observed - expected) < 0.015) func _validate_catch_round_trip_and_sale() -> void: var fish: FishData = Catalog.get_fish_by_id(&"bluegill") assert(fish != null) var selector := FishSelectorType.new() selector.use_deterministic_test_seed = true selector.deterministic_test_seed = 9911 selector.quality_weight_multipliers = [0.0, 0.0, 0.0, 0.0, 1.0] selector.begin_roll() var fish_catch: FishCatch = selector.create_catch(fish) assert(fish_catch != null) assert(fish_catch.quality == FishQualityType.Tier.SHINY) assert( fish_catch.sale_value == FishQualityType.apply_sale_value( fish.get_sale_value_for_weight(fish_catch.weight_lb), FishQualityType.Tier.SHINY, ) ) var save_data: Dictionary = fish_catch.to_save_dict() fish_catch.catch_sequence = 1 save_data = fish_catch.to_save_dict() var restored: FishCatch = FishCatchType.from_save_dict(save_data, fish) assert(restored != null and restored.quality == fish_catch.quality) var network_data: Dictionary = fish_catch.to_network_dict() var replicated: FishCatch = FishCatchType.from_network_dict( network_data, fish, ) assert(replicated != null and replicated.quality == fish_catch.quality) var missing_quality: Dictionary = network_data.duplicate(true) missing_quality.erase("quality") assert(FishCatchType.from_network_dict(missing_quality, fish) == null) var session := NetworkSession.new() var sale_service := NetworkSaleServiceType.new() root.add_child(session) root.add_child(sale_service) sale_service.set("_session", session) sale_service.set("_fish_catalog", Catalog) var accepted: Dictionary = sale_service.call( "_build_authoritative_result", 1, "quality_sale", [network_data], PelicanBuyer, ) assert(bool(accepted.get("accepted", false))) assert( int(accepted["payout"]) == PelicanBuyer.get_quality_offer( fish.get_sale_value_for_weight(fish_catch.weight_lb), fish_catch.quality, ) ) var inventory := FishInventory.new() var wallet := PlayerWallet.new() var local_sale := FishSaleService.new() root.add_child(inventory) root.add_child(wallet) root.add_child(local_sale) local_sale.setup(inventory, wallet) inventory.add_catch(fish_catch) var local_preview: FishSaleResult = local_sale.preview_batch( [fish_catch.catch_id], PelicanBuyer, ) assert(local_preview.is_success()) assert(local_preview.payout == int(accepted["payout"])) var forged: Dictionary = network_data.duplicate(true) forged["quality"] = FishQualityType.Tier.BORING var rejected: Dictionary = sale_service.call( "_build_authoritative_result", 1, "quality_sale_forged", [forged], PelicanBuyer, ) assert(not bool(rejected.get("accepted", false))) local_sale.queue_free() wallet.queue_free() inventory.queue_free() sale_service.queue_free() session.queue_free() func _validate_mail_round_trip() -> void: var fish: FishData = Catalog.get_fish_by_id(&"bluegill") 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.quality = FishQualityType.Tier.IMPRESSIVE fish_catch.sale_value = FishQualityType.apply_sale_value( fish.get_sale_value_for_weight(fish_catch.weight_lb), fish_catch.quality, ) fish_catch.ensure_identity() var attachment: Dictionary = { "type": PlayerAssetReservationService.AttachmentType.FISH, "catch_id": String(fish_catch.catch_id), "catch": fish_catch.to_network_dict(), } var service := NetworkMailService.new() root.add_child(service) service.set("_fish_catalog", Catalog) var restored: FishCatch = service.call("_decode_catch", attachment) assert(restored != null) assert(restored.quality == FishQualityType.Tier.IMPRESSIVE) var signature: Array = NetworkMailProtocol.attachment_signature_fields( attachment ) assert(int(signature[5]) == FishQualityType.Tier.IMPRESSIVE) var altered: Dictionary = attachment.duplicate(true) (altered["catch"] as Dictionary)["quality"] = ( FishQualityType.Tier.BORING ) assert( NetworkMailProtocol.attachment_signature_fields(altered) != signature ) service.queue_free() func _validate_collection_mastery() -> void: var collection := CollectionLogType.new() root.add_child(collection) for quality: int in FishQualityType.TIER_COUNT: collection.mark_quality_discovered(&"bluegill", quality) assert(collection.has_discovered_quality(&"bluegill", quality)) assert(collection.has_discovered(&"bluegill")) assert(collection.has_mastered(&"bluegill")) assert( collection.get_quality_mask(&"bluegill") == FishQualityType.ALL_TIERS_MASK ) collection.queue_free() func _validate_version_four_migration() -> void: var manager := PlayerSaveManager.new() root.add_child(manager) var version_four: Dictionary = { "save_version": 4, "wallet": {"balance": 12}, "collection": {"discovered_fish_ids": ["bluegill"]}, "inventory": { "next_catch_sequence": 2, "catches": [{ "catch_id": "carp:legacy", "catch_sequence": 1, "fish_id": "carp", "weight_lb": 2.0, "display_scale": 1.0, "sale_value": 4, "is_favorited": false, }], }, "bag": {"items": []}, "hotbar": {"selected_slot": 0, "slots": []}, "upgrades": {"reel_speed_level": 0, "barrier_power_level": 0}, "cooler": {"capacity_level": 0}, } var migrated: Dictionary = manager.call( "_migrate_save", version_four, 4, ) assert(int(migrated.get("save_version", -1)) == 6) assert(int((migrated["experience"] as Dictionary)["total_experience"]) == 0) assert( is_equal_approx( float((migrated["world"] as Dictionary)["time_hours"]), 8.0, ) ) var catches: Array = migrated["inventory"]["catches"] assert(int((catches[0] as Dictionary)["quality"]) == 0) var collection: Dictionary = migrated["collection"] var ids: Array = collection["discovered_fish_ids"] assert("bluegill" in ids and "carp" in ids) var masks: Dictionary = collection["discovered_quality_masks"] var boring_bit: int = FishQualityType.bit_for(FishQualityType.Tier.BORING) assert(int(masks["bluegill"]) == boring_bit) assert(int(masks["carp"]) == boring_bit) manager.queue_free()