diff --git a/src/renderer/game/engine/runtime/Systems/missions.ts b/src/renderer/game/engine/runtime/Systems/missions.ts index d7cf91d..d52d7c5 100644 --- a/src/renderer/game/engine/runtime/Systems/missions.ts +++ b/src/renderer/game/engine/runtime/Systems/missions.ts @@ -15,7 +15,8 @@ export function missionTick(w: World): void { m.stageMonth++ if (m.stageMonth < 2) continue - const def = missionById(m.defId) + const wonderDef = w.state.worldSim?.wonders?.[m.defId] as MissionDef | undefined + const def = wonderDef ?? missionById(m.defId) const stage = def.stages[m.stage] if (!stage || m.stageMonth < stage.months) continue diff --git a/src/renderer/game/engine/sim/WorldSim.ts b/src/renderer/game/engine/sim/WorldSim.ts index cf75906..ea8b418 100644 --- a/src/renderer/game/engine/sim/WorldSim.ts +++ b/src/renderer/game/engine/sim/WorldSim.ts @@ -4,7 +4,7 @@ import { WorldSimState, NpcDynamics, WORLDSIM, ERA_CONF, makeWorldSimState, NpcS import { pack } from '../../data/registry' import { ITEMS } from '../../data/items' import { npcById, getNpcDefs, registerNpcDef, unregisterNpcDef } from '../../data/npcs' -import { allMissions, addMission, WONDER_TEMPLATES, type MissionDef } from '../../data/secrets' +import { allMissions, WONDER_TEMPLATES, type MissionDef } from '../../data/secrets' import { MAJORS, MAJOR_ORDER } from '../../data/realms' const MARKET_IDS = ['lingcao', 'lingkuang', 'beastcore', 'lingyu', 'lingmu', 'shoupi', 'lingguo', 'pill-qiyuan', 'pill-ningyuan'] @@ -17,9 +17,9 @@ export class WorldSim { if (!w.state.worldSim) w.state.worldSim = initSim(w) const ws = w.state.worldSim as WorldSimState if (!ws.secretQi || Object.keys(ws.secretQi).length === 0) { - const defs = allMissions() + const defs = [...allMissions(), ...Object.values(ws.wonders ?? {})] ws.secretQi = {} - for (const m of defs) ws.secretQi[m.id] = 50 + for (const m of defs) ws.secretQi[(m as { id: string }).id] = 50 } return ws } @@ -130,18 +130,23 @@ export class WorldSim { npcTrade(this.w, s, rng.next()) // A4:NPC 按 sells/buys 与池交易 driftMarket(s, rng.next(), this.w) // 波动项(保留噪声弹性) - // ---- B0. 新秘境灵机(0.1.34 万象归元:年首机率——乱世×2) ---- + + // ---- B0. 新秘境灵机(0.1.34 万象归元:种子派生伪随机——零 rng 消耗,确定性天生) ---- if (this.w.state.month === 1) { const era2 = s.era === 'luanshi' ? 2 : 1 - if (rng.chance(worldNum('newSecretChance') * era2)) { - const tpl = rng.pick(WONDER_TEMPLATES) - const seedS = rng.int(1000, 9999) + const ch = worldNum('newSecretChance') * era2 + let sh = 0 + for (let i = 0; i < this.w.state.seed.length; i++) sh = (sh * 31 + this.w.state.seed.charCodeAt(i)) >>> 0 + const pick = ((sh ^ (this.w.state.year * 2654435761) ^ (s.npcSuccessions * 40503)) >>> 0) % 1000 / 1000 + if (pick < ch) { + const tpl = WONDER_TEMPLATES[(sh + this.w.state.year) % WONDER_TEMPLATES.length] const def: MissionDef = { ...tpl, - id: `m-wonder-${seedS}`, - name: `${tpl.icon}地灵诏——「${'古幽玄凌'}${rng.int(1, 9)}」` + id: `m-wonder-${(sh + this.w.state.year * 7919) % 9000 + 1000}`, + name: `${tpl.icon}地灵诏——${['古', '幽', '玄', '凌'][(sh + this.w.state.year) % 4]}坛` } - addMission(def) + s.wonders = s.wonders ?? {} + s.wonders[def.id] = def as unknown as Record s.secretQi[def.id] = Math.max(50, s.secretQi[def.id] ?? 50) s.newsFeed.push({ year: this.w.state.year, month: 1, src: '天机', text: `${def.name} 现世——灵光冲霄,远近皆见。`, kind: 'annal' }) this.w.log('info', `【天下】有异宝出世:${def.name}。`) diff --git a/src/renderer/game/engine/sim/worldsim-data.ts b/src/renderer/game/engine/sim/worldsim-data.ts index 8405a68..c1851dc 100644 --- a/src/renderer/game/engine/sim/worldsim-data.ts +++ b/src/renderer/game/engine/sim/worldsim-data.ts @@ -58,6 +58,8 @@ export interface WorldSimState { shortage?: Record /** 断供已播年(id→年——仅始发广播,持续断供不刷屏) */ shortageWarned?: Record + /** 0.1.34 新秘境定义(世界状态内——每档独立,绝无全局跨世界污染) */ + wonders?: Record /** 天下史书(十年一鉴独立留档,免 newsFeed 裁剪;上限 80 页) */ worldAnnals?: { year: number; text: string }[] /** 世鉴(50/100 年宏观:开局 vs 现在/era 路径/最强家更替——独立数组零漂) */ diff --git a/src/renderer/game/types/domain.ts b/src/renderer/game/types/domain.ts index 7eeb0d0..ac7baab 100644 --- a/src/renderer/game/types/domain.ts +++ b/src/renderer/game/types/domain.ts @@ -229,6 +229,8 @@ export interface GameState { overfluxYear?: number warFatigue?: number shortage?: Record + shortageWarned?: Record + wonders?: Record distress?: { id: string; year: number; month: number } worldAnnals?: { year: number; text: string }[] newsFeed?: { year: number; month: number; src: string; text: string; itemId?: string; kind?: string }[] diff --git a/tests/audit-regression.test.ts b/tests/audit-regression.test.ts index 4d174e7..8a206a5 100644 --- a/tests/audit-regression.test.ts +++ b/tests/audit-regression.test.ts @@ -82,7 +82,7 @@ describe('审计回归:P0 修复固化', () => { }) it('防御性修补后金钟罩不变(行为等价确认)', () => { - expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('0a76a33f') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('03184a16') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('70f0223f') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('1788581c') }) }) diff --git a/tests/clock.test.ts b/tests/clock.test.ts index 3d8d096..292efe9 100644 --- a/tests/clock.test.ts +++ b/tests/clock.test.ts @@ -7,20 +7,20 @@ import { World } from '../src/renderer/game/engine/runtime/World' * 任何改动(重构日程/调平衡/加系统)若改变了确定性序列或结果,此测试立刻报红。 * 更新规则:仅当**有意**变更序列逻辑时,三枚 seed 指纹同版更新并注明原因。 */ -// 0.1.33 闭环·止血基线:符箓开战生效(战力乘量+山符减伤)/灾年因子真正接入/ -// 幽灵物品归位(新丹方/玉澜刃)+ 符箓坊(消耗式产符)后固化。 +// 0.1.34 万象归元基线:功法流派效果(crit/guard/trib/secret)/元素时节共鸣/ +// warFatigue/换代恩义清/断供/新秘境(零 rng 派生)后固化(受控变更重算)。 const GOLDEN: Record> = { - 'bell-seed-1': { 560: '0a76a33f', 1200: 'c9407e76', 2160: '4bc550bd' }, - 'bell-seed-2': { 560: 'b1b4d35b', 1200: 'fcc6e262', 2160: '1af450b7' }, - 'bell-seed-3': { 560: '03184a16', 1200: 'c73bb4b1', 2160: 'fca9a59f' } + 'bell-seed-1': { 560: '70f0223f', 1200: 'da91696b', 2160: '952c55c5' }, + 'bell-seed-2': { 560: '700fd9dd', 1200: 'd04b0989', 2160: 'c8aae012' }, + 'bell-seed-3': { 560: '1788581c', 1200: 'cc2ed227', 2160: '991d2684' } } /** 第二金钟罩:自动 resolve 长跑("现实"世界——每 tick 处理待决事件; * 锁事件闸/事件流全程,防"冻结世界"指纹漏锁)。 */ const GOLDEN_RESOLVED: Record> = { - 'bell-seed-1': { 560: '4b0f1f81', 1200: '430d6281', 2160: 'ba0d3816' }, - 'bell-seed-2': { 560: '8865b178', 1200: 'bba0d797', 2160: '6205c6f1' }, - 'bell-seed-3': { 560: 'fa736e3c', 1200: '819b9def', 2160: 'a624ced8' } + 'bell-seed-1': { 560: 'd03824ce', 1200: '237e93f3', 2160: 'cee2dc9c' }, + 'bell-seed-2': { 560: 'db32bc97', 1200: '7b8177d8', 2160: '8257b4e1' }, + 'bell-seed-3': { 560: '4c8b66c5', 1200: '39fc71ea', 2160: '384b1586' } } const TIERS = [ diff --git a/tests/facade-registry.test.ts b/tests/facade-registry.test.ts index 9a72254..060550c 100644 --- a/tests/facade-registry.test.ts +++ b/tests/facade-registry.test.ts @@ -170,7 +170,7 @@ describe('GameFacade 门面', () => { it('默认配置金钟罩不受门面化影响', () => { PACK.reset() - expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('0a76a33f') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('03184a16') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('70f0223f') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('1788581c') }) })