diff --git a/game/core/action_economy.gd b/game/core/action_economy.gd index 679302ef..6becbdfc 100644 --- a/game/core/action_economy.gd +++ b/game/core/action_economy.gd @@ -79,7 +79,7 @@ func try_spend(action_kind: String) -> bool: # 排雷玩法 挖掘专用:chain_size = 连锁附加格数(不含首格);clears_map = 本次动作后全图安全格开完。 -# 深挖钻头付费开掘(dig_paid counter_spend 订阅施加面):按序消耗族——AP 富余(≥基础+对价)时本动作付全价并积本轮付费计数(效果引擎 +# 付费开掘(dig_paid counter_spend 订阅施加面):按序消耗族——AP 富余(≥基础+对价)时本动作付全价并积本轮付费计数(效果引擎 # 计数器=授予段追加产出配额取数面);AP 不足全价时 fallback 降级只付基础 1 照常开格、不积计数 # ——持有钻头永不锁死普通挖格(散件保底 B-2)。 func request_dig(chain_size: int, clears_map: bool) -> bool: diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 0aba641a..d30b320e 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -255,7 +255,7 @@ func begin_battle(enc: Dictionary) -> bool: _begin_battle_fx_state(enc) _begin_battle_attune_params(enc) _begin_battle_preview_economy(enc) - # 工兵晨令=round_start grant_ap(mode:queue) 订阅行承载(when round_index ≤ effective_rounds + # 晨间 AP=round_start grant_ap(mode:queue) 订阅行承载(when round_index ≤ effective_rounds # value_from 定影);round_start 发射先于行动经济轮切换=树规则 queue 型当轮入队当轮兑现 # (先手压制=首轮 AP+1 语义锚);计数器 round_start 重置点同点生效 round_index = 1 @@ -535,7 +535,7 @@ func _exit_distribution() -> void: if not _install_and_reset(): _fatal("换图编排失败") return - # 工兵晨令续队直调拆除(内容数据驱动扩展批 T4——round_start 订阅行承载,见 begin_battle 注) + # 晨间 AP 续队直调拆除(内容数据驱动扩展批 T4——round_start 订阅行承载,见 begin_battle 注) action_economy.notify_settlement_done(mode) # round_start 发射先于行动经济轮切换(queue 型 grant_ap 当轮入队当轮兑现——与首轮同口径) round_index += 1 diff --git a/game/core/damage_controller.gd b/game/core/damage_controller.gd index 4223ef42..2b32f298 100644 --- a/game/core/damage_controller.gd +++ b/game/core/damage_controller.gd @@ -106,7 +106,7 @@ func _per_value(name: String, payload: Dictionary) -> int: return 0 -# 巢穴控制数(fact nest_count 取数面——敌方名下 weaken 实例总数,猎巢人订阅面; +# 巢穴控制数(fact nest_count 取数面——敌方名下 weaken 实例总数,nest_count 订阅消费面; # 个体化后削弱为累加行、每体各持一条——总数 = 被削弱个体数) func _nest_count() -> int: var n := 0 @@ -135,7 +135,7 @@ func submit(t: Dictionary) -> int: # attack_commit 发射点(我方伤害单据开立入口; # 命中挂单据 context 随行——施加面在 _resolve_hero_attack 管线算术前逐族施加,开单时点=主角攻击 # 单恒为段内首单(结账现场与开单现场一致)。载荷 atk/target/matchup + 挖掘 streak 事实 -# (执拗订阅面)+巢穴控制数〔猎巢人〕) +# (开格 streak 订阅面)+巢穴控制数〔nest_count 订阅面〕) # spec 束必载键(缺载 assert——fail-fast):channel/base_value/target_rule/ri; # 可选键缺省=原形缺省:origin_cell=(-1,-1)/attack_type=""/rider=0/highlight=""/context={}。 # 返回单据字典九栏目+requires/source_unit 逐字不变(契约 8)。 @@ -247,9 +247,9 @@ func _resolve(t: Dictionary) -> void: # 主角先手攻击:掷点 → 折扣/暴击 → 克制点数 → hero_offense×enemy_defense 池乘区 → 下限 → 交付 # (attack_commit/clamp_check/armor_roll 订阅施加面:命中=开单入口随单 -# context 携带,按固定算序逐族施加——命中序=数据表声明序,确定性)。 +# context 携带,按固定算序逐族施加——命中序=数据表声明序,确定性;掷点折扣读数→克制点数两拍)。 # 四步分段:命中面(_hero_attack_ctx,目标/护甲/ -# 掷点/按序改写族四拍/克制点数) → 基值装配(_hero_attack_base_value,加法族/计数器施加/倍乘) → +# 掷点/掷点折扣读数/克制点数) → 基值装配(_hero_attack_base_value,加法族/计数器施加) → # 掷点兑现(_hero_attack_redeem,rolled/池乘/下限/rider/落地/击杀发射) → 交付(_hero_attack_deliver, # 状态施加/标签/明细行)。步骤间经 ctx/redemption 字典穿线——算序/取值固定(🔴 逐位一致契约)。 func _resolve_hero_attack(t: Dictionary) -> void: @@ -261,7 +261,7 @@ func _resolve_hero_attack(t: Dictionary) -> void: _hero_attack_deliver(t, ctx, redemption) -# 命中面(目标/护甲克制 matchup/armor_roll 发射→按序改写族四拍→克制点数读值+订阅改写; +# 命中面(目标/护甲克制 matchup/armor_roll 发射→掷点折扣读数→克制点数读值+订阅改写; # 后两步各由 _ctx_rewrites/_ctx_clamp_delta 承载)。返回穿线 ctx(空=目标不可达)。 # clamp_check 只在 _ctx_rewrites 内发射一次——_ctx_clamp_delta 消费同批命中(禁重复发射)。 func _hero_attack_ctx(t: Dictionary) -> Dictionary: @@ -278,54 +278,25 @@ func _hero_attack_ctx(t: Dictionary) -> Dictionary: return {"target": target, "armor": armor, "atk_type": atk_type, "ac_hits": (t.context.get("fx_attack_hits", []) as Array), "ac_payload": ac_payload, "roll_read": rewrites["roll_read"], "threshold": rewrites["threshold"], - "crit": rewrites["crit"], "discount": rewrites["discount"], - "spike_ready": rewrites["spike_ready"], "cdelta": cdelta} + "crit": rewrites["crit"], "discount": rewrites["discount"], "cdelta": cdelta} -# 按序改写族四拍+掷点快照(armor_roll 发射点在此):第一拍矿工重锤命中交换(正负双修原生形态: -# 命中判定减成 trade_hit 点〔更易命中〕换攻击增伤 trade_atk%——高 AC 负期望/低 AC 正期望; -# 改写位先于掷点折扣读数)→ 第二拍专注免掷骰恒定折扣(方差买断成期望——稳定器人格;roll 记 0 归因) -# → 第三拍满层必中(spike ready 时 discount 恒全额、暴击面走倍乘不加掷)→ 第四拍猎弱本能对轻甲敌折扣档改善 value 档 -# (对冲 pierce 列 L1 −1 开局罚;改善 = 折扣表向全额方向移档,nat 最大面/已全额不重复)。 -# clamp_check 同在此发射(唯一发射点——命中随返回值穿给 _ctx_clamp_delta)。 +# 掷点折扣读数+克制点数命中批(armor_roll 发射点在此):一拍护甲掷骰折扣读数(掷点对照 +# 防御阈值定折扣档——阈值=防御 AC+攻击加值,nat 面沿折扣表)→ 二拍 clamp_check 发射 +# (唯一发射点——命中随返回值穿给 _ctx_clamp_delta)。 func _ctx_rewrites(t: Dictionary, target: Dictionary, armor: String, matchup: String) -> Dictionary: - var ac_hits: Array[Variant] = (t.context.get("fx_attack_hits", []) as Array) var roll_read: Dictionary[Variant, Variant] = _armor_roll() var threshold: int = defender_ac(target) + int(db.combat.settlement.damage_roll.attack_bonus) - # armor_roll 发射点(敌护甲掷骰判定;免掷恒折扣订阅面) - var ar_hits := _fx_hits("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)}) - var trade_hit := 0 - for h in ac_hits: - if String(h.get("kind", "")) == "modify_ticket" and int(h.get("trade_hit", 0)) > 0: - trade_hit += int(h.trade_hit) - if trade_hit > 0: - threshold = maxi(1, threshold - trade_hit) + # armor_roll 发射点(敌护甲掷骰判定——事件在册、现役零订阅行,发射面保留) + _fx_hits("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)}) var crit: bool = bool(roll_read.crit) var discount: int = _roll_discount(int(roll_read.roll), threshold) - for h in ar_hits: - if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "armor_rewrite": - discount = int(h.get("value", 0)) - crit = false - roll_read["roll"] = 0 - var spike_ready := false - for h in ac_hits: - if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "spike": - spike_ready = true - discount = 100 var clamp_hits := _fx_hits("clamp_check", {"matchup": matchup}) - var shift := 0 - for h in clamp_hits: - if String(h.get("kind", "")) != "modify_ticket": - continue - if String(h.get("mode", "")) == "discount_shift": - shift += int(h.get("value", 0)) - if shift > 0 and armor == "light" and discount < 100: - discount = _discount_shift_up(discount, shift) return {"roll_read": roll_read, "threshold": threshold, "crit": crit, - "discount": discount, "spike_ready": spike_ready, "clamp_hits": clamp_hits} + "discount": discount, "clamp_hits": clamp_hits} -# 克制点数读值+订阅改写(魔网织者/湛蓝誓约/猎手刺剑 clamp_check modify delta——克制矩阵查表值 +# 克制点数读值+订阅改写(clamp_check 带模式行 modify delta——克制矩阵查表值 # 后的直调;hero_attack 与 board_typed 两消费面同享)。命中批由 _ctx_rewrites 单次发射后传入。 # 淬毒刃纹(行 triggers clamp_check # modify(mode:clamp_bonus) 订阅命中并入本处 delta 合计;mode 路由=主角攻击面专享, @@ -338,8 +309,8 @@ func _ctx_clamp_delta(atk_type: String, armor: String, clamp_hits: Array) -> int return cdelta -# 基值装配(基值+单位标记加伤+矿工重锤增伤面+加法族(attack_bonus/ -# value_counter 层值)+计数器施加(attack_commit 用后清零行——命中序=声明序)+spike 倍乘。 +# 基值装配(基值+单位标记加伤+加法族(attack_bonus/ +# value_counter 层值)+计数器施加(attack_commit 用后清零行——命中序=声明序)。 func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float: var ac_hits: Array[Variant] = ctx["ac_hits"] var ac_payload: Dictionary[Variant, Variant] = ctx["ac_payload"] @@ -347,10 +318,6 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float: # 单位标记加伤(处刑台 target_attack_bonus 挂单位字典 fx_atk_bonus 键; # 阈值沿发射点已写入,本处通用消费——对该敌伤害加算) base_value += float(int((ctx["target"] as Dictionary).get("fx_atk_bonus", 0))) - # 按序改写族第五拍:矿工重锤增伤面(交换的正向侧) - for h in ac_hits: - if String(h.get("kind", "")) == "modify_ticket" and int(h.get("trade_atk", 0)) > 0: - base_value *= 1.0 + float(int(h.trade_atk)) / 100.0 # 加法族:attack_bonus per 计数器层值(层数×value 加算)。 # value_counter 扩展〔疼痛记忆/弃图兑换/陷阱本能——rider 值=计数器账面, # 值读于施加时点=命中序先行 rider 后清零行〕 @@ -365,11 +332,6 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float: if String(h.get("kind", "")) == "counter_add": var set_v = h.get("set", null) _port.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null) - # 任意真族:modify_ticket mode=spike 满层倍乘(强制必中已在折扣拍承载) - if ctx["spike_ready"]: - for h in ac_hits: - if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "spike": - base_value *= float(int(h.get("value", 1))) return base_value @@ -391,7 +353,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D var pooled: int = DamageMath.pool_damage(rolled, clamped_pool("hero_offense", StatusContainer.OWNER_HERO), clamped_pool("enemy_defense", StatusContainer.owner_key_for(target))) var landed: int = maxi(int(db.combat.settlement.damage_roll.damage_floor), pooled) # 加法族:英雄侧 rider(enemy_skill rider 对称位补齐口径)——后置必中附加,独立于掷点与乘区 - # (奥术余烬「普攻命中后恒定结算」语义);猎巢人巢控光环(per nest_count)/矿工的执拗开格加成 + # (普攻命中后恒定结算语义);巢控光环(per nest_count)/开格 streak 加成 # (per dig_streak、cap 封顶)同通道合计;档一质变 rider+1 / 档二(crit 时)rider 乘暴击倍率。 # rider 不改变下限语义(landed 含 rider 后不再过 floor)。 var hero_rider := int(_fact_or("rider_tier_bonus") or 0) # or 0=未绑缺省与 int(null) 缺省同值(int() 构造拒 null——端口缺省 null 面的空值合并) @@ -421,7 +383,7 @@ func _kill_effects(t: Dictionary, target: Dictionary) -> void: _port.reveal_effect() # 斩首学者——击杀揭示 1 格(端口路由宿主入队,授予段末兑付) -# 交付:即时执行族(碎甲震击打重甲附加「护甲裂解」——复用 off_guard,零新状态行; +# 交付:即时执行族(重甲附加「护甲裂解」——复用 off_guard,零新状态行; # apply_effect 域闭/重复授予由状态容器自行守卫)/标签装配/明细行发射/兑现登记。 func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary) -> void: var payload: Dictionary[Variant, Variant] = redemption["payload"] @@ -524,11 +486,11 @@ func _resolve_board_typed(t: Dictionary) -> void: var armor_b: String = String(target.get("armor_type", "light")) var atk_b: String = String(t.attack_type) # clamp_check 发射+施加(克制判定修正点棋盘面;与 hero_attack 同表同点: - # modify delta 订阅改写合计进克制点数;discount_shift 档位面属主角攻击专享,本面不施加) + # modify delta 订阅改写合计进克制点数;带模式行只施放于主角攻击面,本面不施加) var cdelta: int = _counter_delta(atk_b, armor_b) if atk_b != "": for h in _fx_hits("clamp_check", {"matchup": atk_b + "_vs_" + armor_b}): - # 无模式 delta 才入棋盘窗(mode 路由:clamp_bonus=主角攻击面专享、discount_shift=折扣档面) + # 无模式 delta 才入棋盘窗(mode 路由:带模式行=主角攻击面专享,棋盘窗只吃无模式 delta) if String(h.get("kind", "")) == "modify_ticket" and h.has("delta") and String(h.get("mode", "")) == "": cdelta += int(h.delta) var dealt: int = DamageMath.counter_component(int(t.base_value), cdelta, int(db.combat.settlement.damage_roll.damage_floor)) @@ -619,15 +581,6 @@ func _counter_delta(attack_type: String, armor: String) -> int: return int(row.get(armor, 0)) -# 折扣档改善(猎弱本能——折扣表向全额方向移 value 档;查不到当前档 = 不动防御口径) -func _discount_shift_up(discount: int, shift: int) -> int: - var tbl: Array[Variant] = db.combat.settlement.damage_roll.roll_tier_discounts - var idx := tbl.find(discount) - if idx < 0: - return discount - return int(tbl[mini(tbl.size() - 1, idx + shift)]) - - # 池内加算 → clamp [-100,+100](池间乘算归 DamageMath.pool_damage 调用侧); # 池按个体读(增益状态 v1.5):主角侧池传 OWNER_HERO,敌方侧池传被打者/施放者个体键—— # 乘区只合计该个体名下实例(B 敌的削弱不进 A 敌的账) diff --git a/game/core/effect_engine.gd b/game/core/effect_engine.gd index 33d3a725..56b9e75f 100644 --- a/game/core/effect_engine.gd +++ b/game/core/effect_engine.gd @@ -27,7 +27,7 @@ const MINE_EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "seal_se ## bias_rewards/preview。需求卡片标题计 23 系笔误,落码按行内容全收——commit 留痕) const EFFECT_KINDS := ["add_ticket_rider", "modify_ticket", "damage_mult", "open_ticket", "attack_bonus", "yield_mult", "spawn_spell", "grant_material", - "grant_ap", "grant_gp", "bias_wallet", "shield", "spike", "apply_status", "counter_add", + "grant_ap", "grant_gp", "bias_wallet", "shield", "apply_status", "counter_add", "counter_spend", "reveal_tiles", "remove_mines", "refresh_pick", "bias_rewards", "preview", # 六型效果(奇物.md 基础件表 27 件声明清单): @@ -61,8 +61,8 @@ const FACTS := ["matchup", "is_first_dig_of_battle", "terrain_kind", "target_arm "ap_exhausted_last", "hp_ratio", "amount", "ap_left", "pct", "battle_index"] # 关卡域战斗序(1 基,enc 携带;开局姿态组「每关首轮窗」条件面) -## when 算子词汇(封闭——fact 查询面落法:mod_zero=计数器取模判满〔掘进蓄能〕、 -## advantageous=克制矩阵正面判定〔淬毒刃纹〕、gte_ref/lt_ref=与另一计数器比较〔深挖钻头配额〕、 +## when 算子词汇(封闭——fact 查询面落法:mod_zero=计数器取模判满、 +## advantageous=克制矩阵正面判定〔淬毒刃纹〕、gte_ref/lt_ref=与另一计数器比较(计数器配额面)、 ## ends_with=后缀匹配〔对某甲族的泛条件〕、mod_eq=取模余数判定〔双管镐每第 2 次——value=模数 ## ref=余数;dispatch 预评估口径〔when 于命中前读计数器现值〕下「每第 N 次」系用 ref=N-1 表达〕。 ## 均系映射表现状条件装进 fact 的查询面落法,白名单覆盖) @@ -79,7 +79,7 @@ const WHEN_KEYS := ["fact", "op", "value", "id", "ref", "value_from"] ## axis=bonus_delta 加算区轴向) const DO_KEYS := ["kind", "value", "value_from", "delta", "delta_from", "mult", "mult_from", "cap", "cap_from", "cost_mult", "per", "divisor", "dtype", "mode", "channel", "counter_id", - "set", "status_id", "label", "fallback", "target_kinds", "trade_atk", "trade_hit", + "set", "status_id", "label", "fallback", "target_kinds", "value_event", "value_counter", "axis"] ## 主动件分发通道(不经事件订阅——刷新/显影/移除是玩家主动动作;use_treasure 按 kind 分发) @@ -154,7 +154,7 @@ func _push_tree_rule(rk: String, params: Dictionary[Variant, Variant]) -> void: "do": {"kind": "shield", "value": int(params["shield"])}}, {}) "sense_sharp": # 锋=踩中危险格伤害减半·每关限次——mine_hit 挂点现成(damage_mult 收集→halve_pct, - # 与惊巢反制同路径;伤害在结算战斗段末兑付,探索阶段零改动) + # 与挖掘惩罚减半同路径;伤害在结算战斗段末兑付,探索阶段零改动) _push_trigger("tree", rk, {"on": "mine_hit", "when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": int(params["cap_per_level"])}], "do": {"kind": "damage_mult", "value": int(params["halve_pct"])}}, {}) @@ -449,7 +449,7 @@ static func _vtr_from_refs(rid: String, tr: Dictionary, row: Dictionary, errs: A ## 加法族(数值相加,可交换):add_ticket_rider、attack_bonus、grant_material、grant_ap、 ## counter_add、shield——发射点逐条累加收集器。 ## 乘法族(收集后一次施加,正负分桶):yield_mult、damage_mult。 -## 任意真族(任一命中即生效):spawn_spell、spike。 +## 任意真族(任一命中即生效):spawn_spell。 ## 按序改写族(收集→单点按声明序施加):modify_ticket 及一切改写窗口效果。 ## 按序消耗族(按订阅序消耗共享资源,耗尽即止;可选 fallback):counter_spend。 ## 即时执行族(每条命中各自执行一次,执行序=声明序):open_ticket、 diff --git a/game/core/reward_grant.gd b/game/core/reward_grant.gd index db957961..5fb230d6 100644 --- a/game/core/reward_grant.gd +++ b/game/core/reward_grant.gd @@ -1,5 +1,5 @@ ## 奖励授予件(自 battle_orchestrator 收编 grant 族):极性字典路由兑付(grant_routed——含封印溢价 -## 与惊巢反制减半)、三种行应用(flat 四分支/percent/gp)、伤害单据提交面(submit——案C 暂存闸承载)、 +## 与挖掘惩罚减半)、三种行应用(flat 四分支/percent/gp)、伤害单据提交面(submit——案C 暂存闸承载)、 ## 清图奖励与身份授予(完美封印第二份)、战斗内 GP 缓冲 escrow、秘宝使用四步编排(use_treasure—— ## 相位闸→校验→受理闸→逐 kind 应用→消耗)、揭示奖励兑付(reveal_redeem——fx_host flush 的兑付回调面)。 ## 生命周期=随编排器(编排器 _init 同建同灭,每战新建)。 @@ -127,7 +127,7 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary: return {"ok": true, "item_id": v.item_id, "resolved": v.resolved, "record": rec} -# 受理闸(treasure_used 发射+施加:counter_spend 订阅=掘进蓄能积点账本,积点不足拒绝零消耗。 +# 受理闸(treasure_used 发射+施加:counter_spend 订阅=付费积点账本,积点不足拒绝零消耗。 # 零 item_id 特判;拒绝文案读装配集积点进度——宿主 attune 读投影) func _charge_gate(v: Dictionary, target: Vector2i) -> Dictionary: var tu_hits := fx_host.fx_emit("treasure_used", {"item_id": String(v.item_id), "target": target}) @@ -180,13 +180,13 @@ func _safe_dig_open(v: Dictionary) -> Dictionary: return {} -# 爆破工兵包兑付(中立移除危险格:未揭示/无封印/是雷过滤归排雷玩法; +# 中立移除兑付(未揭示/无封印/是雷过滤归排雷玩法; # 邻接数字冻结零重算、清图分母零涉(危险格不入分母);空集拒绝零消耗—— # consume 之前 return(拒绝面注记扩「③内零消耗分支」),拒绝 reason 可呈现) func _cell_removal_open(v: Dictionary) -> Dictionary: var blasted: Array[Variant] = fx_host.minesweeper.remove_mines_in(v.resolved) if blasted.is_empty(): - return {"ok": false, "reason": "爆破工兵包:3×3 区域内无可移除危险格(须未揭示且未封印)"} + return {"ok": false, "reason": "中立移除:3×3 区域内无可移除危险格(须未揭示且未封印)"} return {} @@ -218,12 +218,12 @@ func grant_routed(snap: Dictionary, polarity: String, att: Dictionary) -> bool: row = row.duplicate() row["power"] = int(round(int(row.power) * prem_pct / 100.0)) row["premium_pct"] = prem_pct - # 惊巢反制(减半系数=mine_hit damage_mult 订阅命中〔行 value 50=减 50%,取整 ceil〕; + # 挖掘惩罚减半(减半系数=mine_hit damage_mult 订阅命中〔行 value 50=减 50%,取整 ceil〕; # duplicate 隔离——减半不回写极性字典) if halve_pct > 0 and polarity == "negative" and source_class == "mine_hit": row = row.duplicate() var half: int = int(ceil(float(int(row.power)) * float(100 - halve_pct) / 100.0)) - emit_cb.call({"type": "effect", "source_class": source_class, "origin_cell": cell}, ri, "惊巢反制——危险格挖掘惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"}) + emit_cb.call({"type": "effect", "source_class": source_class, "origin_cell": cell}, ri, "挖掘惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_halve"}) row.power = half return _dispatch_row(row, att) diff --git a/game/core/reward_roller.gd b/game/core/reward_roller.gd index 57010bac..ac1f9e3b 100644 --- a/game/core/reward_roller.gd +++ b/game/core/reward_roller.gd @@ -1,4 +1,4 @@ -## 掷格件:装配期格面资源摇号——池行装配、区间行 roll、权重合成(偏运硬币/board_skew 两路投影)。 +## 掷格件:装配期格面资源摇号——池行装配、区间行 roll、权重合成(订阅加权/board_skew 两路投影)。 ## 自 RewardEconomy 迁出掷格面(architecture v1.29 轴戊②);受理账面(grant/身份查询/快照/retire/ ## polarity_row)留守 RewardEconomy——衔接面=格→奖励身份映射批束(本件产出、受理件接收)。 ## 取号流声明:roll 每批恰开一 reward_roll 流(scope=每次 install 批不变),体序=normalize(行主序 @@ -38,12 +38,12 @@ func _init(bundle: Dictionary) -> void: func roll(eligible: Array) -> Dictionary[Variant, Variant]: var list: Array[Dictionary] = _normalize_eligible(eligible) var stream: RandomNumberGenerator = rng.open_stream("reward_roll") # 恰一 scope 批(格批 + 清图批尾 + 区间行追加取号) - # reward_roll 发射归 BattleFxHost(偏运硬币 bias_rewards 订阅命中合计经注入查询口取回); + # reward_roll 发射归 BattleFxHost(bias_rewards 订阅命中合计经注入查询口取回); # 零命中/未接线=0=原行集零扰动 var luck_bias := 0 if luck_bias_query.is_valid(): luck_bias = luck_bias_query.call(list.size()) - var cell_rows: Array[Variant] = _biased_cell_rows(luck_bias) # cell 池正向行 weight>0(偏运硬币装配时稀有正向 kinds 权重 +N——池结构零触碰) + var cell_rows: Array[Variant] = _biased_cell_rows(luck_bias) # cell 池正向行 weight>0(订阅件装配时稀有正向 kinds 权重 +N——池结构零触碰) var clear_rows: Array[Variant] = _pool_rows("clear_default") # ① 身份批:每非封锁格恰一类型身份(雷安同池——同一行集同一权重分布) var draft := {} @@ -83,7 +83,7 @@ func _normalize_eligible(eligible: Array) -> Array[Dictionary]: return list -# 偏运硬币权重投影(底稿 §3:只动池内权重、雷安仍同池同分布;luck_bias=0 逐位返回原行集——off 态零扰动) +# 订阅加权权重投影(底稿 §3:只动池内权重、雷安仍同池同分布;luck_bias=0 逐位返回原行集——off 态零扰动) # 合成序:先经图级偏斜投影再叠件级加法——合成序锚=图级先、件级后 #(环境事实先定型、玩家构筑叠其上;两投影均为池内权重面,雷安同池与取号次数零变化) func _biased_cell_rows(bias: int) -> Array: diff --git a/game/core/settlement/grant_segment.gd b/game/core/settlement/grant_segment.gd index 1eee08d0..eb472a60 100644 --- a/game/core/settlement/grant_segment.gd +++ b/game/core/settlement/grant_segment.gd @@ -124,8 +124,8 @@ func _reveal_safe(evt: Dictionary, snap: Dictionary, ri: int) -> void: # dig_reveal 施加面(序=计数器〔连击计数——乘区收集后执行=本格兑付用旧值〕→ 揭示入队〔地质学家/ -# 跳格采掘 reveal_tiles 订阅面,授予段末统一掷格兑付〕→ 追加产出〔深挖钻头 v2 双份制:grant_material -# 命中=配额内追加第二份〕→ 奥术矢〔魔能共鸣——每战首次开安全格追加魔法矢;任意真族单次生效〕) +# 跳格采掘 reveal_tiles 订阅面,授予段末统一掷格兑付〕→ 追加产出〔双份制:grant_material +# 命中=配额内追加第二份〕→ 奥术矢(每战首次开安全格追加魔法矢;任意真族单次生效)) func _reveal_safe_apply(dr_hits: Array, evt: Dictionary, ri: int) -> void: for h in dr_hits: if String(h.get("kind", "")) == "counter_add": @@ -163,7 +163,7 @@ func _reveal_mine(evt: Dictionary, snap: Dictionary, ri: int) -> void: reward_grant.grant_routed(reward_grant.feat_yield_adjust(snap, false, 0, 0), "negative", {"source_class": "mine_hit", "cell": evt.cell, "ri": ri, "halve_pct": int(applied["halve_pct"])}) for h in mh_hits: if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "reflect": - var nct := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_nest_counter", "惊巢反制·反弹", + var nct := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_nest_halve", "反弹伤害", {"channel": "board", "base_value": int(h.get("value", 0)), "target_rule": DamageController.TARGET_ALL_ALIVE, "ri": ri, "origin_cell": evt.cell}) reward_grant.submit(nct, false) @@ -229,7 +229,7 @@ func _terrain_batch(ri: int) -> void: # 封印判定批+战封印军规开单段(封印流:seal_judged 批后统一开单:轮末每面封印命中对最前存活敌 # 结算 board_typed 必中伤害〔复用零新配方;「封印所在巢穴敌」简化为 default_target 最前存活敌—— -# 台账披露待追认〕;完美布防=本轮封印批全中(hit>0 且 miss==0)时封印伤 ×mult——全对推理奖励)。 +# 台账披露待追认〕;封印全中倍率=本轮 hit>0 且 miss==0 时封印伤 ×mult——全对推理奖励)。 # 伤害单据案C 暂存战斗段末兑付。 func _seal_batch(ri: int) -> void: for evt in facts.seals: @@ -237,7 +237,7 @@ func _seal_batch(ri: int) -> void: _seal_strike_batch(ri) -# seal_settle 发射+施加(战封印 open_ticket + 完美布防 modify_ticket 改写窗口:封印伤基值×完美倍率后 +# seal_settle 发射+施加(战封印 open_ticket + 封印全中倍率 modify_ticket 改写窗口:封印伤基值×完美倍率后 # 逐封印开单) func _seal_strike_batch(ri: int) -> void: var fs_hits := 0 @@ -262,7 +262,7 @@ func _seal_strike_batch(ri: int) -> void: # 战封印军规基值决议(attack 通道 open_ticket 首命中取基值;seal_mult modify_ticket 连乘; -# 完美布防=miss==0 且 hit>0 时基值×倍率。空旗集=空字典〔不开单合法态〕) +# 封印全中倍率=miss==0 且 hit>0 时基值×倍率。空旗集=空字典〔不开单合法态〕) func _seal_strike_value(fs_fx: Array, fs_hits: int, fs_misses: int) -> Dictionary: var banner_hits: Array[Variant] = [] for h in fs_fx: @@ -276,11 +276,11 @@ func _seal_strike_value(fs_fx: Array, fs_hits: int, fs_misses: int) -> Dictionar if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "seal_mult": seal_mult *= maxi(1, int(h.get("value", 1))) if fs_misses == 0 and fs_hits > 0 and seal_mult > 1: - strike_value *= seal_mult # 完美布防:同轮全中封印伤倍增(全对推理奖励) + strike_value *= seal_mult # 封印全中:同轮全中封印伤倍增(全对推理奖励) return {"value": strike_value} -# round_end 发射+施加(每轮结算收口:熔火之躯反打单开立〔open_ticket 即时执行〕+积层清零〔counter_add +# round_end 发射+施加(每轮结算收口:轮末反打单开立〔open_ticket 即时执行〕+积层清零〔counter_add # set〕;停手侧 AP 已在 request_stop 记账,finish_round_stop 按转存率转存 RAP)+施加面:grant_gp〔爆破会计/收盘——escrow 战斗域 # 缓冲,随 gp_dropped 上抛〕/grant_ap mode:queue〔封印回响——返 1 AP 下轮,queue_ap_delta 通道与轮始 # 加成同面〕+段界收口(转存率入参、清图奖励、揭示兑付、段界结账) @@ -348,7 +348,7 @@ func _judge_seal(evt: Dictionary, ri: int) -> void: # 封印命中(资格以危险格实例为键去重——排雷玩法 诚实重复上报的对侧;seal_hit 发射序: -# 殉爆票据 → 施加面 → 雷区图谱额外兑付 → 基线正向兑付) +# 标中雷反打票据 → 施加面 → 标中雷额外兑付 → 基线正向兑付) func _judge_seal_hit(evt: Dictionary, ri: int) -> void: var cell: Vector2i = evt.cell if facts.seal_consumed.has(cell): @@ -359,7 +359,7 @@ func _judge_seal_hit(evt: Dictionary, ri: int) -> void: "terrain_kind": minesweeper.harmful_adjacent(cell)}) _seal_burst_tickets(fh_hits, cell, ri) _seal_hit_effects(fh_hits, cell, ri) - # 雷区图谱(封印流——标中雷额外获得一份格面资源:封印开始产出对标开格; + # 标中雷追加(封印流——标中雷额外获得一份格面资源:封印开始产出对标开格; # 复用 grant 单点,额外兑付走同一 grant_routed 正向路由) for h in fh_hits: if String(h.get("kind", "")) == "grant_material": @@ -370,7 +370,7 @@ func _judge_seal_hit(evt: Dictionary, ri: int) -> void: reward_grant.grant_routed(snap, "positive", {"source_class": "seal_hit", "cell": cell, "ri": ri, "highlight": "seal_hit"}) -# 标中殉爆票据(行 triggers open_ticket channel blast value_from magnitude——每标中一面封印全体存活敌 +# 标中雷反打票据(行 triggers open_ticket channel blast value_from magnitude——每标中一面封印全体存活敌 # 吃 N 群伤,无类型走 blast 通道;封印爆裂〔专长〕同面消费) func _seal_burst_tickets(fh_hits: Array, cell: Vector2i, ri: int) -> void: for h in DamageController.hits_of(fh_hits, "open_ticket"): diff --git a/game/data/schemas/treasure_item_table.gd b/game/data/schemas/treasure_item_table.gd index ceaeccf0..d4891c9f 100644 --- a/game/data/schemas/treasure_item_table.gd +++ b/game/data/schemas/treasure_item_table.gd @@ -17,7 +17,7 @@ const UNLOCK_COUNTER_KEYS := ["adventure_cleared", "boss_kill_count", "vision_us const DIMENSIONS := ["info", "risk", "action", "plan", "counter"] # enum 九 kind 全部条件触发/机制修正类;mine_reveal/ap_grant 语义不变。 -# cell_removal = 爆破工兵包中立移除格;mine_hit_halve 系固定 +# cell_removal = 中立移除危险格通道;mine_hit_halve 系固定 # 「减半」语义不消费 magnitude(反弹 N 由 reflect_damage 行 magnitude 承载)。 # 主动件 kind 词族(reveal_tiles/preview/grant_ap/remove_mines/ # refresh_pick——效果引擎 ACTIVE_KINDS 同名闭集);被动件不走 effect_kinds 词族 @@ -119,7 +119,7 @@ func _validate_row_head(row: Dictionary, iid: String) -> Array[Variant]: ## 订阅形态域:triggers 事件订阅行判据与行级触发词表校验(被动件形态;与 effect_kinds 互斥校验 ## 面=passive 行(单形态——效果行为只走订阅);active_charged 行两形合法并存 -## (effect_kinds=主动分发通道 + triggers=受理闸订阅,掘进蓄能先例)) +## (effect_kinds=主动分发通道 + triggers=受理闸订阅,积点解锁先例)) func _validate_row_triggers(row: Dictionary, iid: String) -> Array[Variant]: var errs: Array[Variant] = [] if row.has("triggers"): diff --git a/game/sim/adventure_monte_carlo.gd b/game/sim/adventure_monte_carlo.gd index a669679d..4c43b7f6 100644 --- a/game/sim/adventure_monte_carlo.gd +++ b/game/sim/adventure_monte_carlo.gd @@ -72,7 +72,7 @@ func _initialize() -> void: if attune_mode == "on": # 贪心画像:Slots=3(槽位判定源=树同调位节点点亮数,模拟画像=三节点 # 全点亮常量);装配序 = - # 表序 passive 优先至 Slots-1 + 首个 active(slots=3 → [淬毒刃纹, 标中殉爆, 掘进蓄能], + # 表序 passive 优先至 Slots-1 + 首个 active(slots=3 → 表序头三件占满示例, # provisional 画像归调参);Y = 营地升级 Lv1 表值 1。autoplay 默认不刷——Y 仅注入容量, # 刷新消费归玩家策略面(钓鱼归玩家)。 var slots: int = 3 diff --git a/game/sim/level_autoplay.gd b/game/sim/level_autoplay.gd index 13f34021..ec06dab9 100644 --- a/game/sim/level_autoplay.gd +++ b/game/sim/level_autoplay.gd @@ -411,14 +411,12 @@ static func _on_step_row(row: Dictionary, _runner, st: Dictionary, collect_rows: # 封印经济聚合(自结算流水聚合,零新随机源): # ⚠ 行侧判据考证实录:结算段对存续增益实例每轮复发行 effect 行且复用实例 source_class #(battle_orchestrator 状态列举行)——source_class=seal_hit 的行 ≠ 标中事件。事件行唯一判据 - # = 授予行专带 highlight="seal_hit"(实测 87 行 == 受理封印 87 动作,1:1 精确); - # 雷区图谱追加分grant 无高光,以 display_name 后缀补捕(计入事件与合计——同封印第二份兑付)。 + # = 授予行专带 highlight="seal_hit"(实测 87 行 == 受理封印 87 动作,1:1 精确)。 # 满层拒收行(reward_ineffective)零兑付不入。标空事件=归因行(type=seal_miss 每格恰一次); # 标空惩罚合计只入一次性兑付行(flat 直伤 damage/heal + gp_loss escrow 行)——sapped 授予行 # 与其状态复发行行侧不可区分不入合计(登记注记,判读侧按事件数补算)。 if row.source_class == "seal_hit" and row.type != "reward_ineffective": - if String(row.get("highlight", "")) == "seal_hit" \ - or String(row.get("display_name", "")).contains("雷区图谱"): + if String(row.get("highlight", "")) == "seal_hit": st.seal_hit += 1 st.seal_hit_gain += int(row.value) if row.type == "seal_miss": diff --git a/game/tests/unit/battle_settlement/variant_placement_test.gd b/game/tests/unit/battle_settlement/variant_placement_test.gd index 415c86de..620c3be3 100644 --- a/game/tests/unit/battle_settlement/variant_placement_test.gd +++ b/game/tests/unit/battle_settlement/variant_placement_test.gd @@ -236,7 +236,7 @@ func _smoke_global_variant() -> void: Kit.check("生存族下调:heal 4→3 / defense 11→7(v=30 构造例)", wmap.get("heal_minor", -1) == 3 and wmap.get("ac_up", -1) == 7) Kit.check("进攻族上浮:attack 14→15 / blast 16→18 / pierce 8→9 / magic 8→9(v=30 构造例,等比上浮守恒)", wmap.get("attack_up", -1) == 15 and wmap.get("spark_aoe", -1) == 18 and wmap.get("pierce_strike", -1) == 9 and wmap.get("magic_bolt", -1) == 9) eco.roller.board_skew = 30 - var combo_rows: Array = eco.roller._biased_cell_rows(2) # 件级加算 2=偏运硬币行 value(reward_roll bias_rewards 命中面同值) + var combo_rows: Array = eco.roller._biased_cell_rows(2) # 件级加算 2=订阅行 value(reward_roll bias_rewards 命中面同值) var cw := _rows_weight_map(combo_rows) Kit.check("合成序:图级偏斜先(乘法面)件级 luck_bias 后(加法 +2×四稀有类)——合计 77+8", eco.roller._pool_weight(combo_rows) == 77 + 4 * 2) Kit.check("合成序构造例:skew(heal 3)+2=5 / gp 2+2=4 / pierce 9+2=11 / magic 9+2=11(图级定型后玩家构筑叠加)", cw.get("heal_minor", -1) == 5 and cw.get("gp_coin", -1) == 4 and cw.get("pierce_strike", -1) == 11 and cw.get("magic_bolt", -1) == 11) diff --git a/game/tests/unit/core/meta_progression_forge_test.gd b/game/tests/unit/core/meta_progression_forge_test.gd index 7048c521..84bdd583 100644 --- a/game/tests/unit/core/meta_progression_forge_test.gd +++ b/game/tests/unit/core/meta_progression_forge_test.gd @@ -674,7 +674,7 @@ func _smoke_tree_rules() -> void: ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久) Kit.check("锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕", (fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty()) - # 锋·惊巢反制效果行换代断言(迭代36 u13/wa12——_grant_routed 减半分支效果行=战斗日志 + # 锋·挖掘惩罚减半效果行换代断言(迭代36 u13/wa12——_grant_routed 减半分支效果行=战斗日志 # step_row 真实生产面;display_name 玩家可见,禁字「挖雷」由此行清零钉住) var orch_nc = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777003)) orch_nc.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, @@ -687,17 +687,17 @@ func _smoke_tree_rules() -> void: # heal 负向行表值 power=6(极性字典行值——负向 power 走表值不走快照)→ halve 50% ceil=3 orch_nc.reward_grant().grant_routed({"cell_type": "heal", "power": 10}, "negative", {"source_class": "mine_hit", "cell": Vector2i.ZERO, "ri": 0, "halve_pct": 50}) - # 受理判据=惊巢反制效果行落 step_row(halve 分支先于 dispatch 发行——flat 行 _apply_flat_row 分派 + # 受理判据=减半效果行落 step_row(halve 分支先于 dispatch 发行——flat 行 _apply_flat_row 分派 # 恒返 false,返回值不承载受理语义;行发行即分支受理) var nc_hit := "" for r in nc_rows: - if String((r as Dictionary).get("display_name", "")).contains("惊巢反制"): + if String((r as Dictionary).get("display_name", "")).contains("挖掘惩罚减半"): nc_hit = String((r as Dictionary).get("display_name", "")) break - Kit.check("锋:惊巢反制 mine_hit 减半分支受理(wa12 前提——halve_pct=50 效果行落 step_row)〔wa12〕", + Kit.check("锋:挖掘惩罚减半 mine_hit 减半分支受理(wa12 前提——halve_pct=50 效果行落 step_row)〔wa12〕", nc_hit != "") - Kit.check("惊巢反制效果行==「惊巢反制——危险格挖掘惩罚减半(6→3)」零「挖雷」(u13 换代)〔wa12〕", - nc_hit == "惊巢反制——危险格挖掘惩罚减半(6→3)" and not nc_hit.contains("挖雷")) + Kit.check("减半效果行==「挖掘惩罚减半(6→3)」零「挖雷」(u13 换代)〔wa12〕", + nc_hit == "挖掘惩罚减半(6→3)" and not nc_hit.contains("挖雷")) # 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注 var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001)) Kit.check("守侧 rig:战斗编排启动(tree_rules=sense_guard 携带)", diff --git a/game/tests/unit/data_config/effect_kind_retired_test.gd b/game/tests/unit/data_config/effect_kind_retired_test.gd index fe9819cb..a3b46a29 100644 --- a/game/tests/unit/data_config/effect_kind_retired_test.gd +++ b/game/tests/unit/data_config/effect_kind_retired_test.gd @@ -11,12 +11,33 @@ const Kit := preload("res://tests/unit/test_kit.gd") const SRC_DIR := "res://data/tables" const BAD_OVERFLOW_DIR := "user://effect_kind_fixture_overflow" const BAD_SEGMENTS_DIR := "user://effect_kind_fixture_segments" +const BAD_SPIKE_DIR := "user://effect_kind_fixture_spike" +const BAD_TRADEKEY_DIR := "user://effect_kind_fixture_tradekey" # 注入探针:死通道 do.kind(甲案摘名两族——各一探针行) const PROBE_OVERFLOW := ' {\n "feat_id": "kind_overflow_probe",\n "display_name": "通道探针·溢出",\n "rarity": "common",\n "triggers": [ { "on": "dig_reveal", "when": [], "do": { "kind": "overflow", "value": 50 } } ],\n "desc": "夹具探针——甲案退役词 overflow"\n},\n' const PROBE_SEGMENTS := ' {\n "feat_id": "kind_segments_probe",\n "display_name": "通道探针·连珠",\n "rarity": "common",\n "triggers": [ { "on": "dig_reveal", "when": [], "do": { "kind": "attack_segments", "cap": 2, "divisor": 3, "value": 0 } } ],\n "desc": "夹具探针——甲案退役词 attack_segments"\n},\n' +# 注入探针:改写族退役词(改写族清退批 T2)——spike kind 出域与 trade 键出域(各一探针行) +const PROBE_SPIKE_MODE := ' { + "feat_id": "kind_spike_probe", + "display_name": "词表探针·甲", + "rarity": "common", + "triggers": [ { "on": "dig_reveal", "when": [], "do": { "kind": "spike", "value": 2 } } ], + "desc": "夹具探针——改写族退役 kind spike" +}, +' +const PROBE_TRADE_KEY := ' { + "feat_id": "kind_tradekey_probe", + "display_name": "键闭探针·乙", + "rarity": "common", + "triggers": [ { "on": "dig_reveal", "when": [], "do": { "kind": "grant_ap", "trade_hit": 5, "value": 50 } } ], + "desc": "夹具探针——改写族退役 do 键 trade_hit" +}, +' + + func _inject(dir: String, probe: String) -> bool: var path := dir + "/feats.tres" var f := FileAccess.open(path, FileAccess.READ) @@ -65,5 +86,11 @@ func _init() -> void: if not _assert_rejected(BAD_SEGMENTS_DIR, PROBE_SEGMENTS, "effect_kind_retired_test/②attack_segments 出域"): quit(2) return - print("[effect_kind_retired_test] 双探针均正确拒绝(甲案词表退役——死通道误写加载致命)〔架构整改 V3〕") + if not _assert_rejected(BAD_SPIKE_DIR, PROBE_SPIKE_MODE, "effect_kind_retired_test/③词表闭面:kind=spike 出域拒绝"): + quit(2) + return + if not _assert_rejected(BAD_TRADEKEY_DIR, PROBE_TRADE_KEY, "effect_kind_retired_test/④do 键闭面:trade_hit 出域拒绝"): + quit(2) + return + print("[effect_kind_retired_test] 四探针均正确拒绝(甲案词表退役+改写族清退——退役词/键误写加载致命)〔架构整改 V3〕〔改写族清退 T2〕") quit(1)