Files
ChronicleOfTheImmortalClan/src/renderer/game/engine/runtime/Systems/tribulation.ts
T
thzxx 4dfa07cc9f v0.1.32: 万宝琳琅——物品六类化 + 新材料七种 + 循环接入 + 测试 5009
【资源物品大扩容】
- ItemKind 6 值:resource/material/pill/talisman/brew/artifact——市场分群/背包/售卖/modSchema 全链
- 新材料七种:灵玉/灵木/兽皮/灵果(POOL_BASE+MARKET_IDS+worldgen 偏移四同步入世界池)、
  灵露/丹砂/符纸(道具料)
- 循环接入:新建筑灵果园(灵果)/灵木坊(灵木);productionTick 泛化逐建筑读 produceTable 全键;
  秘境掉落材料化(灵玉/兽皮/灵木);材料类同向微漂(物产同源生态)

【新用途】
- 符箓:war 战斗开战武备自动消耗(talismanWarPrep 零 rng)
- 灵酿:渡劫伤后自动回气 +5
- 新丹方和灵丹/虚元丹 + 玉澜刃/灵酿铸器配方

【测试 5009】86 套件(+1000:matrix-treasure-a/b 950 + 断链修整);
【金钟罩】0.1.32 基线重算(物品/池/生产泛化受控变更);build 通过
2026-08-23 21:05:03 +08:00

102 lines
3.6 KiB
TypeScript

import type { World } from '../World'
import { Character } from '../../../types/domain'
import { nextRealm, MAJOR_ORDER, realmDeathChance, describeRealm } from '../../../data/realms'
export const TRIB_MAJORS: string[] = ['foundation', 'core', 'nascent', 'spirit']
export function needsTribulation(c: Character): boolean {
const next = nextRealm(c.realm)
if (!next) return false
if (next.major === c.realm.major) return false
return TRIB_MAJORS.includes(next.major)
}
export function tribulationEventId(c: Character): string {
return `ev-trib-${c.id}-${c.realm.major}-${c.realm.minor}`
}
export function resolveTribulation(w: World, c: Character, mode: 'rash' | 'guard' | 'delay'): string {
if (mode !== 'delay' && (w.state.family.inventory['brew-niang'] ?? 0) > 0 && c.health < 40) {
// R4 灵酿:受创回气(每渡劫一次至多一瓶——疗伤佳酿)
w.state.family.inventory['brew-niang'] = w.state.family.inventory['brew-niang']! - 1
c.health = Math.min(100, c.health + 5)
}
const next = nextRealm(c.realm)
if (!next) return 'peak'
if (mode === 'delay') {
c.tribDelayYear = w.state.year + 1
c.tribBoost = 0
w.log('info', `${c.name} 按兵不动,引而不发,待来年再渡(丹力渐散)。`)
return 'delayed'
}
let successChance = perTribChance(w, c)
let guardian: Character | null = null
if (mode === 'guard') {
const fam = w.state.family
if (fam.stones < 100) {
w.log('bad', '护法之资不足,此行只得咬牙亲渡。')
} else {
fam.stones -= 100
successChance += 0.08
guardian = w
.aliveMembers()
.filter((m) => m.id !== c.id && MAJOR_ORDER.indexOf(m.realm.major) >= MAJOR_ORDER.indexOf(c.realm.major))
.sort((a, b) => MAJOR_ORDER.indexOf(b.realm.major) - MAJOR_ORDER.indexOf(a.realm.major))[0] ?? null
}
}
const p = Math.min(0.92, Math.max(0.06, successChance))
const s = w.state
if (w.rng.chance(p)) {
c.realm = next
c.realmProgress = 0
w.emitFx('spark', `trib:${c.id}`)
c.health = 100
c.lastBreakthroughAttempt = s.year * 12 + s.month
w.chronicle('breakthrough', `${c.name} 渡劫功成,踏入【${describeRealm(next)}】!`, c.id, true)
w.log('good', `✦ 天雷散尽,${c.name} 渡劫成功,晋阶【${describeRealm(next)}】!`)
if (next.major === 'spirit' && !s.flags['firstSpirit']) {
s.flags['firstSpirit'] = s.year
}
return 'success'
}
// 失败
c.lastBreakthroughAttempt = s.year * 12 + s.month
c.realmProgress = Math.max(0, 100 - 28 - w.rng.int(0, 12))
c.health = Math.max(1, c.health - 22 - w.rng.int(0, 12))
if (c.health < 20) c.state = 'wounded'
let text = `${c.name} 渡劫失败,肉身受创。`
const danger = realmDeathChance(c.realm, c.mind)
if (w.rng.chance(danger)) {
c.alive = false
c.deathYear = s.year
c.deathCause = '渡劫陨落'
w.chronicle('death', `${c.name} 天劫加身,灵石俱焚而陨。`, c.id, true)
w.log('bad', `☠ ${c.name} 渡劫陨落。`)
return 'dead'
}
if (guardian) {
const g = guardian
if (w.rng.chance(0.5)) {
g.health = Math.max(1, g.health - 15 - w.rng.int(0, 15))
if (g.health < 25) g.state = 'wounded'
w.log('bad', `${g.name} 为护法所伤。`)
}
}
w.log('bad', text)
return 'fail'
}
export function perTribChance(w: World, c: Character): number {
const base = ({
foundation: 0.6,
core: 0.5,
nascent: 0.36,
spirit: 0.26
} as Record<string, number>)[c.realm.major] ?? 0.9
return Math.min(0.92, base + c.mind * 0.01 + c.health / 400 + (c.tribBoost ?? 0))
}