iter10 追加批: 奖励节点三选一共享池(用户拍板)——AdventureRunner 节点API+AuxItems 句柄面+main 共用面分派+node_overlay 道具面退役+autoplay 节点驱动+reward_overlay_test 随面迁移+a裁备选(L3复杀3)+g裁semver(v2兼容断言+存档序列化v1.3)+f裁调研待办(06新条)+文档五件+任务书(冒烟628/0+run_all 15套+五探针+三闸零新增+熵增PASS)
This commit is contained in:
1 parent
93e4764ca0
commit
4f62a489b6
19 files changed
+377
-154
No files matched your search
@@ -80,6 +80,7 @@ var _bonus_pending := {} # 停点态:{"candidates": Array, "p
|
||||
var _bonus_result := {} # 已兑付待推进态(bonus_resolved 载荷副本——中断续玩不重兑付)
|
||||
var _bonus_aux_carry: Array[String] = [] # 道具件跨关携带(兑付后注入下一关库存——清库后授予的体验窗)
|
||||
var _keystone_drops: Array = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源)
|
||||
var _node_bonus_pending := {} # 奖励节点三选一停点态(迭代 10 追加批——共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
|
||||
|
||||
|
||||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
@@ -197,6 +198,7 @@ func _reset_bonus_state() -> void:
|
||||
_bonus_result = {}
|
||||
_bonus_aux_carry = []
|
||||
_keystone_drops = []
|
||||
_node_bonus_pending = {}
|
||||
|
||||
|
||||
# TunePanel 百分比通道(测试面板覆盖——每关新 runner 都要应用;正式侧归元进度)
|
||||
@@ -331,6 +333,113 @@ func _finish_bonus_pick(picked: String, value: int, hp_before: int) -> void:
|
||||
_snapshot_breakpoint()
|
||||
|
||||
|
||||
# ---- 奖励节点三选一(迭代 10 追加批——用户拍板:与通关三选一共享同池同权重同次数上限)----
|
||||
|
||||
# 候选呈现(节点到达时调用——恰一 reward_pick scope 取数;本停点断点不载,
|
||||
# 续玩重建到选路停点后重走重抽,恢复的全局计数器保证同候选逐位复现)
|
||||
func node_bonus_candidates() -> Array:
|
||||
return _draw_bonus_candidates()
|
||||
|
||||
|
||||
func present_node_bonus(candidates: Array) -> void:
|
||||
_node_bonus_pending = {"candidates": candidates.duplicate(true)}
|
||||
|
||||
|
||||
# 点选兑付(节点域——无部分回血无钥石;件获取计数与牌库剔除与通关三选一同账)
|
||||
func apply_node_bonus(id: String) -> bool:
|
||||
if _node_bonus_pending.is_empty():
|
||||
push_error("[冒险结构] apply_node_bonus 拒绝——无待选停点")
|
||||
return false
|
||||
var candidates: Array = _node_bonus_pending.get("candidates", []) as Array
|
||||
if not candidates.has(id):
|
||||
push_error("[冒险结构] apply_node_bonus 拒绝——件不在候选: %s" % id)
|
||||
return false
|
||||
var value: int = int(db.adventure.bonus_value(id))
|
||||
match id:
|
||||
"attack":
|
||||
bonus["atk"] = int(bonus.get("atk", 0)) + value
|
||||
_bonus_taken["attack"] = int(_bonus_taken.get("attack", 0)) + 1
|
||||
"max_hp":
|
||||
bonus["max_hp"] = int(bonus.get("max_hp", 0)) + value
|
||||
_bonus_taken["max_hp"] = int(_bonus_taken.get("max_hp", 0)) + 1
|
||||
"armor":
|
||||
bonus["armor_class"] = int(bonus.get("armor_class", 0)) + value
|
||||
_bonus_taken["armor"] = int(_bonus_taken.get("armor", 0)) + 1
|
||||
"ap":
|
||||
bonus["ap"] = int(bonus.get("ap", 0)) + value
|
||||
_bonus_taken["ap"] = int(_bonus_taken.get("ap", 0)) + 1
|
||||
"gp":
|
||||
if _runner != null:
|
||||
_runner.grant_bonus_gp(value)
|
||||
"aux_item":
|
||||
return _apply_node_aux(value)
|
||||
_:
|
||||
push_error("[冒险结构] apply_node_bonus 拒绝——未知件: %s" % id)
|
||||
return false
|
||||
_finish_node_bonus(id, value)
|
||||
return true
|
||||
|
||||
|
||||
# 节点渠道道具件(同调位约束):持有=关卡域库存+跨关携带,有位直接入本关库存(当关可用);
|
||||
# 满位转替换子交互(丢弃目标=库存件 handle——与通关渠道的 carry 序号分轨)
|
||||
func _apply_node_aux(count: int) -> bool:
|
||||
var pool: Array = _bonus_aux_pool()
|
||||
if pool.is_empty():
|
||||
push_error("[冒险结构] apply_node_bonus(aux_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
|
||||
return false
|
||||
var picked: Array[String] = []
|
||||
for i in maxi(1, count):
|
||||
picked.append(_roll_bonus_aux_item(pool))
|
||||
var slots := maxi(0, int(db.adventure.transitional_sync_slots))
|
||||
var records: Array = []
|
||||
if _runner != null and _runner.aux_inventory != null:
|
||||
records = _runner.aux_inventory.holding_records()
|
||||
var holding := records.size() + _bonus_aux_carry.size()
|
||||
if holding + picked.size() <= slots:
|
||||
if _runner != null:
|
||||
for iid in picked:
|
||||
_runner.grant_aux_item(iid, "reward_node")
|
||||
_finish_node_bonus("aux_item", picked.size())
|
||||
return true
|
||||
_node_bonus_pending["chosen"] = "aux_item"
|
||||
_node_bonus_pending["incoming"] = picked.duplicate()
|
||||
aux_replace_presented.emit(records, {"handle": -1, "item_id": String(picked[0]), "count": picked.size()})
|
||||
return true
|
||||
|
||||
|
||||
func _resolve_node_replace(discard_handle: int) -> bool:
|
||||
if _node_bonus_pending.is_empty() or String(_node_bonus_pending.get("chosen", "")) != "aux_item":
|
||||
push_error("[冒险结构] resolve_node_replace 拒绝——无替换在途")
|
||||
return false
|
||||
var incoming: Array = _node_bonus_pending.get("incoming", []) as Array
|
||||
if discard_handle >= 0:
|
||||
if _runner == null or _runner.aux_inventory == null or not _runner.aux_inventory.discard_item(discard_handle):
|
||||
push_error("[冒险结构] resolve_node_replace 拒绝——句柄不在持有集: %d" % discard_handle)
|
||||
return false
|
||||
for iid in incoming:
|
||||
_runner.grant_aux_item(String(iid), "reward_node")
|
||||
# 放弃新件(discard_handle < 0)= 原持有集不动、新件不获取
|
||||
_finish_node_bonus("aux_item", incoming.size())
|
||||
return true
|
||||
|
||||
|
||||
# 替换受理分发(单入口——节点停点在途优先按库存句柄,否则按通关停点的携带序号)
|
||||
func resolve_bonus_replace(discard: int) -> bool:
|
||||
if not _node_bonus_pending.is_empty() and _node_bonus_pending.has("incoming"):
|
||||
return _resolve_node_replace(discard)
|
||||
return resolve_aux_replace(discard)
|
||||
|
||||
|
||||
func _finish_node_bonus(picked: String, value: int) -> void:
|
||||
_node_bonus_pending = {}
|
||||
bonus_resolved.emit({
|
||||
"context": "reward_node",
|
||||
"picked": picked,
|
||||
"value": value,
|
||||
"level_index": level_index,
|
||||
})
|
||||
|
||||
|
||||
# 加权洗牌不放回掷 3(E-S weight-key:每件 key = U(0,1)^(1/权重) 取前 3——单次零重复、逐件按权重出
|
||||
# 概率;达次数上限件先剔牌库〔AP 用尽 2 次/属性件达 3 次永不再现;GP/道具件无次数上限,牌库恒非空〕;
|
||||
# 道具件解锁池空集〔白板/门控态〕不入牌库。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同三候选)
|
||||
|
||||
@@ -73,6 +73,24 @@ func holding_count() -> int:
|
||||
return n
|
||||
|
||||
|
||||
# 持有件记录(迭代 10 追加批——奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
|
||||
func holding_records() -> Array:
|
||||
var out: Array = []
|
||||
for rec in _items:
|
||||
if not bool(rec.get("used", false)):
|
||||
out.append({"handle": int(rec.get("handle", 0)), "item_id": String(rec.get("item_id", ""))})
|
||||
return out
|
||||
|
||||
|
||||
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
|
||||
func discard_item(handle: int) -> bool:
|
||||
for i in _items.size():
|
||||
if int(_items[i].get("handle", -1)) == handle and not bool(_items[i].get("used", false)):
|
||||
_items.remove_at(i)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
# 板级零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
|
||||
# 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用);
|
||||
# 纯预读行 = 板上还有未开格才可用。
|
||||
|
||||
Reference in new issue
Block a user