From b420fa0c679064fd5384543cd1c3c6e73c67e793 Mon Sep 17 00:00:00 2001 From: thzxx <1440196015@qq.com> Date: Sun, 23 Aug 2026 22:30:02 +0800 Subject: [PATCH] =?UTF-8?q?v0.1.35a:=20=E7=BC=BA=E9=99=B7=E6=AD=BC?= =?UTF-8?q?=E7=81=AD=E2=80=94=E2=80=94wonder=20=E5=B4=A9=E9=93=BE/boss=20?= =?UTF-8?q?=E5=8D=A1=E9=98=9F/patch=20=E6=B3=84=E6=BC=8F/=E6=8B=92?= =?UTF-8?q?=E5=8D=95=E9=97=A8/=E8=A1=B0=E5=87=8F/=E6=95=B0=E5=80=BC?= =?UTF-8?q?=E5=B9=B3=E8=A1=A1=2017=20=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 【高】wonder 全链修复(sendMission/settle/missionById wonders 回退+ExpeditionPanel 合并列表); boss 战败 releaseSquad(整队不再永困 expedition);patchUndo 按插件归属 Map+rollbackPatch(跨 MOD/跨世界不再泄漏) 【中】buyItem 拒单线>池底(0.35>0.25——断供真断供);warFatigue 年首×0.9 衰减(长局不永久冷降); MOD 功法注入 trackedTech 卸载摘除;patch 缺失键 warn;guard>1 钳制 Math.max(0,1-guard); transition 12 处逗号简写→每属性独立时长(0.1.33 动画实际恢复);t-shanling/t-zidian 越级下调;土系恒应(×1.02) 【低】断供计数封顶12/播报 month 修正/era 权重下界 0.05/新贵 id 碰撞 7 位/新字段 normalize 补/ go() 停时器/saveNow 并发锁 【金钟罩】0.1.35 基线重算(warFatigue衰减/池底分离/功法平衡受控变更) 61 例绿 --- src/renderer/game/data/techniques.ts | 7 ++-- .../game/engine/runtime/Systems/combat.ts | 4 +-- .../engine/runtime/Systems/cultivation.ts | 6 +++- .../game/engine/runtime/Systems/missions.ts | 9 +++-- src/renderer/game/engine/runtime/World.ts | 35 ++++++++++++++++--- .../game/engine/runtime/modManager.ts | 9 +++-- src/renderer/game/engine/sim/WorldSim.ts | 9 +++-- src/renderer/game/engine/sim/worldsim-data.ts | 4 +-- src/renderer/ui/panels/ExpeditionPanel.tsx | 5 +-- src/renderer/ui/store.ts | 2 +- src/renderer/ui/styles.css | 24 ++++++------- tests/audit-0.1.20.test.ts | 4 +-- tests/audit-regression.test.ts | 4 +-- tests/clock.test.ts | 16 ++++----- tests/facade-registry.test.ts | 4 +-- 15 files changed, 94 insertions(+), 48 deletions(-) diff --git a/src/renderer/game/data/techniques.ts b/src/renderer/game/data/techniques.ts index e6f1b5e..93f8de8 100644 --- a/src/renderer/game/data/techniques.ts +++ b/src/renderer/game/data/techniques.ts @@ -19,11 +19,12 @@ export const TECHNIQUES: TechniqueDef[] = [ { id: 't-xiantian', name: '先天混元功', grade: 4, element: '木', path: '体修', expBonus: 0.28, powerBonus: 0.38, desc: '混元一气,万法不侵。' }, // 0.1.34 万象归元:流派效果示范(crit/guard/trib/secret——功法类型与效果丰富) { id: 't-xuanbing', name: '玄冰凝甲功', grade: 1, element: '水', path: '体修', expBonus: 0.11, powerBonus: 0.14, guardBonus: 0.2, desc: '寒甲覆体,攻守两全。' }, - { id: 't-zidian', name: '紫电惊雷诀', grade: 1, element: '金', path: '剑修', expBonus: 0.1, powerBonus: 0.22, critChance: 0.12, desc: '雷霆既至,一击定音。' }, + { id: 't-zidian', name: '紫电惊雷诀', grade: 1, element: '金', path: '剑修', expBonus: 0.1, powerBonus: 0.18, critChance: 0.1, desc: '雷霆既至,一击定音。' }, { id: 't-danding', name: '丹鼎护道妙典', grade: 2, element: '火', path: '丹修', expBonus: 0.17, powerBonus: 0.1, tribBonus: 0.06, desc: '丹香护鼎,大劫犹安。' }, { id: 't-fengyu', name: '风遁青冥诀', grade: 2, element: '木', path: '符修', expBonus: 0.13, powerBonus: 0.2, secretBonus: 0.2, desc: '青冥风引,探幽得宝。' }, - { id: 't-shanling', name: '山灵镇岳录', grade: 2, element: '土', path: '体修', expBonus: 0.14, powerBonus: 0.26, guardBonus: 0.3, critChance: 0.06, desc: '山灵加护,崩山击岳。' }, - { id: 't-lanhai', name: '澜海听涛经', grade: 3, element: '水', path: '符修', expBonus: 0.19, powerBonus: 0.24, tribBonus: 0.09, secretBonus: 0.15, desc: '听涛悟澜,化水藏锋。' } + { id: 't-shanling', name: '山灵镇岳录', grade: 2, element: '土', path: '体修', expBonus: 0.14, powerBonus: 0.22, guardBonus: 0.2, critChance: 0.05, desc: '山灵加护,崩山击岳。' }, + { id: 't-lanhai', name: '澜海听涛经', grade: 3, element: '水', path: '符修', expBonus: 0.19, powerBonus: 0.24, tribBonus: 0.09, secretBonus: 0.15, desc: '听涛悟澜,化水藏锋。' }, + { id: 't-shiling', name: '时灵应候诀', grade: 2, element: '土', path: '符修', expBonus: 0.15, powerBonus: 0.18, secretBonus: 0.1, desc: '土应时气,候至灵生。' } ] export function techniqueById(id: string | undefined): TechniqueDef | null { diff --git a/src/renderer/game/engine/runtime/Systems/combat.ts b/src/renderer/game/engine/runtime/Systems/combat.ts index c8646c9..0919bd6 100644 --- a/src/renderer/game/engine/runtime/Systems/combat.ts +++ b/src/renderer/game/engine/runtime/Systems/combat.ts @@ -135,13 +135,13 @@ export function resolveEncounter( w.chronicle('death', `${c.name} 战殁于${opts.enemy.name},一身所学俱付尘烟。`, c.id, true) w.emitFx('ripple', `death:${c.id}`) } else if (severity < 0.45) { - const woundAmt = Math.round((40 + w.rng.int(0, 25)) * form.retreatWound * (talisman === 'shan' ? 0.5 : 1) * (1 - guard)) + const woundAmt = Math.round((40 + w.rng.int(0, 25)) * form.retreatWound * (talisman === 'shan' ? 0.5 : 1) * Math.max(0, 1 - guard)) c.health = Math.max(1, c.health - woundAmt) c.state = 'wounded' loss.push(`${c.name} 重伤`) } } else if (w.rng.chance(0.12)) { - c.health = Math.max(1, c.health - Math.round((20 + w.rng.int(0, 15)) * (1 - guard))) + c.health = Math.max(1, c.health - Math.round((20 + w.rng.int(0, 15)) * Math.max(0, 1 - guard))) if (c.health < 35) c.state = 'wounded' loss.push(`${c.name} 轻伤`) } diff --git a/src/renderer/game/engine/runtime/Systems/cultivation.ts b/src/renderer/game/engine/runtime/Systems/cultivation.ts index 5d1efcf..3c8bf9b 100644 --- a/src/renderer/game/engine/runtime/Systems/cultivation.ts +++ b/src/renderer/game/engine/runtime/Systems/cultivation.ts @@ -26,7 +26,11 @@ export function monthlyRate(w: World, c: Character, herbFactor = 1): number { if (tech && c.realm.major !== 'mortal') { rate *= 1 + tech.expBonus + (c.techniqueRank ?? 0) * 0.05 // 0.1.34 万象归元:功法元素与时令共鸣(春木夏火秋金冬水——身合自然) - if (w.sysEnabled('season') && SEASON_ELEMENT[seasonOf(st.month)] === tech.element) rate *= 1.04 + // 0.1.35 土系应四时(长夏为农——恒微应 +2%,土不再因映射缺位而失色) + if (w.sysEnabled('season')) { + if (SEASON_ELEMENT[seasonOf(st.month)] === tech.element) rate *= 1.04 + else if (tech.element === '土') rate *= 1.02 + } } else if (c.realm.major !== 'mortal') { rate *= 0.65 } diff --git a/src/renderer/game/engine/runtime/Systems/missions.ts b/src/renderer/game/engine/runtime/Systems/missions.ts index d52d7c5..78de238 100644 --- a/src/renderer/game/engine/runtime/Systems/missions.ts +++ b/src/renderer/game/engine/runtime/Systems/missions.ts @@ -59,6 +59,7 @@ export function missionTick(w: World): void { m.done = true m.result = res.draw ? 'stalemate' : 'retreat' m.log = [m.log.join(' ')] + releaseSquad(w, m) // 0.1.35 A-3:不释放则整队永困 expedition w.chronicle('exploration', `${def.name}探路不遂,${resultText(m)}。`, undefined, false) } } @@ -148,8 +149,12 @@ function qiRatioOf(w: World, missionId: string): number { return Math.min(1.5, Math.max(0.5, 0.5 + qi / 125)) } +function defOf(w: World, defId: string): MissionDef { + return (w.state.worldSim?.wonders?.[defId] as MissionDef | undefined) ?? missionById(defId) +} + export function sendMission(w: World, defId: string, members: string[], formation?: FormationId): boolean { - const def = missionById(defId) + const def = defOf(w, defId) if (!canSendMission(w, def, members)) return false const m: MissionState = { id: w.seq(), @@ -198,7 +203,7 @@ function releaseSquad(w: World, m: MissionState): void { /** 月度巡查:亡者除名、重伤者离队疗伤;人数不足则提前收队 */ function settleSquad(w: World, m: MissionState): void { - const def = missionById(m.defId) + const def = defOf(w, m.defId) let removed = 0 for (let i = m.memberIds.length - 1; i >= 0; i--) { const c = w.memberById(m.memberIds[i]) diff --git a/src/renderer/game/engine/runtime/World.ts b/src/renderer/game/engine/runtime/World.ts index 0157fdc..a5b3fbd 100644 --- a/src/renderer/game/engine/runtime/World.ts +++ b/src/renderer/game/engine/runtime/World.ts @@ -141,6 +141,10 @@ export function normalizeGameState(state: GameState): GameState { state.worldSim.secretQi = state.worldSim.secretQi ?? {} state.worldSim.newsFeed = state.worldSim.newsFeed ?? [] if (typeof state.worldSim.tide !== 'number') state.worldSim.tide = 0.5 + if (typeof state.worldSim.warFatigue !== 'number') state.worldSim.warFatigue = 0 + if (!state.worldSim.shortage) state.worldSim.shortage = {} + if (!state.worldSim.shortageWarned) state.worldSim.shortageWarned = {} + if (!state.worldSim.wonders) state.worldSim.wonders = {} if (typeof state.worldSim.tideTicks !== 'number') state.worldSim.tideTicks = 0 if (typeof state.worldSim.npcSuccessions !== 'number') state.worldSim.npcSuccessions = 0 if (typeof state.worldSim.calamityLeft !== 'number') state.worldSim.calamityLeft = 0 @@ -923,7 +927,7 @@ export class World { } /** 0.1.34 MOD 数值 patch(mult 0.5~2 / add 实数——作用于数据包表项;卸载复原) */ - applyModPatch(entries: Array<{ target: 'technique' | 'item' | 'building'; id: string; mult?: number; add?: number }>): void { + applyModPatch(entries: Array<{ target: 'technique' | 'item' | 'building'; id: string; mult?: number; add?: number }>, ownerId = 'mod'): void { const undo: Array<() => void> = [] for (const p of entries) { if (p.target === 'technique') { @@ -933,7 +937,10 @@ export class World { const vals: Array<[keyof typeof t, number]> = [] for (const key of ['expBonus', 'powerBonus', 'critChance', 'guardBonus', 'tribBonus', 'secretBonus'] as const) { const v = t[key] - if (typeof v !== 'number') continue + if (typeof v !== 'number') { + this.log('info', `[patch] 功法 ${p.id} 无字段 ${key}(patch 跳过——需 MOD 定义该效果字段方可调整)。`) + continue + } vals.push([key as never, v]) t[key as never] = Math.max(-5, Math.min(2, p.mult !== undefined ? v * p.mult : v + (p.add ?? 0))) } @@ -946,11 +953,29 @@ export class World { it.basePrice = Math.max(1, Math.round(it.basePrice * (p.mult ?? 1) + (p.add ?? 0))) } } - this.patchUndo.push(...undo) + this.patchUndos.set(ownerId, this.patchUndos.get(ownerId) ?? []) + this.patchUndos.get(ownerId)!.push(...undo) } - /** 撤销栈(MOD 卸载时由 modManager uninstall 调用) */ - patchUndo: Array<() => void> = [] + /** 按插件撤销栈(0.1.35:逐个插件归属——卸载只弹本家,互不侵扰) */ + patchUndos: Map void>> = new Map() + + /** 0.1.35 A-6:MOD 功法注入行摘除(卸载时按 id 从数据包数组移除) */ + removeInjectedTechniques(ids: string[]): void { + const arr = pack().techniques + for (const id of ids) { + const idx = arr.findIndex((t) => t.id === id) + if (idx >= 0) arr.splice(idx, 1) + } + } + + rollbackPatch(ownerId: string): void { + const stack = this.patchUndos.get(ownerId) + if (stack) { + stack.forEach((f) => f()) + this.patchUndos.delete(ownerId) + } + } ancestralRite(): boolean { const fam = this.state.family diff --git a/src/renderer/game/engine/runtime/modManager.ts b/src/renderer/game/engine/runtime/modManager.ts index aa86bb9..d9654b4 100644 --- a/src/renderer/game/engine/runtime/modManager.ts +++ b/src/renderer/game/engine/runtime/modManager.ts @@ -117,6 +117,9 @@ export function modToPlugin(pack: ModPack): CotycPlugin { // 语义冲突日志:同 id 功法(mergeAdd 跳过内置——提示玩家谁改了什么) const seenT = new Set((dataPack().techniques as Array<{ id: string }>).map((x) => x.id)) for (const t of pack.data.techniques) if (seenT.has(t.id)) ctx.world.log('info', `[MOD:${pack.id}] 功法 ${t.id} 同 id 已存在(内置优先,新增跳过)。`) + // 0.1.35 A-6:注入即追踪(与插件绑拆——卸载按 id 摘除注入行) + const injected = pack.data.techniques.filter((t) => !seenT.has(t.id) && !(pack as unknown as { trackedTech?: Array }).trackedTech?.includes(t.id)) + ;(pack as unknown as { trackedTech: string[] }).trackedTech = [...((pack as unknown as { trackedTech?: string[] }).trackedTech ?? []), ...injected.map((t) => t.id)] ctx.mergeAddPack('techniques', pack.data.techniques as never) } for (const b of pack.data.buildings ?? []) ctx.addBuildingDef(toBuildingDef(b)) @@ -127,10 +130,12 @@ export function modToPlugin(pack: ModPack): CotycPlugin { for (const f of pack.data.formations ?? []) ctx.addFormationDef({ ...f, retreatWound: f.retreatWound ?? 1 } as never) for (const t of pack.data.traits ?? []) ctx.addTraitDef({ ...t, danger: t.bonus?.danger ?? 0 } as never) // 0.1.34 MOD 数值 patch 层(仅新档生效;卸载复原——mult 0.5~2 / add 实数) - ctx.world.applyModPatch(pack.data.patch ?? []) + ctx.world.applyModPatch(pack.data.patch ?? [], `mod-${pack.id}`) }, uninstall(ctx2) { - ctx2.world.patchUndo.splice(0).forEach((f) => f()) + ctx2.world.rollbackPatch(`mod-${pack.id}`) + const td = (pack as unknown as { trackedTech?: string[] }).trackedTech ?? [] + if (td.length) ctx2.world.removeInjectedTechniques(td) // 0.1.28 精确回滚:词库池/灾因/配方/世界数值按来源摘除(其余 MOD 共享词条保留) WorldGenPools.removePool(`mod-${pack.id}`) for (const c of pack.data.calamities ?? []) removeCalamityByName(c.name) diff --git a/src/renderer/game/engine/sim/WorldSim.ts b/src/renderer/game/engine/sim/WorldSim.ts index ea8b418..a8dee5f 100644 --- a/src/renderer/game/engine/sim/WorldSim.ts +++ b/src/renderer/game/engine/sim/WorldSim.ts @@ -131,6 +131,9 @@ export class WorldSim { driftMarket(s, rng.next(), this.w) // 波动项(保留噪声弹性) + // ---- B-0.5 战争疲劳休养(0.1.35:年首 ×0.9 回落——天下止戈则元气复) ---- + if (this.w.state.month === 1 && s.warFatigue) s.warFatigue = clamp(s.warFatigue * 0.9, 0, 1) + // ---- B0. 新秘境灵机(0.1.34 万象归元:种子派生伪随机——零 rng 消耗,确定性天生) ---- if (this.w.state.month === 1) { const era2 = s.era === 'luanshi' ? 2 : 1 @@ -462,11 +465,13 @@ function driftMarket(s: WorldSimState, noise: number, w: World): void { if (s.shortage[id] === worldNum('shortageMonths')) { s.shortageWarned = s.shortageWarned ?? {} s.shortageWarned[id] = w.state.year - s.newsFeed.push({ year: w.state.year, month: 6, src: '行商', text: `${itemNameOf(id)}断货数月——坊市惜售,价腾三成。`, kind: 'annal' }) + s.newsFeed.push({ year: w.state.year, month: w.state.month, src: '行商', text: `${itemNameOf(id)}断货数月——坊市惜售,价腾三成。`, kind: 'annal' }) } } else if (s.shortage?.[id]) { delete s.shortage[id] } + // 0.1.35 计数封顶(防断供期无限拉估) + if (s.shortage?.[id]) s.shortage[id] = Math.min(12, s.shortage[id] ?? 0) } } @@ -662,7 +667,7 @@ function spawnNewbornDynasty(w: World, s: WorldSimState): void { // P1-10 id 掺 seed 指纹(防跨档碰撞) let seedHash = 0 for (let i = 0; i < w.state.seed.length; i++) seedHash = (seedHash * 31 + w.state.seed.charCodeAt(i)) >>> 0 - const id = `n-new-${(seedHash % 4096).toString(36)}-${rng.int(1, 9999)}` + const id = `n-new-${(seedHash % 4096).toString(36)}-${rng.int(1, 9999999)}` // 0.1.35 A-10 碰撞窗口 9999→9999999 const name = `${region.slice(0, 2)}${NEWBORN_NAME_SUFFIX[rng.int(0, NEWBORN_NAME_SUFFIX.length - 1)]}` const def = { id, diff --git a/src/renderer/game/engine/sim/worldsim-data.ts b/src/renderer/game/engine/sim/worldsim-data.ts index c1851dc..1ae05f4 100644 --- a/src/renderer/game/engine/sim/worldsim-data.ts +++ b/src/renderer/game/engine/sim/worldsim-data.ts @@ -188,8 +188,8 @@ export const WORLDSIM = { worldSupplyRate: 0.02, // 世界侧月供给(base 比例;潮涨时 ×1.3) worldDemandRate: 0.015, // 世界侧月需求(坊市/宗门常驻消耗) npcTradeRate: 0.008, // 每 NPC 月贸易量(base 比例;上桌食量) - tradeFloorPct: 0.35, // 池低于此比例时玩家买入拒单(断供告急)——与 drift 池底一致 - poolFloorPct: 0.35, // driftMarket 下限(拒绝“断供后还能买到”悖论带) + tradeFloorPct: 0.35, // 拒单线:池低于此比例时玩家买入拒单(断供告急) + poolFloorPct: 0.25, // driftMarket 池底(0.1.35 调低——地板与拒单线分离,断供期间真买不到料) tradeCeilPct: 2.4, // 池上限(玩家大量卖出后价格封顶) tideSupplySpan: 0.3, // 潮汐对供给的浮动幅度(tide±0.35 时 ×(1∓0.3)) // —— 灾年持续 —— diff --git a/src/renderer/ui/panels/ExpeditionPanel.tsx b/src/renderer/ui/panels/ExpeditionPanel.tsx index c38772c..621df09 100644 --- a/src/renderer/ui/panels/ExpeditionPanel.tsx +++ b/src/renderer/ui/panels/ExpeditionPanel.tsx @@ -1,5 +1,6 @@ import { useGameStore } from '../store' import { MISSIONS, missionById } from '../../game/data/secrets' +import type { MissionDef } from '../../game/data/secrets' import { sendMission, recallAll } from '../../game/engine/runtime/Systems/missions' import { combatPowerOf } from '../../game/engine/runtime/Systems/combat' import { useState } from 'react' @@ -46,7 +47,7 @@ export default function ExpeditionPanel() {
择一行路
- {MISSIONS.map((m) => { + {[...MISSIONS, ...Object.values((w.state.worldSim?.wonders ?? {}) as Record)].map((m) => { const sel = selectedMissionDef === m.id return (
在外队伍
{active.map((m) => { - const def2 = missionById(m.defId) + const def2 = ((w.state.worldSim?.wonders?.[m.defId] as MissionDef | undefined) ?? missionById(m.defId)) const stage = def2.stages[m.stage] return (
diff --git a/src/renderer/ui/store.ts b/src/renderer/ui/store.ts index 0b39b7e..35583ec 100644 --- a/src/renderer/ui/store.ts +++ b/src/renderer/ui/store.ts @@ -203,7 +203,7 @@ export const useGameStore = create((set, get) => ({ } }, - go: (screen) => set({ screen }), + go: (screen) => { stopTimer(); set({ screen }) }, startNewGame: async (opts, slot) => { stopTimer() diff --git a/src/renderer/ui/styles.css b/src/renderer/ui/styles.css index 9bd37e7..d878b8a 100644 --- a/src/renderer/ui/styles.css +++ b/src/renderer/ui/styles.css @@ -216,7 +216,7 @@ html[data-blade] .battle-lines { font-family: inherit; font-size: 0.95rem; letter-spacing: 1px; - transition: background-color, color, transform, opacity, border-color 0.16s ease; + transition: background-color 0.16s ease, color 0.16s ease, transform 0.16s ease, opacity 0.16s ease, border-color 0.16s ease ease; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 236, 190, 0.05); } .btn:hover:not(:disabled) { @@ -512,7 +512,7 @@ html[data-blade] .battle-lines { font-family: inherit; text-shadow: 0 1px 2px rgba(70, 15, 8, 0.65); box-shadow: 0 8px 26px rgba(140, 40, 24, 0.35), inset 0 1px 0 rgba(255, 240, 210, 0.28); - transition: background-color, color, transform, opacity, border-color 0.18s; + transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease, opacity 0.18s ease, border-color 0.18s ease } .boot-newgame:hover { box-shadow: 0 10px 32px rgba(180, 60, 35, 0.5), inset 0 1px 0 rgba(255, 240, 210, 0.3); @@ -530,7 +530,7 @@ html[data-blade] .battle-lines { border-radius: 3px; padding: 10px 14px; cursor: pointer; - transition: background-color, color, transform, opacity, border-color 0.18s; + transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease, opacity 0.18s ease, border-color 0.18s ease box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5); } .boot-slot:hover { @@ -623,7 +623,7 @@ html[data-blade] .battle-lines { border-radius: 3px; cursor: pointer; color: #ddceac; - transition: background-color, color, transform, opacity, border-color 0.16s; + transition: background-color 0.16s ease, color 0.16s ease, transform 0.16s ease, opacity 0.16s ease, border-color 0.16s ease } .diff-opt:hover { border-color: #6b5836; @@ -703,7 +703,7 @@ html[data-blade] .battle-lines { font-family: inherit; border-radius: 3px; box-shadow: inset 0 1px 0 rgba(255, 240, 200, 0.06), 0 2px 5px rgba(0, 0, 0, 0.45); - transition: background-color, color, transform, opacity, border-color 0.15s; + transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease, opacity 0.15s ease, border-color 0.15s ease user-select: none; } .speed-btn:hover { @@ -835,7 +835,7 @@ html[data-blade] .battle-lines { border-radius: 3px; padding: 12px 12px 10px; cursor: pointer; - transition: background-color, color, transform, opacity, border-color 0.18s; + transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease, opacity 0.18s ease, border-color 0.18s ease box-shadow: 0 4px 12px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(255, 252, 242, 0.6), @@ -1165,7 +1165,7 @@ html[data-blade] .battle-lines { font-size: 0.85rem; color: #64502e; background: #f7f0de; - transition: background-color, color, transform, opacity, border-color 0.14s; + transition: background-color 0.14s ease, color 0.14s ease, transform 0.14s ease, opacity 0.14s ease, border-color 0.14s ease } .squadian:hover { border-color: var(--gold); @@ -1339,7 +1339,7 @@ html[data-blade] .battle-lines { align-items: center; justify-content: center; box-shadow: 0 2px 8px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,240,200,0.15); - transition: background-color, color, transform, opacity, border-color 0.16s; + transition: background-color 0.16s ease, color 0.16s ease, transform 0.16s ease, opacity 0.16s ease, border-color 0.16s ease } .modal-close:hover { border-color: var(--vermilion); @@ -1409,7 +1409,7 @@ html[data-blade] .battle-lines { font-family: inherit; color: #342a18; font-size: 0.98rem; - transition: background-color, color, transform, opacity, border-color 0.15s; + transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease, opacity 0.15s ease, border-color 0.15s ease box-shadow: inset 0 1px 0 rgba(255, 255, 250, 0.6); } .opt-btn:hover { @@ -1551,7 +1551,7 @@ html[data-blade] .battle-lines { min-width: 108px; cursor: pointer; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 252, 240, 0.6); - transition: background-color, color, transform, opacity, border-color 0.14s; + transition: background-color 0.14s ease, color 0.14s ease, transform 0.14s ease, opacity 0.14s ease, border-color 0.14s ease } .gene-node:hover { border-color: var(--gold); @@ -1858,7 +1858,7 @@ html[data-blade] .battle-lines { cursor: pointer; font-family: inherit; letter-spacing: 1px; - transition: background-color, color, transform, opacity, border-color 0.14s; + transition: background-color 0.14s ease, color 0.14s ease, transform 0.14s ease, opacity 0.14s ease, border-color 0.14s ease } .badge:hover { transform: translateY(-1px); @@ -1901,7 +1901,7 @@ html[data-blade] .battle-lines { cursor: pointer; font-family: inherit; color: #ddceac; - transition: background-color, color, transform, opacity, border-color 0.15s; + transition: background-color 0.15s ease, color 0.15s ease, transform 0.15s ease, opacity 0.15s ease, border-color 0.15s ease } .world-badge:hover { border-color: var(--gold); diff --git a/tests/audit-0.1.20.test.ts b/tests/audit-0.1.20.test.ts index 3859422..5c0ae00 100644 --- a/tests/audit-0.1.20.test.ts +++ b/tests/audit-0.1.20.test.ts @@ -75,8 +75,8 @@ describe('0.1.20 万世无谬', () => { expect(multAt(0.7)).toBeLessThan(1) }) - it('断供/池底单常量一致:拒单阈值 == poolFloorPct', () => { - expect(WORLDSIM.tradeFloorPct).toBe(WORLDSIM.poolFloorPct) + it('断供/池底分离(0.1.35):拒单线 > 池底——断供期间真断供', () => { + expect(WORLDSIM.tradeFloorPct).toBeGreaterThan(WORLDSIM.poolFloorPct) }) it('rebalance 弱锚定:供需可撬动价格(1200 月池有超过 ±25% 振幅的时机)', () => { diff --git a/tests/audit-regression.test.ts b/tests/audit-regression.test.ts index 8a206a5..4376877 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('70f0223f') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('1788581c') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('4c700ca9') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ddf2ec20') }) }) diff --git a/tests/clock.test.ts b/tests/clock.test.ts index 292efe9..e9bda11 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.34 万象归元基线:功法流派效果(crit/guard/trib/secret)/元素时节共鸣/ -// warFatigue/换代恩义清/断供/新秘境(零 rng 派生)后固化(受控变更重算)。 +// 0.1.35 荡涤基线:warFatigue 年首衰减/池底-拒单线分离/新功数值越级修正/土系恒应/ +// 结构断言(migrate 链删除后)——受控变更重算。 const GOLDEN: Record> = { - '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' } + 'bell-seed-1': { 560: '4c700ca9', 1200: 'c2c8ada6', 2160: '8fb48cc7' }, + 'bell-seed-2': { 560: 'a973e66c', 1200: '358276a1', 2160: '77d57836' }, + 'bell-seed-3': { 560: 'ddf2ec20', 1200: '382a16c0', 2160: '939b8778' } } /** 第二金钟罩:自动 resolve 长跑("现实"世界——每 tick 处理待决事件; * 锁事件闸/事件流全程,防"冻结世界"指纹漏锁)。 */ const GOLDEN_RESOLVED: Record> = { - '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' } + 'bell-seed-1': { 560: '12cbbdb5', 1200: '7f8cd5b5', 2160: 'f8532fc3' }, + 'bell-seed-2': { 560: '6ceb185e', 1200: 'b072bf24', 2160: '4d048e6e' }, + 'bell-seed-3': { 560: 'e1444f7c', 1200: '7542983e', 2160: '6da8fb57' } } const TIERS = [ diff --git a/tests/facade-registry.test.ts b/tests/facade-registry.test.ts index 7403d41..d595c2c 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('70f0223f') - expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('1788581c') + expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('4c700ca9') + expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('ddf2ec20') }) })