fix(iter37①): T1 core 域缺陷修——单据归属判定/数值入表/公式单点/信号契约载体统一
- d1 伤害控制器来源敌作废判定 `==`→`is_same`(同内容异实例不再误废;夹具双敌两断言〔wd1〕) - d11 shop 表新增 relic_base_price/spend_discount 两 knob+域检+ConfigDB 访问器+level_runner 读表; level_shop_economy_test 三表值断言+relic 现价读表与迁移前字面量逐位等价〔wd11〕 - d16a HeroTable.display_name+域检+访问器+unit_container 读表;spawn 真实链断言〔wd16〕 - d14 board_generator 目标雷数公式单点 target_mine_count(两调用点改引)+legality 去 db 参数(两调用点同批改)〔wd14〕 - d15 蓄力层上限 CHARGE_LAYER_CAP 常量(两处字面量+注释随动) - d16b unit_container/status_container 六处 Dictionary 取值显式 int() 包裹(读侧包) - d10 minesweeper 两信号按 §2.1 三要素补契约(载荷键集按发射位并集实查)+全库 28 处信号注释载体 #→## 撤销证明:回退 d1 hunk → 同容异实例断言红(A 单据被 B 死亡误废),还原复绿; 回退 d10 契约 → 契约在位扫描 undocumented=2 红,还原归零。等价类申报(d11/d14/d15/d16)见实现报告。 代拍板:#1 target_mine_count 签名(照接口块)、#2 契约措辞与载荷键并集(实查发射位)、#4 int() 读侧包写法。
This commit is contained in:
1 parent
a0acdf5c43
commit
1b9736f065
25 files changed
+186
-59
No files matched your search
@@ -6,11 +6,12 @@
|
||||
class_name ActionEconomy
|
||||
extends RefCounted
|
||||
|
||||
signal round_end_stopped(payload: Dictionary) # 双发射源之停手路径(行动经济 直发;payload 与 排雷玩法 棋盘路径同构)
|
||||
signal round_end_stopped(payload: Dictionary) ## 双发射源之停手路径(行动经济 直发;payload 与 排雷玩法 棋盘路径同构)
|
||||
|
||||
const PHASE_IDLE := "Idle"
|
||||
const PHASE_ACTIVE := "Active"
|
||||
const PHASE_WAITING := "Waiting"
|
||||
const CHARGE_LAYER_CAP := 3 # 蓄力层上限(机制结构常量,非玩法表值——整备军备面)
|
||||
|
||||
# 相位轴:内部 enum + match,外部 phase getter 保持
|
||||
# String 面(presentation_snapshot 与冒烟链的 "Idle"/"Active"/"Waiting" 对照契约)。
|
||||
@@ -182,14 +183,14 @@ func grant_reserve_ap(n: int) -> void:
|
||||
|
||||
|
||||
# 轮末停手转存决策(round_end 发射点调用——编排器授予段尾):transfer=true=剩余 AP 转蓄力层
|
||||
# (上限 3,整备军备面);false=按转存率转存 RAP(transfer_pct 百分比口径缺省 100=1:1;
|
||||
# (上限 CHARGE_LAYER_CAP,整备军备面);false=按转存率转存 RAP(transfer_pct 百分比口径缺省 100=1:1;
|
||||
# 储备罐 rap_transfer_mult magnitude 125=1.25:1,整数除法向下取整=每 4 AP 多转 1)。
|
||||
# 非停手轮(cleared/耗尽)_last_stop_ap=0 两分支均零操作。转蓄后达上限的沿发 charge_full(R6 框架先行——现无订阅者)。
|
||||
func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void:
|
||||
if transfer:
|
||||
var prev_layers := _charge_layers
|
||||
_charge_layers = mini(3, _charge_layers + _last_stop_ap)
|
||||
if _fx != null and _charge_layers >= 3 and prev_layers < 3:
|
||||
_charge_layers = mini(CHARGE_LAYER_CAP, _charge_layers + _last_stop_ap)
|
||||
if _fx != null and _charge_layers >= CHARGE_LAYER_CAP and prev_layers < CHARGE_LAYER_CAP:
|
||||
_fx.dispatch(_ctx, "charge_full", {"layers": _charge_layers})
|
||||
else:
|
||||
_rap += _last_stop_ap * maxi(1, transfer_pct) / 100
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
class_name AdventureRunner
|
||||
extends RefCounted
|
||||
|
||||
signal level_started(level_index: int, display_name: String) # 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行
|
||||
signal curio_choice_presented(payload: Dictionary) # 专长三选一停点(非末关通关恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
|
||||
signal curio_resolved(result: Dictionary) # 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index}
|
||||
signal adventure_finished(summary: Dictionary) # 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops)
|
||||
signal level_started(level_index: int, display_name: String) ## 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行
|
||||
signal curio_choice_presented(payload: Dictionary) ## 专长三选一停点(非末关通关恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
|
||||
signal curio_resolved(result: Dictionary) ## 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index}
|
||||
signal adventure_finished(summary: Dictionary) ## 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops)
|
||||
# ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明;S6 经 _forward_* 方法)----
|
||||
signal node_entered(node: Dictionary, info: Dictionary)
|
||||
signal choices_presented(candidates: Array)
|
||||
|
||||
@@ -7,10 +7,10 @@ class_name BattleOrchestrator
|
||||
extends RefCounted
|
||||
|
||||
signal step_row(row: Dictionary)
|
||||
signal battle_finished(payload: Dictionary) # {result, hero_hp, gp_dropped}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护);gp_dropped = 战斗掉落合计
|
||||
signal battle_finished(payload: Dictionary) ## {result, hero_hp, gp_dropped}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护);gp_dropped = 战斗掉落合计
|
||||
signal round_started(round_index: int)
|
||||
signal board_changed()
|
||||
signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析)
|
||||
signal settlement_snapshot(snap: Dictionary) ## 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析)
|
||||
|
||||
# 明细流水行型协议(战斗结算 Core Rule;9 类;
|
||||
# 清单改动须同步 battle_log_view 的 _format_row 与本文档头注):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 确定性棋盘生成器:生成-验证循环出图,不达标走接受最优/最不坏诚实降级(垂直切片验证:每张图可推理可归因)。
|
||||
## 只消费传入的单条 rng 流(重试共用,attempt 边界非 scope 事件)——同流同参逐位复现。
|
||||
## generate/legality 的 db 参数鸭子型(不标类型)——冒烟测量面注入桩表依赖此形态,勿收紧签名。
|
||||
## generate 的 db 参数鸭子型(不标类型)——冒烟测量面注入桩表依赖此形态,勿收紧签名。
|
||||
class_name BoardGenerator
|
||||
extends RefCounted
|
||||
|
||||
@@ -10,6 +10,11 @@ const METRIC_VERSION := 1
|
||||
|
||||
# ==================== 生成入口 ====================
|
||||
|
||||
# 目标雷数 N = round(d×(W×H−9))(params 快照键:density/w/h)——布雷定数与产物断言两处的单点公式
|
||||
static func target_mine_count(params: Dictionary) -> int:
|
||||
return int(round(float(params.density) * float(int(params.w) * int(params.h) - BoardData.SPAWN_AREA_CELLS)))
|
||||
|
||||
|
||||
# rng_stream = 一次 open_stream("board_gen") 实例(一张图的全部取数;重试共用——attempt 边界非 scope 事件)。
|
||||
# db 参数不标具体类型原因:冒烟工具注入 FakeDB 桩(experiment_density.gd 等)——typed 形参会拒收桩表
|
||||
func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardData: # type-hint: ignore(测量面 _BoardRowStub 冒烟夹具鸭子型注入——typed 参拒 stub)
|
||||
@@ -50,7 +55,7 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat
|
||||
# R₀≤cap 重掷闸触发计数(迭代14① T3——尝试事实,与合法性判定独立记录;占比=T5 闸触发率读数)
|
||||
if int(params.r0_cap) > 0 and b.compute_r0().size() > int(params.r0_cap):
|
||||
r0_cap_rejected += 1
|
||||
if legality(b, params, db):
|
||||
if legality(b, params):
|
||||
var m: Dictionary[Variant, Variant] = measure_inferability(b) # 求解器每合法板恰一次(O3 收口——此前重复调用两次)
|
||||
var inf: float = m["inferability"]
|
||||
if best_legal == null or inf > best_legal_score:
|
||||
@@ -94,7 +99,7 @@ func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> Boar
|
||||
for c in b.all_cells():
|
||||
if not spawn.has(c):
|
||||
domain.append(c)
|
||||
var n_mines: int = int(round(params.density * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS)))
|
||||
var n_mines: int = target_mine_count(params)
|
||||
# 部分 Fisher-Yates(确定性,消费 board_gen 流)
|
||||
for i in range(0, mini(n_mines, domain.size())):
|
||||
var j := rng_stream.randi_range(i, domain.size() - 1)
|
||||
@@ -278,7 +283,7 @@ func is_clearable(b: BoardData) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
func legality(b: BoardData, params: Dictionary, db) -> bool: # type-hint: ignore(同 generate——测量面 stub 注入)
|
||||
func legality(b: BoardData, params: Dictionary) -> bool:
|
||||
var reasons := _legality_faults(b, params)
|
||||
return reasons.is_empty()
|
||||
|
||||
@@ -331,7 +336,7 @@ func _neighbor_mine_count(b: BoardData, c: Vector2i) -> int:
|
||||
func _finalize(b: BoardData, params: Dictionary, attempts_used: int, accept_mode: String, _db, r0_cap_rejected: int = 0) -> void: # type-hint: ignore(同 generate——测量面 stub 注入)
|
||||
var m := measure_inferability(b)
|
||||
# 产物断言(棋盘生成 出生区安全与反退化布雷):实际雷数 ≤ 目标雷数 N——先布后删只减不增,失败即致命
|
||||
var n_target: int = int(round(float(params.density) * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS)))
|
||||
var n_target: int = target_mine_count(params)
|
||||
if b.mines.size() > n_target:
|
||||
_fatal("产物断言违例:实际雷数 %d > 目标雷数 N=%d(先布后删只减不增)" % [b.mines.size(), n_target])
|
||||
var attempt_facts := b.metrics # 保留尝试期事实(ring_removed)
|
||||
|
||||
@@ -342,6 +342,20 @@ func shop_price_b() -> float:
|
||||
return float(shop.price_b) if shop != null else 0.0
|
||||
|
||||
|
||||
# relic 保底卡基价 / 散折(economy_spend 激活时购买价折扣——表未入库 = 表 schema 缺省值)
|
||||
func shop_relic_base_price() -> float:
|
||||
return float(shop.relic_base_price) if shop != null else 0.0
|
||||
|
||||
|
||||
func shop_spend_discount() -> float:
|
||||
return float(shop.spend_discount) if shop != null else 0.0
|
||||
|
||||
|
||||
# 英雄显示名(单位快照读取面单源——与敌人行 display_name 同轴)
|
||||
func hero_display_name() -> String:
|
||||
return String(hero.display_name) if hero != null else ""
|
||||
|
||||
|
||||
# 行族数组读取(ShopTable 属性名 = 行族名;表未已入库/属性缺失 = 空集非错误态)
|
||||
func _shop_rows(family: String) -> Array:
|
||||
if shop == null:
|
||||
|
||||
@@ -212,7 +212,7 @@ func _on_unit_died(u: Dictionary) -> void:
|
||||
_halted = true # 主角倒下——flush 余单作废;Game Over 归判定段既有 hp 读数路径
|
||||
return
|
||||
for t in _queue:
|
||||
if t.source_unit == u:
|
||||
if is_same(t.source_unit, u):
|
||||
t["cancelled"] = true # 死亡即不动手——该敌未结单据作废(静默零行零 commit)
|
||||
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
class_name LevelRunner
|
||||
extends RefCounted
|
||||
|
||||
signal node_entered(node: Dictionary, info: Dictionary) # info 按 type:battle/boss={} · reward={}(内容待定) · rest={healed,hp,max_hp} · shop={gp}
|
||||
signal choices_presented(candidates: Array) # 选路口(兑付确认后——候选节点引用数组,零自动推进)
|
||||
signal step_row(row: Dictionary) # 转发当前 战斗结算 结算行
|
||||
signal node_entered(node: Dictionary, info: Dictionary) ## info 按 type:battle/boss={} · reward={}(内容待定) · rest={healed,hp,max_hp} · shop={gp}
|
||||
signal choices_presented(candidates: Array) ## 选路口(兑付确认后——候选节点引用数组,零自动推进)
|
||||
signal step_row(row: Dictionary) ## 转发当前 战斗结算 结算行
|
||||
signal round_started(round_index: int)
|
||||
signal board_changed()
|
||||
signal level_finished(payload: Dictionary) # {result, hp, node_index, node_id, node_type}
|
||||
signal wallet_changed(event: String, gp: int, extra: Dictionary) # GP 钱包事件投影:gain{amount}/buy{product,price}/clear——试玩日志与商店覆盖层刷新源(GP+商店房实施批)
|
||||
signal level_finished(payload: Dictionary) ## {result, hp, node_index, node_id, node_type}
|
||||
signal wallet_changed(event: String, gp: int, extra: Dictionary) ## GP 钱包事件投影:gain{amount}/buy{product,price}/clear——试玩日志与商店覆盖层刷新源(GP+商店房实施批)
|
||||
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
@@ -758,12 +758,12 @@ func current_price(prow: Dictionary) -> int:
|
||||
+ int(prow.get("price_increment", 0)) * purchased_tier(String(prow.get("product_id", ""))))
|
||||
|
||||
|
||||
# 散折价单点(财富之道·散 economy_spend 激活时商品购买价 ×80% provisional,
|
||||
# 散折价单点(财富之道·散 economy_spend 激活时商品购买价 ×spend_discount〔表 knob,缺省 0.8〕,
|
||||
# floor 取整偏向玩家、下限 1 防 1 元商品折成 0;范围=商品标价定义边界〔legacy 商品面 current_price
|
||||
# + 池货架面 pool_price 同折〕——商店刷新价 shop_refresh_cost 与三选一付费刷新不折)
|
||||
func _spend_discounted(price: int) -> int:
|
||||
if _tree_rules.has("economy_spend"):
|
||||
return maxi(1, int(floor(float(price) * 0.8)))
|
||||
return maxi(1, int(floor(float(price) * db.shop_spend_discount())))
|
||||
return price
|
||||
|
||||
|
||||
@@ -874,12 +874,12 @@ func pool_stock(kind: String) -> int:
|
||||
|
||||
# 现价 = ceil(基价 × b^(关序−1) × A^(本次进店已购次数))(P4 甲 ceil 向上单一取整——玩家不利向;
|
||||
# A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5)。relic 卡(保底占位——shop 词表外)
|
||||
# 基价 provisional 15 固定锚(台账披露待追认——shop.tres 词表扩列归数值批)。
|
||||
# 基价 provisional 15(表 knob relic_base_price——shop.tres 词表扩列归数值批)。
|
||||
func pool_price(kind: String) -> int:
|
||||
var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind)
|
||||
if row.is_empty():
|
||||
if kind == "relic":
|
||||
return _spend_discounted(int(ceil(15.0 * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) * pow(db.shop_price_a(), float(_shop_visit_buys)))))
|
||||
return _spend_discounted(int(ceil(db.shop_relic_base_price() * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) * pow(db.shop_price_a(), float(_shop_visit_buys)))))
|
||||
return 999999
|
||||
var base := float(maxi(1, int(row.get("base_price", 1))))
|
||||
var price_f: float = base * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) \
|
||||
|
||||
@@ -5,9 +5,23 @@
|
||||
class_name MinesweeperRules
|
||||
extends RefCounted
|
||||
|
||||
## 揭示事实流:每格首次进入已开集恰发一次(已开格零事件)。
|
||||
## 载荷键集(四处发射位并集):cell: Vector2i/kind: "safe"|"mine"/digit: int(安全格邻雷数;雷格 -1)/
|
||||
## variant_ids: Array(地形变异身份,雷格空集)/channel: "initial"|"dig"|"chain"|"fx_reveal"/
|
||||
## resource: bool(格面资源兑付资格:initial=false,其余 true)/dug_under_seal: bool(首格带封印直挖标记——
|
||||
## initial 通道不发此键)。
|
||||
## 发送时点:initial=install_board 初始展开逐格;dig/chain=_do_dig 揭示闭包内(本格 AP 扣费在其后);
|
||||
## fx_reveal=fx_reveal 闭包内(零 AP 消耗零相位要求)。
|
||||
## 消费方依赖:战斗结算 _on_cell_revealed 以 resource 分流缓冲(fx_reveal 由兑付点直入账,禁二次缓冲)、
|
||||
## 以 kind 驱动积点计数、以 cell 取奖励快照——禁改键名与语义;事件序 cell_revealed 批先于 seal_judged 批。
|
||||
signal cell_revealed(evt: Dictionary)
|
||||
## 封印判定事实流:轮末事务开始时的封印位逐格判定(行主序确定性枚举,被拔的不判)。
|
||||
## 载荷键集:cell: Vector2i(封印格)/hit: bool(该格是否为雷)。
|
||||
## 发送时点:停手路径 _do_stop 委托行动经济之前;棋盘路径揭示闭包收尾(phase 非 Active 时)——
|
||||
## 全路径统一先于 round_end 发射(事件序契约 seal_judged < round_end)。
|
||||
## 消费方依赖:战斗结算 _on_seal_judged 收批后统一开封印命中单——载荷缺键即错判,禁改键名与语义。
|
||||
signal seal_judged(evt: Dictionary)
|
||||
signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发)
|
||||
signal round_end_board(payload: Dictionary) ## 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发)
|
||||
|
||||
var db: ConfigDb
|
||||
var action_economy: ActionEconomy
|
||||
|
||||
@@ -56,9 +56,9 @@ func apply_effect(effect_id: String, source: Dictionary, duration_override: int
|
||||
"stack":
|
||||
for inst in _instances:
|
||||
if inst.effect_id == effect_id:
|
||||
if inst.layers >= max_stack:
|
||||
if int(inst.layers) >= max_stack:
|
||||
return false # 满层拒绝原子——容器零写入(拒收由战斗结算记"奖励未生效"行)
|
||||
inst.layers += 1 # stack 授予不触碰 R(spec 决议)
|
||||
inst.layers = int(inst.layers) + 1 # stack 授予不触碰 R(spec 决议)
|
||||
return true
|
||||
_instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display))
|
||||
return true
|
||||
@@ -99,7 +99,7 @@ func query_active(side: String) -> Array:
|
||||
"display_name": inst.snap_display,
|
||||
"snap_magnitude": inst.snap_m,
|
||||
"snap_unit": inst.snap_unit,
|
||||
"effective_value": inst.layers * inst.snap_m, # L×m 加算(percent 越域不 clamp——收口归 战斗结算)
|
||||
"effective_value": int(inst.layers) * int(inst.snap_m), # L×m 加算(percent 越域不 clamp——收口归 战斗结算)
|
||||
})
|
||||
return out
|
||||
|
||||
@@ -111,7 +111,7 @@ func decay_round() -> int:
|
||||
var k := 0
|
||||
var survivors: Array[Dictionary] = []
|
||||
for inst in _instances:
|
||||
inst.remaining -= 1
|
||||
inst.remaining = int(inst.remaining) - 1
|
||||
if inst.remaining <= 0:
|
||||
k += 1
|
||||
else:
|
||||
|
||||
@@ -52,14 +52,6 @@ func inventory_count() -> int:
|
||||
return _items.size()
|
||||
|
||||
|
||||
# 持有数(同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有)
|
||||
# 同调位守卫谓词(读取源 transitional_sync_slots 过渡键已随表退役;
|
||||
# 容量闸现行事实源 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
|
||||
# 冒烟「过渡键读取点归零」断言覆盖)
|
||||
|
||||
|
||||
# 持有件记录(奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
|
||||
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
|
||||
# 整图零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
|
||||
# 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用);
|
||||
# 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——自动兑付无玩家入口)
|
||||
|
||||
@@ -52,7 +52,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides
|
||||
return false
|
||||
# max_hp = 生效上限(非入参 hero_hp)——跨战斗携带 HP 时上限不得压扁
|
||||
# (恢复点/治疗包的满血守卫与治疗上限随之失效)
|
||||
_hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": "探险者", "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面)
|
||||
_hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": db.hero_display_name(), "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面)
|
||||
_enemies.clear() # 难度曲线与Boss DifficultyTable 修饰:施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10);混编行集内全部敌种同乘((L,D) 精确匹配一次取值——F9 同乘不改变异,敌人系统 v0.15)
|
||||
var mod: Dictionary[Variant, Variant] = db.resolve_difficulty_modifiers(level_index, node_depth)
|
||||
var hp_mult: float = float(mod.get("hp_mult", 1.0))
|
||||
@@ -254,7 +254,7 @@ func apply_damage(u: Dictionary, n: int) -> bool:
|
||||
if u.is_empty() or _despawned or not u.alive:
|
||||
_diag("apply_damage 失效/死亡句柄")
|
||||
return false
|
||||
u.hp = maxi(0, u.hp - n)
|
||||
u.hp = maxi(0, int(u.hp) - n)
|
||||
if u.hp == 0:
|
||||
u.alive = false # Dead 不可逆(死亡判定时机归 战斗结算)
|
||||
unit_died.emit(u) # 死亡事件——单位自行宣布
|
||||
@@ -267,7 +267,7 @@ func heal(u: Dictionary, n: int) -> int:
|
||||
_diag("heal 非法/失效句柄")
|
||||
return 0
|
||||
var actual: int = mini(n, int(u.max_hp) - int(u.hp))
|
||||
u.hp += actual
|
||||
u.hp = int(u.hp) + actual
|
||||
return actual
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user