feat(slice): 充能退役+board_type 词表定稿+06 待办两轮清理批(2026-09-19 用户逐项拍板)——含并行会话未提交工作一并入库
①充能退役批(用户终裁删除,结构批):
- 主角攻击回归单分量:battle_orchestrator 攻击段单分量化(payload 携带实际攻击类型键 + roll/ac/tier 掷点读数)、_charge_points/_charge_counts/_charge_component 与 round_facts 充能键全删
- DamageMath.charge_component → counter_component 改名(唯一消费方 = 奖励直伤道具的克制直伤,棋盘火力不受影响)
- settlement.tres charge_grants 键与供给注释删除;settlement_table schema 字段与校验删除
- 冒烟改形 298→293(净减 5 = 充能断言组退役):乘区接线探针单分量基线 100(原充电注入 102)、三分量断言改"类型键+掷点读数"、②′充能定义+②″改投桶探针整组退役、标中旗断言改正向兑付行、恢复点探针换种子 4242→42(原种子在新输出模型下恢复点前败北——探针目的 = 验回复公式,换种注释在案)
- 文档:战斗结算 v0.16(挖格/旗判定批充能条款删、退役注记替原供给表、乘区公式改攻击合计、调参表行删)、07 清单速览与③④观测项、README 三行退役注记、registry 四处(charge_grants 行删/ac_roll 范围/effect_pool_map Σ三分量→攻击合计/mechanics F6 改型)、难度曲线与Boss 联动表三行、01 核心循环数值语言句去充能
- 裁决依据:充能 ≠ 改变攻击类型(是在普攻外叠加穿刺/魔法分量);删除代价 = 混合甲战斗主角常态输出只吃一种克制(用户知情拍板);克制策略归冒险前配置(hero.attack_type 营地换型 + 未来冒险前参数面)
②board_type 词表定稿批(用户拍板:图级单值字段命名 board_type、玩家侧显示名「地图类型」、词表 {dungeon, underdark}):
- board_gen_table 域校验升级(非空 → 词表成员检查,BOARD_TYPE_VOCABULARY 常量);board_gen.tres 12 行单值化(L1 五行 dungeon、L2 七行 underdark——「第二关=幽暗地域」从文案变数据);board_generator 单值化;board_data.biome → board_type;config_db variant_pool 签名 Array → String(休眠无调用方)
- 棋盘生成.md 图类型节/调参表行/下游行、地图战斗资源.md 两处"词表已定稿"注记、registry 三处、experiment_density.gd 旧键同步;零行为变化(消费方全部休眠)
③board_data compute_r0 变异死分支删除(用户拍板——「带变异的 0 格不参与初始展开传播」从未触发,与排雷玩法展开实现口径归一;变异定稿批按正式语义重接)
④06 待办两轮清理(解决即移除新口径 + 用户逐项拍板):
- 第一轮 7 项:图版措辞对齐(battle.md"节点链"×6→"节点图"+冒险结构句+数据配置行)、轮内刷新读数核销(实现已在:编排器计数+AP 条"剩 N 次"文案;行动经济.md 补只读观测量句)、敌种线索键集核销(映射已类型级)、敌方攻击区间落文核销(字段表已在)、坍塌显形对位批次=表现层升级批(音频×2+战斗表现写入)、音量分工收口(入口归设置/生效归音频)、快照域进 Alpha 存档首批
- 第一轮改写:关卡表半边核销(level.tres 齐备)剩冒险表半边;变异条目死分支半边清偿
- 第一轮拍板登记:未开格类型微光方案(魔法紫/穿刺蓝/群伤红/治疗绿/增益黄,服务护甲克制规划;连带:不做清单 owner 修订合法/art-bible §4.6.3 对齐归美术批/探宝罗盘价值清空/色板检查归样张批/实施=表现层升级批切片默认不开)
- 第二轮:连锁溢出核销(满层拒绝+「溢出未生效」行即正式语义,零代码;并行会话同裁)、标错惩罚核销(极性矩阵已取代——标空=该安全格资源负向兑付)、监控阈值定案(07 新⑥⑦节:速杀预警 provisional 40% + 刷新监控日志先行阈值等基线——纯日志侧不影响玩法)、两关序列列入 Alpha 验收条件(切片期直入口可接受)、设置持久化独立存储(用户两分制:系统级设置一安装一份、删游戏才消失、不进存档;存档只存该档生涯数据——设置与本地化三处+存档序列化边界表补行+条目删除)、未开格微光并入预读边界+探宝罗盘两方案新立项(色光永久化路线 vs 黑帝斯塔罗牌式可选天赋路线,两案不互斥,归魔法道具内容重定批)、第二关"解锁新元素"删除、Alpha 全怪物技能池数值验证新立项(范围含池构成/权重/倍率/冷却/保底/EV 台阶/克制覆盖+倍率取整抖动复核+卓尔增益技与敌人系统文档 L2 行两缺漏复核;时点 Alpha 随验收重测同窗口)
- 过程更正:6-10 曾越权执行被用户指出,已全部回滚(四条目按 HEAD 原文恢复/伪"用户裁决"注记抹除/设置与存档两文档还原)后按真实拍板重落地;台账 77 立规——决策项必须逐项明确批准,推荐≠批准,严禁把建议写成"用户裁决"落文
⑤并行会话未提交工作一并入库(归属注明,均已通过冒烟):甲效矩阵用户调参批(settlement.tres 加法核矩阵 0/+1/−1、smoke 矩阵断言改形 300→298、enemy/hero.tres 批注)、普通策略档 v2 novice 推理画像(auto_player.gd +46 行/level_autoplay.gd——2026-09-19 用户拍板替代旧"无推理最近格"口径)、roadmap 收口纪律五步(+loop_dashboard 软纪律)、元进度定稿批文档、analyze_playtest.py 微调、并行会话 06 新增两提案条目(插旗不可逆/GP+商店房)
⑥验证:冒烟 293 项 0 失败(提交前新鲜复跑与在飞态一致);ui/click/layout/tune 四探针 exit=0;冒烟基线 300→298→293 的减项全部为断言组合并/退役,零失败记录
⑦未入库标旗:未跟踪文件维持未跟踪(美术清单×2、格面差异化提案、loop_dashboard 系列、config_fatal_probe.gd.uid);第 7 项 L2 池对比两候选缺漏(卓尔池增益技去留/敌人系统文档补 L2 行)待用户二轮定夺,条目在册
active.md 快照节 + 台账 72-79
This commit is contained in:
1 parent
03264bd953
commit
e66d1561bc
37 files changed
+398
-422
No files matched your search
@@ -47,6 +47,8 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/s
|
||||
|
||||
**日志合规(2026-09-12 用户要求)**:任何模拟运行与真人试玩均落全量 JSONL(user://logs/)——逐操作(op/cell/accepted/round)、逐结算明细行、轮次状态快照、battle_end、level_end。模拟(level_monte_carlo / 调整板批量模拟 / supply_scan)一文件多局,run 行携带种子/档位切分局次。
|
||||
|
||||
**试玩画像口径(2026-09-19 用户拍板固化——全项目试玩/扫描/面板模拟一律只用以下两档,随机策略口径退役)**:`policy="normal"` = **普通画像·基础定式**(单数字满足规则——满足即标/满足即开,无处可推盲挖边界格、不刷新;教学后正常玩家的下限近似);`policy="perfect"` = **完美画像**(single+subset 全推理零盲挖/卡死刷新——技能上界仪)。实现单一出口 = `smoke/auto_player.gd`(novice_action / perfect_action)经 `smoke/level_autoplay.gd`,调整板批量模拟与 level_monte_carlo 自动同口径;战斗层冒烟夹具与调试转储的 `choose_dig` 直调是机制测试夹具、不承载画像语义。分级依据与新旧读数(乱点 0%/5% → 基础定式 65%/90%)见 auto_player.gd 普通策略档节与台账 77。
|
||||
|
||||
单关诊断:`--script res://smoke/level_debug.gd -- [seed] [atk%] [hp%]`。
|
||||
战斗级工具(v1 遗产):`dump_battle.gd`(结算流水)/ `monte_carlo.gd`(战斗期望)/ `experiment_density.gd`(密度调参)。
|
||||
|
||||
@@ -243,14 +245,14 @@ Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://smoke/s
|
||||
| 设定 | 实现落点 |
|
||||
|---|---|
|
||||
| 敌人护甲类型:轻甲 / 魔法护甲 / 重甲 | EnemyTable 新字段 `armor_type`({light,ward,heavy},缺省 light 向后兼容)· 映射:goblin=light / bugbear=ward / ogre_chief=heavy(教学梯度候选,provisional)· B2.spawn 行值快照入单位字典 |
|
||||
| 伤害三段:①普通 ②穿刺 ③魔法(后两者由充能供给) | B1 战斗段主角先手攻击改三分量:普通 = atk×(1+attack_up)(**不吃克制**,attack_up 消费面不变);穿刺/魔法 = 充能点数(按攻击类型分桶)×克制(分量各自 floor 后相加);hero_attack 行携带 normal/pierce/magic 分量(P2 归因)。**攻击类型是伤害的属性轴,与来源无关**(2026-09-15 抽象分离批) |
|
||||
| 充能供给(奖励侧定义,可改) | SettlementTable 字段 `charge_grants`(触发源 → {attack_type, per_unit, cap}):现行 safe_cell(本轮开格,初始展开格不计 reward_eligible=false)→ 穿刺桶 1/格、单轮计数上限 8;flag_hit(首次标中雷,与 weaken 同键每雷实例一次去重)→ 魔法桶 2/雷。换桶/换量/换成增益或道具都只改本字典——攻击类型与矩阵不动 |
|
||||
| 伤害单分量(2026-09-19 充能退役) | ~~B1 三分量~~ 主角攻击 = 单一基础分量:量 = atk(表值)、类型 = hero.attack_type(营地换型 = 改字段);hero_attack 行携带实际攻击类型键 + roll/ac/tier 掷点读数(P2 归因)。**攻击类型是伤害的属性轴,与来源无关**(2026-09-15 抽象分离批纪律保留) |
|
||||
| ~~充能供给~~(2026-09-19 退役) | SettlementTable 字段 `charge_grants` 已删除——挖掘攒弹药的第二伤害供给取消,克制应对 = 冒险前配型(hero.attack_type)+ 棋盘火力(穿刺弹/魔法矢直伤道具) |
|
||||
| 克制矩阵:克制 ×150 / 中性 ×100 / 被克 ×50 | SettlementTable 新字段 `armor_effectiveness`({normal,pierce,magic}×{light,ward,heavy} percent,缺项回退 100)——刀/AOE/清图奖励**恒定表值不吃克制**(道具=万能稳定伤害,暂定待拍板) |
|
||||
| 标错旗 → 随机轻惩罚三选一(时效 1 结算) | `_apply_miss_backlash()`:① `enrage` 暴走(敌 +25% 进 enemy_output 池)② 敌方伏击 +2 层(复用 ambush 行)③ `off_guard` 失衡(我方 +25% 进 hero_defense 池 = 多挨打);新 B3 行 ×2(dur=1 与 weaken/ambush 同款先兑付后衰减);沿用 `_flag_miss_seen` 每雷格实例一次去重 |
|
||||
| 随机载体 | **零新流确定性派生**:(x×7 + y×13 + 板序号×5) mod 3——同种子可回放、不扰动既有三条封闭流取号序;正式侧候选 = RngService 新增封闭流(待拍板) |
|
||||
| 呈现 | 敌人卡环点行常驻护甲标签(轻甲/魔法护甲/重甲——克制对策可读前提)· 结算明细主角攻击行拆"(普通 X + 穿刺 Y + 魔法 Z)" · 反噬效果行入明细流 · settlement_snapshot 增 armor/charge 字段(JSONL 数值分析面) |
|
||||
|
||||
**数值全部 provisional 待拍板/调参**(登记于 enemy.tres / settlement.tres 注释):charge_grants(开格→穿刺桶 1/格 · 计数上限 8 · 标中旗→魔法桶 2/雷 · 无上限)· 矩阵 150/100/50 · enrage/off_guard 25% · 伏击 +2 层 · 护甲映射 goblin/bugbear/ogre——C3 调参时按 F6 供给平衡式复算(三段充能是新增伤害供给,裸装败北/基线通关率两条冒烟路径已复验仍成立)。
|
||||
**数值全部 provisional 待拍板/调参**(登记于 enemy.tres / settlement.tres 注释):~~charge_grants~~(2026-09-19 退役)· ~~矩阵 150/100/50~~(加法核 +1/0/−1 换装)· enrage/off_guard 25% · 伏击 +2 层 · 护甲映射 goblin/bugbear/ogre——C3 调参时按 F6 供给平衡式复算(三段充能是新增伤害供给,裸装败北/基线通关率两条冒烟路径已复验仍成立)。
|
||||
|
||||
**正式侧义务(GDD/registry 修订清单,待文档批)**:B2 GDD(EnemyTable armor_type 字段 + schema)、B1 GDD(F3 主角攻击三分量构成 + flag_miss 反噬编排 + 克制矩阵载体)、B3 GDD(六行 MVP 状态表 + source_class flag_miss 授予)、A2 OQ1(标错惩罚候选升级为三选一随机池 + 重复性口径=每雷格实例一次)、registry(enemy/settlement_table/status_effect 条目 + A3 条款②锚与条款③不等式重推导——标中旗收益从纯 weaken 变"weaken+魔法 充能")、C3(数值 provisional 接管)。lore 侧:`design/research/lore-nest-metaphor-2026-09-12.md`(穿刺=地利/魔法=封印暴露、反噬=封印失败——待新会话拍板后写回 game-concept)。
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ var _perfect_hit_false := 0
|
||||
var _enemy_cap := 0 # EnemyTable 行值快照(伏击 cap)
|
||||
var _m_amb := 0 # StatusEffectTable.ambush.magnitude 快照
|
||||
var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变)
|
||||
var _charge_counts: Dictionary = {} # 本轮充能计数 {奖励触发源: 计数}——投哪个攻击类型桶由 settlement.charge_grants(奖励定义)决定,本类不认识"开格=穿刺"这类绑定
|
||||
|
||||
|
||||
func _init(db_ref, rng_ref) -> void:
|
||||
@@ -209,7 +208,6 @@ func _settle() -> void:
|
||||
if not (snap is Dictionary):
|
||||
continue
|
||||
if evt.kind == "safe":
|
||||
_charge_counts["safe_cell"] = int(_charge_counts.get("safe_cell", 0)) + 1 # 充能计数(开安全格 = 奖励触发源;只计数,投哪个桶由 charge_grants 决定)
|
||||
_grant_routed(snap, "positive", "safe_cell", evt.cell, ri, "")
|
||||
elif evt.kind == "mine":
|
||||
_grant_routed(snap, "negative", "mine_hit", evt.cell, ri, "")
|
||||
@@ -313,10 +311,10 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri:
|
||||
_emit_row("reward_ineffective", source_class, cell, ri, "%s 未生效(无存活敌人)" % display, 0, {"batch_key": String(row.reward_id)}, highlight)
|
||||
return "zero"
|
||||
var atk_type: String = String(row.get("attack_type", ctype))
|
||||
var eff: int = _armor_effectiveness(atk_type, String(target.get("armor_type", "light")))
|
||||
var dealt: int = DamageMath.charge_component(power, eff)
|
||||
var delta: int = _counter_delta(atk_type, String(target.get("armor_type", "light")))
|
||||
var dealt: int = DamageMath.counter_component(power, delta)
|
||||
_enemy_system.apply_damage(target, dealt)
|
||||
_emit_row("damage", source_class, cell, ri, "%s → %s" % [display, String(target.display_name)], dealt, {"target": String(target.display_name), "attack_type": atk_type, "effectiveness": eff}, highlight)
|
||||
_emit_row("damage", source_class, cell, ri, "%s → %s" % [display, String(target.display_name)], dealt, {"target": String(target.display_name), "attack_type": atk_type, "effectiveness": delta}, highlight)
|
||||
return "redeemed"
|
||||
if ctype == "heal":
|
||||
if String(row.get("polarity", "positive")) == "negative":
|
||||
@@ -361,7 +359,6 @@ func _judge_flag(evt: Dictionary, ri: int) -> void:
|
||||
return # 去重跳过(资格以雷格实例为键——排雷玩法 诚实重复上报的对侧);零明细行
|
||||
_flag_consumed[cell] = true
|
||||
_perfect_hit_true += 1
|
||||
_charge_counts["flag_hit"] = int(_charge_counts.get("flag_hit", 0)) + 1 # 充能计数(标中旗 = 奖励触发源;每雷实例一次——忘拔旗不重复充能)
|
||||
var snap = _reward_economy.grant(cell)
|
||||
if not (snap is Dictionary):
|
||||
return
|
||||
@@ -441,43 +438,32 @@ func _roll_round_actions() -> void:
|
||||
func _combat_segment(ri: int) -> void:
|
||||
var L := _status_effects.pool_layers("ambush") # 授予段后的当轮值;池共享不消耗
|
||||
# ⓪ 主角先手攻击(基础攻击恒存在——2026-09-11 用户裁决;先于敌方行动;授予段负向直伤致死 → 致电短路不先手)。
|
||||
# 三段伤害构成(2026-09-12 用户拍板;2026-09-15 命名统一批 + 攻击类型/奖励抽象分离批):基础分量(atk,吃掷点,
|
||||
# 类型 = 主角 attack_type 表值——2026-09-18 营地扩展前置批:量与类型抽象分离,原硬编码 normal 退役;
|
||||
# attack_up 增益不再直乘本分量——2026-09-18 结构修复批改经 hero_offense 池乘三段合计,见池乘区注记)
|
||||
# + 穿刺/魔法(由充能供给——充能按攻击类型分桶,谁能充/充哪个桶归奖励定义 charge_grants)。攻击类型只管查甲效矩阵哪一行,与来源无关。
|
||||
# 护甲等级掷点批(2026-09-16):普通分量吃掷点判定(含暴击);穿刺/魔法充能分量与棋盘来源伤害不掷不折;
|
||||
# 单分量伤害构成(2026-09-19 充能退役批·用户拍板):主角攻击 = 唯一基础分量(atk,吃掷点含暴击;
|
||||
# 类型 = 主角 attack_type 表值——2026-09-18 营地扩展前置批:量与类型抽象分离;查甲效矩阵对应行取克制修正)。
|
||||
# 原"基础 + 穿刺/魔法充能三分量"模型随充能系统整体退役——克制策略归冒险前配置(营地换型 / 未来冒险前参数面),
|
||||
# 局内不再有挖掘攒弹药的第二伤害供给。attack_up 增益经 hero_offense 池乘攻击合计(见池乘区注记)。
|
||||
# 护甲等级掷点批(2026-09-16):基础分量吃掷点判定(含暴击);棋盘来源伤害不掷不折;
|
||||
# 任何攻击落地伤害 ≥ damage_floor(全局 max(1,·))——"被减伤归零 v=0"零值伤害行分支随批退役。
|
||||
var hero_alive: bool = int(_enemy_system.hero_unit().get("hp", 0)) > 0
|
||||
var hero_target = _enemy_system.default_target()
|
||||
if hero_alive and hero_target != null:
|
||||
var armor: String = String(hero_target.get("armor_type", "light"))
|
||||
var base_atk_type: String = String(_enemy_system.hero_unit().get("attack_type", "normal")) # 基础分量类型 = 主角表值(营地扩展前置批 2026-09-18)
|
||||
var points := _charge_points() # {攻击类型: 充能点数}——奖励定义投递的结果(本段不认识"开格/标雷")
|
||||
var roll_read: Dictionary = _armor_roll() # 基础分量掷点(每吃掷点分量逐次掷——掷点只吃基础分量口径不变)
|
||||
var threshold: int = _defender_ac(hero_target) + int(db.settlement.attack_bonus)
|
||||
var crit: bool = bool(roll_read.crit)
|
||||
var discount: int = _roll_discount(int(roll_read.roll), threshold)
|
||||
var base_val: float = float(_hero_atk) # 基础分量 = 表值攻击裸值(增益直乘旁路已退役——乘区统一走池)
|
||||
var comp: Dictionary = {}
|
||||
var d_hero := 0
|
||||
for atk_type in SettlementTable.ATTACK_TYPE_KEYS:
|
||||
var c: int = 0
|
||||
if atk_type == base_atk_type:
|
||||
c = DamageMath.rolled_component(base_val, discount, crit, int(db.settlement.crit_multiplier), _armor_effectiveness(atk_type, armor))
|
||||
c += _charge_component(int(points.get(atk_type, 0)), atk_type, armor) # 同桶充能照加(充能分量不掷不折口径不变)
|
||||
else:
|
||||
c = _charge_component(int(points.get(atk_type, 0)), atk_type, armor)
|
||||
comp[atk_type] = c
|
||||
d_hero += c
|
||||
var d_hero: int = DamageMath.rolled_component(base_val, discount, crit, int(db.settlement.crit_multiplier), _counter_delta(base_atk_type, armor))
|
||||
var payload: Dictionary = {"target": hero_target.display_name}
|
||||
payload.merge(comp)
|
||||
payload[base_atk_type] = d_hero # 分量键 = 实际攻击类型(归因字段,单分量模型)
|
||||
payload["roll"] = int(roll_read.roll)
|
||||
payload["ac"] = threshold
|
||||
payload["tier"] = discount
|
||||
if crit:
|
||||
payload["crit"] = true
|
||||
# 增益池乘区·主角侧 + 敌方防御池(2026-09-18 结构修复批——hero_offense 乘区接线落地,
|
||||
# attack_up 普通分量直乘旁路退役:增伤/乏力改乘 Σ三分量合计,覆盖面 = 普通+穿刺+魔法;
|
||||
# attack_up 普通分量直乘旁路退役:增伤/乏力乘攻击合计〔2026-09-19 充能退役后 = 单分量〕;
|
||||
# 与既有 enemy_defense 池乘区同段串乘)。四池对称:敌打主角 = hero_defense×enemy_output
|
||||
# (_resolve_damage);主角打敌 = hero_offense×enemy_defense。
|
||||
d_hero = DamageMath.pool_damage(d_hero, _clamped_pool("hero_offense"), _clamped_pool("enemy_defense"))
|
||||
@@ -504,14 +490,14 @@ func _combat_segment(ri: int) -> void:
|
||||
_enemy_system.commit_action(u)
|
||||
continue
|
||||
# 伤害技(含普攻——倍率恒 1):三乘法链 ① A = 攻击基线 × 技能倍率(判定对象 = 技能基础伤害分量)
|
||||
# → ② d20 vs 主角 AC(nat20 暴击 ≠ 技能重击,两层严格区分)→ ③ ×甲效(敌打主角无甲轴恒中性)
|
||||
# → ② d20 vs 主角 AC(nat20 暴击 ≠ 技能重击,两层严格区分)→ ③ +克制修正点数(敌打主角无甲轴恒中性 delta 0——加法核 2026-09-18 换装)
|
||||
# → 伏击附加(后置加法、必中全额、不吃 AC)→ 增益池乘区 → 伤害下限 max(1,·)。
|
||||
var baseline: int = int(round(float(u.atk) * float(srow.get("damage_mult", 1.0))))
|
||||
var roll_read_e: Dictionary = _armor_roll()
|
||||
var threshold_e: int = _defender_ac(_enemy_system.hero_unit()) + int(db.settlement.attack_bonus)
|
||||
var crit_e: bool = bool(roll_read_e.crit)
|
||||
var discount_e: int = _roll_discount(int(roll_read_e.roll), threshold_e)
|
||||
var rolled_base: int = DamageMath.rolled_component(float(baseline), discount_e, crit_e, int(db.settlement.crit_multiplier), 100)
|
||||
var rolled_base: int = DamageMath.rolled_component(float(baseline), discount_e, crit_e, int(db.settlement.crit_multiplier), 0) # 敌打主角无甲轴恒中性——加法核中性 delta = 0(原 percent 100 退役)
|
||||
var d_tick := _resolve_damage(rolled_base + L * _m_amb)
|
||||
d_tick = maxi(int(db.settlement.damage_floor), d_tick)
|
||||
_enemy_system.apply_damage(_enemy_system.hero_unit(), d_tick)
|
||||
@@ -566,34 +552,11 @@ func _resolve_damage(d_raw: int) -> int:
|
||||
return DamageMath.pool_damage(d_raw, p_hero, p_enemy)
|
||||
|
||||
|
||||
# 充能点数合成:奖励定义(charge_grants:触发源→{attack_type, per_unit, cap})× 本轮各源计数 → {攻击类型: 点数}
|
||||
# cap = 单轮计数上限(0 = 不封顶);投桶只读 attack_type 字段——换桶/换量是奖励侧改动,本类不认识"开格=穿刺"这类绑定
|
||||
func _charge_points() -> Dictionary:
|
||||
var pts: Dictionary = {}
|
||||
for src in db.settlement.charge_grants.keys():
|
||||
var g: Dictionary = db.settlement.charge_grants[src]
|
||||
var attack_type: String = String(g.get("attack_type", ""))
|
||||
var per: int = int(g.get("per_unit", 0))
|
||||
var cap: int = int(g.get("cap", 0))
|
||||
var n: int = int(_charge_counts.get(src, 0))
|
||||
if cap > 0:
|
||||
n = mini(n, cap)
|
||||
if attack_type == "" or per <= 0 or n <= 0:
|
||||
continue
|
||||
pts[attack_type] = int(pts.get(attack_type, 0)) + n * per
|
||||
return pts
|
||||
|
||||
|
||||
# 充能分量:点数(已含 per_unit × 计数换算)× 克制 percent → floor
|
||||
func _charge_component(points: int, attack_type: String, armor: String) -> int:
|
||||
return DamageMath.charge_component(points, _armor_effectiveness(attack_type, armor))
|
||||
|
||||
|
||||
# 克制矩阵读值(SettlementTable.armor_effectiveness:{攻击类型: {护甲: percent}}——缺项回退 100 中性)
|
||||
func _armor_effectiveness(attack_type: String, armor: String) -> int:
|
||||
# 克制修正点数读值(SettlementTable.armor_effectiveness:{攻击类型: {护甲: 点数}}——缺项回退 0 中性;加法核 2026-09-18 换装)
|
||||
func _counter_delta(attack_type: String, armor: String) -> int:
|
||||
var tbl: Dictionary = db.settlement.armor_effectiveness
|
||||
var row: Dictionary = tbl.get(attack_type, {})
|
||||
return int(row.get(armor, 100))
|
||||
return int(row.get(armor, 0))
|
||||
|
||||
|
||||
func _clamped_pool(pool: String) -> int:
|
||||
@@ -663,7 +626,7 @@ func _emit_settlement_snapshot(verdict: String) -> void:
|
||||
"enemies": enemies,
|
||||
"buffs": buffs,
|
||||
"ambush_layers": _status_effects.pool_layers("ambush"),
|
||||
"round_facts": {"path": _facts_path, "safe": safe_n, "mines": mines_hit, "flag_events": _facts_flags.size(), "charge_counts": _charge_counts.duplicate(), "charge_points": _charge_points()},
|
||||
"round_facts": {"path": _facts_path, "safe": safe_n, "mines": mines_hit, "flag_events": _facts_flags.size(), },
|
||||
})
|
||||
|
||||
|
||||
@@ -813,7 +776,6 @@ func _reset_round_facts() -> void:
|
||||
_facts_reveals.clear()
|
||||
_facts_flags.clear()
|
||||
_facts_path = ""
|
||||
_charge_counts.clear() # 充能计数随轮 facts 重置(每轮重新攒——结算段按奖励定义折算当轮点数)
|
||||
|
||||
|
||||
# ---- 行协议(Core Rule:公共字段 + 行类型特有字段)----
|
||||
|
||||
@@ -12,7 +12,7 @@ var spawn_center := Vector2i.ZERO
|
||||
var mines := {} # Vector2i → true
|
||||
var digits := {} # Vector2i → int(安全格恒有值;数字恒真 = 8 邻雷数)
|
||||
var variants := {} # Vector2i → Array[String](生成期快照,抗表重载)
|
||||
var biome := ""
|
||||
var board_type := "" # 图类型标签(词表 v1.0:dungeon/underdark;玩家侧显示名「地图类型」)
|
||||
var level_index: int = 1
|
||||
var node_depth: int = 0
|
||||
var metrics := {} # 生成期指标集(键清单单点定义在 board_generator._finalize)
|
||||
@@ -95,7 +95,7 @@ func compute_r0() -> Array:
|
||||
var queue: Array = b3.duplicate()
|
||||
while not queue.is_empty():
|
||||
var c: Vector2i = queue.pop_front()
|
||||
if is_safe(c) and digit_of(c) == 0 and variants.get(c, []).is_empty():
|
||||
if is_safe(c) and digit_of(c) == 0: # 变异死分支退役(2026-09-18 用户拍板删——与 排雷玩法 展开实现口径归一;变异定稿批按正式语义重接)
|
||||
for n in neighbors8(c):
|
||||
if is_safe(n) and not r0.has(n):
|
||||
r0[n] = true
|
||||
|
||||
@@ -28,7 +28,7 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat
|
||||
"solver_rules": String(row.solver_rules),
|
||||
"min_safe": int(row.min_safe_count),
|
||||
"max_attempts": int(row.max_gen_attempts),
|
||||
"biomes": Array(row.board_biomes),
|
||||
"board_type": String(row.board_type),
|
||||
}
|
||||
var best_legal: BoardData = null
|
||||
var best_legal_score := -1.0
|
||||
@@ -76,7 +76,7 @@ func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> Boar
|
||||
b.width = params.w
|
||||
b.height = params.h
|
||||
b.spawn_center = Vector2i(int((params.w - 1) / 2.0), int((params.h - 1) / 2.0))
|
||||
b.biome = str(params.biomes[0])
|
||||
b.board_type = str(params.board_type)
|
||||
b.level_index = params.L
|
||||
b.node_depth = params.D
|
||||
# ② 布雷:N = round(d×(W×H−9)) 先定数;域 = 全格 − 出生 3×3;均匀布雷后"先布后删"
|
||||
|
||||
@@ -285,7 +285,7 @@ func _ceil_key(sorted_keys: Array, request: int) -> int:
|
||||
|
||||
# ---- 地形变异 variant_pool_gate:候选池门控(∅ biome_tag = 通配)----
|
||||
|
||||
func variant_pool(level_index: int, board_biomes: Array) -> Array:
|
||||
func variant_pool(level_index: int, board_type: String) -> Array:
|
||||
var pool: Array = []
|
||||
for row in variation.rows:
|
||||
if int(row.get("unlock_level", 999)) > level_index:
|
||||
@@ -294,10 +294,8 @@ func variant_pool(level_index: int, board_biomes: Array) -> Array:
|
||||
if tags.is_empty():
|
||||
pool.append(row)
|
||||
continue
|
||||
for t in tags:
|
||||
if board_biomes.has(t):
|
||||
pool.append(row)
|
||||
break
|
||||
if tags.has(board_type):
|
||||
pool.append(row)
|
||||
return pool
|
||||
|
||||
|
||||
|
||||
@@ -14,9 +14,13 @@ static func pool_damage(d_raw: int, p_hero: int, p_enemy: int) -> int:
|
||||
return maxi(0, v)
|
||||
|
||||
|
||||
# 充能分量:充能点数(调用方已按奖励定义换算点数、并按源套单轮计数上限)× 克制 percent → floor
|
||||
static func charge_component(points: int, effectiveness_percent: int) -> int:
|
||||
return int(floor(float(points) * float(effectiveness_percent) / 100.0))
|
||||
# 充能分量:充能点数(调用方已按奖励定义换算)+ 克制修正点数(加法核——2026-09-18 换装,原 percent 乘区退役);
|
||||
# 空桶(点数 ≤ 0)不吃修正恒 0——零充能分量无伤害可修正,不产生负贡献(等价旧量纲 0×任何 percent = 0)
|
||||
# 类型化点数分量:点数 + 克制修正(加法核)——2026-09-19 充能退役后唯一消费方 = 奖励直伤道具(棋盘火力)
|
||||
static func counter_component(points: int, counter_delta: int) -> int:
|
||||
if points <= 0:
|
||||
return 0
|
||||
return points + counter_delta
|
||||
|
||||
|
||||
# ---- 护甲等级掷点批(2026-09-16 用户拍板——判定式与档位参数全在 settlement.tres,本类只做算术)----
|
||||
@@ -34,10 +38,10 @@ static func roll_discount_percent(roll: int, threshold: int, bounds: Array, disc
|
||||
return int(discounts[tier])
|
||||
|
||||
|
||||
# 吃掷点分量算术(纯函数):基线值 × 掷点折扣 ×(暴击时)暴击倍率 × 克制 percent → floor(单一取整;
|
||||
# 乘法交换——折扣/暴击/克制次序无关,取整链保持现登记:每分量恰一次 floor)。暴击恒全额由 discount=100 承载。
|
||||
static func rolled_component(base_value: float, discount_percent: int, crit: bool, crit_multiplier: int, effectiveness_percent: int) -> int:
|
||||
# 吃掷点分量算术(纯函数):基线值 × 掷点折扣 ×(暴击时)暴击倍率 → floor 后 + 克制修正点数(加法核)(单一取整;
|
||||
# 乘法交换——折扣/暴击次序无关,克制修正为整数点数加在 floor 之后,取整链保持现登记:每分量恰一次 floor)。暴击恒全额由 discount=100 承载。
|
||||
static func rolled_component(base_value: float, discount_percent: int, crit: bool, crit_multiplier: int, counter_delta: int) -> int:
|
||||
var v: float = base_value * float(discount_percent) / 100.0
|
||||
if crit:
|
||||
v *= float(crit_multiplier) / 100.0
|
||||
return int(floor(v * float(effectiveness_percent) / 100.0))
|
||||
return int(floor(v)) + counter_delta
|
||||
@@ -10,6 +10,7 @@ extends Resource
|
||||
const SOLVER_RULES := ["single", "single_subset"]
|
||||
const COST_BUDGET := 16384 # F2c provisional 量级护栏(禁墙钟逃生)
|
||||
const TARGET_ABS_FLOOR := 0.85 # 战斗结算 修订——target 域下限(P1 绝对锚)
|
||||
const BOARD_TYPE_VOCABULARY := ["dungeon", "underdark"] # 图类型词表 v1.0(玩家侧显示名「地图类型」)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
@@ -60,9 +61,10 @@ func validate() -> Array:
|
||||
# F2c 成本可行性
|
||||
if att * w * h > COST_BUDGET:
|
||||
errs.append({"category": "cross_field", "message": "(%d,%d) cost_feasible 违例 attempts×W×H ≤ %d" % [L, D, COST_BUDGET]})
|
||||
var biomes: Array = row.get("board_biomes", [])
|
||||
if biomes.is_empty():
|
||||
errs.append({"category": "cross_field", "message": "(%d,%d) board_biomes 非空域校验违例(地形变异⑮)" % [L, D]})
|
||||
# 图类型词表 v1.0(2026-09-19 用户拍板定稿:dungeon=地牢基线 / underdark=幽暗地域;扩词随内容批走修订)
|
||||
var bt: String = str(row.get("board_type", ""))
|
||||
if not BOARD_TYPE_VOCABULARY.has(bt):
|
||||
errs.append({"category": "cross_field", "message": "(%d,%d) board_type 越词表: %s" % [L, D, bt]})
|
||||
anchor_ok[L] = anchor_ok.get(L, false) or D == 0
|
||||
for L in anchor_ok.keys():
|
||||
if not anchor_ok[L]:
|
||||
|
||||
@@ -18,9 +18,8 @@ extends Resource
|
||||
# 主角攻击 = 普通(atk×(1+attack_up),不吃克制)+ 穿刺 + 魔法(后两者由充能供给),三分量各自 floor 后相加。
|
||||
# 【两条抽象分离,勿焊死】攻击类型 = 伤害的属性轴,只决定查 armor_effectiveness 哪一行,定义里不含任何棋盘来源;
|
||||
# 充能 = 按攻击类型分桶的附加伤害储备,"谁往桶里放东西"属**奖励的定义**(开格/标雷只是奖励触发源,奖励内容可改)。
|
||||
# 原 damage_a_per_cell / damage_a_cap / damage_b_per_flag 三字段即"类型×来源"焊死的产物,已由 charge_grants 取代。
|
||||
@export var charge_grants: Dictionary = {} # 奖励侧定义:触发源 → {attack_type(投哪个桶), per_unit(每单位点数), cap(单轮计数上限,0 = 不封顶)}
|
||||
@export var armor_effectiveness: Dictionary = {} # 克制矩阵 percent:{攻击类型 {normal|pierce|magic}: {护甲 {light|ward|heavy}: percent}}
|
||||
# 原 damage_a_per_cell / damage_a_cap / damage_b_per_flag 与 charge_grants 充能奖励定义均退役(2026-09-19 充能退役批——攻击模型回归单分量)。
|
||||
@export var armor_effectiveness: Dictionary = {} # 克制修正矩阵(加法点数 delta):{攻击类型 {normal|pierce|magic}: {护甲 {light|ward|heavy}: 点数 [-3,+3]}}
|
||||
# ---- 护甲等级掷点批(2026-09-16 用户拍板,数字即规范——registry settlement_table.ac_roll/ac_discount_tiers 同批定稿)----
|
||||
# 判定式:掷 1d20,掷点 ≥ 防守方 AC + 攻击加成 → 全额;低掷按缺口(AC−掷点)查档打折,无 miss、无零收益档;
|
||||
# nat 最大面(crit_roll)= 暴击:该次基础攻击分量 ×2 且恒全额;任何攻击落地伤害全局下限 damage_floor(≥1)。
|
||||
@@ -79,22 +78,10 @@ func validate() -> Array:
|
||||
var d: int = int(roll_tier_discounts[i])
|
||||
if d < 1 or d > 99 or (i > 0 and d >= int(roll_tier_discounts[i - 1])):
|
||||
errs.append({"category": "cross_field", "message": "roll_tier_discounts[%d] 越域/非降序(档位不设零档): %d" % [i, d]})
|
||||
# 充能奖励定义域校验(触发源 → {attack_type, per_unit, cap})
|
||||
for src in charge_grants.keys():
|
||||
var g: Dictionary = charge_grants[src]
|
||||
var g_at: String = String(g.get("attack_type", ""))
|
||||
if not ATTACK_TYPE_KEYS.has(g_at):
|
||||
errs.append({"category": "enum", "message": "charge_grants.%s.attack_type 未知攻击类型: %s(域 %s)" % [src, g_at, str(ATTACK_TYPE_KEYS)]})
|
||||
var g_per: int = int(g.get("per_unit", -1))
|
||||
if g_per < 0 or g_per > 999:
|
||||
errs.append({"category": "domain", "message": "charge_grants.%s.per_unit 越域 [0,999]: %d" % [src, g_per]})
|
||||
var g_cap: int = int(g.get("cap", -1))
|
||||
if g_cap < 0 or g_cap > 999:
|
||||
errs.append({"category": "domain", "message": "charge_grants.%s.cap 越域 [0,999](0 = 不封顶): %d" % [src, g_cap]})
|
||||
for atk_type in ATTACK_TYPE_KEYS:
|
||||
var row: Dictionary = armor_effectiveness.get(atk_type, {})
|
||||
for armor in ARMOR_KEYS:
|
||||
var pct: int = int(row.get(armor, -1))
|
||||
if pct < 0 or pct > 999:
|
||||
errs.append({"category": "domain", "message": "armor_effectiveness.%s.%s 越域 [0,999]: %d" % [atk_type, armor, pct]})
|
||||
var pct: int = int(row.get(armor, -99))
|
||||
if pct < -3 or pct > 3:
|
||||
errs.append({"category": "domain", "message": "armor_effectiveness.%s.%s 越域 [-3,+3]: %d" % [atk_type, armor, pct]})
|
||||
return errs
|
||||
@@ -14,7 +14,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 50,
|
||||
"max_gen_attempts": 16,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "dungeon"
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 1,
|
||||
@@ -25,7 +25,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 50,
|
||||
"max_gen_attempts": 16,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "dungeon"
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 2,
|
||||
@@ -36,7 +36,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "dungeon"
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 3,
|
||||
@@ -47,7 +47,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "dungeon"
|
||||
}, {
|
||||
"level_index": 1,
|
||||
"node_depth": 4,
|
||||
@@ -58,7 +58,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "dungeon"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 0,
|
||||
@@ -69,7 +69,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 1,
|
||||
@@ -80,7 +80,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 4,
|
||||
@@ -91,7 +91,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 5,
|
||||
@@ -102,7 +102,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 6,
|
||||
@@ -113,7 +113,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 7,
|
||||
@@ -124,7 +124,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}, {
|
||||
"level_index": 2,
|
||||
"node_depth": 8,
|
||||
@@ -135,7 +135,7 @@ rows = [{
|
||||
"solver_rules": "single_subset",
|
||||
"min_safe_count": 60,
|
||||
"max_gen_attempts": 24,
|
||||
"board_biomes": ["dungeon"]
|
||||
"board_type": "underdark"
|
||||
}]
|
||||
|
||||
; 切片调参登记(SLICE-TUNED provisional,调参所有权仍归 C3):
|
||||
@@ -151,4 +151,4 @@ rows = [{
|
||||
; L2 战斗/Boss 节点 (2, 0/1/4/5/6/7/8) 七行精确铺满(跨表校验强制——ConfigDB ④ level_node_dangling);
|
||||
; 奖励点 (2,2)/恢复点 (2,3) 不开战斗、无板行(跨表校验只查 battle/boss 节点)。
|
||||
; 板面参数与 L1 高档同值(12×12 d0.25 t0.88)——密度/推理率轴差异化留给 难度曲线与Boss 调参批;
|
||||
; board_biomes 维持 ["dungeon"](幽暗地域独立 biome tag 归 地形变异 批,本批零触及排雷循环)。
|
||||
; board_type 词表 v1.0(2026-09-19 用户拍板):L1=dungeon 地牢基线 / L2=underdark 幽暗地域(玩家侧显示名「地图类型」);域校验在 board_gen_table.gd 词表常量。
|
||||
@@ -338,6 +338,10 @@ rows = [{
|
||||
;
|
||||
; 护甲类型重配批(2026-09-18 用户拍板):关卡 1 全部轻甲(食人魔酋长 heavy→light、熊地精两行 ward→light);
|
||||
; 关卡 2 除 Boss(蛛化卓尔精灵 heavy→ward 魔法甲)外全部轻甲(熊地精四行 ward→light,巨蜘蛛既有 light 不动)。
|
||||
; 现行克制三角(pierce 克 ward ×150 / magic 克 heavy ×150 / 被克 ×50 / light 双向中性)下的读数效果:
|
||||
; L1 克制轴数值中性化(全 light);L2 仅 Boss 战非中性——魔法矢克 Boss ×150、穿刺弹被克 ×50。
|
||||
; hero 侧两轴(armor_type/attack_type)同批入列(hero.tres 批注),敌方技能攻击类型轴仍未开(全部 normal 档)。
|
||||
;
|
||||
; 甲效矩阵加法核换装批(2026-09-18 用户拍板——settlement.tres armor_effectiveness 量纲 percent→整数点数,
|
||||
; 三档 克制 +1 / 中性 0 / 被克 −1,五档百分比词表随批退役)。
|
||||
; 现行读数效果:L1/L2 小怪全 light → 穿刺类(开格充能/穿刺弹)被克 −1(穿刺弹 2→1、充能 3 点→2)、普通/魔法中性;
|
||||
; L2 仅 Boss 战非中性(魔法甲)——基础攻击与穿刺弹克制 +1、魔法矢被克 −1(原威力 + delta 自行核算后落笔)。
|
||||
; 原重配批注「魔法矢克 Boss ×150、穿刺弹被克 ×50」与当批矩阵正反互倒(系笔误),随本批订正为上行读数。
|
||||
@@ -19,8 +19,9 @@ attack_type = "normal"
|
||||
; EV 参考数按判定式复算 = 期望减免 21.25%);ac_up 增益 +1 AC/层(×2 层 = AC+2)在其上加算。
|
||||
;
|
||||
; 营地扩展前置批(2026-09-18 用户指令):新增两根可配置轴——
|
||||
; armor_type = 主角护甲类型(light/ward/heavy,缺省 light):甲效矩阵消费面已通(敌方攻击现行恒 normal 档
|
||||
; = 三甲全 100% 数值中性,类型轴生效待敌方技能攻击类型批);
|
||||
; armor_type = 主角护甲类型(light/ward/heavy,缺省 light):轴现行休眠——甲效矩阵消费面 = 主角侧,
|
||||
; 敌方攻击不查矩阵(2026-09-18 甲效矩阵调参批归因改述,原「恒 normal 档故三甲全 100% 中性」随旧三档
|
||||
; 结构退役),类型轴生效待敌方技能攻击类型批;
|
||||
; attack_type = 主角基础攻击类型(normal/pierce/magic,缺省 normal):基础攻击量(atk)与攻击类型抽象分离——
|
||||
; 营地永久强化"换成魔法/穿刺攻击"= 改此字段(atk 量轴零改动),战斗段基础分量即按此查甲效矩阵。
|
||||
; 两轴全部 provisional,营地建设批(元进度侧)拍板正式投值。
|
||||
@@ -28,33 +28,21 @@ effect_pool_map = {
|
||||
pool_clamp_lo = -100
|
||||
pool_clamp_hi = 100
|
||||
round_direction = "floor"
|
||||
charge_grants = {
|
||||
"safe_cell": {
|
||||
"attack_type": "pierce",
|
||||
"per_unit": 1,
|
||||
"cap": 3
|
||||
},
|
||||
"flag_hit": {
|
||||
"attack_type": "magic",
|
||||
"per_unit": 1,
|
||||
"cap": 0
|
||||
}
|
||||
}
|
||||
armor_effectiveness = {
|
||||
"normal": {
|
||||
"light": 100,
|
||||
"ward": 100,
|
||||
"heavy": 100
|
||||
"light": 0,
|
||||
"ward": 1,
|
||||
"heavy": -1
|
||||
},
|
||||
"pierce": {
|
||||
"light": 100,
|
||||
"ward": 150,
|
||||
"heavy": 50
|
||||
"light": -1,
|
||||
"ward": 1,
|
||||
"heavy": 0
|
||||
},
|
||||
"magic": {
|
||||
"light": 100,
|
||||
"ward": 50,
|
||||
"heavy": 150
|
||||
"light": 0,
|
||||
"ward": -1,
|
||||
"heavy": 1
|
||||
}
|
||||
}
|
||||
roll_faces = 20
|
||||
@@ -67,13 +55,10 @@ damage_floor = 1
|
||||
armor_roll_stream = "armor_roll"
|
||||
|
||||
; 攻击类型 × 护甲克制批(2026-09-12 用户拍板结构;2026-09-15 命名统一批 + 抽象分离批,数值 provisional 待用户/C3 调参):
|
||||
; 主角攻击 = 普通(atk×(1+attack_up),对全部护甲 100%)+ 穿刺 + 魔法(后两者由充能供给),三分量各自 floor 后相加;
|
||||
; 克制语义 = 克制 ×150 / 中性 ×100 / 被克 ×50(穿甲弹/高爆弹式对策三角)。
|
||||
; 【两条抽象分离,勿焊死】攻击类型 {normal, pierce, magic} = 普通 / 穿刺 / 魔法 =
|
||||
; 伤害的属性轴,只决定查矩阵哪一行,定义里不含任何棋盘来源;护甲类型 {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲
|
||||
; 是敌单位属性。充能 = 按攻击类型分桶的附加伤害储备:谁能充、充哪个桶 = 奖励定义。
|
||||
; charge_grants 现行供给(奖励侧定义,可整体替换):safe_cell(本轮每挖开 1 个安全格)→ 穿刺桶 1/格、
|
||||
; 单轮计数上限 8(0 = 不封顶);flag_hit(每颗首次标中雷,每雷实例一次去重)→ 魔法桶 2/雷,无上限。
|
||||
; 主角攻击 = 单一基础分量(2026-09-19 充能退役批·用户拍板——原三分量模型退役,克制策略归冒险前配置:
|
||||
; 类型 = hero 表 attack_type,营地换型 = 改字段;局内无挖掘攒弹药的第二供给)。
|
||||
; 【抽象分离纪律保留】攻击类型 {normal, pierce, magic} = 普通 / 穿刺 / 魔法 = 伤害的属性轴,
|
||||
; 只决定查矩阵哪一行,定义里不含任何棋盘来源;护甲类型 {light, ward, heavy} = 轻甲 / 魔法护甲 / 重甲是敌单位属性。
|
||||
; 格面资源批(2026-09-15 用户拍板):段序七段 → 五段(item_window/item 两段随可选类道具删除下线);
|
||||
; source_label_map 删 item 键;effect_pool_map 增 sapped→hero_offense(weaken 型负向——乏力)。
|
||||
; enrage(敌方暴走 +25%,enemy_output 池)/ off_guard(我方失衡 +25%,hero_defense 池)/
|
||||
@@ -97,9 +82,6 @@ armor_roll_stream = "armor_roll"
|
||||
;
|
||||
; 数值压缩批(2026-09-17 用户拍板暂定):
|
||||
; equivalence_ref_damage 12→4(当量参考敞口,绝对值轴 ÷3 取整);
|
||||
; charge_grants 按粒度约束压缩——per_unit 是点数粒度(最小 1)不可 ÷3,压上限/单价近似等比:
|
||||
; 穿刺 cap 8→3(per_unit 1 不变,EV ≈ ÷2.7);魔法 2/雷→1/雷(per_unit ÷3 进 1,EV ≈ ÷2)。
|
||||
; 充能期望压缩比 ≈1/2~1/3 之间,粒度限制下的近似,登记为已知偏差。
|
||||
; roll_faces/折扣档/暴击/damage_floor/克制矩阵/pool_clamp 全部不动(相对轴)。clamp 域 [-100,+100] 共用。
|
||||
;
|
||||
; 战斗数值结构修复批(2026-09-18 盘问会话用户拍板):
|
||||
@@ -109,3 +91,19 @@ armor_roll_stream = "armor_roll"
|
||||
; 变的只是池归属)。hero_offense 乘区接线同步落地(battle_orchestrator 主角攻击段):Σ三分量后
|
||||
; ×(100+clamp(hero_offense))/100×(100+clamp(enemy_defense))/100——attack_up 普通分量直乘旁路退役,
|
||||
; 增伤/乏力覆盖面 = 普通+穿刺+魔法 合计;上方"hero_offense 现行走直乘——登记遗留"注记随之销项。
|
||||
;
|
||||
; 甲效矩阵用户调参批(2026-09-18 用户逐格给值,五格改值、四格不动):
|
||||
; normal ward 100→120 · normal heavy 100→50 · pierce light 100→80 · pierce ward 150→120 · pierce heavy 50→80;
|
||||
; magic 全行与 normal light 不动。上方「克制语义 = 克制 ×150 / 中性 ×100 / 被克 ×50(对策三角)」三档措辞退役——
|
||||
; 现行五档:克制 ×150 / 弱克制 ×120 / 中性 ×100 / 弱被克 ×80 / 被克 ×50,普通行不再恒中性。
|
||||
; 键集 3×3 满格结构冻结不动;矩阵消费面 = 主角侧(道具直伤 / 主角攻击三分量 / 充能分量),
|
||||
; 敌打主角现行不查矩阵——主角 armor_type 轴仍休眠,轴生效待敌方技能攻击类型批。
|
||||
; 实战读数(现行敌种甲配置):L1/L2 小怪全轻甲 → 穿刺类(开格充能/穿刺弹)×80 小幅折损、普通/魔法中性;
|
||||
; L2 Boss 魔法甲 → 基础攻击与穿刺弹 ×120、魔法矢 ×50(不变)。重甲现行无消费敌种(零消费格)。
|
||||
;
|
||||
; 甲效矩阵加法核换装批(2026-09-18 用户拍板数值语言——小整数加法核,与 01-核心循环 数值语言条款同批):
|
||||
; 量纲 percent→整数点数,三档 克制 +1 / 中性 0 / 被克 −1,九格 normal [0,+1,−1] / pierce [−1,+1,0] / magic [0,−1,+1];
|
||||
; pierce×heavy = 0 中性为用户特别拍板(重甲挡刀刃不排斥穿刺;现行无重甲敌种,纯结构位)。
|
||||
; 上方 2026-09-18 甲效矩阵用户调参批(五档 ×150/×120/×100/×80/×50)与更早三档三角批注的百分比量纲全部随批退役(历史留痕不删)。
|
||||
; 键集 3×3 满格结构冻结不动;缺项回退 = 0 中性(battle_orchestrator._counter_delta);域 [−3,+3](schema 校验)。
|
||||
; 奖励直伤行 payload 键 effectiveness 保留键名、值语义改点数(JSONL 契约稳定——analyze_playtest 等分析工具不破坏)。
|
||||
@@ -28,6 +28,52 @@ static func choose_dig(minesweeper) -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
# ==================== 普通策略档 v2·基础定式画像(2026-09-19 用户拍板——替代旧"无推理最近格"口径) ====================
|
||||
# 扫雷推理分级(经典定式文献口径):新手级 = 单数字满足规则——「数字 = 邻雷未开数 ⇒ 全是雷(满足即标)」
|
||||
# 「数字 = 已标旗数 ⇒ 余邻全安全(满足即开)」,教学一眼会、正常玩家两局即会的最低门槛判断;
|
||||
# 进阶级 = 联动定式(1-1 / 1-2 / 1-2-1 / 1-2-2-1 / 子集缩减)与概率分析——本切片归完美档 subset 推理承载,
|
||||
# 不入普通画像(新手不可靠掌握)。本档语义 = 只做新手级推理 + 无处可推时按既有口径盲挖边界格(新手冲动)+
|
||||
# 不主动刷新(新手无刷新策略意识)。测量定位:教会基础规则后的真实玩家下限近似(旧"乱点/最近格"口径测的是
|
||||
# 无认知玩家下限,对通关率带判读失真——2026-09-18/19 普通画像 0%/5% 越带下界与真人方向性结论矛盾)。
|
||||
|
||||
# 普通画像单步决策入口(每动作一查——挖掘揭示新数字后重推理):move/dig/flag/stop 四值(无 refresh)。
|
||||
static func novice_action(minesweeper) -> Dictionary:
|
||||
var ded := _deduce_single(minesweeper)
|
||||
for c in ded["mines"]:
|
||||
if not minesweeper.is_revealed(c) and not minesweeper.has_flag(c):
|
||||
return _plan_on(minesweeper, c, "flag")
|
||||
for c in ded["safe"]:
|
||||
if not minesweeper.is_revealed(c) and not minesweeper.has_flag(c):
|
||||
return _plan_on(minesweeper, c, "dig")
|
||||
var plan := choose_dig(minesweeper)
|
||||
if plan.is_empty():
|
||||
return {"kind": "stop"}
|
||||
return _plan_on(minesweeper, plan.dig_cell, "dig")
|
||||
|
||||
|
||||
# 新手级推理不动点:仅 single 规则(约束余雷=0⇒未知全安全 / 余雷=未知数⇒全雷),无 subset 联动——
|
||||
# 满足规则连锁(标一雷使邻数字余雷清零)由 while-changed 不动点承载。
|
||||
static func _deduce_single(minesweeper) -> Dictionary:
|
||||
var board = minesweeper.current_board()
|
||||
var safe := {}
|
||||
var mines := {}
|
||||
var changed := true
|
||||
while changed:
|
||||
changed = false
|
||||
for con in _constraints(minesweeper, board, mines):
|
||||
if int(con["mines"]) == 0:
|
||||
for c in con["unknown"].keys():
|
||||
if not safe.has(c):
|
||||
safe[c] = true
|
||||
changed = true
|
||||
elif int(con["mines"]) == (con["unknown"] as Dictionary).size():
|
||||
for c in con["unknown"].keys():
|
||||
if not mines.has(c):
|
||||
mines[c] = true
|
||||
changed = true
|
||||
return {"safe": _row_major(safe), "mines": _row_major(mines)}
|
||||
|
||||
|
||||
# ==================== 完美策略档(2026-09-14 用户拍板:OQ7 技能轴上界测量仪——只作测量,非玩法数值) ====================
|
||||
# 策略语义:零盲挖(只挖已证安全格 ⇒ 构造保证零踩雷)· 已证雷格全标旗(旗先于挖——旗 = 纯收益
|
||||
# 〔轮末 weaken + 魔法 首次充能〕且雷上旗不阻连锁)· 卡死且刷新资源可用 → 轮内刷新
|
||||
|
||||
@@ -21,7 +21,7 @@ func _initialize() -> void:
|
||||
"level_index": 1, "node_depth": 0, "board_width": w, "board_height": h,
|
||||
"mine_density": d, "inferability_target": t, "solver_rules": "single_subset",
|
||||
"min_safe_count": ms, "max_gen_attempts": 16,
|
||||
"board_biomes": ["dungeon"],
|
||||
"board_type": "dungeon",
|
||||
}
|
||||
_patch_row(row, d)
|
||||
var params := _params_from(row)
|
||||
@@ -53,7 +53,7 @@ func _params_from(row: Dictionary) -> Dictionary:
|
||||
"L": 1, "D": 0, "w": int(row.board_width), "h": int(row.board_height),
|
||||
"density": float(row.mine_density), "target": float(row.inferability_target),
|
||||
"solver_rules": "single_subset", "min_safe": int(row.min_safe_count),
|
||||
"max_attempts": int(row.max_gen_attempts), "biomes": ["dungeon"],
|
||||
"max_attempts": int(row.max_gen_attempts), "board_type": "dungeon",
|
||||
}
|
||||
|
||||
class FakeDB:
|
||||
|
||||
@@ -123,27 +123,34 @@ static func _autoplay_battle(runner, settlement, st: Dictionary, logger) -> void
|
||||
if String(st.get("policy", "normal")) == "perfect":
|
||||
_autoplay_battle_perfect(runner, settlement, st, logger)
|
||||
return
|
||||
# 普通策略档 v2(2026-09-19 用户拍板):基础定式画像——单步决策循环(move/dig/flag 经 settle_batch 提交,
|
||||
# 推理分级见 auto_player.gd 普通策略档节:新手级单数字满足规则 + 盲挖边界兜底、无 refresh);
|
||||
# 旧"无推理挖最近格"口径随批退役。连续拒绝 ≥3 = 编排违约防御(同完美档 fail-fast 语义)。
|
||||
var round_guard := 60
|
||||
while st.result == "" and runner.current_battle() == settlement and settlement.battle_result == "" and round_guard > 0:
|
||||
round_guard -= 1
|
||||
st.rounds += 1
|
||||
var start_round: int = settlement.round_index
|
||||
var action_guard := 400
|
||||
var rejects := 0
|
||||
while runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" \
|
||||
and settlement.round_index == start_round and action_guard > 0:
|
||||
and settlement.round_index == start_round and action_guard > 0 and rejects < 3:
|
||||
action_guard -= 1
|
||||
var plan := AutoPlayer.choose_dig(settlement.minesweeper)
|
||||
if plan.is_empty():
|
||||
var act := AutoPlayer.novice_action(settlement.minesweeper)
|
||||
var kind := String(act.get("kind", "stop"))
|
||||
if kind == "stop":
|
||||
break
|
||||
if plan.hero_cell != settlement.minesweeper.hero_pos():
|
||||
var out_m: Array = settlement.minesweeper.settle_batch([{"action": "move", "cell": plan.hero_cell}])
|
||||
if logger != null:
|
||||
logger.log_action("move", plan.hero_cell, _accepted(out_m), {"round": start_round})
|
||||
if settlement.action_economy.phase != "Active" or settlement.round_index != start_round:
|
||||
break
|
||||
var out_d: Array = settlement.minesweeper.settle_batch([{"action": "dig", "cell": plan.dig_cell}])
|
||||
if logger != null:
|
||||
logger.log_action("dig", plan.dig_cell, _accepted(out_d), {"round": start_round})
|
||||
var ok := false
|
||||
if kind == "move":
|
||||
ok = _submit_intent(settlement, "move", act.get("cell"), logger, start_round)
|
||||
elif kind == "dig":
|
||||
ok = _submit_intent(settlement, "dig", act.get("cell"), logger, start_round)
|
||||
elif kind == "flag":
|
||||
ok = _submit_intent(settlement, "flag_place", act.get("cell"), logger, start_round)
|
||||
if ok:
|
||||
rejects = 0
|
||||
else:
|
||||
rejects += 1
|
||||
if runner.current_battle() == settlement and settlement.battle_result == "" and settlement.action_economy.phase == "Active" and settlement.round_index == start_round:
|
||||
var out_s: Array = settlement.minesweeper.settle_batch([{"action": "stop"}])
|
||||
if logger != null:
|
||||
|
||||
@@ -182,7 +182,8 @@ func _smoke_level() -> void:
|
||||
check(win.boss_depth == 4, "Boss 节点以 Dmax=4 解析(棋盘生成 第三轴登记约定)")
|
||||
check(win.hp_at_boss >= 1 and win.hp_at_boss <= int(_db.hero.max_hp) * 3, "进 Boss HP 携带在域内(%d)" % win.hp_at_boss)
|
||||
# ② 恢复点:回复 = min(floor(生效max_hp×pct/100), 缺口)——hp+100% 档生效 max = 基线×2
|
||||
var rest = autoplay.run_level(_db, 4242, 1, 100, 100)
|
||||
# seed 42(2026-09-19 充能退役批换种:原 4242 因主角输出模型变化在恢复点前败北——探针目的 = 验回复公式,种子仅载体)
|
||||
var rest = autoplay.run_level(_db, 42, 1, 100, 100)
|
||||
if rest.result != "start_failed" and rest.hp_before_rest >= 0:
|
||||
var eff_max := maxi(1, int(round(float(_db.hero.max_hp) * 2.0))) # hp_pct=100(run_level 已恢复基线)
|
||||
var expect: int = mini(int(floor(float(eff_max) * 0.3)), eff_max - rest.hp_before_rest)
|
||||
@@ -398,27 +399,28 @@ func _ac12_tier_samples() -> Array:
|
||||
|
||||
# ---- 主角乘区接线 + 硬化折半方向(2026-09-18 结构修复批护栏)----
|
||||
# 同种子停手轮恒同掷点(效果只改乘区、不动掷点流)→ 落地伤害精确关系:
|
||||
# 增伤 = 基线 ×1.5、乏力 = ×0.75(hero_offense 池乘 Σ三分量合计——普通+穿刺同乘,直乘旁路退役);
|
||||
# 增伤 = 基线 ×1.5、乏力 = ×0.75(hero_offense 池乘攻击合计——2026-09-19 充能退役后 = 单分量,直乘旁路退役);
|
||||
# 硬化 = 基线 ×0.5 且 < 基线(enemy_defense 池负值折半——+100 翻倍符号 bug 随批纠正)。
|
||||
# 基线构成(加法核 2026-09-18 + 充能退役 2026-09-19):基线 100——hero attack_type=normal 对轻甲中性 0 修正
|
||||
# (目标 = L1 地精轻甲;AC 0 恒全额非暴击种子)。
|
||||
func _smoke_pool_wiring() -> void:
|
||||
print("--- 主角乘区接线 + 硬化折半方向 ---")
|
||||
var base_row := _hero_attack_row_with({"pierce_charge": 4}, 100, 20260918)
|
||||
var up_row := _hero_attack_row_with({"pierce_charge": 4, "effects": ["attack_up"]}, 100, 20260918)
|
||||
var sapped_row := _hero_attack_row_with({"pierce_charge": 4, "effects": ["sapped"]}, 100, 20260918)
|
||||
var fortify_row := _hero_attack_row_with({"pierce_charge": 4, "effects": ["fortify"]}, 100, 20260918)
|
||||
var base_row := _hero_attack_row_with({}, 100, 20260918)
|
||||
var up_row := _hero_attack_row_with({"effects": ["attack_up"]}, 100, 20260918)
|
||||
var sapped_row := _hero_attack_row_with({"effects": ["sapped"]}, 100, 20260918)
|
||||
var fortify_row := _hero_attack_row_with({"effects": ["fortify"]}, 100, 20260918)
|
||||
var base_v := int(base_row.get("value", -1))
|
||||
check(int(base_row.get("pierce", -1)) == 3 and base_v > 0, "充电注入基线在位(穿刺分量 = cap 3、基线 %d——合计含充电分量前提)" % base_v)
|
||||
check(int(base_row.get("normal", -1)) == base_v and base_v > 0, "单分量基线在位(normal 分量 = 落地值、基线 %d——充能退役批)" % base_v)
|
||||
check(int(up_row.get("value", -1)) == int(floor(base_v * 1.5)),
|
||||
"增伤经乘区加成合计(%d → %d = ×1.5——attack_up 直乘旁路退役,覆盖面=普通+穿刺+魔法)" % [base_v, int(up_row.get("value", -1))])
|
||||
"增伤经乘区加成合计(%d → %d = ×1.5——attack_up 直乘旁路退役,覆盖面=攻击合计)" % [base_v, int(up_row.get("value", -1))])
|
||||
check(int(sapped_row.get("value", -1)) == int(floor(base_v * 0.75)),
|
||||
"乏力经乘区削减合计(%d → %d = ×0.75——充电分量同削)" % [base_v, int(sapped_row.get("value", -1))])
|
||||
"乏力经乘区削减合计(%d → %d = ×0.75)" % [base_v, int(sapped_row.get("value", -1))])
|
||||
check(int(fortify_row.get("value", -1)) == int(floor(base_v * 0.5)) and int(fortify_row.get("value", -1)) < base_v,
|
||||
"硬化折半方向(%d → %d = ×0.5 且 < 基线——敌方防御池负值减伤,翻倍 bug 退役)" % [base_v, int(fortify_row.get("value", -1))])
|
||||
|
||||
|
||||
# 构造停手轮首行主角攻击行:opts = {pierce_charge: int, effects: Array};hero_atk 覆盖 100 拉开量级
|
||||
# (floor 关系可精确断言);效果直授沿 _weaken_refresh_probe 前例;充电计数直注 _charge_counts
|
||||
# (重置只在轮末出口分发/战斗终——停手轮前注入可存活到战斗段)。
|
||||
# 构造停手轮首行主角攻击行:opts = {effects: Array};hero_atk 覆盖 100 拉开量级
|
||||
# (floor 关系可精确断言);效果直授沿 _weaken_refresh_probe 前例。
|
||||
func _hero_attack_row_with(opts: Dictionary, hero_atk_val: int, seed_val: int) -> Dictionary:
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
rng.set_master_seed(seed_val)
|
||||
@@ -429,8 +431,6 @@ func _hero_attack_row_with(opts: Dictionary, hero_atk_val: int, seed_val: int) -
|
||||
return {}
|
||||
for eff in opts.get("effects", []):
|
||||
settlement._status_effects.apply_effect(String(eff), {"source_class": "mine_hit", "origin_cell": Vector2i(0, 0), "round_index": settlement.round_index})
|
||||
if opts.has("pierce_charge"):
|
||||
settlement._charge_counts["safe_cell"] = int(opts.pierce_charge)
|
||||
settlement.minesweeper.settle_batch([{"action": "stop"}])
|
||||
for row in rows:
|
||||
if String(row.get("type", "")) == "damage" and String(row.get("source_class", "")) == "hero_attack":
|
||||
@@ -527,14 +527,15 @@ func _smoke_full_battle(L: int, D: int, label: String) -> void:
|
||||
has_verdict = true
|
||||
if row.get("source_class", "") == "hero_attack":
|
||||
hero_attacks += 1
|
||||
# 三段伤害构成行(攻击类型×护甲克制批):hero_attack 行必须携带 normal/pierce/magic 分量(P2 归因)
|
||||
if not (row.has("normal") and row.has("pierce") and row.has("magic")):
|
||||
# 单分量伤害构成行(2026-09-19 充能退役批):hero_attack 行必须携带实际攻击类型键 + 掷点读数(P2 归因)
|
||||
var atk_t: String = String(_db.hero.attack_type)
|
||||
if not (row.has(atk_t) and row.has("roll") and row.has("ac") and row.has("tier")):
|
||||
hero_breakdown_ok = false
|
||||
check(has_verdict, "%s 步骤流含判定行" % label)
|
||||
# 伤害道具批(2026-09-18):单弱敌遭遇可被挖掘期兑付(穿刺弹/魔法矢/爆裂粉尘)在授予段单独击杀——
|
||||
# 敌全灭后战斗段整段跳过、零先手行为合法结局;先手规则本身由强敌场景(L1/L2 深节点)继续守护。
|
||||
check(hero_attacks >= 1 or run1.terminated, "%s 主角先手攻击行存在或授予段终局(2026-09-11 拍板① + 伤害道具批)×%d" % [label, hero_attacks])
|
||||
check(hero_breakdown_ok, "%s 主角攻击行携带三分量 normal/pierce/magic(克制批)" % label)
|
||||
check(hero_breakdown_ok, "%s 主角攻击行携带类型键与掷点读数(单分量——充能退役批)" % label)
|
||||
|
||||
|
||||
func _run_battle(L: int, D: int) -> Dictionary:
|
||||
@@ -790,52 +791,26 @@ func _smoke_damage_types() -> void:
|
||||
check(enemy_system_b.spawn(1, 2, int(_db.hero.max_hp)) and String(enemy_system_b.all_enemies()[0].armor_type) == "light", "bugbear 护甲 = light(护甲类型重配批 2026-09-18:关卡 1 全轻甲)")
|
||||
var enemy_system_c = load("res://core/unit_container.gd").new(_db)
|
||||
check(enemy_system_c.spawn(1, 4, int(_db.hero.max_hp)) and String(enemy_system_c.all_enemies()[0].armor_type) == "light", "ogre_chief 护甲 = light(护甲类型重配批 2026-09-18:关卡 1 全轻甲)")
|
||||
# ② 克制矩阵表值在位(三角:克制 150 / 中性 100 / 被克 50)
|
||||
# ② 克制矩阵表值在位(2026-09-18 加法核换装:三档点数 克制 +1 / 中性 0 / 被克 −1——percent 量纲退役)
|
||||
var am: Dictionary = _db.settlement.armor_effectiveness
|
||||
check(int(am.pierce.ward) == 150 and int(am.pierce.heavy) == 50 and int(am.pierce.light) == 100, "穿刺克制行:魔法护甲 150 / 轻甲 100 / 重甲 50")
|
||||
check(int(am.magic.heavy) == 150 and int(am.magic.ward) == 50 and int(am.magic.light) == 100, "魔法克制行:重甲 150 / 轻甲 100 / 魔法护甲 50")
|
||||
check(int(am.normal.light) == 100 and int(am.normal.ward) == 100 and int(am.normal.heavy) == 100, "普通攻击全护甲中性 100(不吃克制)")
|
||||
# ②′ 充能奖励定义在位(抽象分离批:触发源 → 攻击类型桶;攻击类型定义里不含任何来源)
|
||||
var grants: Dictionary = _db.settlement.charge_grants
|
||||
var g_safe: Dictionary = grants.get("safe_cell", {})
|
||||
var g_flag: Dictionary = grants.get("flag_hit", {})
|
||||
check(String(g_safe.get("attack_type", "")) == "pierce" and int(g_safe.get("per_unit", 0)) == 1 and int(g_safe.get("cap", -1)) == 3, "充能奖励定义:开格 → 穿刺桶 1/格(单轮计数上限 3)")
|
||||
check(String(g_flag.get("attack_type", "")) == "magic" and int(g_flag.get("per_unit", 0)) == 1 and int(g_flag.get("cap", -1)) == 0, "充能奖励定义:标中旗 → 魔法桶 1/雷(无计数上限)")
|
||||
# ②″ 奖励可改(行为断言):把「标中旗」奖励从魔法桶改投穿刺桶 → 同一战斗魔法分量归零;还原后步骤流逐行相同
|
||||
# (证明改道唯一入口 = 奖励定义,攻击类型与甲效矩阵不参与;玩家可见语义见 战斗结算 GDD 战斗段)
|
||||
var run_base = _run_flag_backlash_round(20260912)
|
||||
var base_magic := 0
|
||||
for row in run_base.rows:
|
||||
if String(row.get("source_class", "")) == "hero_attack":
|
||||
base_magic += int(row.get("magic", 0))
|
||||
var save_grant: Dictionary = _db.settlement.charge_grants["flag_hit"]
|
||||
_db.settlement.charge_grants["flag_hit"] = {"attack_type": "pierce", "per_unit": 2, "cap": 0}
|
||||
var run_swap = _run_flag_backlash_round(20260912)
|
||||
var swap_magic := 0
|
||||
for row in run_swap.rows:
|
||||
if String(row.get("source_class", "")) == "hero_attack":
|
||||
swap_magic += int(row.get("magic", 0))
|
||||
_db.settlement.charge_grants["flag_hit"] = save_grant
|
||||
check(base_magic >= 1, "充能落桶基线:标中旗奖励进魔法桶(魔法分量合计 %d)" % base_magic)
|
||||
check(swap_magic == 0, "改投穿刺桶后:主角攻击行魔法分量归零(×%d——改奖励即改道,攻击类型定义未动)" % swap_magic)
|
||||
check(JSON.stringify(run_base.rows) == JSON.stringify(_run_flag_backlash_round(20260912).rows), "还原奖励定义后步骤流逐行相同(改道只经奖励定义,矩阵零改动)")
|
||||
# ③ 标错(类型化负向兑付)+ 标中充能(奖励定义现投魔法桶;Boss 行 150HP 多轮样本;板真相直读保证 hit/miss 确定性)
|
||||
check(int(am.normal.light) == 0 and int(am.normal.ward) == 1 and int(am.normal.heavy) == -1, "普通行:轻甲 0 / 魔法护甲 +1 / 重甲 −1(加法核)")
|
||||
check(int(am.pierce.light) == -1 and int(am.pierce.ward) == 1 and int(am.pierce.heavy) == 0, "穿刺行:轻甲 −1 / 魔法护甲 +1 / 重甲 0 中性(用户拍板)")
|
||||
check(int(am.magic.light) == 0 and int(am.magic.ward) == -1 and int(am.magic.heavy) == 1, "魔法行:轻甲 0 / 魔法护甲 −1 / 重甲 +1(加法核)")
|
||||
# ③ 标错(类型化负向兑付)+ 标中正向兑付(2026-09-19 充能退役后标中旗只兑格面资源正向;Boss 行多轮样本;板真相直读保证 hit/miss 确定性)
|
||||
var run1 = _run_flag_backlash_round(20260912)
|
||||
var miss_rows := 0
|
||||
var negative_rows := 0
|
||||
var magic_round := -1
|
||||
var magic_seen := 0
|
||||
var hit_rows := 0
|
||||
for row in run1.rows:
|
||||
if row.get("type", "") == "flag_miss":
|
||||
miss_rows += 1
|
||||
if String(row.get("source_class", "")) == "flag_miss" and String(row.get("type", "")) in ["effect", "damage"]:
|
||||
negative_rows += 1 # 标错的负向兑付行(该安全格资源的负向字典行——effect/直伤)
|
||||
if row.get("source_class", "") == "hero_attack" and int(row.get("magic", 0)) >= 1:
|
||||
magic_round = int(row.get("round_index", -1))
|
||||
magic_seen = int(row.get("magic", 0))
|
||||
if String(row.get("source_class", "")) == "flag_hit":
|
||||
hit_rows += 1 # 标中的正向兑付行(该雷格资源的正向字典行——2026-09-19 充能退役后标中只走格面资源正向)
|
||||
check(miss_rows >= 1, "标错旗产生 flag_miss 归因行(×%d)" % miss_rows)
|
||||
check(negative_rows >= 1, "标错旗兑付该安全格资源的负向字典行(×%d——attack/defense/weaken 效果行或 heal 直伤行)" % negative_rows)
|
||||
check(magic_round >= 1, "标中雷充能进主角攻击(magic=%d @R%d——每雷实例一次)" % [magic_seen, magic_round])
|
||||
check(hit_rows >= 1, "标中旗兑付该雷格资源的正向字典行(×%d——充能退役后无魔法分量,正向=格面资源)" % hit_rows)
|
||||
# ④ 同种子回放(极性路由零随机——同种子逐行相同)
|
||||
var run2 = _run_flag_backlash_round(20260912)
|
||||
check(JSON.stringify(run1.rows) == JSON.stringify(run2.rows), "标错惩罚轮回放断言:同种子步骤流逐行相同(类型化负向兑付确定性)")
|
||||
|
||||
Reference in new issue
Block a user