fix(review-batch3): 类型收紧生产清零137处(P1-2三步+延伸)+autoload class_name(ConfigDb/RngServiceCore)与_init标注(P2-1)+validate_skill_pool_links/ShopTable标注(P2-4)+相位enum外String面(P2-2)+typed-rows探针实证.tres装载转换;smoke缝合20处rows.assign+str(bb)同语义;审计生产双归零(240→103/83→59全测试链),单测7套绿,冒烟576/0同基线
This commit is contained in:
1 parent
f5edb65d25
commit
104cea6461
46 files changed
+233
-218
No files matched your search
+29
-16
@@ -13,19 +13,23 @@ const PHASE_IDLE := "Idle"
|
||||
const PHASE_ACTIVE := "Active"
|
||||
const PHASE_WAITING := "Waiting"
|
||||
|
||||
var db
|
||||
# 相位轴(P6 状态机——code-review 2026-09-29 P2-2):内部 enum + match,外部 phase getter 保持
|
||||
# String 面(presentation_snapshot 与冒烟链的 "Idle"/"Active"/"Waiting" 对照契约零变化)。
|
||||
enum Phase { IDLE, ACTIVE, WAITING }
|
||||
|
||||
var db: ConfigDb
|
||||
# 相位与账本只读出口(E5 封装收口 2026-09-13):外部读取经 getter-only 属性(架构文档
|
||||
# "ap/rap/phase 只读" 的机制化),写入只经 notify_* / request_* 方法通道。
|
||||
var _phase: String = PHASE_IDLE
|
||||
var _phase: int = Phase.IDLE
|
||||
var phase: String:
|
||||
get: return _phase
|
||||
get: return _phase_text(_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[String, String] = {} # {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
|
||||
@@ -33,10 +37,19 @@ var _next_round_mode := "" # 调试观测量(同板续轮 /
|
||||
var _pending_ap_delta := 0 # 下轮行动点修正排队(格面资源八型批 2026-09-20——action 型 ±1 排队,轮开始兑现后清零)
|
||||
|
||||
|
||||
func _init(db_ref) -> void:
|
||||
func _init(db_ref: ConfigDb) -> void:
|
||||
db = db_ref
|
||||
|
||||
|
||||
func _phase_text(p: int) -> String:
|
||||
match p:
|
||||
Phase.ACTIVE:
|
||||
return PHASE_ACTIVE
|
||||
Phase.WAITING:
|
||||
return PHASE_WAITING
|
||||
return PHASE_IDLE
|
||||
|
||||
|
||||
func _cost(kind: String) -> int:
|
||||
var ae = db.action_economy
|
||||
match kind:
|
||||
@@ -54,7 +67,7 @@ func _cost(kind: String) -> int:
|
||||
|
||||
# 通用扣减(排雷玩法 用于旗动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数)
|
||||
func try_spend(action_kind: String) -> bool:
|
||||
if phase != PHASE_ACTIVE:
|
||||
if _phase != Phase.ACTIVE:
|
||||
_reject("phase")
|
||||
return false
|
||||
return _spend_and_arbitrate(action_kind, false)
|
||||
@@ -62,7 +75,7 @@ func try_spend(action_kind: String) -> bool:
|
||||
|
||||
# 排雷玩法 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完
|
||||
func request_dig(chain_size: int, clears_map: bool) -> bool:
|
||||
if phase != PHASE_ACTIVE:
|
||||
if _phase != Phase.ACTIVE:
|
||||
_reject("phase")
|
||||
return false
|
||||
return _spend_and_arbitrate("dig", clears_map)
|
||||
@@ -86,7 +99,7 @@ func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool:
|
||||
|
||||
# 停手:剩余 AP 1:1 转存 RAP;行动经济 直发 stopped 路径 round_end(双发射源)
|
||||
func request_stop() -> bool:
|
||||
if phase != PHASE_ACTIVE:
|
||||
if _phase != Phase.ACTIVE:
|
||||
_reject("phase")
|
||||
return false
|
||||
_rap += _ap
|
||||
@@ -100,7 +113,7 @@ func request_stop() -> bool:
|
||||
# Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算
|
||||
# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。
|
||||
func request_refresh() -> bool:
|
||||
if phase != PHASE_ACTIVE:
|
||||
if _phase != Phase.ACTIVE:
|
||||
_reject("phase")
|
||||
return false
|
||||
if _rap < db.action_economy.rap_refresh_cost:
|
||||
@@ -111,7 +124,7 @@ func request_refresh() -> bool:
|
||||
|
||||
# 战斗结算 回发:结算完成(仅记录编排意图;phase 停留 Waiting——AC-14 门不被绕过)
|
||||
func notify_settlement_done(next_round_mode: String) -> bool:
|
||||
if phase != PHASE_WAITING:
|
||||
if _phase != Phase.WAITING:
|
||||
_reject("phase")
|
||||
return false
|
||||
_settlement_consumed = true
|
||||
@@ -130,25 +143,25 @@ func pending_ap_delta() -> int:
|
||||
|
||||
|
||||
func notify_round_start() -> bool:
|
||||
if phase == PHASE_IDLE:
|
||||
if _phase == Phase.IDLE:
|
||||
pass # 战斗首轮唯一出口
|
||||
elif phase == PHASE_WAITING and _settlement_consumed:
|
||||
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
|
||||
_phase = Phase.ACTIVE
|
||||
_settlement_consumed = false
|
||||
return true
|
||||
|
||||
|
||||
func notify_combat_end() -> bool:
|
||||
if phase != PHASE_WAITING:
|
||||
if _phase != Phase.WAITING:
|
||||
_reject("phase")
|
||||
return false
|
||||
_phase = PHASE_IDLE
|
||||
_phase = Phase.IDLE
|
||||
_rap = 0 # RAP 战斗域清零(用户拍板;HUD框架可见反馈义务)
|
||||
_ap = 0
|
||||
return true
|
||||
@@ -160,7 +173,7 @@ func _finish_round(path_tag: String, continuation_hint: String) -> void:
|
||||
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
|
||||
_phase = Phase.WAITING
|
||||
|
||||
|
||||
func _reject(source: String) -> void:
|
||||
|
||||
@@ -66,7 +66,7 @@ var _base_override := {"atk": 0, "max_hp": 0, "armor_class": 0}
|
||||
var _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(元进度实现批——冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板)
|
||||
|
||||
|
||||
func _init(db_ref, rng_ref) -> void:
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
db = db_ref
|
||||
rng = rng_ref
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ const SOURCE_EXEMPT_TYPES := ["decay", "verdict"]
|
||||
# 行来源断言豁免集合(来源类别层)——事实来源是单位 / 清图而非棋盘格
|
||||
const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward"]
|
||||
|
||||
var db
|
||||
var rng
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
# E1 封装收口(2026-09-13):协作对象 board_generator/enemy_system/status_effects/reward_economy 私有化——跨层读取一律经 presentation_snapshot()/
|
||||
# diagnostic_snapshot() 成文出口(呈现层链式穿透 settlement._enemy_system.hero_unit() 的根因收口)。
|
||||
# minesweeper/action_economy 保持公有 = 架构文档 API Boundaries 的 HUD框架消费面(意图提交 settle_batch + compute_move_path
|
||||
@@ -52,13 +52,13 @@ var _status_effects: StatusContainer
|
||||
var _reward_economy: RewardEconomy
|
||||
var _damage: DamageController # 伤害控制器(伤害管线单据化批 2026-09-24——单据唯一兑付管线)
|
||||
|
||||
var encounter: Dictionary = {}
|
||||
var encounter: Dictionary[String, Variant] = {}
|
||||
var round_index := 0
|
||||
var battle_result := "" # "" / "victory" / "defeat" / "fatal_error"
|
||||
var escrow := 0 # 战斗内 GP 缓冲(战斗域中间态——begin_battle 清零;gp_gain/gp_loss 兑付累记;随 gp_dropped 上抛即弃,不入钱包)
|
||||
|
||||
var _facts_reveals: Array = []
|
||||
var _facts_flags: Array = []
|
||||
var _facts_reveals: Array[Dictionary] = []
|
||||
var _facts_flags: Array[Dictionary] = []
|
||||
var _facts_path := ""
|
||||
var _settling := false
|
||||
var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_cap_per_round)
|
||||
@@ -71,7 +71,7 @@ var _perfect_hit_false := 0
|
||||
var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变)
|
||||
|
||||
|
||||
func _init(db_ref, rng_ref) -> void:
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
db = db_ref
|
||||
rng = rng_ref
|
||||
_board_generator = BoardGenerator.new()
|
||||
@@ -94,7 +94,7 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
if round_index != 0:
|
||||
push_warning("[战斗结算] begin_battle 重复受理")
|
||||
return false
|
||||
encounter = enc
|
||||
encounter.assign(enc)
|
||||
escrow = 0 # 战斗域零 GP 持久(GP+商店房实施批——每场战斗缓冲从零起算)
|
||||
_damage.set_hero_ac_override(int(enc.get("hero_armor_class", -1))) # 主角护甲合流入参(伤害控制器 defender_ac 消费——口径同原 _defender_ac)
|
||||
db.set_reload_gate(true)
|
||||
@@ -150,7 +150,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
var r0 := {}
|
||||
for c in board.compute_r0():
|
||||
r0[c] = true
|
||||
var eligible: Array = []
|
||||
var eligible: Array[Dictionary] = []
|
||||
for c in board.safe_cells():
|
||||
if not r0.has(c):
|
||||
eligible.append({"cell": c})
|
||||
@@ -189,7 +189,7 @@ func _place_variants(board: BoardData) -> void:
|
||||
var r0 := {}
|
||||
for c in board.compute_r0():
|
||||
r0[c] = true
|
||||
var candidates: Array = []
|
||||
var candidates: Array[Vector2i] = []
|
||||
for c in board.safe_cells():
|
||||
if not r0.has(c):
|
||||
candidates.append(c)
|
||||
@@ -203,7 +203,7 @@ func _place_variants(board: BoardData) -> void:
|
||||
var placed := {}
|
||||
while placed.size() < total:
|
||||
var pack_size: int = mini(total - placed.size(), stream.randi_range(2, int(db.variation.pack_max)))
|
||||
var free: Array = []
|
||||
var free: Array[Vector2i] = []
|
||||
for c in candidates:
|
||||
# 新片种子/生长格禁与其他已布片 8 邻贴接——否则两片粘连成一个超 pack_max 的连通分量
|
||||
# (R3 随机流下实测暴露:独立生长的相邻片合流击穿单片上限断言)
|
||||
@@ -217,7 +217,7 @@ func _place_variants(board: BoardData) -> void:
|
||||
var pack: Array = [seed_c]
|
||||
var pack_set := {seed_c: true}
|
||||
while pack.size() < pack_size:
|
||||
var fringe: Array = []
|
||||
var fringe: Array[Vector2i] = []
|
||||
for c in pack:
|
||||
for n in board.neighbors8(c):
|
||||
if candidates.has(n) and not placed.has(n) and not fringe.has(n) \
|
||||
@@ -628,10 +628,10 @@ func _emit_composition_effects(side: String, ri: int) -> void:
|
||||
# ---- 结算快照(playtest 数值分析数据面——每次结算全量状态;运行时零决策读取)----
|
||||
|
||||
func _emit_settlement_snapshot(verdict: String) -> void:
|
||||
var enemies: Array = []
|
||||
var enemies: Array[Dictionary] = []
|
||||
for u in _enemy_system.query_alive("enemy"):
|
||||
enemies.append({"id": String(u.enemy_id), "hp": int(u.hp), "max_hp": int(u.max_hp), "atk": int(u.atk), "pending_skill": _enemy_system.pending_action(u), "armor": String(u.get("armor_type", "light")), "armor_class": int(u.get("armor_class", 0))})
|
||||
var buffs: Array = []
|
||||
var buffs: Array[Dictionary] = []
|
||||
for eff in _status_effects.query_active("hero"):
|
||||
buffs.append({"id": eff.effect_id, "side": "hero", "layers": eff.layers, "left": eff.remaining_settlements})
|
||||
for eff in _status_effects.query_active("enemy"):
|
||||
@@ -746,10 +746,10 @@ func _roll_gp_drop() -> int:
|
||||
# (敌人技能池批批 2 定稿:beat/ring_size/ring_index 拆除 → pending_* 三字段 + 敌方增益 effects 投影;池与权重不公开;
|
||||
# 八型批 2026-09-20:ambush_layers 拆除——伏击退役,警觉并入 effects 投影)hero_effects[](增益状态 query_active 投影)
|
||||
func presentation_snapshot() -> Dictionary:
|
||||
var enemies: Array = []
|
||||
var enemies: Array[Dictionary] = []
|
||||
var all: Array = _enemy_system.all_enemies()
|
||||
# 敌方增益投影(side-wide 状态——蓄力/硬化/削弱/暴走 percent 行 + 警觉 ac 行,八型批 2026-09-20)
|
||||
var enemy_effects: Array = []
|
||||
var enemy_effects: Array[Dictionary] = []
|
||||
for eff in _status_effects.query_active("enemy"):
|
||||
var erow: Dictionary = db.status_effect_row(eff.effect_id)
|
||||
if not erow.is_empty() and String(erow.unit) in ["percent", "ac"]:
|
||||
|
||||
@@ -49,7 +49,7 @@ func has_variant(c: Vector2i, vid: String) -> bool:
|
||||
|
||||
|
||||
func neighbors8(c: Vector2i) -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Vector2i] = []
|
||||
for dy in range(-1, 2):
|
||||
for dx in range(-1, 2):
|
||||
if dx == 0 and dy == 0:
|
||||
@@ -61,7 +61,7 @@ func neighbors8(c: Vector2i) -> Array:
|
||||
|
||||
|
||||
func spawn_cells() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Vector2i] = []
|
||||
for dy in range(-1, 2):
|
||||
for dx in range(-1, 2):
|
||||
var c := Vector2i(spawn_center.x + dx, spawn_center.y + dy)
|
||||
@@ -71,7 +71,7 @@ func spawn_cells() -> Array:
|
||||
|
||||
|
||||
func all_cells() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Vector2i] = []
|
||||
for y in height:
|
||||
for x in width:
|
||||
out.append(Vector2i(x, y))
|
||||
@@ -79,7 +79,7 @@ func all_cells() -> Array:
|
||||
|
||||
|
||||
func safe_cells() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Vector2i] = []
|
||||
for c in all_cells():
|
||||
if is_safe(c):
|
||||
out.append(c)
|
||||
|
||||
@@ -83,7 +83,7 @@ func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> Boar
|
||||
var spawn := {}
|
||||
for c in b.spawn_cells():
|
||||
spawn[c] = true
|
||||
var domain: Array = []
|
||||
var domain: Array[Vector2i] = []
|
||||
for c in b.all_cells():
|
||||
if not spawn.has(c):
|
||||
domain.append(c)
|
||||
@@ -198,7 +198,7 @@ func measure_inferability(b: BoardData) -> Dictionary:
|
||||
|
||||
|
||||
func _build_constraints(b: BoardData, r: Dictionary, known_mines: Dictionary, safe_set: Dictionary) -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for c in r.keys():
|
||||
if not safe_set.has(c):
|
||||
continue # 只对已开安全格的数字建约束
|
||||
@@ -273,7 +273,7 @@ func legality(b: BoardData, params: Dictionary, db) -> bool:
|
||||
|
||||
|
||||
func _legality_faults(b: BoardData, params: Dictionary) -> Array:
|
||||
var faults: Array = []
|
||||
var faults: Array[String] = []
|
||||
# ① 出生区无雷 ② 出生区∪R₀ 无变异 ③ 数字恒真 ④ R₀ ≠ S(排雷玩法 修订义务)
|
||||
for c in b.spawn_cells():
|
||||
if b.is_mine(c):
|
||||
|
||||
+12
-11
@@ -4,6 +4,7 @@
|
||||
# ConfigDB autoload(数据配置)——数值表单点访问入口(Rule 2)+ 校验双通道(Rule 6)+ R1 行解析。
|
||||
# 实现 GDD:design/archive/gdd-2026-09-14/d2-data-config.md(系统 ID D2·数据配置——registry 追溯锚,命名规范见 CLAUDE.md)
|
||||
# 切片注:headless --script 模式下由冒烟脚本显式实例化(数据配置 AC 前言);玩法代码禁止绕过本单点自建 load()。
|
||||
class_name ConfigDb
|
||||
extends Node
|
||||
|
||||
const SCHEMA_VERSION := 1
|
||||
@@ -36,16 +37,16 @@ var reward: RewardTable
|
||||
var settlement: SettlementTable
|
||||
var level: LevelTable
|
||||
var difficulty: DifficultyTable
|
||||
# GP+商店房实施批(2026-09-21):ShopTable 暂持无类型注记——schema class_name 落籍归并行数据面,
|
||||
# 落籍后可收紧为 `var shop: ShopTable`(消费面一律走下方 shop_* 访问器,禁直读行集)
|
||||
var shop = null
|
||||
# GP+商店房实施批(2026-09-21):ShopTable 类型化持用(schema class_name 已落籍——code-review 2026-09-29 P2-4 收口;
|
||||
# 消费面一律走下方 shop_* 访问器,禁直读行集)
|
||||
var shop: ShopTable = null
|
||||
# 元进度实现批(2026-09-21):MetaTable 类型化持用(schema 与表同批落籍,无 shop 先例的落籍间隙问题)
|
||||
var meta: MetaTable
|
||||
var adventure: AdventureTable
|
||||
|
||||
var is_ready := false
|
||||
var _diagnostics: Array = [] # {severity, category, message}
|
||||
var _warn_dedup: Dictionary = {} # R1 四元组去重注册表
|
||||
var _diagnostics: Array[Dictionary] = [] # {severity, category, message}
|
||||
var _warn_dedup: Dictionary[String, bool] = {} # R1 四元组去重注册表
|
||||
var _reload_gate := false # 数据配置 EC8——战斗结算 唯一调用方
|
||||
|
||||
|
||||
@@ -170,7 +171,7 @@ func _cross_validate(loaded: Dictionary) -> void:
|
||||
for lrow in lv.rows:
|
||||
var li: int = int(lrow.level_index)
|
||||
# 模板池感知(关卡节点图生成 v0.4——池内全模板跨表校验,不只 row.nodes)
|
||||
var node_lists: Array = []
|
||||
var node_lists: Array[Array] = []
|
||||
var tpls: Array = lrow.get("templates", [])
|
||||
if tpls.is_empty():
|
||||
node_lists.append(lrow.get("nodes", []))
|
||||
@@ -199,8 +200,8 @@ func _cross_validate(loaded: Dictionary) -> void:
|
||||
# proposal-status-vocabulary §1.2)+ 施放类 side 守卫(增益引用 enemy 侧 / 妨害引用 hero 侧状态行)。
|
||||
# status_table 显式入参:装载序上本校验先于成员赋值执行(self.status_effects 尚为 Nil)——装载通道传 loaded 表,
|
||||
# 冒烟直验传 _db.status_effects;传 null 时 side 守卫跳过(其他校验不受影响)。
|
||||
func validate_skill_pool_links(skill_table, enemy_table, status_table = null) -> Array:
|
||||
var errs: Array = []
|
||||
func validate_skill_pool_links(skill_table: SkillTable, enemy_table: EnemyTable, status_table: StatusEffectTable = null) -> Array:
|
||||
var errs: Array[Dictionary] = []
|
||||
var skill_types := {}
|
||||
for row in skill_table.rows:
|
||||
skill_types[String(row.get("skill_id", ""))] = String(row.get("type", ""))
|
||||
@@ -267,7 +268,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String) -> Diction
|
||||
if L < 1 or D < 0:
|
||||
_fatal("request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D])
|
||||
return {}
|
||||
var level_keys: Array = []
|
||||
var level_keys: Array[int] = []
|
||||
for row in rows:
|
||||
var lv := int(row.get("level_index", 0))
|
||||
if not level_keys.has(lv):
|
||||
@@ -280,7 +281,7 @@ func _resolve_progress(rows: Array, L: int, D: int, table_id: String) -> Diction
|
||||
var l_clamped := l_star < 0
|
||||
if l_clamped:
|
||||
l_star = int(level_keys[-1]) # 超出最大行 → 取最大行封顶(2026-09-18 追加拍板:可取范围内向上取整,超出用最大值)
|
||||
var depth_keys: Array = []
|
||||
var depth_keys: Array[int] = []
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star:
|
||||
var dv := int(row.get("node_depth", 0))
|
||||
@@ -328,7 +329,7 @@ func _ceil_key(sorted_keys: Array, request: int) -> int:
|
||||
# board_type 词表——biome_tags 系旧观念残留退役)----
|
||||
|
||||
func variant_pool(level_index: int, board_type: String) -> Array:
|
||||
var pool: Array = []
|
||||
var pool: Array[Dictionary] = []
|
||||
for row in variation.rows:
|
||||
if int(row.get("unlock_level", 999)) > level_index:
|
||||
continue
|
||||
|
||||
@@ -48,14 +48,14 @@ const TARGET_FRONT_ALIVE := "front_alive_enemy"
|
||||
const TARGET_HERO := "hero"
|
||||
const TARGET_ALL_ALIVE := "all_alive_enemies"
|
||||
|
||||
var db
|
||||
var db: ConfigDb
|
||||
var rng
|
||||
var units # UnitContainer——目标解析/伤害交付/存活查询(句柄即 Dictionary 引用)
|
||||
var effects # StatusContainer——池值/AC 型效果读取(增益状态 只读消费面)
|
||||
var emit_row_cb: Callable # 行协议注入口:func(type, source_class, origin_cell, ri, display, value, extra, highlight)
|
||||
|
||||
var policy := POLICY_SEGMENT # 结账时机开关(2026-09-24 用户拍板第二步拨 B=攒批段界统一结;回切 A 改回 POLICY_IMMEDIATE 即还原迁移前行为)
|
||||
var _queue: Array = [] # 未结单据(B 模式攒批;A 模式恒空)
|
||||
var _queue: Array[Dictionary] = [] # 未结单据(B 模式攒批;A 模式恒空)
|
||||
var _outcomes := {} # ticket_id → "redeemed"/"zero"/"voided"(结账后可查——清图高光份数消费)
|
||||
var _seq := 0 # 流水号
|
||||
var _halted := false # 主角倒下终止旗(死亡事件置位——flush 余单作废)
|
||||
|
||||
+13
-13
@@ -40,9 +40,9 @@ signal board_changed()
|
||||
signal level_finished(payload: Dictionary) # {result, hp, node_index, node_id, node_type}
|
||||
signal wallet_changed(event: String, gp: int, extra: Dictionary) # GP 钱包事件投影:gain{amount}/buy{product,price}/clear——试玩日志与商店覆盖层刷新源(GP+商店房实施批)
|
||||
|
||||
var db
|
||||
var rng
|
||||
var level_row: Dictionary = {}
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
var level_row: Dictionary[String, Variant] = {}
|
||||
var node_index := -1
|
||||
# ---- 账本载体(冒险结构实现批——漂移②清偿)----
|
||||
# 冒险模式 = AdventureRunner 共享字典(Dictionary 引用共享——hero_hp/gp 属性读写直接落冒险账本);
|
||||
@@ -68,12 +68,12 @@ var hero_hp_pct := 0
|
||||
var _running := false
|
||||
var level_result := "" # "" / "victory" / "defeat"
|
||||
var _awaiting_choice := false # 选路态(兑付确认后、pick 前为零自动推进的可见停点)
|
||||
var _candidates: Array = [] # 当前选路候选(节点引用数组)
|
||||
var visited: Array = [] # 已兑付/到达节点序列(呈现层图条数据源;含当前节点)
|
||||
var _candidates: Array[Dictionary] = [] # 当前选路候选(节点引用数组)
|
||||
var visited: Array[Dictionary] = [] # 已兑付/到达节点序列(呈现层图条数据源;含当前节点)
|
||||
var _graph_mode := false # 图形态缓存(行集任一节点带 next;单链 = false)
|
||||
|
||||
|
||||
func _init(db_ref, rng_ref) -> void:
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
db = db_ref
|
||||
rng = rng_ref
|
||||
|
||||
@@ -142,7 +142,7 @@ func start_level(level_index: int, seed_val: int, base_override_in: Dictionary =
|
||||
break
|
||||
hero_hp = effective_max_hp()
|
||||
rng.new_adventure(seed_val) # 关卡起点 = 冒险起点(切片口径;跨关接续归 冒险结构)
|
||||
level_row = _resolve_template_row(row, template_pick) # 池解析在播种后(node_map 抽签须已播种——EC7)
|
||||
level_row.assign(_resolve_template_row(row, template_pick)) # 池解析在播种后(node_map 抽签须已播种——EC7)
|
||||
_running = true
|
||||
var entry := _entry_node()
|
||||
if entry.is_empty():
|
||||
@@ -177,7 +177,7 @@ func start_level_external(level_index: int, ledger: Dictionary, bonus_ref: Dicti
|
||||
base_override = base_override_ref
|
||||
_shop_purchased = purchased_ref
|
||||
_rest_handler = rest_handler
|
||||
level_row = _resolve_template_row(row, template_pick)
|
||||
level_row.assign(_resolve_template_row(row, template_pick))
|
||||
node_index = -1
|
||||
level_result = ""
|
||||
_awaiting_choice = false
|
||||
@@ -286,11 +286,11 @@ func _out_ids(node: Dictionary) -> Array:
|
||||
|
||||
|
||||
# 出边候选(节点引用数组——选路口/呈现层共用读面)
|
||||
func _out_nodes(node: Dictionary) -> Array:
|
||||
func _out_nodes(node: Dictionary) -> Array[Dictionary]:
|
||||
var ids := {}
|
||||
for n in nodes():
|
||||
ids[_node_id(n)] = n
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for tid in _out_ids(node):
|
||||
if ids.has(tid):
|
||||
out.append(ids[tid])
|
||||
@@ -304,7 +304,7 @@ func next_candidates() -> Array:
|
||||
|
||||
# 当前选路候选编号表(pick_next_node 的合法入参域——编号空间解析单一出口)
|
||||
func next_candidate_ids() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[int] = []
|
||||
for c in _candidates:
|
||||
out.append(_node_id(c))
|
||||
return out
|
||||
@@ -326,7 +326,7 @@ func node_out_ids(node: Dictionary) -> Array:
|
||||
|
||||
# 已到节点编号序列(只读投影——编号空间解析单一出口)
|
||||
func visited_ids() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[int] = []
|
||||
for v in visited:
|
||||
out.append(_node_id(v))
|
||||
return out
|
||||
@@ -493,7 +493,7 @@ func close_wallet() -> void:
|
||||
|
||||
# 商店覆盖层数据源(只读投影——现价/已购档数在此现算,呈现层禁复算定价公式)
|
||||
func shop_products() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for prow in db.shop_products():
|
||||
out.append({
|
||||
"product_id": String(prow.get("product_id", "")),
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
class_name MetaProgression
|
||||
extends RefCounted
|
||||
|
||||
var db
|
||||
var save
|
||||
var db: ConfigDb
|
||||
var save: SaveManager
|
||||
|
||||
|
||||
func _init(db_ref, save_ref) -> void:
|
||||
func _init(db_ref: ConfigDb, save_ref: SaveManager) -> void:
|
||||
db = db_ref
|
||||
save = save_ref
|
||||
|
||||
@@ -83,7 +83,7 @@ func _workshop_progress(channel: String) -> int:
|
||||
|
||||
# 工坊六行只读投影(呈现层数据源):{item_id, display_name, threshold, unlocked, progress}
|
||||
func workshop_status() -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for row in db.meta_workshop_items():
|
||||
var iid := String(row.get("item_id", ""))
|
||||
out.append({
|
||||
@@ -98,7 +98,7 @@ func workshop_status() -> Array:
|
||||
|
||||
# 达标未解锁 → unlock_item + 写盘;返回新解锁 item_id 列(冒险收束后与营地打开时各调一次)
|
||||
func refresh_workshop_unlocks() -> Array:
|
||||
var new_unlocks: Array = []
|
||||
var new_unlocks: Array[String] = []
|
||||
for row in db.meta_workshop_items():
|
||||
var iid := String(row.get("item_id", ""))
|
||||
if save.item_unlocked(iid):
|
||||
@@ -118,7 +118,7 @@ func refresh_workshop_unlocks() -> Array:
|
||||
func settle_adventure(summary: Dictionary) -> Dictionary:
|
||||
# 首次冒险布尔位置位(关卡节点图生成 v0.4——完成/失败/放弃三结局同经本收束挂点,写入只在收束)
|
||||
save.mark_first_adventure_done()
|
||||
var drops: Array = []
|
||||
var drops: Array[Dictionary] = []
|
||||
for li in range(1, int(summary.get("levels_cleared", 0)) + 1):
|
||||
var first: bool = not save.boss_first_kill_marked(li)
|
||||
var row: Dictionary = db.meta_boss_keystone_row(li)
|
||||
|
||||
@@ -15,18 +15,18 @@ signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(
|
||||
|
||||
const SUB_REASONS := ["not_adjacent", "target_revealed", "target_concealed", "flag_present", "no_path", "out_of_catalog"] # flag_absent 随 flag_pull 退役(2026-09-19)
|
||||
|
||||
var db
|
||||
var action_economy
|
||||
var db: ConfigDb
|
||||
var action_economy: ActionEconomy
|
||||
# 棋盘状态私有(E2 封装收口 2026-09-13):对外只暴露 current_board()/reveal_record() 等查询面,
|
||||
# 禁止跨模块直读/直写原始字典——先例教训 = board_view 绕过查询面直读 revealed。
|
||||
var _board: BoardData = null
|
||||
var _revealed := {} # Vector2i → {"kind": "safe"/"mine", "digit": int}
|
||||
var _flags := {} # Vector2i → true(棋盘单态;跨轮保留随图存续)
|
||||
var _hero := Vector2i.ZERO
|
||||
var _spawn: Dictionary = {}
|
||||
var _spawn: Dictionary[Vector2i, bool] = {}
|
||||
|
||||
|
||||
func _init(db_ref, action_economy_ref) -> void:
|
||||
func _init(db_ref: ConfigDb, action_economy_ref: ActionEconomy) -> void:
|
||||
db = db_ref
|
||||
action_economy = action_economy_ref
|
||||
|
||||
@@ -125,7 +125,7 @@ func retire_board(during_round := false) -> Dictionary:
|
||||
|
||||
# 四不变式(诊断类别封闭枚举):inv1 出生区雷 / inv2a 出生区变异 / inv2b R₀ 变异 / inv3 数字恒真 / inv4 R₀=S
|
||||
func _install_invariant_faults(b: BoardData) -> Array:
|
||||
var faults: Array = []
|
||||
var faults: Array[String] = []
|
||||
var r0 := {}
|
||||
for c in b.compute_r0():
|
||||
r0[c] = true
|
||||
@@ -171,7 +171,7 @@ func _initial_expansion_order(b: BoardData) -> Array:
|
||||
# ---- 批次结算(唯一提交入口;单意图批 = 平凡形 ≡ 便捷 API)----
|
||||
|
||||
func settle_batch(intents: Array) -> Array:
|
||||
var outcomes: Array = []
|
||||
var outcomes: Array[Dictionary] = []
|
||||
if intents.is_empty():
|
||||
return outcomes
|
||||
if action_economy.phase != action_economy.PHASE_ACTIVE or _board == null:
|
||||
@@ -179,7 +179,7 @@ func settle_batch(intents: Array) -> Array:
|
||||
outcomes.append(_outcome(false, "rejected", "phase", ""))
|
||||
return outcomes
|
||||
# F4 语义序仲裁:C_dig(真模拟清图)→ C_stop → C_other;类内稳定序 = 到达序
|
||||
var indexed: Array = []
|
||||
var indexed: Array[Dictionary] = []
|
||||
for i in intents.size():
|
||||
indexed.append({"i": i, "intent": intents[i], "cls": _classify(intents[i])})
|
||||
indexed.sort_custom(func(a, b):
|
||||
@@ -395,7 +395,7 @@ func compute_move_path(target: Vector2i) -> Array:
|
||||
queue.append(n)
|
||||
if not prev.has(target):
|
||||
return []
|
||||
var path: Array = []
|
||||
var path: Array[Vector2i] = []
|
||||
var cur := target
|
||||
while cur != _hero:
|
||||
path.push_front(cur)
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
class_name RewardEconomy
|
||||
extends RefCounted
|
||||
|
||||
var db
|
||||
var rng
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
var _phase := "unrolled" # unrolled → ready → retired
|
||||
var _eligible: Array = [] # [{cell}] 行主序规范序
|
||||
var _eligible: Array[Dictionary] = [] # [{cell}] 行主序规范序
|
||||
var _mapping := {} # Vector2i → reward_id(正向行身份——格面资源类型由 cell_type 承载)
|
||||
var _power_roll := {} # Vector2i → 区间行 roll 值(身份批内追加取号)
|
||||
var _clear_power := -1 # 清图槽区间行 roll 值(-1 = 定值行)
|
||||
@@ -25,7 +25,7 @@ var _clear_granted := false
|
||||
var _clear_snapshot := {}
|
||||
|
||||
|
||||
func _init(db_ref, rng_ref) -> void:
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
db = db_ref
|
||||
rng = rng_ref
|
||||
|
||||
@@ -43,7 +43,7 @@ func roll_board(eligible: Array) -> bool:
|
||||
return false
|
||||
# 行主序规范化 + 去重(排序责任不外推给 战斗结算)
|
||||
var seen := {}
|
||||
var list: Array = []
|
||||
var list: Array[Dictionary] = []
|
||||
for e in eligible:
|
||||
var c: Vector2i = e.cell
|
||||
if seen.has(c):
|
||||
@@ -175,7 +175,7 @@ func polarity_row(cell_type: String, polarity: String) -> Dictionary:
|
||||
# ---- F6 当量供给(行动经济 条款①对照实物输入——全活行投影 + tier_min_buff)----
|
||||
|
||||
func equivalence_supply() -> Dictionary:
|
||||
var live: Array = []
|
||||
var live: Array[Dictionary] = []
|
||||
var tier_min_buff := 99
|
||||
for row in db.reward.rows:
|
||||
if int(row.weight) <= 0:
|
||||
@@ -206,7 +206,7 @@ func weighted_pick(rows: Array, r: int) -> String:
|
||||
|
||||
func _rollable_rows() -> Array:
|
||||
# 摇号池 = cell 池正向行 weight>0(负向行纯字典不消费权重;封锁格不在本函数职责域——清单由调用方裁剪)
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for row in db.reward.rows:
|
||||
if String(row.get("pool_tag", "")) == "cell" and String(row.get("polarity", "")) == "positive" and int(row.weight) > 0:
|
||||
out.append(row)
|
||||
@@ -214,7 +214,7 @@ func _rollable_rows() -> Array:
|
||||
|
||||
|
||||
func _pool_rows(pool_tag: String) -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for row in db.reward.rows:
|
||||
if row.pool_tag == pool_tag and int(row.weight) > 0:
|
||||
out.append(row)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
# splitmix64 finalizer 变体终末混淆,见 _mix64 注记;并流前分盐黄金向量随批作废,新向量按单盐重登记(scope 0-3 与
|
||||
# 旧 board_gen 向量逐位连续——单盐沿用原 board_gen 盐)。
|
||||
# 取号语义:全新冒险首个 open_stream = 全局 scope 0;事件日志逐次落盘 user://logs/rng_log_<master_seed>.jsonl。
|
||||
class_name RngServiceCore
|
||||
extends Node
|
||||
|
||||
# 用途标签封闭词表(并流前七流名全保留;新增用途走扩签通道 = 改代码 + 评审——清单封闭语义由「流」改「标签」)
|
||||
|
||||
@@ -33,8 +33,8 @@ const FORGE_ATTRS := ["max_hp", "atk", "armor_class"] # 属性锻造三档合
|
||||
|
||||
var _path: String # 存档文件路径(默认 user://save.json;冒烟注入隔离路径)
|
||||
var _read_only := false # 高版本拒读置位——write_to_disk 一律拒绝(契约 4 禁写)
|
||||
var _data: Dictionary = {} # 内存数据模型(_init 即内置默认;load 后为档案内容)
|
||||
var _migrations: Dictionary = {} # from_version → Callable(data)->data(升一版;生产链现为空——骨架先行)
|
||||
var _data: Dictionary[String, Variant] = {} # 内存数据模型(_init 即内置默认;load 后为档案内容)
|
||||
var _migrations: Dictionary[int, Callable] = {} # from_version → Callable(data)->data(升一版;生产链现为空——骨架先行)
|
||||
|
||||
|
||||
func _init(path: String = "user://save.json") -> void:
|
||||
@@ -44,7 +44,7 @@ func _init(path: String = "user://save.json") -> void:
|
||||
|
||||
# ---- 默认数据模型(新档合法态——全零;白板存档 = 完整可玩〔元进度 单局循环零依赖〕)----
|
||||
|
||||
func _default_data() -> Dictionary:
|
||||
func _default_data() -> Dictionary[String, Variant]:
|
||||
return {
|
||||
"version": SAVE_FORMAT_VERSION,
|
||||
"meta": {
|
||||
@@ -222,7 +222,7 @@ func _corrupt_fallback(reason: String) -> bool:
|
||||
|
||||
|
||||
# 迁移后/低版本档字段补齐(改版不弃档:缺新域按默认补齐,既有值保留)
|
||||
func _normalize(data: Dictionary) -> Dictionary:
|
||||
func _normalize(data: Dictionary) -> Dictionary[String, Variant]:
|
||||
var merged := _default_data()
|
||||
for key in data:
|
||||
merged[key] = data[key]
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
class_name StatusContainer
|
||||
extends RefCounted
|
||||
|
||||
var db
|
||||
var db: ConfigDb
|
||||
var _open := false
|
||||
var _instances: Array = [] # 插入序;元素 = {effect_id, layers, remaining, source_class, origin_cell, round_index, snap_m, snap_unit, snap_display}
|
||||
var _instances: Array[Dictionary] = [] # 插入序;元素 = {effect_id, layers, remaining, source_class, origin_cell, round_index, snap_m, snap_unit, snap_display}
|
||||
|
||||
|
||||
func _init(db_ref) -> void:
|
||||
func _init(db_ref: ConfigDb) -> void:
|
||||
db = db_ref
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ func _new_instance(effect_id: String, layers: int, dur: int, source: Dictionary,
|
||||
|
||||
# 只读投影(插入序稳定枚举;六归因字段 + display_name + 授予时快照)
|
||||
func query_active(side: String) -> Array:
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for inst in _instances:
|
||||
var row: Dictionary = db.status_effect_row(inst.effect_id)
|
||||
if row.is_empty():
|
||||
@@ -119,7 +119,7 @@ func decay_round() -> int:
|
||||
if not _open:
|
||||
return 0 # EC13 域闭兜底读法——容器恒空,无独立分支不诊断
|
||||
var k := 0
|
||||
var survivors: Array = []
|
||||
var survivors: Array[Dictionary] = []
|
||||
for inst in _instances:
|
||||
inst.remaining -= 1
|
||||
if inst.remaining <= 0:
|
||||
|
||||
@@ -20,18 +20,18 @@ extends RefCounted
|
||||
# 消费方 = 伤害控制器(怪死 → 其未结单据作废〔死亡即不动手〕;主角死 → 兑付终止旗)。
|
||||
signal unit_died(u: Dictionary)
|
||||
|
||||
var db
|
||||
var db: ConfigDb
|
||||
# 单位容器私有(E3 封装收口 2026-09-13):hero/enemies 原始数组不再外露——读取经 hero_unit()/
|
||||
# all_enemies() 投影面(死亡残位查询包含在内),写入只经 apply_damage/heal/set_pending_action/commit_action/
|
||||
# tick_cooldowns/spawn/despawn。
|
||||
# 句柄语义不变:投影返回的 Dictionary 引用即单位句柄(同 query_alive 先例)。
|
||||
var _hero: Dictionary = {} # {hp, max_hp, atk, is_hero: true}
|
||||
var _enemies: Array = [] # 行主序生成序;元素 = 单位 Dictionary
|
||||
var _hero: Dictionary[String, Variant] = {} # {hp, max_hp, atk, is_hero: true}
|
||||
var _enemies: Array[Dictionary] = [] # 行主序生成序;元素 = 单位 Dictionary
|
||||
var _spawned := false
|
||||
var _despawned := false
|
||||
|
||||
|
||||
func _init(db_ref) -> void:
|
||||
func _init(db_ref: ConfigDb) -> void:
|
||||
db = db_ref
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_override:
|
||||
for i in range(count):
|
||||
var eff_row_atk: int = enemy_atk_override if enemy_atk_override >= 0 else int(row.atk)
|
||||
# 实例池快照(proposal §2.2:开战把模板快照一份,冷却全零——与"生成拿到当时的表值快照"同一条快照纪律)
|
||||
var pool_snap: Array = []
|
||||
var pool_snap: Array[Dictionary] = []
|
||||
for entry in row.get("pool", []):
|
||||
pool_snap.append({
|
||||
"skill_id": String(entry.get("skill_id", "")),
|
||||
@@ -103,7 +103,7 @@ func query_alive(side: String) -> Array:
|
||||
return []
|
||||
if side == "hero":
|
||||
return [] if (_hero.is_empty() or not _hero.alive) else [_hero]
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for u in _enemies:
|
||||
if u.alive:
|
||||
out.append(u)
|
||||
@@ -118,7 +118,7 @@ func rollable_skills(u: Dictionary) -> Array:
|
||||
if u.is_empty() or _despawned or u.get("is_hero", false):
|
||||
_diag("rollable_skills 失效句柄")
|
||||
return []
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for e in u.pool:
|
||||
if int(e.cooldown_left) <= 0:
|
||||
out.append({"skill_id": String(e.skill_id), "weight": int(e.weight)})
|
||||
@@ -130,7 +130,7 @@ func special_skills(u: Dictionary) -> Array:
|
||||
if u.is_empty() or _despawned or u.get("is_hero", false):
|
||||
_diag("special_skills 失效句柄")
|
||||
return []
|
||||
var out: Array = []
|
||||
var out: Array[Dictionary] = []
|
||||
for e in u.pool:
|
||||
var srow: Dictionary = db.skill_row(String(e.skill_id))
|
||||
if not srow.is_empty() and String(srow.type) != "basic":
|
||||
|
||||
Reference in new issue
Block a user