Files
gplan-2/game/core/settlement/round_cycle.gd
T
2026-11-04 05:08:31 +08:00

136 lines
8.9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 轮循环驱动件:每轮结算周期的唯一驱动——结算重入门闸(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