# VERTICAL SLICE - NOT FOR PRODUCTION # Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2) # Date: 2026-09-10 # 行动经济:被动账本(AP 硬重置 / 统一价目 / 三路径结束仲裁 cleared > stopped > ap_exhausted / # RAP 池战斗域 / 双发射源之 stopped 直发)。相位域 = {Idle, Active, Waiting}。 # 实现 GDD:design/archive/gdd-2026-09-14/a3-action-economy.md(系统 ID A3·行动经济——registry 追溯锚,命名规范见 CLAUDE.md) class_name ActionEconomy extends RefCounted signal round_end_stopped(payload: Dictionary) # 双发射源之停手路径(行动经济 直发;payload 与 排雷玩法 棋盘路径同构) const PHASE_IDLE := "Idle" const PHASE_ACTIVE := "Active" const PHASE_WAITING := "Waiting" var db # 相位与账本只读出口(E5 封装收口 2026-09-13):外部读取经 getter-only 属性(架构文档 # "ap/rap/phase 只读" 的机制化),写入只经 notify_* / request_* 方法通道。 var _phase: String = PHASE_IDLE var phase: String: get: return _phase var _ap := 0 var ap: int: get: return _ap var _rap := 0 var rap: int: get: return _rap var _last_round_end: Dictionary = {} # {path_tag, continuation_hint}——排雷玩法 组包 round_end 的合法数据源 var last_round_end: Dictionary: get: return _last_round_end var _settlement_consumed := false # AC-14 门:notify_settlement_done 消费后才受理 round_start var _next_round_mode := "" # 调试观测量(同板续轮 / 换新图) var _pending_ap_delta := 0 # 下轮行动点修正排队(格面资源八型批 2026-09-20——action 型 ±1 排队,轮开始兑现后清零) func _init(db_ref) -> void: db = db_ref func _cost(kind: String) -> int: var ae = db.action_economy match kind: "dig": return ae.dig_ap_cost "flag_place": return ae.flag_place_ap_cost "move": return ae.move_ap_cost "chain": return ae.chain_ap_cost assert(false, "未知动作价目: " + kind) return 0 # 通用扣减(排雷玩法 用于旗动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数) func try_spend(action_kind: String) -> bool: if phase != PHASE_ACTIVE: _reject("phase") return false return _spend_and_arbitrate(action_kind, false) # 排雷玩法 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完 func request_dig(chain_size: int, clears_map: bool) -> bool: if phase != PHASE_ACTIVE: _reject("phase") return false return _spend_and_arbitrate("dig", clears_map) func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool: var cost := _cost(action_kind) if _ap < cost: _reject("resource_face") return false _ap -= cost if clears_map: # cleared 胜出(同帧仲裁 cleared > ap_exhausted);剩余 AP 当场清零作废、不进 RAP(EC7/EC10) _ap = 0 _finish_round("cleared", "new_board") elif _ap == 0: # AP 归 0 瞬间轮次结束——不存在 0-AP 操作窗口 _finish_round("ap_exhausted", "same_board") return true # 停手:剩余 AP 1:1 转存 RAP;行动经济 直发 stopped 路径 round_end(双发射源) func request_stop() -> bool: if phase != PHASE_ACTIVE: _reject("phase") return false _rap += _ap _ap = 0 _finish_round("stopped", "same_board") round_end_stopped.emit({"path_tag": "stopped", "continuation_hint": "same_board"}) return true # 轮内刷新(2026-09-12 方案 A 用户拍板;等待期通道 2026-09-15 用户拍板整体移除):唯一入口 = # Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算 # (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。 func request_refresh() -> bool: if phase != PHASE_ACTIVE: _reject("phase") return false if _rap < db.action_economy.rap_refresh_cost: return false # EC13 资源不足拒绝——合法相位内资源拒绝,非编排错误 _rap -= db.action_economy.rap_refresh_cost return true # 战斗结算 回发:结算完成(仅记录编排意图;phase 停留 Waiting——AC-14 门不被绕过) func notify_settlement_done(next_round_mode: String) -> bool: if phase != PHASE_WAITING: _reject("phase") return false _settlement_consumed = true _next_round_mode = next_round_mode return true # 下轮行动点修正排队(格面资源八型批 2026-09-20):action 型正负行结算期调用,同轮多次授予累加; # 相位无关(授予发生在结算段,此时本类可能处于 ACTIVE/WAITING 任一相位) func queue_ap_delta(delta: int) -> void: _pending_ap_delta += delta func pending_ap_delta() -> int: return _pending_ap_delta func notify_round_start() -> bool: if phase == PHASE_IDLE: pass # 战斗首轮唯一出口 elif phase == PHASE_WAITING and _settlement_consumed: pass else: _reject("phase") return false _ap = maxi(0, db.action_economy.ap_per_round + _pending_ap_delta) # 每轮基线 + 排队修正(下限 0) _pending_ap_delta = 0 _phase = PHASE_ACTIVE _settlement_consumed = false return true func notify_combat_end() -> bool: if phase != PHASE_WAITING: _reject("phase") return false _phase = PHASE_IDLE _rap = 0 # RAP 战斗域清零(用户拍板;HUD框架可见反馈义务) _ap = 0 return true func _finish_round(path_tag: String, continuation_hint: String) -> void: # 实现层断言兜底(行动经济 轮次结束仲裁表):耗尽轮剩余恒为 0;清图轮剩余已当场清零(不进储备池)。 # 两断言守在唯一仲裁出口,未来若在扣费与仲裁之间挪动 AP 写入在此红灯。 assert(path_tag != "ap_exhausted" or _ap == 0, "耗尽轮剩余行动点恒为 0(实现层断言兜底)") assert(path_tag != "cleared" or _ap == 0, "清图轮剩余行动点当场清零(不进储备池)") _last_round_end = {"path_tag": path_tag, "continuation_hint": continuation_hint} _phase = PHASE_WAITING func _reject(source: String) -> void: push_warning("[行动经济] 动作被拒(%s 相位守卫/资源)" % source if source == "phase" else "[行动经济] 资源不足拒绝")