v0.1.29: 续命——第四轮全量审计歼灭(2014 全绿)
【P0-A 续代断链(3seed×183年实测:170 年零出生老人院)——本轮最重】 - 生育窗随境界延长(化神 300+ 可育);天年=min(span,200)(850 寿元永不达的死亡回归) - 族外婚注入(媒人枯竭引外姓——0.1.1 至今缺失的最后一环) - 全局年胎 cap(≤18 人年 3 胎/18-24 1 胎/>24 停)→ 稳态 ~25 人、gens 3、2200 月 420ms - 确定性回归:inlaw 判定长度化+单一 chance(修复过程中的真 bug——det 二分锁定) 【P1 七件】legacypass 年锚(同一年 12 连发 224 次实测)/敌强度相对化(0.35+rel×0.55, cap 2.6——5000+ 战力零风险碾压的定锚)/eventRoll 插件权重防垄断(>核心 50% 压缩)/ allEvents 按 enabled 过滤(禁用 MOD 事件双闸全面兑现)/bundle 全量导入(modParseAll 导出 2 回 2)/ chronicle 表治理(write-only→no-op)/Stash 并发(open in-flight 去重+save try) + PluginManager catch 全回滚 + about 版本统一 【P2 群】decline 150 激活生灭/化神月俸 20 石泄灵石死水/灾因 brief+descOf 接线/ worldGenMods 落空/EventModal 文案对齐/LegacyPanel 恒等清理 【测试】70 套件 2014 全绿(audit-0.1.29 六例:人口曲线/传薪年锚/强度公式/bundle 全量/异常回滚/灾因 brief); 【金钟罩】0.1.29 基线重算(续代/战斗/事件数值受控变更);build 通过
This commit is contained in:
@@ -167,9 +167,11 @@ export function resolveRaid(
|
||||
const npc = w.state.npcFamilies[npcId]
|
||||
const def = npcById(npcId)
|
||||
if (!def) return { win: false, draw: true, lines: ['此族已散。'], loot: undefined, losses: [] }
|
||||
// 宗主实力分层:power 由年度成长+换代驱动,劫掠强度随之浮动(0.5↔1.8)
|
||||
// P1-C 敌强度随玩家队伍战力动态化(0.1.29:防 5000+ 战力零风险碾压——战争永远有戏剧性)
|
||||
const teamPower = team.reduce((a, c) => a + combatPowerOf(w, c), 0)
|
||||
const npcPower = npc.power ?? 60
|
||||
const strength = Math.min(1.8, Math.max(0.5, 0.5 + (npcPower / 120) * 0.5))
|
||||
const rel = teamPower > 0 ? npcPower / teamPower : 0
|
||||
const strength = Math.min(2.6, Math.max(0.5, 0.35 + rel * 0.55))
|
||||
const enemy: EnemyDef = {
|
||||
id: npcId,
|
||||
name: `${npc.name}的劫掠队`,
|
||||
|
||||
@@ -77,6 +77,12 @@ export function cultivationTick(w: World): void {
|
||||
if (c.realm.major === 'mortal') continue
|
||||
demand += c.state === 'meditation' ? 2 : 1
|
||||
}
|
||||
// P2 灵石泄压:化神者月俸 20 石(高境界吃族俸——后期灵石死水破口)
|
||||
if (w.state.family.stones >= 20) {
|
||||
for (const c of Object.values(w.state.members)) {
|
||||
if (c.alive && c.realm.major === 'spirit') w.state.family.stones -= 20
|
||||
}
|
||||
}
|
||||
let herbFactor = 1
|
||||
if (demand > 0) {
|
||||
const supply = inv.lingcao ?? 0
|
||||
|
||||
@@ -337,11 +337,12 @@ export function eventRoll(w: World): void {
|
||||
}
|
||||
return
|
||||
}
|
||||
// 名宿传薪(每八年一掷)
|
||||
if (s.year % 8 === 4 && w.sysEnabled('apprentice') && !s.completedEvents.includes('ev-legacypass')) {
|
||||
// 名宿传薪(每八年一掷;年锚 flag 防同一年循环连发——曾实测 y%8==4 年 12 连发)
|
||||
if (s.year % 8 === 4 && w.sysEnabled('apprentice') && !s.completedEvents.includes('ev-legacypass') && !s.family.flag[`legacyDone-${s.year}`]) {
|
||||
const elder = w.aliveMembers().some((c) => w.ageOf(c) >= 66 && c.realm.major !== 'mortal')
|
||||
if (elder) {
|
||||
fire(w, 'ev-legacypass')
|
||||
const ok = fire(w, 'ev-legacypass')
|
||||
if (ok) s.family.flag[`legacyDone-${s.year}`] = true
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -391,12 +392,25 @@ export function eventRoll(w: World): void {
|
||||
matchesCond(w, e.cond)
|
||||
)
|
||||
if (candidates.length === 0) return
|
||||
const total = candidates.reduce((a, e) => a + e.weight, 0)
|
||||
// P1-N 权重洪水治理:插件池权重总额 > 核心同类 50% 时按比例压缩(MOD 不垄断世界节奏)
|
||||
const coreWeight = candidates
|
||||
.filter((e) => !poolTouchedByPlugin(w, e.id))
|
||||
.reduce((a, e) => a + e.weight, 0)
|
||||
const plugWeight = candidates
|
||||
.filter((e) => poolTouchedByPlugin(w, e.id))
|
||||
.reduce((a, e) => a + e.weight, 0)
|
||||
const cap = Math.max(1, coreWeight * 0.5)
|
||||
const compress = plugWeight > cap ? cap / plugWeight : 1
|
||||
const weighted: Array<{ e: (typeof candidates)[number]; w: number }> = candidates.map((e) => ({
|
||||
e,
|
||||
w: poolTouchedByPlugin(w, e.id) ? e.weight * compress : e.weight
|
||||
}))
|
||||
const total = weighted.reduce((a, x) => a + x.w, 0)
|
||||
let r = w.rng.next() * total
|
||||
for (const e of candidates) {
|
||||
r -= e.weight
|
||||
for (const x of weighted) {
|
||||
r -= x.w
|
||||
if (r <= 0) {
|
||||
fire(w, e.id)
|
||||
fire(w, x.e.id)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -408,6 +422,15 @@ export function eventRoll(w: World): void {
|
||||
* 被顶替者转存 eventQueue 下月兑现,不丢失)。
|
||||
* 返回 true=已在档(pending=id);false=被占未投放(调用方应自行冷却/重试)。
|
||||
*/
|
||||
function poolTouchedByPlugin(w: World, eventId: string): boolean {
|
||||
// 池命中判定:任一非 core 池(插件池)拥有该事件 → true
|
||||
for (const p of w.eventPoolSnapshot()) {
|
||||
if (p.id === 'core') continue
|
||||
if (p.events.some((e) => e.id === eventId)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export function fire(w: World, id: string, priority: 0 | 1 = 0): boolean {
|
||||
const s = w.state
|
||||
if (s.pendingEvent) {
|
||||
|
||||
@@ -11,11 +11,13 @@ export function deathTick(w: World): void {
|
||||
if (!c.alive) continue
|
||||
const age = w.ageOf(c)
|
||||
const span = lifespanOf(w, c)
|
||||
const softCap = span * 0.85
|
||||
// P0-A 天年:仙家 240 年为限(span 供凡人/低境界;高境界同样 240 内退场——世界人口会换血)
|
||||
const cap = Math.min(span, 200)
|
||||
const softCap = cap * 0.85
|
||||
let p = 0
|
||||
if (age >= span) p = 0.35
|
||||
if (age >= cap) p = 0.35
|
||||
else if (age >= softCap) {
|
||||
const t = (age - softCap) / (span - softCap)
|
||||
const t = (age - softCap) / (cap - softCap)
|
||||
p = Math.min(0.3, Math.pow(t, 5) * 0.9)
|
||||
}
|
||||
if (c.health < 30) p += 0.18
|
||||
|
||||
@@ -4,6 +4,7 @@ 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)
|
||||
@@ -15,18 +16,30 @@ export function yearStartMarriage(w: World): void {
|
||||
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)
|
||||
if (fatherAge < 18 || fatherAge > 52 || motherAge < 16 || motherAge > 46) continue
|
||||
// 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
|
||||
const p = birthBase * (0.75 + mother.physique * 0.05) + offspringBonus
|
||||
// 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)
|
||||
@@ -42,11 +55,12 @@ export function yearStartMarriage(w: World): void {
|
||||
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
|
||||
@@ -78,6 +92,29 @@ export function yearStartMarriage(w: World): void {
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user