diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 86cadd3c..82629ae3 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -77,6 +77,7 @@ var _attune_safe_seen := 0 # 掘进蓄能 battle 域安全开格 var _relic_first_dig_used := false var _relic_combo := 0 # 矿脉直觉连击计数(battle 域——safe +1/mine 清零) var _relic_deep_dug := 0 # 深挖钻头深层产出计数(battle 域——每 cap 格额外 grant 一份) +var _relic_magma_layers := 0 # 熔火之躯积层(battle 域——受地形伤 +1,轮末反打清零) func _attune_has(item_id: String) -> bool: @@ -158,6 +159,14 @@ func _relic_flags_for(relic_ids: Array) -> void: flags["shield_per_layer"] = int(fx.get("shield_per_layer", 0)) "charge_spike": flags["spike_mult"] = int(fx.get("value", 0)) + "terrain_refund": + flags["terrain_refund"] = int(fx.get("value", 0)) + "terrain_dig_double": + flags["terrain_dig_double"] = int(fx.get("value", 0)) + "flag_neutralize": + flags["flag_neutralize"] = int(fx.get("value", 0)) + "variant_counter": + flags["variant_counter_value"] = int(fx.get("value", 0)) var mt := int(tiers.get("magic", 0)) if mt >= 1: flags["rider_flat"] = int(flags.get("rider_flat", 0)) + 1 # 档一质变(≥2 件):rider +1(provisional) @@ -243,6 +252,7 @@ func begin_battle(enc: Dictionary) -> bool: _relic_first_dig_used = false _relic_combo = 0 _relic_deep_dug = 0 + _relic_magma_layers = 0 # 迭代 10 奖励重构批——跨冒险行动点加成轴(统一冒险加算区第四轴):经排队修正通道兑现 # 「每场战斗开始首轮 AP +加成值」(queue_ap_delta = 下轮开轮基线加算、notify_round_start 兑现后 # 清零——首轮即消费,后续轮次不重复兑现;语义锚 = 行动经济 GDD「跨冒险行动点加成轴」节) @@ -465,6 +475,20 @@ func _settle() -> void: var vt := _damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, "地形变异", String(vrow.get("display_name", vid)), "board", \ int(vrow.get("effect_value", 0)), DamageController.TARGET_HERO, ri, evt.cell, "", 0, "", {"variant_id": String(vid)}) _damage.submit(vt) + # 吞噬流四面(迭代 12 T6——地形从纯税到第三资源带;伤害仍发生=乙稿判据 3 承载): + # ①荆棘编织者=挖开伤害型地形格额外掉落一次格面资源(主动吃地形换资源) + if int(_damage.relic_flags.get("terrain_dig_double", 0)) > 0: + var tw_snap = _reward_economy.grant(evt.cell) + if tw_snap is Dictionary: + _grant_routed(tw_snap, "positive", "safe_cell", evt.cell, ri, "", "(荆棘编织)") + # ②地脉采撷=受地形伤掉落小额资源补偿(薄矿化——每次受伤兑一份) + if int(_damage.relic_flags.get("terrain_refund", 0)) > 0: + var lh_snap = _reward_economy.grant(evt.cell) + if lh_snap is Dictionary: + _grant_routed(lh_snap, "positive", "safe_cell", evt.cell, ri, "", "(地脉补偿)") + # ③熔火之躯=受地形伤积层(触发键=「受地形伤」通用不按种类特化——定稿红线承载) + if int(_damage.relic_flags.get("variant_counter_value", 0)) > 0: + _relic_magma_layers += 1 for evt in _facts_flags: _judge_flag(evt, ri) # 战旗军规开单段(迭代 12 T3 标旗流——flag_judged 批后统一开单:轮末每面标中旗对最前存活敌 @@ -491,6 +515,15 @@ func _settle() -> void: "战旗军规", "board", strike_value, DamageController.TARGET_FRONT_ALIVE, ri, fevt.cell, "normal") _damage.submit(st_t) + # 熔火之躯轮末反打(迭代 12 T6 吞噬流——受地形伤积层,轮末每层对全体敌 ~1 必中反伤; + # 触发键=「受地形伤」通用不按地形种类特化——定稿红线承载;反打后清零) + var magma := int(_damage.relic_flags.get("variant_counter_value", 0)) + if magma > 0 and _relic_magma_layers > 0: + var mg_t := _damage.open_ticket(DamageController.KIND_BOARD_BLAST, "relic_magma_body", + "熔火反噬 ×%d" % _relic_magma_layers, "board", magma * _relic_magma_layers, + DamageController.TARGET_ALL_ALIVE, ri, minesweeper.hero_pos(), "", 0, "", {}) + _damage.submit(mg_t) + _relic_magma_layers = 0 if _facts_path == "cleared": _grant_clear_reward(ri) _damage.flush() # 授予段界结账(B 模式——段内开单攒批到此统一兑现;A 模式 no-op;清图批前另有同步结账点) @@ -729,6 +762,22 @@ func _judge_flag(evt: Dictionary, ri: int) -> void: var fbt := _damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_flag_burst", "标中殉爆", "board", _attune_mag("flag_burst"), DamageController.TARGET_ALL_ALIVE, ri, cell, "", 0, "highlight", {}) _submit(fbt, false) + # 净化工兵(迭代 12 T6 吞噬流——标中旗中和相邻伤害型地形格):🔴 R4 甲永不对冲边界= + # **仅限伤害型**(thorn/magma/rockfall——variation 表 effect=flat_damage 判据),信息型 + # (hide_number 族)永不清除(红线①看门税不可对冲);中和=变普通格(BoardData.clear_variants)。 + if int(_damage.relic_flags.get("flag_neutralize", 0)) > 0: + var board: BoardData = minesweeper.current_board() + for n in board.neighbors8(cell): + var vids_n: Array = board.variant_ids(n) + var harmful := false + for vid_n in vids_n: + var vrow_n: Dictionary = db.variation.row_by_id(String(vid_n)) + if String(vrow_n.get("effect", "")) == "flat_damage": + harmful = true + break + if harmful: + board.clear_variants(n) + _emit_row("terrain_neutralized", "relic_purge_sapper", n, ri, "净化工兵·地形中和", 0, {}, "") # 雷区图谱(迭代 12 T3 标旗流——标中雷额外获得一份格面资源:插旗开始产出对标开格; # 复用 grant 单点,额外兑付走同一 _grant_routed 正向路由) if int(_damage.relic_flags.get("flag_resource_extra", 0)) > 0: diff --git a/game/core/board_data.gd b/game/core/board_data.gd index a02ffacf..56dd7dd7 100644 --- a/game/core/board_data.gd +++ b/game/core/board_data.gd @@ -50,6 +50,11 @@ func variant_ids(c: Vector2i) -> Array: return variants.get(c, []) +# 中和格变异(迭代 12 T6 净化工兵——伤害型地形格变普通格;🔴 R4 甲仅限伤害型的过滤在调用方) +func clear_variants(c: Vector2i) -> void: + variants.erase(c) + + func neighbors8(c: Vector2i) -> Array: var out: Array[Vector2i] = [] for dy in range(-1, 2): diff --git a/game/core/level_runner.gd b/game/core/level_runner.gd index 7fa71113..97c020ef 100644 --- a/game/core/level_runner.gd +++ b/game/core/level_runner.gd @@ -762,6 +762,26 @@ func _shop_draw_shelf() -> void: _shop_shelf.append(tok) if _shop_shelf.size() >= 3: break + # 商店保底(迭代 12 T6——「本关变异交互件必刷」简化口径:货架零流派件时**挤掉最后一张普通卡** + # 换吞噬流件〔地形交互件广义映射——按关变异精细绑定归数值批,台账披露待追认〕;占位制货架恒 + # ≤3;池内无 devour 令牌=不保底合法态) + var has_relic := false + for tok in _shop_shelf: + if String((tok as Dictionary).get("kind", "")) == "relic": + has_relic = true + break + if not has_relic and not _shop_shelf.is_empty(): + for row in db.relic_rows(): + if String(row.get("archetype", "")) != "devour": + continue + var rid := String(row.get("relic_id", "")) + for t in _pool_deck: + var tok2: Dictionary = t as Dictionary + if String(tok2.get("kind", "")) == "relic" and String(tok2.get("relic_id", "")) == rid: + _shop_shelf[_shop_shelf.size() - 1] = {"kind": "relic", "tier": String(tok2.get("tier", "")), "relic_id": rid} + break + if String((_shop_shelf[_shop_shelf.size() - 1] as Dictionary).get("kind", "")) == "relic": + break # 换一张即止 # 库存余量(TP0——令牌库内该 kind 剩余枚数单点读;未注入/防御读 = 0) @@ -774,9 +794,14 @@ func pool_stock(kind: String) -> int: # 现价 = ceil(基价 × b^(关序−1) × A^(本次进店已购次数))(P4 甲 ceil 向上单一取整——玩家不利向; -# A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5) +# A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5)。relic 卡(保底占位——shop 词表外) +# 基价 provisional 15 固定锚(台账披露待追认——shop.tres 词表扩列归数值批)。 func pool_price(kind: String) -> int: var row: Dictionary = db.shop_pool_row(kind) + if row.is_empty(): + if kind == "relic": + return 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 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))) \ * pow(db.shop_price_a(), float(_shop_visit_buys)) @@ -791,11 +816,18 @@ func shop_shelf() -> Array: var kind := String((tok as Dictionary).get("kind", "")) var tier := String((tok as Dictionary).get("tier", "")) var row: Dictionary = db.shop_pool_row(kind) + var display := String(row.get("display_name", kind)) + var value := int(db.adventure.bonus_value(kind, tier)) + if row.is_empty() and kind == "relic": + var rrow: Dictionary = db.relic_row(String(tok.get("relic_id", ""))) + display = String(rrow.get("display_name", "流派件")) + value = 0 out.append({ "kind_id": kind, "tier": tier, - "display_name": String(row.get("display_name", kind)), - "value": int(db.adventure.bonus_value(kind, tier)), + "relic_id": String(tok.get("relic_id", "")), + "display_name": display, + "value": value, "price": pool_price(kind), "stock_left": pool_stock(kind), }) diff --git a/game/data/tables/relics.tres b/game/data/tables/relics.tres index 77f7019d..461c09c5 100644 --- a/game/data/tables/relics.tres +++ b/game/data/tables/relics.tres @@ -222,6 +222,42 @@ rows = [{ "effects": { "value": 2 } +}, { +"relic_id": "ley_harvest", +"display_name": "地脉采撷", +"rarity": "common", +"archetype": "devour", +"hook": "terrain_refund", +"effects": { +"value": 1 +} +}, { +"relic_id": "thorn_weaver", +"display_name": "荆棘编织者", +"rarity": "rare", +"archetype": "devour", +"hook": "terrain_dig_double", +"effects": { +"value": 1 +} +}, { +"relic_id": "purge_sapper", +"display_name": "净化工兵", +"rarity": "rare", +"archetype": "devour", +"hook": "flag_neutralize", +"effects": { +"value": 1 +} +}, { +"relic_id": "magma_body", +"display_name": "熔火之躯", +"rarity": "rarest", +"archetype": "devour", +"hook": "variant_counter", +"effects": { +"value": 1 +} }] ; 双层 BD 实施批(2026-10-04):机制件表——行 = 流派件定义 diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index 52172bf4..bc619b20 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -602,8 +602,8 @@ func _smoke_adventure() -> void: func _smoke_relics() -> void: print("--- 机制件基建(relic_table · 档位激活引擎 · 机制件带接线)---") - # ① 现行表牌库构成:五流派 23 件入池——98 数值令牌+23 relic 令牌(T6 吞噬流续填) - check(_db.relics != null and (_db.relics.rows as Array).size() == 23, "relic_table 已加载(T2-T5 五流派 23 件在池——T6 续填)") + # ① 现行表牌库构成:七流派 27 件全量入池——98 数值令牌+27 relic 令牌(批次一铺量完成) + check(_db.relics != null and (_db.relics.rows as Array).size() == 27, "relic_table 已加载(七流派 27 件全量——批次一 T2-T6 铺量完成)") var a_r0 = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(71)) check(a_r0.start_adventure(71, {"first_script": true}), "机制件带场景:冒险启动(seed 71)") var relic_tokens := 0 @@ -613,8 +613,8 @@ func _smoke_relics() -> void: relic_tokens += 1 if String((t as Dictionary).get("tier", "")) == "rarest": rarest_tokens += 1 - check(relic_tokens == 23 and rarest_tokens == 5 and a_r0._bonus_deck.size() == 121, - "机制件带入池:23 件(rare 档 18 + rarest 锚点 5)——牌库 98+23=121") + check(relic_tokens == 27 and rarest_tokens == 6 and a_r0._bonus_deck.size() == 125, + "机制件带入池:27 件(rare 档 21 + rarest 锚点 6)——牌库 98+27=125") check(_db.relics.validate().is_empty(), "relic_table.validate 零违例(有行流派扫雷端铁律——magic first_dig_spell 在案)") # ② 档位激活引擎(纯函数直测——构造表 4 件 magic + 1 件 flag + 1 件 rarest 锚点) var rt := RelicTable.new() @@ -724,6 +724,14 @@ func _smoke_relics() -> void: "深挖钻头=开掘 +1 AP/每 4 格深层产出(R4 甲非信息税对价)· 蓄力三件=层值 1/减伤 1/层/满层 ×2") check(orch.action_economy.relic_ap_carry and orch.action_economy.relic_dig_extra == 1, "蓄力/钻头 action_economy 注入(ap_carry 改写停手转存 + dig 成本 +1——关着 false/0 零扰动)") + # ⑧ T6 吞噬流求值面(27 件全量——七流派档位成型) + var ids27: Array = ids23.duplicate() + ids27.append_array(["ley_harvest", "thorn_weaver", "purge_sapper", "magma_body"]) + orch._relic_flags_for(ids27) + var rf27: Dictionary = orch._damage.relic_flags + check(int(rf27.get("terrain_refund", -1)) == 1 and int(rf27.get("terrain_dig_double", -1)) == 1 + and int(rf27.get("flag_neutralize", -1)) == 1 and int(rf27.get("variant_counter_value", -1)) == 1, + "吞噬流求值:地脉采撷补偿 1 · 荆棘编织双产出 · 净化工兵中和旗(🔴 仅伤害型——过滤在消费面)· 熔火反噬 1/层") var orch0 = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(74)) orch0._relic_flags_for([]) check((orch0._damage.relic_flags as Dictionary).is_empty(), @@ -3556,11 +3564,19 @@ func _smoke_shop_pool() -> void: # ② 货架投影形制:kind 词表封闭 + 现价公式推导(ceil(base×b^(关序−1)×A^0)——核心现算,呈现零复算) var l2 := int(r8.level_row.level_index) for c in r8.shop_shelf(): - check(shelf_kinds.has(String(c.get("kind_id", ""))), "货架 kind 在封闭词表(%s)" % str(c.get("kind_id", ""))) - var prow: Dictionary = _db.shop_pool_row(String(c.get("kind_id", ""))) - var expect_p: int = int(ceil(float(int(prow.get("base_price", 0))) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) + # T6 商店保底后 relic 卡合法入架(保底占位)——词表断言放行 relic;价格断言分叉 + # (数值卡走 shop_pool_row 推导 / relic 卡走 15 固定锚——同公式不同基价) + var ckind := String(c.get("kind_id", "")) + var is_relic := ckind == "relic" + check(is_relic or shelf_kinds.has(ckind), "货架 kind 在封闭词表或保底 relic 卡(%s)" % ckind) + var expect_p: int + if is_relic: + expect_p = int(ceil(15.0 * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) + else: + var prow: Dictionary = _db.shop_pool_row(ckind) + expect_p = int(ceil(float(int(prow.get("base_price", 0))) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) check(int(c.get("price", -1)) == expect_p, - "首购现价 = ceil(基价×b^(关序−1))(%s: %d = %d 推导)" % [str(c.get("kind_id", "")), int(c.get("price", -1)), expect_p]) + "首购现价 = ceil(基价×b^(关序−1))(%s: %d = %d 推导)" % [ckind, int(c.get("price", -1)), expect_p]) # ③ 买 1 全重抽:出账+加算区+账本三落点 + 现价 A 链递增(买后 visit 计数=1;TP0——消费在架令牌本体) var buy_c: Dictionary = {} for c in r8.shop_shelf():