v0.1.34a(修正): 新秘境改种子派生伪随机——零 rng 消耗(修复跨世界/跨跑分叉)
B0 原用 World.rng.chance/pick/int——序列漂移疑虑(实际为模块级全局 EXTRA_MISSIONS 残留污染); 现改为 seed 哈希派生(year/npcSuccessions 异或哈希——纯确定性零 rng),金钟罩序列零伤; SecretQi/mission def 全走世界状态 s.wonders(每档独立),确定性复跑矩阵恢复绿; 金钟罩 0.1.34 基线固化(功法流派/时节共鸣/warFatigue/换代/断供受控变更——值变序列受控重算 62 例绿)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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<string, unknown>
|
||||
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}。`)
|
||||
|
||||
@@ -58,6 +58,8 @@ export interface WorldSimState {
|
||||
shortage?: Record<string, number>
|
||||
/** 断供已播年(id→年——仅始发广播,持续断供不刷屏) */
|
||||
shortageWarned?: Record<string, number>
|
||||
/** 0.1.34 新秘境定义(世界状态内——每档独立,绝无全局跨世界污染) */
|
||||
wonders?: Record<string, unknown>
|
||||
/** 天下史书(十年一鉴独立留档,免 newsFeed 裁剪;上限 80 页) */
|
||||
worldAnnals?: { year: number; text: string }[]
|
||||
/** 世鉴(50/100 年宏观:开局 vs 现在/era 路径/最强家更替——独立数组零漂) */
|
||||
|
||||
@@ -229,6 +229,8 @@ export interface GameState {
|
||||
overfluxYear?: number
|
||||
warFatigue?: number
|
||||
shortage?: Record<string, number>
|
||||
shortageWarned?: Record<string, number>
|
||||
wonders?: Record<string, unknown>
|
||||
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 }[]
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
+8
-8
@@ -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<string, Record<number, string>> = {
|
||||
'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<string, Record<number, string>> = {
|
||||
'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 = [
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user