Files
aetherbound-guild/runtime/domain/run_state.gd
T
wushenghua e7eb408e0e feat: build p9.1 opening market runtime
Implement the bounded Godot opening Company and first-Market preparation flow with deterministic one-shot state, real touch verification, responsive layouts, prototype feedback, and current design regressions.\n\nCo-Authored-By: Codex <noreply@anthropic.com>
2026-08-12 16:47:44 +08:00

231 lines
6.8 KiB
GDScript

class_name AetherboundRunState
extends RefCounted
enum Step {
CONTRACT,
OFFERS,
OPENING_REVIEW,
OPENING_RECEIPT,
MARKET_MAEL,
MARKET_ILYA,
EQUIPMENT_PURCHASE,
EQUIPMENT_EQUIP,
ORDER,
READY,
}
const CAPACITY := 4
const OPENING_COIN := 52
const HIRE_PRICE := 14
const EQUIPMENT_PRICE := 12
const CONTRACT_ID := "CTR-R1-01"
const EQUIPMENT_ID := "EQ-012"
const OFFERS := [
{"id": "OFF-01", "name": "Elian Rook", "profession": "Hearthguard", "trait": "Narrow Guard", "item": "EQ-002"},
{"id": "OFF-02", "name": "Nia Orrow", "profession": "Farstring", "trait": "Patient Start", "item": "EQ-014"},
{"id": "OFF-03", "name": "Tomas Vale", "profession": "Wayrunner", "trait": "Open Stance", "item": "EQ-001"},
{"id": "OFF-04", "name": "Sable Kest", "profession": "Kindhand", "trait": "Shared Cup", "item": "EQ-009"},
]
const MARKET_RECRUITS := {
"MKT-R1-01": {"id": "MKT-R1-01", "name": "Mael Corin", "profession": "Hearthguard", "trait": "Steady Brace", "item": "EQ-002"},
"MKT-R1-02": {"id": "MKT-R1-02", "name": "Ilya Venn", "profession": "Farstring", "trait": "Clear Measure", "item": "EQ-014"},
}
var step := Step.CONTRACT
var contract_selected := false
var difficulty := ""
var inspected: Array[String] = []
var selected: Array[String] = []
var company: Array[String] = []
var coin := OPENING_COIN
var opening_committed := false
var committed_hires: Array[String] = []
var equipment_purchased := false
var equipment_wearer := ""
var order_source := ""
var order_changed := false
var order_committed := false
var operation_counts := {
"opening": 0,
"MKT-R1-01": 0,
"MKT-R1-02": 0,
"purchase": 0,
"equip": 0,
"order": 0,
}
func select_contract() -> Dictionary:
if step != Step.CONTRACT:
return _reject("wrong_step")
contract_selected = true
return _ok("contract_selected")
func select_difficulty(value: String) -> Dictionary:
if step != Step.CONTRACT or not contract_selected:
return _reject("contract_required")
if value not in ["wayfinder", "standard", "oathbound"]:
return _reject("invalid_difficulty")
difficulty = value
return _ok("difficulty_selected")
func open_offers() -> Dictionary:
if step != Step.CONTRACT:
return _reject("wrong_step")
if not contract_selected or difficulty.is_empty():
return _reject("contract_and_difficulty_required")
step = Step.OFFERS
return _ok("offers_opened")
func inspect_offer(id: String) -> Dictionary:
if step != Step.OFFERS or not _is_offer(id):
return _reject("invalid_offer")
if id not in inspected:
inspected.append(id)
return _ok("offer_inspected")
func toggle_offer(id: String) -> Dictionary:
if step != Step.OFFERS or not _is_offer(id):
return _reject("invalid_offer")
if id in selected:
selected.erase(id)
return _ok("offer_removed")
if selected.size() >= 2:
return _reject("exactly_two_limit")
selected.append(id)
return _ok("offer_selected")
func review_opening() -> Dictionary:
if step != Step.OFFERS:
return _reject("wrong_step")
if selected.size() != 2:
return _reject("exactly_two_required")
step = Step.OPENING_REVIEW
return _ok("opening_reviewed")
func revise_opening() -> Dictionary:
if step != Step.OPENING_REVIEW or opening_committed:
return _reject("opening_locked")
step = Step.OFFERS
return _ok("opening_revised")
func commit_opening() -> Dictionary:
if opening_committed:
return _reject("opening_already_committed")
if step != Step.OPENING_REVIEW or selected.size() != 2:
return _reject("opening_not_ready")
opening_committed = true
operation_counts.opening += 1
company.assign(selected)
step = Step.OPENING_RECEIPT
return _ok("opening_committed")
func open_market() -> Dictionary:
if step != Step.OPENING_RECEIPT or not opening_committed:
return _reject("opening_receipt_required")
step = Step.MARKET_MAEL
return _ok("market_opened")
func hire(id: String) -> Dictionary:
var expected := "MKT-R1-01" if step == Step.MARKET_MAEL else "MKT-R1-02" if step == Step.MARKET_ILYA else ""
if id != expected or not MARKET_RECRUITS.has(id):
return _reject("wrong_hire")
if id in committed_hires:
return _reject("hire_already_committed")
if company.size() >= CAPACITY or coin < HIRE_PRICE:
return _reject("hire_ineligible")
coin -= HIRE_PRICE
company.append(id)
committed_hires.append(id)
operation_counts[id] += 1
step = Step.MARKET_ILYA if id == "MKT-R1-01" else Step.EQUIPMENT_PURCHASE
return _ok("hire_committed")
func purchase_equipment() -> Dictionary:
if equipment_purchased:
return _reject("equipment_already_purchased")
if step != Step.EQUIPMENT_PURCHASE or coin < EQUIPMENT_PRICE:
return _reject("purchase_ineligible")
coin -= EQUIPMENT_PRICE
equipment_purchased = true
operation_counts.purchase += 1
step = Step.EQUIPMENT_EQUIP
return _ok("equipment_purchased")
func equip_member(id: String) -> Dictionary:
if not equipment_purchased or step != Step.EQUIPMENT_EQUIP:
return _reject("equipment_not_ready")
if equipment_wearer == id:
return _reject("equipment_already_equipped")
if id not in company:
return _reject("wearer_not_in_company")
equipment_wearer = id
operation_counts.equip += 1
step = Step.ORDER
return _ok("equipment_equipped")
func choose_order_unit(id: String) -> Dictionary:
if step != Step.ORDER or id not in company:
return _reject("invalid_order_unit")
if order_source.is_empty():
order_source = id
return _ok("order_source_selected")
if order_source == id:
order_source = ""
return _ok("order_source_cleared")
var first := company.find(order_source)
var second := company.find(id)
var temporary := company[first]
company[first] = company[second]
company[second] = temporary
order_source = ""
order_changed = true
return _ok("order_swapped")
func commit_order() -> Dictionary:
if order_committed:
return _reject("order_already_committed")
if step != Step.ORDER or not order_changed or not order_source.is_empty():
return _reject("order_change_required")
order_committed = true
operation_counts.order += 1
step = Step.READY
return _ok("readiness_committed")
func snapshot() -> Dictionary:
return {
"step": step,
"contract": CONTRACT_ID if contract_selected else "",
"difficulty": difficulty,
"inspected": inspected.duplicate(),
"selected": selected.duplicate(),
"company": company.duplicate(),
"coin": coin,
"equipment_purchased": equipment_purchased,
"equipment_wearer": equipment_wearer,
"order_changed": order_changed,
"order_committed": order_committed,
"operation_counts": operation_counts.duplicate(true),
}
func offer(id: String) -> Dictionary:
for entry in OFFERS:
if entry.id == id:
return entry
if MARKET_RECRUITS.has(id):
return MARKET_RECRUITS[id]
return {}
func _is_offer(id: String) -> bool:
for entry in OFFERS:
if entry.id == id:
return true
return false
func _ok(code: String) -> Dictionary:
return {"ok": true, "code": code}
func _reject(code: String) -> Dictionary:
return {"ok": false, "code": code}