140 lines
7.0 KiB
GDScript
140 lines
7.0 KiB
GDScript
extends SceneTree
|
|
|
|
const Content = preload("res://fg4/content_registry.gd")
|
|
|
|
var failures: Array[String] = []
|
|
var rejected_cases := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
verify_complete_graph()
|
|
verify_bilingual_authority()
|
|
verify_route_graph()
|
|
verify_negative_boundaries()
|
|
if failures.is_empty():
|
|
print("ABG_FG4_CONTENT_REGISTRY_OK equipment=80 artifacts=15 enemies=24 bosses=4 contracts=4 markets=48 routes=144 bilingual=true rg03_enemy_usage_min=2 bosses_executable=4 bosses_descriptor_only=0 negative_cases=%d" % rejected_cases)
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func verify_complete_graph() -> void:
|
|
var first := Content.snapshot()
|
|
var second := Content.snapshot()
|
|
var validation := Content.validate(first)
|
|
expect(validation.ok, "canonical graph validates: %s" % str(validation.errors))
|
|
expect(first == second, "snapshot is byte-identical")
|
|
expect(String(first.catalog_hash).length() == 64, "catalog has source-bound hash")
|
|
expect(first.equipment.size() == 80, "equipment EQ-081..160 is complete")
|
|
expect(first.artifacts.size() == 15, "artifacts AR-016..030 are complete")
|
|
expect(first.enemies.size() == 24, "RG-03/RG-04 enemy identities are complete")
|
|
expect(first.bosses.size() == 4, "all RG-03/RG-04 Boss descriptors resolve")
|
|
expect(first.markets.size() == 12 and first.contract2_markets.size() == 12, "both twelve-Market Contracts resolve")
|
|
expect(first.routes.size() == 36 and first.contract2_routes.size() == 36, "both 36-route graphs resolve")
|
|
expect(first.rg04_markets.size() == 12 and first.rg04_contract2_markets.size() == 12, "both RG-04 twelve-Market Contracts resolve")
|
|
expect(first.rg04_routes.size() == 36 and first.rg04_contract2_routes.size() == 36, "both RG-04 36-route graphs resolve")
|
|
expect(first.contract.id == "CTR-R3-01" and first.contract.boss_id == "BO-R3-01", "Contract-1 boundary is exact")
|
|
expect(first.contract2.id == "CTR-R3-02" and first.contract2.boss_id == "BO-R3-02", "Contract-2 boundary is exact")
|
|
var bosses := Content.rows_by_id(first, "bosses")
|
|
expect(bosses["BO-R3-01"].executable and bosses["BO-R3-02"].executable, "both RG-03 Bosses are executable")
|
|
expect(bosses["BO-R4-01"].executable and bosses["BO-R4-02"].executable, "both accepted RG-04 Bosses are executable")
|
|
|
|
|
|
func verify_bilingual_authority() -> void:
|
|
var content := Content.snapshot()
|
|
for family in ["equipment", "artifacts", "enemies", "bosses"]:
|
|
for row in content[family]:
|
|
expect(has_copy(row.name), "%s has bilingual name" % row.id)
|
|
expect(has_copy(row.preview), "%s has bilingual preview" % row.id)
|
|
for item in content.equipment:
|
|
expect(has_copy(item.effect) and has_copy(item.tradeoff), "%s has bilingual decision copy" % item.id)
|
|
for artifact in content.artifacts:
|
|
expect(has_copy(artifact.rule) and has_copy(artifact.tradeoff), "%s has bilingual Artifact copy" % artifact.id)
|
|
for enemy in content.enemies:
|
|
expect(has_copy(enemy.rule) and has_copy(enemy.responses) and has_copy(enemy.escalation), "%s has bilingual enemy copy" % enemy.id)
|
|
for market in content.markets:
|
|
expect(has_copy(market.name) and has_copy(market.preview), "Market %d has bilingual decision copy" % market.market_index)
|
|
expect(has_copy(market.pressure) and has_copy(market.failure) and has_copy(market.milestone), "Market %d has bilingual consequence copy" % market.market_index)
|
|
for market in content.contract2_markets:
|
|
expect(has_copy(market.name) and has_copy(market.preview), "Contract-2 Market %d has bilingual decision copy" % market.market_index)
|
|
expect(has_copy(market.pressure) and has_copy(market.failure) and has_copy(market.milestone), "Contract-2 Market %d has bilingual consequence copy" % market.market_index)
|
|
|
|
|
|
func verify_route_graph() -> void:
|
|
var content := Content.snapshot()
|
|
var usage := {}
|
|
for enemy in content.enemies:
|
|
if String(enemy.id).begins_with("EN-R3-"):
|
|
usage[String(enemy.id)] = 0
|
|
for route in content.routes:
|
|
var market_index := int(route.market_index)
|
|
var risk := int(route.risk)
|
|
expect(route.id == "RO-R3-C01-M%02d-Q%d" % [market_index, risk], "%s identity is exact" % route.id)
|
|
expect(route.reward_coin == 22 + 2 * market_index + 7 * risk, "%s reward formula is exact" % route.id)
|
|
expect(route.recovery_coin == 12 + market_index + 3 * risk, "%s recovery cost is exact" % route.id)
|
|
expect(route.response_tags.size() >= 3, "%s exposes multiple rational responses" % route.id)
|
|
expect(route.enemy_ids.size() == route.wave_order.size(), "%s wave count matches composition" % route.id)
|
|
for enemy_id in route.enemy_ids:
|
|
usage[String(enemy_id)] += 1
|
|
for enemy_id in usage:
|
|
expect(usage[enemy_id] >= 2, "%s appears in at least two routes" % enemy_id)
|
|
for market in content.markets:
|
|
var valve: Dictionary = market.pressure_valve
|
|
expect(valve.id == "RV-R3-M%02d" % market.market_index, "Market %d valve identity is exact" % market.market_index)
|
|
expect(valve.position >= 1 and valve.position <= 10, "Market %d valve position is bounded" % market.market_index)
|
|
expect(valve.trigger in ["occupy", "vacate", "cross"], "Market %d valve trigger is disclosed" % market.market_index)
|
|
expect(valve.resolve_tick > valve.warning_ticks and valve.fixed_tick_ms == 100, "Market %d countdown is deterministic" % market.market_index)
|
|
for route in content.contract2_routes:
|
|
var market_index := int(route.market_index)
|
|
var risk := int(route.risk)
|
|
expect(route.id == "RO-R3-C02-M%02d-Q%d" % [market_index, risk], "%s identity is exact" % route.id)
|
|
expect(route.reward_coin == 22 + 2 * market_index + 7 * risk, "%s reward formula is exact" % route.id)
|
|
expect(route.spark_disclosure.repeat_threshold == 3, "%s discloses repeat threshold" % route.id)
|
|
|
|
|
|
func verify_negative_boundaries() -> void:
|
|
var wrong_source := Content.snapshot()
|
|
wrong_source.source.region_catalog_blob = "wrong"
|
|
expect_rejected(wrong_source, "source drift")
|
|
var missing_copy := Content.snapshot()
|
|
missing_copy.equipment[0].effect.zh_CN = ""
|
|
expect_rejected(missing_copy, "missing localized copy")
|
|
var duplicate_id := Content.snapshot()
|
|
duplicate_id.equipment[1].id = duplicate_id.equipment[0].id
|
|
expect_rejected(duplicate_id, "duplicate identity")
|
|
var bad_reward := Content.snapshot()
|
|
bad_reward.routes[0].reward_coin += 1
|
|
expect_rejected(bad_reward, "reward drift")
|
|
var bad_valve := Content.snapshot()
|
|
bad_valve.markets[0].pressure_valve.position = 11
|
|
expect_rejected(bad_valve, "valve outside line")
|
|
var bad_spark := Content.snapshot()
|
|
bad_spark.contract2_markets[0].spark_disclosure.repeat_threshold = 2
|
|
expect_rejected(bad_spark, "spark threshold drift")
|
|
|
|
|
|
func expect_rejected(content: Dictionary, label: String) -> void:
|
|
var rejected := not bool(Content.validate(content).ok)
|
|
expect(rejected, "%s rejects" % label)
|
|
if rejected:
|
|
rejected_cases += 1
|
|
|
|
|
|
func has_copy(value) -> bool:
|
|
return (
|
|
value is Dictionary
|
|
and not String(value.get("en", "")).is_empty()
|
|
and not String(value.get("zh_CN", "")).is_empty()
|
|
)
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("FG-4 content registry: %s" % message)
|