## 轮循环驱动件:每轮结算周期的唯一驱动——结算重入门闸(settling)、五段段序执行 ## (段序骨架冻结于 SettlementTable.segment_order)、开轮摇号(共识点 B:技能选择前置到轮次开始, ## 摇定即本拍结算的选技)、踩雷计数账(转存时点=摇号轮末)、换图刷新流程件持有与交接。 ## 单一变化原因:轮结算周期的推进结构(段序、重入闸、摇号时序)。 ## 耦合边界(5.1 封装修复换代):引用束注入——构造收编排器组装的 20 键封闭束(六子对象+协作件 ## 引用+读数引用+七回调+两读投影),解包自用七键(facts/units/fx_host/db/action_economy/rng/ ## emit_row_cb)后原束分发五段(各段同束自取所需),段类对编排器零引用;变值标量 ## (battle_result/round_index)走调用参数现值交付(settle_if_due/roll_round_actions 签名扩展); ## 换图刷新流程件=本件持有:五段构造后复制束写入 grant_seg 段间引用再建 BattleRefreshFlow ## (授予段引用直接交换——不再穿编排器链式取段)。随机只经注入 rng 的 behavior_roll 封闭流 ## (流名、开流时点、取号序逐字);零信号连接、零 await;随编排器每战新建同灭。 ## 类变量封闭面(15≤15 贴顶禁再增):束解包 7(facts/units/fx_host/db/action_economy/rng/emit_row_cb) ## +mines_hits+五段+_settling+_refresh_flow。 class_name RoundCycle extends RefCounted var facts: BattleFacts # 战斗事实件(受理事实/轮缓冲/封印账/溯源/条件计数) var units: UnitContainer # 单位容器(摇号/解锁/冷却消费面) var fx_host: BattleFxHost # 效果宿主件(enemy_hp_threshold 发射面) var db: ConfigDb var action_economy: ActionEconomy var rng: RngServiceCore var emit_row_cb: Callable # 宿主 _emit_row——行协议面单点(5 参 head 束形) var mines_hits: Dictionary[Variant, Variant] = {"total": 0, "last": 0, "round": 0} # 踩雷计数(本场累计/上轮转存/当轮累计三读数合一账) var _grant_seg = null # GrantSegment(①授予段——含封印判与 counter/grant 助手单点) var _aura_seg = null # AuraSegment(②增益/减益段) var _combat_seg = null # CombatSegment(③战斗段) var _decay_seg = null # DecaySegment(④衰减段) var _verdict_seg = null # VerdictSegment(⑤判定段——返回裁定,终局早退语义归宿主) var _settling := false var _refresh_flow: BattleRefreshFlow # 换图刷新流程件(本件持有——编排器两公开方法单行转发经此) func _init(bundle: Dictionary) -> void: for k in ["facts", "units", "fx_host", "db", "action_economy", "rng", "emit_row_cb"]: assert(bundle.has(k), "[轮循环] 引用束缺键: %s" % k) facts = bundle.facts units = bundle.units fx_host = bundle.fx_host db = bundle.db action_economy = bundle.action_economy rng = bundle.rng emit_row_cb = bundle.emit_row_cb # 结算五段接线(五段同束自取所需;信号连接纪律不涉段〔段零信号连接〕) _grant_seg = GrantSegment.new(bundle) _aura_seg = AuraSegment.new(bundle) _combat_seg = CombatSegment.new(bundle) _decay_seg = DecaySegment.new(bundle) _verdict_seg = VerdictSegment.new(bundle) # 换图刷新流程件(段间引用交付:复制束写入授予段引用——授予段直接交换图流程件, # 编排器 _rounds._grant_seg 跨两层链式取段随之消灭) var flow_bundle: Dictionary[Variant, Variant] = bundle.duplicate() flow_bundle["grant_seg"] = _grant_seg _refresh_flow = BattleRefreshFlow.new(flow_bundle) # 结算重入门闸+段序体(原宿主 _maybe_settle 三闸与 _settle 合体——闸序 settling→battle_result→phase # 逐字;终局早退不回置 _settling,_battle_over 接管。battle_result/round_index=调用参数现值交付) func settle_if_due(battle_result: String, ri: int) -> void: if _settling or battle_result != "": return if action_economy.phase != action_economy.PHASE_WAITING: return _settling = true # 段序编排(五段——段体归 game/core/settlement/,宿主只做行发射/开单原语单点) _grant_seg.run(ri) # ① 授予段(案C 闸随段装载) _aura_seg.run(ri) # ② 增益/减益段(续存实例组合行) _combat_seg.run(ri) # ③ 战斗段(主角先手 → 逐敌还手 → 地图伤害账殿后〔案C〕) _decay_seg.run(ri) # ④ 衰减段(恰一次恒执行含末轮——先兑付后衰减) var verdict: String = _verdict_seg.run(ri) # ⑤ 判定段(防御性优先级 hero 先判) if verdict != "continue": return # 终局:_settling 不回置——_battle_over 已接管 _settling = false func is_settling() -> bool: return _settling # 换图刷新流程件读出口(编排器 can_refresh/player_request_refresh 两公开方法单行转发经此) func refresh_flow() -> BattleRefreshFlow: return _refresh_flow # ---- 开轮摇号(共识点 B:技能选择前置到轮次开始,数值计算留结算段——原宿主函数逐字迁入)---- # 每实例:保底判定(连续执行普攻 ≥ M → 必出特殊技、特殊技内按权重、不受冷却限制)→ 排除冷却中技能 # (中签率 = 权重 ÷ 可摇权重合计——份额归一)→ 按行序逐敌从统一流顺序取号摇定本拍动作。 # 随机消费只在换算层(本方法 + SkillLottery);单位容器收确定值(零随机纪律——armor_roll 先例同构)。 # 统一流:behavior_roll 无子流派生向量(位置交错,单点定位由事件日志承载)。 # 摇定后、兑现前死亡 → 本拍技能作废。round_index=调用参数现值交付。 func roll_round_actions(ri: int) -> void: var pity_m: int = int(db.combat.skill.pity_threshold) # facts 组包(开轮摇号前一次成型——last_round_end 空字典首轮映射 none 唯一落点;hero 事实不进 facts, # 容器内直读主角行) var facts_pack := { "round_index": ri, "last_round_end": "none" if action_economy.last_round_end().is_empty() else String(action_economy.last_round_end().get("path_tag", "none")), "mines_hit_last": int(mines_hits["last"]), "mines_hit_total": int(mines_hits["total"]), } for u in units.query_alive("enemy"): # Boss 半血解锁闩(迭代14④ T4——开轮摇号前逐敌检查;一次过门单向闩;解锁出一次归因明细行 # 「XX 解锁了 YY」带 skill_unlock 高光键〔type=highlight 既有行型零新增〕,呈现层弹一次性 # 技能名提示〔轻量 toast 非演出——D8 呈现面〕。门=确定性谓词,机制面零随机,摇号随机 # 消费仍在 behavior_roll 既有流——同种子逐位复现不破坏) for sid in units.check_trigger_unlocks(u, facts_pack): var srow_u: Dictionary[Variant, Variant] = db.combat.skill.row_by_id(sid) emit_row_cb.call({"type": "highlight", "source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "highlight": "skill_unlock"}, ri, "%s 解锁了 %s" % [String(u.display_name), String(srow_u.get("display_name", sid))], 0, {"skill_id": sid}) # enemy_hp_threshold 发射点(内容数据驱动扩展批 T1——血量阈值沿;对所有带该字段敌人逐个发射, # 非 Boss 专属——Boss 语义留给订阅者 when 判定。pct=本次解锁条目的 trigger 行值) var pct_v := 0 for pe in (u.get("pool", []) as Array): var pe_d: Dictionary[Variant, Variant] = pe if String(pe_d.get("skill_id", "")) == sid and (pe_d.get("trigger", {}) as Dictionary).has("value"): pct_v = int((pe_d.get("trigger", {}) as Dictionary).get("value", 0)) break # target_attack_bonus 施加面(处刑台:敌血量阈值沿挂单位字典标记, # 主角攻击面 _resolve_hero_attack 通用消费——fx_atk_bonus 键加算进 base_value) var eht_hits := fx_host.fx_emit("enemy_hp_threshold", {"unit": String(u.enemy_id), "pct": pct_v, "is_boss": String(facts.encounter.get("node_type", "")) == "boss"}) for h in eht_hits: if String(h.get("kind", "")) == "target_attack_bonus": u["fx_atk_bonus"] = int(u.get("fx_atk_bonus", 0)) + int(h.get("value", 0)) var entries: Array[Variant] = units.rollable_skills(u, facts_pack) if units.basic_streak(u) >= pity_m: var specials: Array[Variant] = units.special_skills(u, facts_pack) if not specials.is_empty(): entries = specials # 保底命中——普攻恒可摇 → 非保底路径池恒非空,此为唯一替代集合 var stream: RandomNumberGenerator = rng.open_stream("behavior_roll") units.set_pending_action(u, SkillLottery.draw(entries, stream)) units.tick_cooldowns() # 递减在摇号读值之后——"施放后连续 N 拍不可摇中"的等价实现(单位容器 注记) mines_hits["last"] = mines_hits["round"] # 轮次真正结束转存(轮内部分结算不更新) mines_hits["round"] = 0