v0.1.0: 仙途家族志 · Chronicle of the Immortal Clan

家族模拟器首版:修仙/经营/战斗/外交/叙事全套系统
- Electron + React + TS + MetonaSqlark(aria+OPFS)
- 水墨中国风 UI
- 引擎种子随机、确定性可回放
- 19 项单元测试、端到端冒烟验证
This commit is contained in:
Hermes Agent
2026-08-23 00:04:16 +08:00
commit 2f81b29878
69 changed files with 14305 additions and 0 deletions
@@ -0,0 +1,93 @@
import { World } from '../world'
import { npcById } from '../../data/npcs'
import { findEvent, fire } from './events'
export function diplomacyTick(w: World): void {
const s = w.state
const drift = w.rng.chance(0.15)
for (const npc of Object.values(s.npcFamilies)) {
if (drift) {
if (npc.relation > 0) npc.relation -= 1
else if (npc.relation < 0) npc.relation += 1
}
if (npc.relation < -50) {
const last = (w.state.family.flag[`raidCD-${npc.id}`] as number | undefined) ?? 0
if (s.year - last >= 2 && w.rng.chance(0.045)) {
fire(w, `ev-raid-${npc.id}`)
}
}
}
}
export function yearGrowth(w: World): void {
const s = w.state
for (const npc of Object.values(s.npcFamilies)) {
const def = npcById(npc.id)
const [a, b] = def.powerGrowth
npc.power += w.rng.int(a, b)
}
}
export function npcRelation(w: World, npcId: string): number {
return w.state.npcFamilies[npcId]?.relation ?? 0
}
export function giftNpc(w: World, npcId: string, stones: number): boolean {
const fam = w.state.family
if (stones <= 0 || fam.stones < stones) return false
fam.stones -= stones
const npc = w.state.npcFamilies[npcId]
const gain = Math.max(1, Math.round(stones / 12))
npc.relation = Math.min(100, npc.relation + gain)
w.log('info', `厚礼送往${npc.name},两家关系 +${gain}`)
return true
}
export function makePeace(w: World, npcId: string): boolean {
const fam = w.state.family
const npc = w.state.npcFamilies[npcId]
if (fam.stones < 200) return false
fam.stones -= 200
npc.relation = Math.max(npc.relation + 35, 0)
w.chronicle('diplomacy', `${npc.name}立下和约,两家罢兵互市。`, undefined, true)
w.log('good', `${npc.name}言和。`)
return true
}
export function marryNpcFamily(w: World, npcId: string): boolean {
const s = w.state
const fam = s.family
const npc = s.npcFamilies[npcId]
if (!npc || npc.relation < 25) return false
const eligible = w
.aliveMembers()
.filter((c) => w.ageOf(c) >= 18 && w.ageOf(c) <= 42 && c.state !== 'expedition')
.filter((c) => !c.spouseId)
if (eligible.length === 0) return false
const npcDef = npcById(npcId)
const candidate = w.rng.pick(eligible)
candidate.spouseHouse = npc.name
npc.relation += 20
fam.reputation += 4
w.chronicle('marriage', `${candidate.name}${npc.name}联姻,两家绸缪通好。`, candidate.id, true)
w.log('good', `${candidate.name}${npc.name}联姻成功!每年或降麟儿。`)
return true
}
export function arrangeWedding(w: World, aId: string, bId: string): boolean {
const a = w.memberById(aId)
const b = w.memberById(bId)
if (!a.alive || !b.alive || a.spouseId || b.spouseId) return false
if (a.gender === b.gender) return false
if (a.fatherId === b.fatherId && a.fatherId) return false
a.spouseId = b.id
b.spouseId = a.id
const aAge = w.ageOf(a)
const bAge = w.ageOf(b)
if (w.state.family.flag['tenants']) {
w.state.family.reputation += 1
}
w.chronicle('marriage', `${a.name}${aAge})与${b.name}${bAge})拜堂成亲。`, a.id, true)
w.log('good', `${a.name}${b.name} 结为连理。`)
return true
}