Files
gplan-2/game/core/action_economy.gd
T
DKChan 63a08d1e01 feat(t1-t3): 魔法道具数据面+库存管线+授予路由生命周期(三卡一轮交付——文件交叉合并提交)
T1 数据面:AuxItemTable schema(七字段+retired/deferred 两 additive 标记+真视窄域/board_wide 双承重护栏)+六行 tres(三有效+罗盘 retired 留痕+显影尘/全图卷 deferred)+ConfigDB 加载/访问器+RewardTable 十型扩值(aux_item)+三投放行 5/4/1+aux_none 负占位行(雷格极性字典兜底零兑付)+双表悬空校验(reward_dangling_aux,负占位豁免)+F2 投放维度覆盖(豁免面 plan/info/counter 登记)+两 fatal 夹具(expect=1 复制真表注入违规)+run_all manifest+2
T2 core 骨架:AuxItems 类(api 8 入口+probe 探针;零随机零特判;Used 不可逆;真视恒可用/预读点击级零信息拒绝;3x3 边缘窗口∩板)+action_economy.grant_ap 第 9 API(当轮加量,区别 queue_ap_delta 排队)
T3 授予路由+生命周期:_grant_routed aux 分支(正=入库存发「获得」行;负=零兑付 effect 型「道具随雷沉没」行)+use_aux 四步受理(相位 gate→validate→逐 kind 空间先 AP 后→consume;结算明细无道具行)+叠加层三清(装板点/战斗结束)+LevelRunner 关卡域库存持有注入+关卡末 reset+aux_cleared 快照计数扩签
冒烟锚随词表扩值更新:九型→十型封闭/字典 18→22 行;负向字典扫描三处稳健化(aux 不入五型轴+break 改 percent_map 三型齐备+rng 每板新实例——原第一板 break 掩盖 set_master_seed _counter 守卫违例)
闸:冒烟 577/0(净增 1)+单测 11 套全过(含两新夹具)+四探针 PASS
2026-09-29 07:07:38 +08:00

182 lines
6.8 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 任一相位)
# 第 9 API(registry 行动经济×① 接口漂移预授权——迭代 7 T2 魔法道具内容批落位):ap_grant 道具的
# 当轮行动点加量(GDD「加行动点效果给当轮」——区别于 queue_ap_delta 的下轮排队语义)。Active 受理、
# 无上限域、零随机;使用道具本身零扣费(成本在获取时已付——捡道具格不出战斗奖励)。
func grant_ap(amount: int) -> bool:
if amount <= 0 or _phase != PHASE_ACTIVE:
_reject("grant_ap")
return false
_ap += amount
return true
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:
if source == "phase":
push_warning("[行动经济] 动作被拒(%s 相位守卫/资源)" % source)
else:
push_warning("[行动经济] 资源不足拒绝")