import type { World } from '../World' import { Character } from '../../../types/domain' import { produceOffspring } from './cultivation' import { yearGrowth } from './diplomacy' import { MALE_GIVEN, FEMALE_GIVEN } from '../../kernel/names' import { aspirationById } from '../../../data/aspirations' import { lifespanOf } from './lifecycle' export function yearStartMarriage(w: World): void { yearGrowth(w) const s = w.state const fam = s.family const zongci = fam.buildings['zongci'] ?? 0 const birthBase = 0.6 + zongci * 0.04 + (fam.difficulty === 'easy' ? 0.06 : fam.difficulty === 'hard' ? -0.06 : 0) const couples = buildCouples(w) // 族内夫妇 let yearBirths = 0 const pop = Object.values(s.members).filter((m) => m.alive).length for (const couple of couples.internal) { const [a, b] = couple const father = a.gender === 'male' ? a : b const mother = a.gender === 'male' ? b : a const fatherAge = w.ageOf(father) const motherAge = w.ageOf(mother) // P0-A 续代:生育窗随境界延长(高境界者容颜常驻、生育期延) const fatherLife = lifespanOf(w, father) const motherLife = lifespanOf(w, mother) const fatherMax = Math.min(520, 52 + (fatherLife - 75) * 0.35) const motherMax = Math.min(520, 46 + (motherLife - 75) * 0.35) if (fatherAge < 18 || fatherAge > fatherMax || motherAge < 16 || motherAge > motherMax) continue const offspringBonus = Object.values(s.members) .filter((m) => m.alive && aspirationById(m.aspiration)?.effect.type === 'offspring') .length * 0.05 // P0-A 人口卫生:生育率稳定在 0.2~0.35/对/年;家族进入大龄期再衰减(防指数爆炸) // P0-A 人口卫生(终版):全局年胎 cap 按人口档——小族多生、大族停育(稳态 ~20 人防爆炸) if (yearBirths >= 3 || pop > 24) continue if (pop > 18 && yearBirths >= 1) continue const p = Math.min(0.6, birthBase * 0.35 * (0.75 + mother.physique * 0.05)) + offspringBonus if (!w.rng.chance(p)) continue yearBirths++ const gen = Math.max(father.generation, mother.generation) + 1 const first = produceOffspring(w, { father, mother, generation: gen, bornYear: s.year, surname: fam.surname }) w.addMember(first, father, mother) let note = `${fam.surname}氏新增一员,名唤${first.name},生年 ${s.year}。` if (w.rng.chance(0.06)) { const twin = produceOffspring(w, { father, mother, generation: gen, bornYear: s.year, surname: fam.surname }) w.addMember(twin, father, mother) w.state.yearStats.births += 1 note += ` 双生之喜!双子名唤${twin.name}。` } w.chronicle('birth', note, first.id, true) w.state.yearStats.births += 1 w.log('good', `${fam.surname}家诞下新丁:${first.name}。`) } // 联姻外孙来投(男方娶亲:妻室携子来归;或女儿携子回门)——与族内育共用人口 cap for (const member of Object.values(s.members)) { if (!member.alive || !member.spouseHouse) continue const age = w.ageOf(member) if (age < 18 || age > 44) continue if (yearBirths >= 2 || pop > 24) continue if (!w.rng.chance(0.15)) continue const gen = member.generation + 1 const house = member.spouseHouse const child = produceOffspring(w, { father: null, mother: null, generation: gen, bornYear: s.year, surname: fam.surname }) child.spouseHouse = house w.addMember(child) w.chronicle('birth', `${member.name}自${house}携幼子归庄,名唤${child.name}。`, child.id, true) w.state.yearStats.births += 1 w.log('info', `${member.name} 领着小辈回门投亲。`) } // 媒人撮合族内婚(含续弦与再醮) const isWidowed = (c: Character): boolean => { if (!c.spouseId) return false const sp = w.state.members[c.spouseId] return !!sp && !sp.alive } const men = w .aliveMembers() .filter((c) => c.gender === 'male' && c.state !== 'expedition' && (isWidowed(c) || !c.spouseId)) .filter((c) => { const age = w.ageOf(c) return age >= 18 && age <= 48 }) const women = w .aliveMembers() .filter((c) => c.gender === 'female' && c.state !== 'expedition' && (isWidowed(c) || !c.spouseId)) .filter((c) => { const age = w.ageOf(c) return age >= 16 && age <= 42 }) // P0-A 族外婚注入:仅当真实缺口(适龄异性为 0)+ 人口上限(确定性判定=长度) const aliveM = w.aliveMembers().length const inlawNeeded = aliveM < 24 && men.length > 0 && women.length === 0 && w.rng.chance(0.8) const inlawFemaleNeeded = aliveM < 24 && women.length > 0 && men.length === 0 && w.rng.chance(0.8) if (inlawNeeded || inlawFemaleNeeded) { const gender = inlawNeeded ? 'female' : 'male' const inlaw = produceOffspring(w, { father: null, mother: null, generation: 1, bornYear: s.year - (18 + w.rng.int(0, 10)), surname: '远' as never }) inlaw.spouseHouse = '外姓' w.addMember(inlaw) if (gender === 'male') men.push(inlaw) else women.push(inlaw) w.log('info', `媒人引介一位${gender === 'male' ? '外姓男' : '外姓女'}士入族相亲。`) } for (const m of men) { if (w.rng.chance(0.55) && women.length > 0) { const candidate = women.filter( (x) => !(x.fatherId && x.fatherId === m.fatherId) && !(x.motherId && x.motherId === m.motherId) && x !== m && !x.children.includes(m.id) && !m.children.includes(x.id) ) if (candidate.length === 0) continue const bride = w.rng.pick(candidate) m.spouseId = bride.id bride.spouseId = m.id w.chronicle('marriage', `${m.name}与${bride.name}缔结连理。`, m.id, true) w.log('info', `${m.name} 与 ${bride.name} 成婚。`) const idx = women.indexOf(bride) if (idx >= 0) women.splice(idx, 1) } } fam.generation = Math.max( fam.generation, ...Object.values(s.members).filter((c) => c.alive).map((c) => c.generation) ) } function buildCouples(w: World): { internal: [Character, Character][]; cross: Character[] } { const internal: [Character, Character][] = [] const cross: Character[] = [] for (const member of Object.values(w.state.members)) { if (!member.alive || !member.spouseId) continue const spouse = w.state.members[member.spouseId] if (!spouse?.alive) continue const key = [member.id, spouse.id].sort().join('|') if (internal.some(([a, b]) => [a.id, b.id].sort().join('|') === key)) continue internal.push([member, spouse]) } for (const member of Object.values(w.state.members)) { if (member.alive && member.spouseHouse) cross.push(member) } return { internal, cross } }