v0.1.2: 宗族气韵(职事/悟道/谱系/碑录/庆典/飞升/音效)

- 职事任命:长老(+修炼)/供奉(+战力)/执事(+坊市)/掌教(+闭关) 每人加成叠加,上限管理
- 功法悟道:修习积悟性点,小成/大成逐级+战力,大境界突破传道于直系晚辈
- 宗族谱系页:世代分带 + 夫妻同卷 + 子孙连枝,点节点开详情,先人簿可查
- 功德碑:宗主辞世即勒石,宗祠页碑录永存
- 百年庆典 + 飞升之路事件链(留世坐镇/仙风入体 或 云游飞升/宗族蒙荫)
- 合成音效(WebAudio):鼓点/钟鸣/纸韵/磬响,设置页开关
- 单测 29→38;typecheck/build 通过;GUI 冒烟受限于 WSLg 掉线(SMOKE-TIMEOUT 优雅退出)
This commit is contained in:
2026-08-23 08:28:49 +08:00
parent 6b15a97d0c
commit fefbe1f1d7
22 changed files with 864 additions and 8 deletions
@@ -7,6 +7,7 @@ import { MISSIONS } from '../../data/secrets'
import { npcById } from '../../data/npcs'
import { resolveRaid } from './combat'
import { sendMission } from './missions'
import { findInheritor } from '../creation'
const ALL_EVENTS: EventDef[] = [...EVENTS]
@@ -31,6 +32,35 @@ export function dynamicEventFor(id: string): EventDef | undefined {
]
}
}
if (id === 'ev-centennial') {
return {
id,
name: '百年庆典',
category: 'fate',
weight: 0,
once: true,
text: '百年元辰已至:这百年间,苗裔繁衍、香火未断。大宴宾客?还是告天祭祖?',
options: [
{ label: '大开宴席,张灯结彩', hint: '灵石-200,声望+15,全族心境大悦', eff: { res: { stones: -200 }, rep: 15, memberBy: { by: 'inspire', target: 'all', n: 4 } } },
{ label: '设坛告天', hint: '灵石-100,声望+10', eff: { res: { stones: -100 }, rep: 10, flag: { centennial: 'rite' } } },
{ label: '阖家简庆', hint: '声望+5', eff: { rep: 5 } }
]
}
}
if (id === 'ev-feisheng') {
return {
id,
name: '飞升之路',
category: 'fate',
weight: 0,
once: true,
text: '化神之上本为天堑,如今族中已有人触碰仙门。飞升之路敞开:宗族该作何抉择?',
options: [
{ label: '留仙坐镇', hint: '他留世镇族:天赋大盛', eff: { feisheng: { stay: true } } },
{ label: '放仙飞升', hint: '他云游而去寻仙门:宗族蒙荫', eff: { feisheng: { stay: false } } }
]
}
}
return undefined
}
@@ -81,6 +111,18 @@ export function eventRoll(w: World): void {
}
return
}
// 里程碑检测(优先于日常事件)
if (s.year >= 100 && !s.completedEvents.includes('ev-centennial')) {
fire(w, 'ev-centennial')
return
}
const firstSpirit = s.flags['firstSpirit'] as number | undefined
if (firstSpirit && s.year - firstSpirit >= 2 && s.year - firstSpirit <= 3 && !s.completedEvents.includes('ev-feisheng')) {
fire(w, 'ev-feisheng')
return
}
const roll = w.rng.next()
const category: 'daily' | 'major' | 'fate' | undefined = roll < 0.5 ? 'daily' : roll < 0.78 ? 'major' : roll < 0.86 ? 'fate' : undefined
if (!category) return
@@ -206,6 +248,30 @@ function applyEffect(w: World, eff: EffectDef, squad?: string[]): void {
if (eff.addTech) {
if (!fam.techniques.includes(eff.addTech)) fam.techniques.push(eff.addTech)
}
if (eff.feisheng) {
const s2 = w.state
const immortal = w
.aliveMembers()
.sort((a, b) => rankPower(w, b) - rankPower(w, a))[0]
if (immortal) {
if (eff.feisheng.stay) {
if (!immortal.traits.includes('fengxian')) immortal.traits.push('fengxian')
w.chronicle('event', `${immortal.name} 谢绝仙门征召,愿镇守此世,护佑宗族万世。`, immortal.id, true)
w.log('good', `${immortal.name} 留世坐镇(仙风入体)。`)
} else {
immortal.alive = false
immortal.deathYear = s2.year
immortal.deathCause = '云游飞升'
s2.family.flag['fengFeiBless'] = true
w.chronicle('event', `${immortal.name} 于月首孤身东去,驾鹤而飞。天地留一缕仙风,庇佑宗族。`, immortal.id, true)
w.log('good', `${immortal.name} 飞升而去,宗族蒙庇。`)
if (s2.family.headId === immortal.id) {
const heir = findInheritor(w)
if (heir) w.assignHead(heir.id, true)
}
}
}
}
if (eff.memberBy) {
const targets = pickMember(w, eff.memberBy)
const by = eff.memberBy.by