merge: 伏击上限删除批+格面资源八型批落籍——分支 ambush-cap-review-20260919/20 两笔(656f000 上限删除/9类行协议+7876642 八型批:伏击退役换装警觉、action 第8型行动点±1、护甲型±1、六 GDD+registry+06+ux 同步、工具口径注记);冒烟 330 项 0 失败;台账 101;警觉/行动点 provisional 归曲线调参批(06 已登记),读数位移待重投,真人批建议终版规则上执行
# Conflicts: # design/gdd/06-待办清单.md # design/gdd/systems/战斗循环/战斗结算.md # design/registry/entities.yaml # design/ux/battle.md # prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd
This commit is contained in:
commit
3468066fcb
33 files changed
+275
-267
No files matched your search
@@ -30,6 +30,7 @@ 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:
|
||||
@@ -118,6 +119,16 @@ func notify_settlement_done(next_round_mode: String) -> bool:
|
||||
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 # 战斗首轮唯一出口
|
||||
@@ -126,7 +137,8 @@ func notify_round_start() -> bool:
|
||||
else:
|
||||
_reject("phase")
|
||||
return false
|
||||
_ap = db.action_economy.ap_per_round # 每轮硬重置(RAP 不动——唯一跨轮携带量)
|
||||
_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
|
||||
|
||||
@@ -20,9 +20,9 @@ signal round_started(round_index: int)
|
||||
signal board_changed()
|
||||
signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析)
|
||||
|
||||
# 明细流水行型协议(战斗结算 Core Rule;10 类——2026-09-15 格面资源批由 15 类收拢,
|
||||
# 清单改动须同步 battle_log_view 的 _format_row 与本文档头注):
|
||||
# 效果行 effect · 伤害行 damage · 治疗行 heal · 溢出未生效行 overflow ·
|
||||
# 明细流水行型协议(战斗结算 Core Rule;9 类——2026-09-20 伏击单轮上限退役,溢出未生效行 overflow 随之下线;
|
||||
# 2026-09-15 格面资源批由 15 类收拢,清单改动须同步 battle_log_view 的 _format_row 与本文档头注):
|
||||
# 效果行 effect · 伤害行 damage · 治疗行 heal ·
|
||||
# 奖励未生效行 reward_ineffective · 标旗未中行 flag_miss · 施放行 cast(敌人技能池批由蓄力行 charge 改型)· 衰减行 decay ·
|
||||
# 判定行 verdict · 高光行 highlight
|
||||
# 行来源断言豁免集合(行型层)——判定段 / 衰减段本就无棋盘来源,勿一刀切误炸
|
||||
@@ -58,8 +58,6 @@ var _flag_consumed := {} # 兑付资格以雷格实例为键
|
||||
var _flag_miss_seen := {} # 标旗未中行去重(每安全格实例一次)
|
||||
var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置)
|
||||
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 收口——表值运行期不可变)
|
||||
|
||||
|
||||
@@ -113,8 +111,6 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
_fatal("敌人系统 spawn 被拒")
|
||||
return false
|
||||
var erow: Dictionary = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth))
|
||||
_enemy_cap = int(erow.get("advantage_per_round_cap", 0))
|
||||
_m_amb = int(db.status_effect_row("ambush").get("magnitude", 0))
|
||||
if not _install_new_board():
|
||||
return false
|
||||
_reset_round_facts() # 首板 install 的初始展开事件不入轮 facts(resource 恒 false 的双保险)
|
||||
@@ -369,7 +365,7 @@ func _apply_percent_row(row: Dictionary, source_class: String, cell: Vector2i, r
|
||||
|
||||
|
||||
# flat 行兑付(负向恒即时,无库存语义):
|
||||
# blast 正向 = 全体存活敌各 power 点(无敌 → 奖励未生效行);blast 负向 = 敌方伏击 +stacks 层(cap/溢出行协议不变);
|
||||
# blast 正向 = 全体存活敌各 power 点(无敌 → 奖励未生效行);blast 负向 = 警觉 percent 行(effect_id=alert,走 _apply_percent_row 不入本函数);
|
||||
# heal 正向 = 主角回复 clamp(满血 → 奖励未生效行);heal 负向 = 主角直伤 power 点(不经增益状态;归零 → 战斗段短路/判定段判负)。
|
||||
# pierce/magic(伤害道具批 2026-09-18)正向 = 单目标克制直伤(必中全额);负向 = 主角直伤。
|
||||
# 伤害落地全局下限(2026-09-20 用户拍板「任何伤害取值域 [1,+∞)」):本函数全部伤害兑付点经 damage_floor 保底——
|
||||
@@ -379,9 +375,12 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri:
|
||||
var ctype := String(row.cell_type)
|
||||
var power := int(row.power)
|
||||
var display := String(row.display_name) + label_suffix
|
||||
if ctype == "action":
|
||||
var delta: int = power if String(row.get("polarity", "positive")) == "positive" else -power
|
||||
action_economy.queue_ap_delta(delta)
|
||||
_emit_row("effect", source_class, cell, ri, "%s(下轮行动点 %+d)" % [display, delta], delta, {"effect_id": "ap_delta", "pool_value": action_economy.pending_ap_delta()}, highlight)
|
||||
return "redeemed"
|
||||
if ctype == "blast":
|
||||
if String(row.get("polarity", "positive")) == "negative":
|
||||
return _apply_ambush_grant(row, source_class, cell, ri, highlight, label_suffix)
|
||||
var alive: Array = _enemy_system.query_alive("enemy")
|
||||
if alive.is_empty():
|
||||
_emit_row("reward_ineffective", source_class, cell, ri, "%s 未生效(无存活敌人)" % display, 0, {"batch_key": String(row.reward_id)}, highlight)
|
||||
@@ -429,21 +428,6 @@ func _apply_flat_row(row: Dictionary, source_class: String, cell: Vector2i, ri:
|
||||
return "zero"
|
||||
|
||||
|
||||
# blast 负向:敌方伏击 +stacks 层(伏击池 cap 与"溢出未生效"行协议不变——敌人系统 F3 逐层守恒,
|
||||
# 多源共享同一池值;单结算内先到先占 cap,超出部分逐层记溢出行)
|
||||
func _apply_ambush_grant(row: Dictionary, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "") -> String:
|
||||
var requested: int = maxi(1, int(row.get("stacks", 1)))
|
||||
var remaining_cap: int = maxi(0, _enemy_cap - _status_effects.pool_layers("ambush"))
|
||||
var plan: Dictionary = _enemy_system.ambush_grant_plan(requested, remaining_cap)
|
||||
for i in range(int(plan.granted)):
|
||||
_status_effects.apply_effect("ambush", {"source_class": source_class, "origin_cell": cell, "round_index": ri})
|
||||
if int(plan.granted) > 0:
|
||||
_emit_row("effect", source_class, cell, ri, "敌方伏击 +%d 层%s" % [int(plan.granted), label_suffix], int(plan.granted), {"effect_id": "ambush", "pool_value": _status_effects.pool_layers("ambush") * _m_amb}, highlight)
|
||||
for i in range(int(plan.overflow)):
|
||||
_emit_row("overflow", source_class, cell, ri, "伏击溢出未生效", 0, {"batch_key": "ambush_overflow"})
|
||||
return "redeemed" if int(plan.granted) > 0 else "zero"
|
||||
|
||||
|
||||
# 旗判定批:标中 = 该雷格资源的正向兑付(每雷实例一次;高光 = 强制检查点);
|
||||
# 标空 = "标旗未中"归因行 + 该安全格资源的负向兑付(同键去重——每格实例一次)。
|
||||
func _judge_flag(evt: Dictionary, ri: int) -> void:
|
||||
@@ -530,7 +514,6 @@ func _roll_round_actions() -> void:
|
||||
# ---- 战斗段 ----
|
||||
|
||||
func _combat_segment(ri: int) -> void:
|
||||
var L := _status_effects.pool_layers("ambush") # 授予段后的当轮值;池共享不消耗
|
||||
# ⓪ 主角先手攻击(基础攻击恒存在——2026-09-11 用户裁决;先于敌方行动;授予段负向直伤致死 → 致电短路不先手)。
|
||||
# 单分量伤害构成(2026-09-19 充能退役批·用户拍板):主角攻击 = 唯一基础分量(atk,吃掷点含暴击;
|
||||
# 类型 = 主角 attack_type 表值——2026-09-18 营地扩展前置批:量与类型抽象分离;查甲效矩阵对应行取克制修正)。
|
||||
@@ -577,8 +560,8 @@ func _combat_segment(ri: int) -> void:
|
||||
_fatal("敌实例待发动作悬空: %s" % skill_id)
|
||||
continue
|
||||
if String(srow.type) == "buff" or String(srow.type) == "debuff":
|
||||
# 增益/妨害技(新效应词汇批 2026-09-19)= 0 伤害施放行:不兑付伏击(旧蓄力拍"零兑付"语义由施放行
|
||||
# 继承——本轮喂的伏击逢施放拍不兑现的读怪奖励保留);状态授予即数值兑现,技能冷却与增益持续两计时器分账。
|
||||
# 增益/妨害技(新效应词汇批 2026-09-19)= 0 伤害施放行:不掷不折;状态授予即数值兑现,技能冷却与增益持续两计时器分账。
|
||||
# (原"本轮喂的伏击逢施放拍不兑现"读怪奖励随伏击退役一并作废——2026-09-20 八型批)
|
||||
# 落点由状态行 target_side 路由(buff → 敌方侧 / debuff → 主角侧——容器零改动,proposal-status-vocabulary §1)。
|
||||
var dur: int = int(srow.get("buff_duration", 1))
|
||||
_status_effects.apply_effect(String(srow.buff_ref), {"source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "round_index": ri}, dur)
|
||||
@@ -587,7 +570,7 @@ func _combat_segment(ri: int) -> void:
|
||||
continue
|
||||
# 伤害技(含普攻——倍率恒 1):三乘法链 ① A = 攻击基线 × 技能倍率(判定对象 = 技能基础伤害分量)
|
||||
# → ② d20 vs 主角 AC(nat20 暴击 ≠ 技能重击,两层严格区分)→ ③ +克制修正点数(敌打主角无甲轴恒中性 delta 0——加法核 2026-09-18 换装)
|
||||
# → 伏击附加(后置加法、必中全额、不吃 AC)→ 增益池乘区 → 伤害下限 max(1,·)
|
||||
# → 增益池乘区 → 伤害下限 max(1,·)
|
||||
# → 必中附加分量 rider(新效应词汇批 2026-09-19:后置加法、必中全额、不进乘区不出暴击——
|
||||
# proposal-status-vocabulary §2;价位锚 rider_mult=0.75 = AC 折扣档最轻档"不掉档的稳定伤害")。
|
||||
var baseline: int = int(round(float(u.atk) * float(srow.get("damage_mult", 1.0))))
|
||||
@@ -599,10 +582,10 @@ func _combat_segment(ri: int) -> void:
|
||||
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), 0) # 敌打主角无甲轴恒中性——加法核中性 delta = 0(原 percent 100 退役)
|
||||
var d_tick := _resolve_damage(rolled_base + L * _m_amb)
|
||||
var d_tick := _resolve_damage(rolled_base)
|
||||
d_tick = maxi(int(db.settlement.damage_floor), d_tick) + rider # rider 后置于伤害下限——独立必中加项,不进乘区
|
||||
_enemy_system.apply_damage(_enemy_system.hero_unit(), d_tick)
|
||||
var payload_e: Dictionary = {"target": "hero", "skill_id": skill_id, "baseline": baseline, "attach": L * _m_amb,
|
||||
var payload_e: Dictionary = {"target": "hero", "skill_id": skill_id, "baseline": baseline,
|
||||
"roll": int(roll_read_e.roll), "ac": threshold_e, "tier": discount_e}
|
||||
if crit_e:
|
||||
payload_e["crit"] = true
|
||||
@@ -648,7 +631,12 @@ func _defender_ac(unit: Dictionary) -> int:
|
||||
if not row.is_empty() and String(row.unit) == "ac":
|
||||
total += int(eff.effective_value)
|
||||
return total
|
||||
return int(unit.get("armor_class", 0))
|
||||
var total: int = int(unit.get("armor_class", 0))
|
||||
for eff_e in _status_effects.query_active("enemy"):
|
||||
var erow_ac: Dictionary = db.status_effect_row(eff_e.effect_id)
|
||||
if not erow_ac.is_empty() and String(erow_ac.unit) == "ac":
|
||||
total += int(eff_e.effective_value)
|
||||
return total
|
||||
|
||||
|
||||
# F2 乘区:池内加算 → clamp [-100,+100] → 池间乘算 → floor(单一取整规则)——算术归 DamageMath(O3)
|
||||
@@ -731,7 +719,6 @@ func _emit_settlement_snapshot(verdict: String) -> void:
|
||||
"board": board_stats,
|
||||
"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(), },
|
||||
})
|
||||
|
||||
@@ -796,18 +783,17 @@ func _battle_over(result: String) -> void:
|
||||
# 协作对象(_enemy_system/_status_effects/_reward_economy)私有化后,跨层读取一律经本快照或下列命名面(diagnostic_snapshot 同款模式,
|
||||
# 禁再链式穿透 settlement.enemy_system.hero 一类路径)。载荷 schema 成文(code-conventions 2.1):
|
||||
# round / battle_result / phase / ap / rap / can_refresh / hero{hp,max_hp,atk,alive,armor_class} /
|
||||
# enemies[{order,display_name,hp,max_hp,enemy_id,armor_type,armor_class,pending_skill,pending_type,pending_name,effects[],ambush_layers,alive}] /
|
||||
# (敌人技能池批批 2 定稿:beat/ring_size/ring_index 拆除 → pending_* 三字段 + 敌方增益 effects 投影;池与权重不公开)
|
||||
# ambush_layers / hero_effects[](增益状态 query_active 投影)
|
||||
# enemies[{order,display_name,hp,max_hp,enemy_id,armor_type,armor_class,pending_skill,pending_type,pending_name,effects[],alive}] /
|
||||
# (敌人技能池批批 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 all: Array = _enemy_system.all_enemies()
|
||||
var ambush: int = _status_effects.pool_layers("ambush")
|
||||
# 敌方增益投影(side-wide 状态——蓄力/硬化/削弱/暴走等 percent 行;伏击单独走 ambush_layers)
|
||||
# 敌方增益投影(side-wide 状态——蓄力/硬化/削弱/暴走 percent 行 + 警觉 ac 行,八型批 2026-09-20)
|
||||
var enemy_effects: Array = []
|
||||
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) == "percent" and String(eff.effect_id) != "ambush":
|
||||
if not erow.is_empty() and String(erow.unit) in ["percent", "ac"]:
|
||||
enemy_effects.append({"effect_id": eff.effect_id, "display_name": String(eff.display_name), "layers": int(eff.layers)})
|
||||
for i in all.size():
|
||||
var u: Dictionary = all[i]
|
||||
@@ -827,7 +813,6 @@ func presentation_snapshot() -> Dictionary:
|
||||
"pending_type": String(pending_row.get("type", "")) if not pending_row.is_empty() else "",
|
||||
"pending_name": String(pending_row.get("display_name", "")) if not pending_row.is_empty() else "",
|
||||
"effects": enemy_effects,
|
||||
"ambush_layers": ambush,
|
||||
"alive": alive,
|
||||
})
|
||||
var hero := {}
|
||||
@@ -842,7 +827,6 @@ func presentation_snapshot() -> Dictionary:
|
||||
"can_refresh": can_refresh(),
|
||||
"hero": hero,
|
||||
"enemies": enemies,
|
||||
"ambush_layers": ambush,
|
||||
"hero_effects": _status_effects.query_active("hero"),
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ func polarity_row(cell_type: String, polarity: String) -> Dictionary:
|
||||
and String(row.get("cell_type", "")) == cell_type \
|
||||
and String(row.get("polarity", "")) == polarity:
|
||||
return row.duplicate() # E6:表行值拷贝出口——消费方禁改配置源
|
||||
_fatal("极性字典缺行(五型×正负齐备为加载前提): %s %s" % [cell_type, polarity])
|
||||
_fatal("极性字典缺行(八型×正负齐备为加载前提): %s %s" % [cell_type, polarity])
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ func apply_effect(effect_id: String, source: Dictionary, duration_override: int
|
||||
for inst in _instances:
|
||||
if inst.effect_id == effect_id:
|
||||
if inst.layers >= max_stack:
|
||||
return false # 满层拒绝原子——容器零写入(溢出行归 战斗结算)
|
||||
return false # 满层拒绝原子——容器零写入(拒收由战斗结算记"奖励未生效"行)
|
||||
inst.layers += 1 # stack 授予不触碰 R(spec 决议)
|
||||
return true
|
||||
_instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display))
|
||||
|
||||
@@ -82,7 +82,6 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_override:
|
||||
"pending_skill": "", # 本拍摇定动作(开轮摇号写入;兑现前死亡 = 作废不 commit)
|
||||
"roll_seq": 0, # 单敌摇号序(behavior_roll 子流 scope 派生向量——registry 登记)
|
||||
"basic_streak": 0, # 保底计数:连续执行普攻的拍数(执行上报驱动)
|
||||
"advantage_per_round_cap": int(row.advantage_per_round_cap),
|
||||
"armor_type": String(row.get("armor_type", "light")), # 护甲类型快照(克制矩阵消费面——战斗结算·战斗段;2026-09-15 命名统一批:light/ward/heavy)
|
||||
"armor_class": int(row.get("armor_class", 0)), # 护甲等级快照(掷点判定阈值基数——战斗结算·战斗段;2026-09-16 护甲等级掷点批)
|
||||
"alive": true,
|
||||
@@ -238,12 +237,6 @@ func all_enemies() -> Array:
|
||||
return _enemies.duplicate()
|
||||
|
||||
|
||||
# F3 机制算术纯函数(战斗结算 预截流消费面):cap 判定在授予前;守恒 granted + overflow = mines_r
|
||||
func ambush_grant_plan(mines_r: int, cap: int) -> Dictionary:
|
||||
var granted: int = mini(mines_r, cap)
|
||||
return {"granted": granted, "overflow": mines_r - granted}
|
||||
|
||||
|
||||
func despawn_all() -> bool:
|
||||
if _despawned or not _spawned:
|
||||
_diag("重复/无效 despawn_all")
|
||||
|
||||
@@ -86,9 +86,6 @@ func validate() -> Array:
|
||||
var w: int = int(entry.get("weight", 0))
|
||||
if w <= 0 or w > 9999:
|
||||
errs.append({"category": "domain", "message": "%s pool 权重越域 (0,9999]: %d" % [eid, w]})
|
||||
var cap: int = int(row.get("advantage_per_round_cap", -1))
|
||||
if cap < 0 or cap > 16:
|
||||
errs.append({"category": "domain", "message": "%s advantage_per_round_cap 越域 [0,16]" % eid})
|
||||
# armor_type(可省略——缺省 light 向后兼容)
|
||||
var at: String = String(row.get("armor_type", "light"))
|
||||
if not ARMOR_TYPES.has(at):
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
# Date: 2026-09-10(2026-09-15 格面资源批结构性重写:category/use_timing/库存/供给轴下线,五型 × 极性字典取代)
|
||||
# 数据配置 RewardTable schema(地图战斗资源 回填值)。
|
||||
# 行 = Dictionary,键 reward_id(Rule 4 豁免:定义表);跨字段校验族 + 池护栏。
|
||||
# 五型封闭枚举(attack/defense/heal/blast/weaken)× 极性(positive/negative):
|
||||
# 八型封闭枚举(attack/defense/heal/blast/weaken/pierce/magic/action)× 极性(positive/negative):
|
||||
# 正向行 = 摇号池(权重 = 类型频次);负向行 = 纯兑付字典(不消费权重)。
|
||||
class_name RewardTable
|
||||
extends Resource
|
||||
|
||||
@export var rows: Array = [] # Array[Dictionary]
|
||||
|
||||
const CELL_TYPES := ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic"] # 七型封闭(伤害道具批 2026-09-18 五→七)——清单外加载拒绝
|
||||
const CELL_TYPES := ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic", "action"] # 八型封闭(格面资源八型批 2026-09-20 七→八)——清单外加载拒绝
|
||||
const POLARITIES := ["positive", "negative"]
|
||||
const POOL_TAGS := ["cell", "clear_default"] # cell = 格面资源单一池;clear_<biome> 命名空间预留拒绝
|
||||
const CELL_TYPE_KEYS := ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic"] # 字典完整性校验轴(类型 × 极性 = 十四格)
|
||||
const CELL_TYPE_KEYS := ["attack", "defense", "heal", "blast", "weaken", "pierce", "magic", "action"] # 字典完整性校验轴(类型 × 极性 = 十六格)
|
||||
|
||||
|
||||
func validate() -> Array:
|
||||
@@ -61,15 +61,12 @@ func validate() -> Array:
|
||||
if stacks < 1 or stacks > 16:
|
||||
errs.append({"category": "domain", "message": "%s stacks 越域 [1,16]: %d" % [rid, stacks]})
|
||||
# 跨字段:effect_id 非空 = percent 行(引用悬空校验归 ConfigDB 跨表通道);空 = flat 行。
|
||||
# flat 行 power ≥ 1(直付行:治疗/群伤/直伤)——例外 = blast 负向(伏击加层行,消费 stacks 不消费 power)。
|
||||
# flat 行 power ≥ 1(直付/直兑行:治疗/群伤/直伤/行动点)——action 型 = 排队点数行(power = ±点数量级)。
|
||||
var eff: String = row.get("effect_id", "")
|
||||
var is_layer_row: bool = eff == "" and ctype == "blast" and pol == "negative"
|
||||
if power < 0 or power > 999:
|
||||
errs.append({"category": "domain", "message": "%s power 越域 [0,999]: %d" % [rid, power]})
|
||||
if eff == "" and power < 1 and not is_layer_row:
|
||||
if eff == "" and power < 1:
|
||||
errs.append({"category": "cross_field", "message": "%s flat 行 power ≥ 1" % rid})
|
||||
if is_layer_row and int(row.get("stacks", 1)) < 1:
|
||||
errs.append({"category": "cross_field", "message": "%s 加层行 stacks ≥ 1" % rid})
|
||||
# 区间配对(缺省 = 定值行向后兼容):必成对 · percent 行禁止 · power = 中点锚
|
||||
var has_min: bool = row.has("power_min")
|
||||
var has_max: bool = row.has("power_max")
|
||||
|
||||
@@ -22,7 +22,6 @@ rows = [{
|
||||
"skill_id": "lunge",
|
||||
"weight": 1
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -43,7 +42,6 @@ rows = [{
|
||||
"skill_id": "lunge",
|
||||
"weight": 1
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -66,7 +64,6 @@ rows = [{
|
||||
"skill_id": "heavy",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -90,7 +87,6 @@ rows = [{
|
||||
"skill_id": "heavy",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -113,7 +109,6 @@ rows = [{
|
||||
"skill_id": "harden",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -136,7 +131,6 @@ rows = [{
|
||||
"skill_id": "heavy",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -160,7 +154,6 @@ rows = [{
|
||||
"skill_id": "heavy",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -183,7 +176,6 @@ rows = [{
|
||||
"skill_id": "charge",
|
||||
"weight": 1
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -206,7 +198,6 @@ rows = [{
|
||||
"skill_id": "heavy",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -230,7 +221,6 @@ rows = [{
|
||||
"skill_id": "charge",
|
||||
"weight": 1
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -254,7 +244,6 @@ rows = [{
|
||||
"skill_id": "charge",
|
||||
"weight": 1
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "light",
|
||||
"armor_class": 0
|
||||
}, {
|
||||
@@ -280,7 +269,6 @@ rows = [{
|
||||
"skill_id": "web_snare",
|
||||
"weight": 2
|
||||
}],
|
||||
"advantage_per_round_cap": 4,
|
||||
"armor_type": "ward",
|
||||
"armor_class": 0
|
||||
}]
|
||||
|
||||
@@ -87,13 +87,12 @@ rows = [{
|
||||
"pool_tag": "cell",
|
||||
"tier": 1
|
||||
}, {
|
||||
"reward_id": "ambush_double",
|
||||
"reward_id": "enemy_alert",
|
||||
"cell_type": "blast",
|
||||
"polarity": "negative",
|
||||
"effect_id": "",
|
||||
"display_name": "敌方伏击",
|
||||
"effect_id": "alert",
|
||||
"display_name": "警觉",
|
||||
"power": 0,
|
||||
"stacks": 2,
|
||||
"weight": 0,
|
||||
"pool_tag": "cell",
|
||||
"tier": 1
|
||||
@@ -161,6 +160,26 @@ rows = [{
|
||||
"weight": 0,
|
||||
"pool_tag": "cell",
|
||||
"tier": 1
|
||||
}, {
|
||||
"reward_id": "ap_up",
|
||||
"cell_type": "action",
|
||||
"polarity": "positive",
|
||||
"effect_id": "",
|
||||
"display_name": "行动点",
|
||||
"power": 1,
|
||||
"weight": 6,
|
||||
"pool_tag": "cell",
|
||||
"tier": 1
|
||||
}, {
|
||||
"reward_id": "ap_drain",
|
||||
"cell_type": "action",
|
||||
"polarity": "negative",
|
||||
"effect_id": "",
|
||||
"display_name": "迟滞",
|
||||
"power": 1,
|
||||
"weight": 0,
|
||||
"pool_tag": "cell",
|
||||
"tier": 1
|
||||
}]
|
||||
|
||||
; 格面资源批(2026-09-15 用户拍板——registry reward_table 条目为现行 schema 事实源):
|
||||
@@ -168,8 +187,11 @@ rows = [{
|
||||
; 装板期一次摇定;摇号只消费正向行权重(类型频次 = attack 20 / defense 16 / blast 16 / heal 3 / weaken 12,合计 67;
|
||||
; 头段"heal 15 / weaken 12,合计 79"为历史注记——数值压缩批后 heal 3、合计 67,见本文件尾部 2026-09-17 批注);
|
||||
; 负向行 weight=0 不参与摇号,纯(类型, 极性)兑付字典——heal 负向 = 主角直伤 4(不经增益状态),
|
||||
; blast 负向 = ambush +2 层(stacks=2,伏击池 cap 与"溢出未生效"行协议不变)。
|
||||
; blast 负向 = 警觉(敌方 AC +1,2026-09-20 伏击退役换装)。
|
||||
; 可选类三行(knife_t1/knife_adv/heal_pack)与注入池 active_inject 随可选类道具整体删除(供给控制器/保底/窗口同步下线)。
|
||||
; 格面资源八型批(2026-09-20 用户拍板):① blast 负向 ambush_double 退役 → enemy_alert(警觉,effect_id=alert
|
||||
; 敌方 AC +1 percent 行);② 新增第 8 型 action 两行——ap_up(正向,下轮行动点 +1)/ ap_drain(负向,下轮
|
||||
; 行动点 −1),排队兑现于下一轮开始(action_economy.queue_ap_delta);③ off_guard 数值 −2→−1 归 status_effects.tres。
|
||||
; 区间行(伤害量值区间批 2026-09-14):heal_minor [2,6] · clear_blast [25,55]——中点锚 power 恒等期望当量,
|
||||
; roll 时机 = 身份预分配批内追加取号(既有取号序零扰动);spark_aoe 定值行无区间字段。
|
||||
; 减伤墙调参批(2026-09-13):defense_up 权重 24→16——攻击成为池内最高行(历史注记)。
|
||||
|
||||
@@ -19,7 +19,6 @@ source_label_map = {
|
||||
effect_pool_map = {
|
||||
"attack_up": "hero_offense",
|
||||
"weaken": "enemy_output",
|
||||
"ambush": "flat_attach",
|
||||
"enrage": "enemy_output",
|
||||
"sapped": "hero_offense",
|
||||
"empower": "enemy_output",
|
||||
@@ -63,7 +62,7 @@ armor_roll_stream = "armor_roll"
|
||||
; source_label_map 删 item 键;effect_pool_map 增 sapped→hero_offense(weaken 型负向——乏力)。
|
||||
; enrage(敌方暴走 +25%,enemy_output 池)/ off_guard(我方失衡 +25%,hero_defense 池)/
|
||||
; sapped(我方乏力 -25%,hero_offense 池)= attack/defense/weaken 三型负向字典行
|
||||
; (极性路由兑付:挖雷格 / 标错安全格按该格 cell_type 取负向行;heal 负向 = 主角直伤不经状态、blast 负向 = ambush+2 层)。
|
||||
; (极性路由兑付:挖雷格 / 标错安全格按该格 cell_type 取负向行;heal 负向 = 主角直伤不经状态、blast 负向 = 警觉(敌方 AC +1,2026-09-20 八型批换装))。
|
||||
; 减伤墙调参批(2026-09-13):pool_clamp 域 [-100,+100] 结构冻结不动;减伤杠杆走 status_effects.tres(批注在该表)。
|
||||
; 护甲等级掷点批(2026-09-16 用户拍板,数字即规范——战斗结算 GDD 护甲等级掷点节定稿同批):
|
||||
; roll_faces=20 / roll_tier_bounds=[3,7] / roll_tier_discounts=[75,50,25] / crit_roll=20 / crit_multiplier=200 /
|
||||
|
||||
@@ -23,15 +23,6 @@ rows = [{
|
||||
"stack_policy": "stack",
|
||||
"max_stack": 2
|
||||
}, {
|
||||
"effect_id": "ambush",
|
||||
"display_name": "伏击",
|
||||
"target_side": "enemy",
|
||||
"magnitude": 1,
|
||||
"unit": "flat",
|
||||
"duration_settlements": 1,
|
||||
"stack_policy": "stack",
|
||||
"max_stack": 16
|
||||
}, {
|
||||
"effect_id": "weaken",
|
||||
"display_name": "削弱",
|
||||
"target_side": "enemy",
|
||||
@@ -53,7 +44,7 @@ rows = [{
|
||||
"effect_id": "off_guard",
|
||||
"display_name": "失衡",
|
||||
"target_side": "hero",
|
||||
"magnitude": -2,
|
||||
"magnitude": -1,
|
||||
"unit": "ac",
|
||||
"duration_settlements": 1,
|
||||
"stack_policy": "stack",
|
||||
@@ -85,6 +76,15 @@ rows = [{
|
||||
"duration_settlements": 2,
|
||||
"stack_policy": "refresh",
|
||||
"max_stack": 1
|
||||
}, {
|
||||
"effect_id": "alert",
|
||||
"display_name": "警觉",
|
||||
"target_side": "enemy",
|
||||
"magnitude": 1,
|
||||
"unit": "ac",
|
||||
"duration_settlements": 1,
|
||||
"stack_policy": "stack",
|
||||
"max_stack": 16
|
||||
}]
|
||||
|
||||
; 减伤墙调参批(2026-09-13,playtest-2026-09-12 报告 Q1 触发——defense_scan 实测墙轮 39%、
|
||||
@@ -120,3 +120,8 @@ rows = [{
|
||||
; weaken(削弱)叠层改续时:stack_policy stack→refresh、duration_settlements 1→2(同名重复授予
|
||||
; 只刷新剩余结算数、层数恒 1——结构性消饱和,×2 叠满池触底 ×0 免伤轮从根杜绝;refresh 实现与
|
||||
; empower/fortify 同款零代码)。duration 终值、off_guard N 值、当量锚复算全部归 状态层重设计批。
|
||||
;
|
||||
; 格面资源八型批(2026-09-20 用户拍板):① off_guard magnitude −2→−1(护甲型奖励/惩罚数值对齐 = ±1);
|
||||
; ② ambush(伏击)行整体退役——群伤负向不再放大敌方伤害;③ 新增 alert(警觉:敌方护甲等级 +1,
|
||||
; target_side enemy、unit ac、1 结算、stack 上限 16)——blast 型负向新载体,消费 = _defender_ac 敌方侧
|
||||
; ac 型读法(本批新接线)。状态表九行→九行(伏击出、警觉进)。数值全部 provisional 归 难度曲线与Boss。
|
||||
@@ -15,7 +15,7 @@ signal review_requested
|
||||
|
||||
const BAND_H_DIG := 24
|
||||
const BAND_H_SETTLE := 84
|
||||
const AGG_TYPES := ["reward_ineffective", "overflow"]
|
||||
const AGG_TYPES := ["reward_ineffective"]
|
||||
const COL_GOLD := "#f0a828"
|
||||
const COL_INK := "#33261b"
|
||||
|
||||
@@ -194,8 +194,8 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
|
||||
return prefix + "[color=%s]%s[/color]" % [body_col, body]
|
||||
"heal":
|
||||
return prefix + "[color=#7cc47c]%s +%d%s%s[/color]" % [name_, v, tail, suffix]
|
||||
"reward_ineffective", "overflow":
|
||||
# 未生效两类行:⚠ icon + 划线形状冗余(非色相独载)+ 聚合计数
|
||||
"reward_ineffective":
|
||||
# 未生效类行:⚠ icon + 划线形状冗余(非色相独载)+ 聚合计数
|
||||
var n := (" ×%d" % agg_count) if agg_count > 1 else ""
|
||||
return prefix + "[s][color=#b08060]⚠ %s%s[/color][/s]%s" % [name_, n, suffix]
|
||||
"flag_miss":
|
||||
|
||||
@@ -78,7 +78,7 @@ func _init() -> void:
|
||||
|
||||
|
||||
# data = 战斗结算 presentation_snapshot 投影:{order, display_name, hp, max_hp, enemy_id,
|
||||
# pending_skill/pending_type/pending_name(摇定动作三字段), effects[](敌方增益), ambush_layers}
|
||||
# pending_skill/pending_type/pending_name(摇定动作三字段), effects[](敌方增益,含警觉 ac 行)}
|
||||
func set_data(d: Dictionary) -> void:
|
||||
_name_label.text = "%s" % String(d.get("display_name", "敌人"))
|
||||
_hp_bar.max_value = maxi(1, int(d.get("max_hp", 1)))
|
||||
@@ -87,11 +87,8 @@ func set_data(d: Dictionary) -> void:
|
||||
_armor = String(d.get("armor_type", "light"))
|
||||
_armor_class = int(d.get("armor_class", 0))
|
||||
set_pending(String(d.get("pending_type", "")), String(d.get("pending_skill", "")))
|
||||
# 状态行:伏击池(flat 共享池,单独通道)+ 敌方增益 percent 状态(蓄力/硬化/削弱/暴走——side-wide)
|
||||
# 状态行:敌方增益状态(蓄力/硬化/削弱/暴走 percent + 警觉 ac——side-wide,八型批 2026-09-20)
|
||||
var parts: Array = []
|
||||
var ambush: int = int(d.get("ambush_layers", 0))
|
||||
if ambush > 0:
|
||||
parts.append("伏击池 ×%d(全体共享)" % ambush)
|
||||
for eff in d.get("effects", []):
|
||||
parts.append("%s ×%d" % [String(eff.get("display_name", "")), int(eff.get("layers", 1))])
|
||||
_status_label.text = " · ".join(parts)
|
||||
|
||||
@@ -11,7 +11,7 @@ extends HBoxContainer
|
||||
const MAX_SLOTS := 4
|
||||
# 极性/语义映射(数据源 = SettlementTable.effect_pool_map 效果语义 + StatusEffectTable display_name):
|
||||
# 益 = ▲/◆/▼/◇(金/蓝族);害 = ⚡/⚔/✕/☾/⇧/▣(旗朱族;⇧/▣ = 敌方增益威胁纹样)——形状+色相双通道,
|
||||
# 文字名常驻第三通道。状态表九行全映射(attack_up/ac_up/weaken/ambush/enrage/off_guard/sapped/empower/fortify)
|
||||
# 文字名常驻第三通道。状态表九行全映射(attack_up/ac_up/weaken/alert/enrage/off_guard/sapped/empower/fortify)
|
||||
# ——HUD框架 图标键契约"一键一枚专属纹样,查不到的键立即断言 + ? 占位 + 诊断"
|
||||
# (2026-09-15 格面资源批补 sapped;2026-09-16 护甲等级掷点批 defense_up → ac_up 换装;
|
||||
# 同日 敌人技能池批补 empower/fortify 敌方增益两键——敌方增益显示 = 敌卡状态行文本 + 本图标键词汇)。
|
||||
@@ -19,7 +19,7 @@ const EFFECT_ICON := {
|
||||
"attack_up": ["▲", Color8(176, 122, 20), "益"],
|
||||
"ac_up": ["◇", Color8(46, 110, 142), "益"],
|
||||
"weaken": ["▼", Color8(46, 110, 142), "益"],
|
||||
"ambush": ["⚡", Color8(198, 61, 34), "害"],
|
||||
"alert": ["◎", Color8(198, 61, 34), "害"],
|
||||
"enrage": ["⚔", Color8(198, 61, 34), "害"],
|
||||
"off_guard": ["✕", Color8(198, 61, 34), "害"],
|
||||
"sapped": ["☾", Color8(198, 61, 34), "害"],
|
||||
@@ -30,12 +30,12 @@ const EFFECT_SEMANTIC := {
|
||||
"attack_up": "主角攻击加成(hero_offense 池加算)",
|
||||
"ac_up": "护甲等级 +1/层(掷点折扣判定阈值——AC 数值轴,受击低掷按档位打折)",
|
||||
"weaken": "敌方输出削弱(enemy_output 池乘算)",
|
||||
"ambush": "敌方全体攻击附加(flat_attach——blast 型负向,战斗段共享不消耗)",
|
||||
"alert": "敌方护甲等级 +1/层(blast 型负向——我方普通攻击掷点更易落入折扣档)",
|
||||
"enrage": "敌方输出加成(enemy_output 池加算——attack 型负向)",
|
||||
"off_guard": "主角承伤加深(hero_defense 池加算——defense 型负向)",
|
||||
"sapped": "主角输出降低(hero_offense 池加算——weaken 型负向)",
|
||||
"empower": "敌方输出 ×2(enemy_output 池加算——蓄力技施放;持续 1 个有效结算)",
|
||||
"fortify": "敌方防御 +100(enemy_defense 池——硬化技施放,主角攻击折半;持续 1 个有效结算)",
|
||||
"fortify": "敌方防御 −50(enemy_defense 池——硬化技施放,主角攻击 ×0.5;持续 1 个有效结算)",
|
||||
}
|
||||
const SOURCE_NAMES := {"safe_cell": "安全格", "flag_hit": "标中旗", "mine_hit": "踩雷", "clear_reward": "清图奖励"}
|
||||
# 未知图标键的占位纹样(HUD框架:占位也要可见,禁静默空图标)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Date: 2026-09-13(playtest-2026-09-12 报告 Top1——零承伤根因分割探针)
|
||||
# 2026-09-16 改掷点口径(护甲等级掷点批):defense_up 墙已随 AC 换装退役——
|
||||
# H1 旧 defense_up 叠满墙失义(hero_defense 池现仅 off_guard 正向行,×0 墙结构面消失)
|
||||
# H2 Boss/敌攻击链健康:fresh 池下非蓄力拍 d_tick = max(下限, 池乘区(掷点档位基线 + 伏击附加))
|
||||
# H2 Boss/敌攻击链健康:fresh 池下非蓄力拍 d_tick = max(下限, 池乘区(掷点档位基线))(伏击附加随八型批 2026-09-20 退役)
|
||||
# 快照盲区说明:settlement_snapshot 取在衰减段之后,时效 1 的 ac_up 当轮授予当轮衰减——
|
||||
# 真人局 JSONL buffs[] 恒空 ≠ 状态为空,本探针改从结算行(授予/续存 effect 行的 pool_value)重建战斗段时点池值。
|
||||
# 观测面:全部读战斗结算步骤行 / 表值 / 呈现快照(零核心穿透);池归属/档位/下限全读表(数据配置 Rule 1——零数值字面量)。
|
||||
@@ -214,7 +214,7 @@ func _analyze_round(db, math, rows: Array, beats: Dictionary) -> Dictionary:
|
||||
var expected: int = maxi(int(db.settlement.damage_floor), math.pool_damage(raw, pd, pe))
|
||||
var roll_txt := "掷%s" % str(int(row.get("roll", -1)))
|
||||
var zeroed := "✓" if tick == expected else "✗ 算术不符!"
|
||||
lines.append("拍=%s %s/档%d%s 基线%d→掷后%d +伏击%d hero_def=%d enemy_out=%d d_tick=%d 期望=%d %s" % [
|
||||
lines.append("拍=%s %s/档%d%s 基线%d→掷后%d 附加%d hero_def=%d enemy_out=%d d_tick=%d 期望=%d %s" % [
|
||||
String(beats.get(int(row.get("round_index", 0)), "?")), roll_txt, tier, "暴击" if crit else "",
|
||||
baseline, rolled_base, attach, pd, pe, tick, expected, zeroed])
|
||||
ticks.append({"raw": raw, "tick": tick, "expected": expected, "ok": tick == expected})
|
||||
|
||||
@@ -355,7 +355,7 @@ func _smoke_armor_roll() -> void:
|
||||
board_ok = false # 棋盘来源(爆裂/直伤/清障炸药)必中全额——不掷不留档
|
||||
check(all_positive, "全战斗流零 v=0 伤害行(伤害下限全局生效)")
|
||||
check(attack_ok, "hero_attack/enemy_action 伤害行掷点读数+档位随行留档(P2 归因)")
|
||||
check(board_ok, "棋盘来源伤害行零掷点字段(伏击附加/爆裂/直伤/清障炸药必中全额)")
|
||||
check(board_ok, "棋盘来源伤害行零掷点字段(爆裂/直伤/清障炸药/奖励即时必中全额)")
|
||||
# ⑥ 全来源伤害下限(2026-09-20 用户拍板:任何伤害落地取值域 [1,+∞) 无上限——棋盘克制直伤此前
|
||||
# 靠表值 2+(−1)=1 贴底无结构保证,反噬/地形变异/爆裂同为表值直落,本批全部收进 damage_floor 闸内)
|
||||
var df: int = int(_db.settlement.damage_floor)
|
||||
@@ -1142,7 +1142,7 @@ func _smoke_playtest_logger() -> void:
|
||||
minesweeper.settle_batch([{"action": "stop"}])
|
||||
check(snaps.size() == 2, "每结算一快照(%d 次结算 → %d 快照)" % [2, snaps.size()])
|
||||
var s1: Dictionary = snaps[0]
|
||||
var required := ["kind", "level", "depth", "round", "verdict", "hero", "economy", "board", "enemies", "buffs", "ambush_layers", "round_facts", "spawn_count"]
|
||||
var required := ["kind", "level", "depth", "round", "verdict", "hero", "economy", "board", "enemies", "buffs", "round_facts", "spawn_count"]
|
||||
var missing := []
|
||||
for k in required:
|
||||
if not s1.has(k):
|
||||
@@ -1257,7 +1257,7 @@ func _smoke_attack_chain_fresh_pool() -> void:
|
||||
var ticks: Array = [] # {baseline, attach, roll, tier, crit, tick}——敌行动伤害行重建
|
||||
settlement.step_row.connect(func(row):
|
||||
if row.type == "damage" and row.source_class == "enemy_action":
|
||||
ticks.append({"baseline": int(row.baseline), "attach": int(row.attach), "roll": int(row.roll),
|
||||
ticks.append({"baseline": int(row.baseline), "attach": int(row.get("attach", 0)), "roll": int(row.roll),
|
||||
"tier": int(row.tier), "crit": bool(row.get("crit", false)), "tick": int(row.value)}))
|
||||
check(settlement.begin_battle({"level_index": 1, "node_depth": 4, "hero_hp": int(_db.hero.max_hp)}), "fresh 池回归战斗启动(Boss 行)")
|
||||
# 停手轮:零挖掘零旗 → 零授予 → 池恒空;Boss 拍 = 开轮摇号结果(伤害技/普攻兑付伤害行、硬化施放行不计)
|
||||
@@ -1270,10 +1270,10 @@ func _smoke_attack_chain_fresh_pool() -> void:
|
||||
for t in ticks:
|
||||
var base_factor: float = float(t.tier) * (float(_db.settlement.crit_multiplier) / 100.0 if t.crit else 1.0)
|
||||
var rolled_base := int(floor(float(t.baseline) * base_factor / 100.0))
|
||||
var expected: int = maxi(int(_db.settlement.damage_floor), rolled_base + int(t.attach))
|
||||
var expected: int = maxi(int(_db.settlement.damage_floor), rolled_base + int(t.get("attach", 0)))
|
||||
if int(t.tick) != expected or int(t.tick) <= 0:
|
||||
all_full = false
|
||||
check(all_full, "fresh 池非蓄力拍 d_tick = max(1, 掷点档位基线 + 伏击附加)(Boss 攻击链健康——Bug#2 回归断言)")
|
||||
check(all_full, "fresh 池非蓄力拍 d_tick = max(1, 掷点档位基线)(Boss 攻击链健康——Bug#2 回归断言)")
|
||||
|
||||
func _encounter_replay(seed_val: int) -> Array:
|
||||
var out: Array = []
|
||||
@@ -1399,9 +1399,9 @@ func _smoke_refresh_provenance_guard() -> void:
|
||||
# 极性路由断言(战斗结算 验收):挖安全格/标中旗只出正向、挖雷格/标错旗只出负向、兑付行指回来源格。
|
||||
# 正向字典效果集 = {attack_up, ac_up, weaken}(ac_up = 2026-09-16 护甲等级掷点批换装,原 defense_up)
|
||||
# + spark_aoe/heal_minor(flat);
|
||||
# 负向字典效果集 = {enrage, off_guard, sapped}(percent)+ 直伤(heal flat)/ ambush(blast flat)。
|
||||
# 负向字典效果集 = {enrage, off_guard, sapped}(percent)+ 直伤(heal flat)/ alert 警觉(blast percent——2026-09-20 换装)。
|
||||
const POSITIVE_EFFECTS := ["attack_up", "ac_up", "weaken"]
|
||||
const NEGATIVE_EFFECTS := ["enrage", "off_guard", "sapped", "ambush"]
|
||||
const NEGATIVE_EFFECTS := ["enrage", "off_guard", "sapped", "alert"]
|
||||
|
||||
|
||||
func _smoke_polarity_routing() -> void:
|
||||
@@ -1422,7 +1422,7 @@ func _smoke_polarity_routing() -> void:
|
||||
if eff != "" and not POSITIVE_EFFECTS.has(eff):
|
||||
safe_all_pos = false
|
||||
check(safe_all_pos, "挖安全格 → 只出正向兑付(效果行 ∈ 正向字典 + 每格 ≥1 行——%d 格)" % probe["safe_cells"].size())
|
||||
# 挖到的雷格:只出负向——来源行必存在且 effect 行 ∈ 负向集(ambush = blast 负向;heal 负向 = damage 行)
|
||||
# 挖到的雷格:只出负向——来源行必存在且 effect 行 ∈ 负向集(alert = blast 负向;heal 负向 = damage 行)
|
||||
var mine_all_neg := true
|
||||
for e in probe["mine_cells"].keys():
|
||||
var cell_arr: Array = [e.x, e.y]
|
||||
@@ -1517,9 +1517,9 @@ func _rows_at(rows: Array, source_class: String, cell) -> Array:
|
||||
|
||||
|
||||
# 负向字典逐型直验(类型化兑付行协议):找真实板上各型非封锁格,直调 _grant_routed 负向路由,
|
||||
# 断言每型的兑付形态(enrage/off_guard/sapped 效果行、heal 直伤行、blast ambush+cap 溢出协议)。
|
||||
# 断言每型的兑付形态(enrage/off_guard/sapped 效果行、heal 直伤行、blast 警觉行、action 行动点排队)。
|
||||
func _smoke_negative_dictionary() -> void:
|
||||
print("--- 负向字典逐型直验(sapped 在位 / heal 直伤 / blast 伏击+2 与 cap 溢出)---")
|
||||
print("--- 负向字典逐型直验(sapped 在位 / heal 直伤 / blast 警觉 / action 行动点)---")
|
||||
# 数据面:sapped 行在位(状态表七行 + effect_pool_map 收编)
|
||||
var sapped: Dictionary = _db.status_effect_row("sapped")
|
||||
check(not sapped.is_empty() and String(sapped.get("target_side", "")) == "hero" \
|
||||
@@ -1582,20 +1582,23 @@ func _smoke_negative_dictionary() -> void:
|
||||
if String(row.get("reward_id", "")) == "heal_backlash":
|
||||
backlash_power = int(row.power)
|
||||
check(hp_after == hp_before - backlash_power, "heal 型负向 → 主角直伤 %d(%d → %d,不经增益状态)" % [backlash_power, hp_before, hp_after])
|
||||
# blast:负向 = 伏击 +2 层;cap 内生效、超 cap 逐层溢出(cap 读敌行值——零字面量)
|
||||
# blast:负向 = 警觉(敌方护甲等级 +1 percent 行——2026-09-20 伏击退役换装)
|
||||
if by_type.has("blast"):
|
||||
var cap: int = int(_db.resolve_enemy_rowset(1, 0).get("advantage_per_round_cap", 0))
|
||||
rows.clear()
|
||||
var snap_b: Dictionary = settlement._reward_economy.query_full_snapshot(by_type["blast"])
|
||||
settlement._grant_routed(snap_b, "negative", "mine_hit", by_type["blast"], 1)
|
||||
var layers_after_first: int = settlement._status_effects.pool_layers("ambush")
|
||||
check(layers_after_first == mini(2, cap), "blast 型负向 → 伏击 +%d 层(cap=%d)" % [layers_after_first, cap])
|
||||
while settlement._status_effects.pool_layers("ambush") < cap:
|
||||
settlement._grant_routed(snap_b, "negative", "mine_hit", by_type["blast"], 1)
|
||||
rows.clear()
|
||||
settlement._grant_routed(snap_b, "negative", "mine_hit", by_type["blast"], 1)
|
||||
check(_rows_of_type(rows, "overflow").size() >= 1 and settlement._status_effects.pool_layers("ambush") == cap,
|
||||
"blast 型负向超 cap → 溢出未生效行(伏击池守恒在 cap=%d)" % cap)
|
||||
var alert_ok: bool = false
|
||||
for eff_a in settlement._status_effects.query_active("enemy"):
|
||||
if String(eff_a.effect_id) == "alert":
|
||||
alert_ok = true
|
||||
check(alert_ok, "blast 型负向 → 警觉(敌方护甲等级 +1 状态在位)")
|
||||
# action:负向 = 下轮行动点 −1 / 正向 = +1(排队,轮开始兑现——八型批 2026-09-20;确定性直验不依赖板面摇号)
|
||||
rows.clear()
|
||||
var d0: int = settlement.action_economy.pending_ap_delta()
|
||||
settlement._grant_routed({"reward_id": "ap_drain", "cell_type": "action", "polarity": "negative", "display_name": "迟滞", "power": 1}, "negative", "mine_hit", Vector2i(0, 0), 1)
|
||||
check(settlement.action_economy.pending_ap_delta() == d0 - 1, "action 型负向 → 下轮行动点 −1(排队)")
|
||||
settlement._grant_routed({"reward_id": "ap_up", "cell_type": "action", "polarity": "positive", "display_name": "行动点", "power": 1}, "positive", "safe_open", Vector2i(0, 0), 1)
|
||||
check(settlement.action_economy.pending_ap_delta() == d0, "action 型正负抵消 → 排队净值归零")
|
||||
|
||||
|
||||
# 资源面断言:chain/dig 事件恒携带 resource=true(雷格同权);initial 恒 false(封锁格);
|
||||
@@ -2137,7 +2140,7 @@ func _has_err_category(errs: Array, category: String) -> bool:
|
||||
|
||||
|
||||
# ⑦ 新效应词汇批实装探针(2026-09-19——妨害技蛛网束缚 + 毒爆必中附加分量,实装面 = L2 Boss 卓尔):
|
||||
# 停手轮驱动 (2,8)——零挖掘零旗 → 零池零伏击,伤害行可逐拍精确复算;种子 hunt 至少各命一粒毒爆伤害行
|
||||
# 停手轮驱动 (2,8)——零挖掘零旗 → 零池,伤害行可逐拍精确复算;种子 hunt 至少各命一粒毒爆伤害行
|
||||
# 与蛛网束缚施放行;毒分量随开战掷定的攻击基线缩放(atk 区间 2–4 → rider 1–3)。
|
||||
func _smoke_debuff_rider() -> void:
|
||||
var venom_seed := -1
|
||||
@@ -2180,7 +2183,7 @@ func _smoke_debuff_rider() -> void:
|
||||
# 终值精确复算(停手轮池恒空):主分量链结果 + 必中附加后置
|
||||
var base_factor: float = float(int(row.tier)) * (float(_db.settlement.crit_multiplier) / 100.0 if bool(row.get("crit", false)) else 1.0)
|
||||
var rolled_main: int = int(floor(float(int(row.baseline)) * base_factor / 100.0))
|
||||
var expected: int = maxi(floor_v, rolled_main + int(row.attach)) + rider_v
|
||||
var expected: int = maxi(floor_v, rolled_main + int(row.get("attach", 0))) + rider_v
|
||||
if int(row.value) != expected:
|
||||
venom_check_ok = false
|
||||
elif row.has("rider"):
|
||||
@@ -2200,7 +2203,7 @@ func _smoke_debuff_rider() -> void:
|
||||
if has_venom and venom_seed < 0:
|
||||
venom_seed = s
|
||||
check(venom_seed > 0 and venom_rows_seen >= 1, "毒爆必中附加分量种子可达(seed=%d,毒爆伤害行 ×%d)" % [venom_seed, venom_rows_seen])
|
||||
check(venom_check_ok, "毒爆伤害行逐拍精确复算:终值 = max(1, 掷点档位主分量 + 伏击附加) + 必中附加(rider 只随 rider_mult 行出现)")
|
||||
check(venom_check_ok, "毒爆伤害行逐拍精确复算:终值 = max(1, 掷点档位主分量) + 必中附加(rider 只随 rider_mult 行出现)")
|
||||
check(snare_seed > 0, "蛛网束缚落主角:施放后掷点阈值 = 基准 AC + 失衡幅度(off_guard ac 轴端到端,seed=%d)" % snare_seed)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user