29 lines
1.7 KiB
GDScript
29 lines
1.7 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2)
|
||
# Date: 2026-09-16
|
||
# 敌人系统·技能摇号器(proposal-skill-pool D9 封装纪律):独立服务类——输入 实例池可摇条目 + 随机子流,
|
||
# 输出 skill_id。权重份额归一:中签率 = 该技能权重 ÷ 可摇权重合计(排除谁,谁的份额按比例摊给其余技能)。
|
||
# 不碰血量、不碰表、不碰容器——过滤与冷却状态由调用方(战斗结算换算层)注入确定值。
|
||
# 零随机纪律:随机消费只在子流参数(behavior_roll 派生流),本类无自建随机源。
|
||
# 实现 GDD:design/gdd/systems/战斗循环/敌人系统.md(registry 追溯锚,命名规范见 CLAUDE.md)
|
||
class_name SkillLottery
|
||
extends RefCounted
|
||
|
||
|
||
# 权重摇号(整数份额,行序累计走查——同份额前行优先,确定性);entries = [{skill_id, weight}](非空)
|
||
static func draw(entries: Array, stream: RandomNumberGenerator) -> String:
|
||
assert(not entries.is_empty(), "[技能摇号] 空可摇集合(普攻冷却恒 0 → 池恒非空——结构违例即致命)")
|
||
var total := 0
|
||
for e in entries:
|
||
var w: int = int(e.get("weight", 0))
|
||
assert(w > 0, "[技能摇号] 池条目权重须 > 0: %s" % str(e))
|
||
total += w
|
||
assert(total > 0, "[技能摇号] 可摇权重合计为 0")
|
||
var r: int = stream.randi_range(1, total)
|
||
var acc := 0
|
||
for e in entries:
|
||
acc += int(e.weight)
|
||
if r <= acc:
|
||
return String(e.skill_id)
|
||
return String(entries[entries.size() - 1].skill_id) # 数值防御分支(不可达——r ≤ total 恒有落点)
|