Files
aetherbound-guild/runtime/tests/dev6/region_advance_test.gd
T

197 lines
9.5 KiB
GDScript

extends SceneTree
## M79-B gate: Contracts advance by ordinal, so the second region becomes
## reachable. Contract 1 and 2 are region one; contract 3 opens Siltwake Fens
## with its own equipment band, enemy pool and boss anchor.
const DraftService = preload("res://entry/initial_company_draft_service.gd")
const ContractService = preload("res://entry/guild_contract_service.gd")
const ContentRegistry = preload("res://p10_0/content_registry.gd")
const COMPANY_PATH := "user://aetherbound_region_advance_company.json"
const DRAFT_PATH := "user://aetherbound_region_advance_draft.json"
const OP_CREATE_1 := "11111111111111111111111111111111"
const OP_RETURN_1 := "22222222222222222222222222222222"
const OP_CREATE_2 := "33333333333333333333333333333333"
const OP_RETURN_2 := "44444444444444444444444444444444"
const OP_CREATE_3 := "55555555555555555555555555555555"
const OP_RETURN_3 := "66666666666666666666666666666666"
const OP_CREATE_4 := "77777777777777777777777777777777"
const OP_RETURN_4 := "88888888888888888888888888888888"
const OP_CREATE_5 := "99999999999999999999999999999999"
const OP_RETURN_5 := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
const OP_CREATE_6 := "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
const OP_RETURN_6 := "cccccccccccccccccccccccccccccccc"
const OP_CREATE_7 := "dddddddddddddddddddddddddddddddd"
const OP_RETURN_7 := "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
const OP_CREATE_8 := "ffffffffffffffffffffffffffffffff"
const OP_RETURN_8 := "00000000000000000000000000000000"
const OP_CREATE_9 := "12121212121212121212121212121212"
const OP_RETURN_9 := "34343434343434343434343434343434"
const OP_CREATE_10 := "56565656565656565656565656565656"
const OP_RETURN_10 := "78787878787878787878787878787878"
const OP_CREATE_11 := "9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a"
const OP_RETURN_11 := "b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7b7"
const OP_CREATE_12 := "c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8"
const OP_RETURN_12 := "d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9d9"
const OP_CREATE_13 := "e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0"
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_cleanup()
var company := _create_company()
if company.is_empty():
push_error("region advance: company creation failed")
quit(1)
return
expect(String(company.active_contract.region_id) == "RG-01", "first contract opens region one")
expect(_equipment_band(company) == "EQ-001..EQ-040", "region one market draws EQ-001..EQ-040")
expect(int(company.active_contract.region_index) == 1, "region index starts at one")
# M79-H: drive every contract in order and assert the region band it opens.
# Contract ids follow the catalogue order: two per region, so contract 15 is
# the first Crown of Morn contract.
var expected_region := 1
var equipment_low := 1
var checked := 1
var last_region_id := "RG-01"
var last_boss := ""
var last_enemies := 0
for ordinal in range(1, 16):
var returned: Dictionary = _service().return_contract(_operation_id("return", ordinal))
if not returned.ok:
push_error("region advance: return %d failed: %s" % [ordinal, String(returned.get("code", ""))])
quit(1)
return
var contract_rows: Array = Array(ContentRegistry.snapshot().get("contracts", []))
contract_rows.sort_custom(func(a, b): return String(Dictionary(a).get("id", "")) < String(Dictionary(b).get("id", "")))
var row: Dictionary = Dictionary(contract_rows[ordinal])
var created: Dictionary = _service().create_contract(_operation_id("create", ordinal), String(row.get("id", "")), "standard")
if not created.ok:
push_error("region advance: create %d (%s) failed: %s" % [ordinal, row.get("id", ""), String(created.get("code", ""))])
quit(1)
return
var contract: Dictionary = created.company.active_contract
checked += 1
# Loop step `ordinal` creates contract number `ordinal + 1`; a region's
# first contract is an odd ordinal, so a new region opens on even steps.
if ordinal % 2 == 0:
expected_region = (ordinal + 2) / 2
equipment_low = 1 + (expected_region - 1) * 40
expect(int(contract.region_index) == expected_region, "contract %d opens region %d, got %s" % [ordinal + 1, expected_region, String(contract.region_id)])
expect(String(contract.region_id) == "RG-%02d" % expected_region, "contract %d region id, got %s" % [ordinal, String(contract.region_id)])
expect(String(contract.boss_id).begins_with("BO-R%d-" % expected_region), "contract %d anchors a region %d boss, got %s" % [ordinal, expected_region, String(contract.boss_id)])
else:
expect(int(contract.region_index) == expected_region, "contract %d stays in region %d" % [ordinal + 1, expected_region])
expect(_equipment_band(created.company) == "EQ-%03d..EQ-%03d" % [equipment_low, equipment_low + 39],
"contract %d market draws EQ-%03d..EQ-%03d" % [ordinal, equipment_low, equipment_low + 39])
var region_enemies: Array[String] = []
for offer in Array(contract.market_board.battle_offers):
for enemy_id in Array(Dictionary(offer).get("enemy_ids", [])):
region_enemies.append(String(enemy_id))
var leaked: Array[String] = []
for enemy_id in region_enemies:
if not enemy_id.begins_with("EN-R%d-" % expected_region) and not enemy_id.begins_with("BO-R%d-" % expected_region):
leaked.append(enemy_id)
expect(leaked.is_empty(), "contract %d fields only region %d identities, leaked %s" % [ordinal, expected_region, str(leaked)])
if ordinal == 15:
last_region_id = String(contract.region_id)
last_boss = String(contract.boss_id)
last_enemies = region_enemies.size()
# Close the final contract, then the Guild reports a completed route.
# Voluntary withdrawals grant no clear facts or renown.
var final_return: Dictionary = _service().return_contract(_operation_id("return", 16))
expect(final_return.ok, "the final contract returns cleanly: %s" % String(final_return.get("code", "")))
var final_company: Dictionary = Dictionary(final_return.get("company", {}))
expect(Array(final_company.get("clear_facts", [])).is_empty(), "voluntary withdrawals grant no clear facts")
expect(int(final_company.get("renown", 0)) == 0, "voluntary withdrawals grant no renown")
var guild_controller := preload("res://entry/guild_page_controller.gd").new()
var guild_snapshot: Dictionary = guild_controller.snapshot(final_company, "zh_CN")
expect(bool(guild_snapshot.get("route_complete", false)), "the guild reports a completed route after sixteen contracts")
var contract_destination: Dictionary = guild_controller.destination_state(guild_snapshot, "contract")
expect(String(contract_destination.get("reason", "")) == "route_complete", "the board closes with the completion reason, got %s" % String(contract_destination.get("reason", "")))
expect(not bool(contract_destination.get("enabled", true)), "the board is closed once the route is complete")
var content: Dictionary = ContentRegistry.snapshot()
expect(ContentRegistry.localized_name(content, "RG-02", "zh_CN") == "淤醒沼泽", "region two zh authority resolves")
expect(ContentRegistry.localized_name(content, "RG-08", "zh_CN") == "晨冠", "region eight zh authority resolves")
expect(ContentRegistry.localized_name(content, "RG-08", "en") == "Crown of Morn", "region eight en authority resolves")
_cleanup()
if failures.is_empty():
print("ABG_M79_REGION_ADVANCE_OK contracts=%d region=%s equipment=EQ-281..EQ-320 enemies=%d boss=%s route_complete=true" % [checked, last_region_id, last_enemies, last_boss])
quit(0)
return
for failure in failures:
push_error("M79 region advance: %s" % failure)
quit(1)
func _equipment_band(company: Dictionary) -> String:
# The market draws four identities from the region band; the gate checks that
# every drawn identity stays inside the band rather than the band's extremes.
var ids: Array[String] = []
for offer in Array(Dictionary(company.get("active_contract", {})).get("market_board", {}).get("equipment_offers", [])):
ids.append(String(Dictionary(offer).get("catalog_id", "")))
if ids.is_empty():
return "none"
var low := 9999
var high := 0
for catalog_id in ids:
var ordinal := int(catalog_id.split("-")[1])
low = mini(low, ordinal)
high = maxi(high, ordinal)
var expected_low := 1 + (int(Dictionary(company.get("active_contract", {})).get("region_index", 1)) - 1) * 40
var expected_high := expected_low + 39
if low >= expected_low and high <= expected_high:
return "EQ-%03d..EQ-%03d" % [expected_low, expected_high]
return "out_of_band:%d..%d" % [low, high]
func _create_company() -> Dictionary:
var draft: Dictionary = _draft_service().begin_or_load("abcdef0123456789abcdef0123456789")
if not draft.ok:
return {}
var selected := [String(draft.draft.offers[0].offer_id), String(draft.draft.offers[1].offer_id)]
if not _draft_service().set_selection(selected).ok:
return {}
if not _draft_service().commit_company().ok:
return {}
var created: Dictionary = _service().create_contract(OP_CREATE_1, "CTR-R1-01", "standard")
return created.company if created.ok else {}
func _operation_id(kind: String, ordinal: int) -> String:
return ("%s-%08d" % [kind, ordinal]).sha256_text().substr(0, 32)
func _service() -> RefCounted:
var service := ContractService.new()
service.company_path = COMPANY_PATH
return service
func _draft_service() -> RefCounted:
var service := DraftService.new()
service.path = DRAFT_PATH
service.company_path = COMPANY_PATH
return service
func _cleanup() -> void:
for path in [COMPANY_PATH, DRAFT_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH]:
if FileAccess.file_exists(path):
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append(message)