v0.1.11: 全源码审计 + 缺陷歼灭 + UI/UX 打磨
【引擎审计修复】 - P0:大比 once(原整年刷 12 场、奖励通胀);flag 年份键 3 年剪枝(长线膨胀) - P1:飞升离世累加 feishengCount(飞升之资称号可达);圣者护族接消费(原死代码); 大比/传薪挂能力卡门控;pending 抢渡劫时不再重设 - P2:version 硬编码统一 0.1.11;yearaxis 飞升归类;MonumentList 挂载 【UI 审计修复】 - P0:已故成员卡显示「已殁」(原秀活动 status);空队迎战禁用+提示(原静默失效) - P1:指婚候选去截断(滚动列表);终局出口(回卷/主菜单按钮);saveNow 反馈; 事件来临写入 feed(k-event 使用);遣队「最强 N 人/清空」一键;寻衅确认+联姻原因提示; 未建建筑视觉 uc 类 - P2:speed 死代码;Modal 分层 z-index;FamilyPanel 在世/先人分隔 【UX 三件套】 - 警讯徽章(瓶颈/重伤/在外/媒缘/孤嗣之忧)顶栏一屏秒答「我该干嘛」 - 首启引导任务条(四步目标 + 完成推进,可跳不强制) - 顶栏丹药瘦身(4 资源);数字千/万化;瓶颈卡红框高亮 【测试】905→923(urgency/guide/format 三模块 + 审计修复回归 6 项) 金钟罩三档重固化(0.1.11),typecheck/build 通过
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { World } from '../src/renderer/game/engine/world'
|
||||
import { applyEventChoice } from '../src/renderer/game/engine/systems/events'
|
||||
import { computeUrgency } from '../src/renderer/game/core/urgency'
|
||||
import { monthlyRate } from '../src/renderer/game/engine/systems/cultivation'
|
||||
import { resetSaveBus, attachLogSink } from './world.helpers'
|
||||
|
||||
function baseWorld(seed: string): World {
|
||||
const w = World.create({ seed, surname: '凌', familyName: '凌家', motto: 'm', difficulty: 'normal' })
|
||||
resetSaveBus()
|
||||
attachLogSink(w)
|
||||
return w
|
||||
}
|
||||
|
||||
describe('0.1.11 审计修复回归', () => {
|
||||
it('大比 once:一季只触一次(不会整年刷 12 场)', () => {
|
||||
const w = baseWorld('tourn-once')
|
||||
w.state.year = 9
|
||||
w.state.month = 12
|
||||
w.advanceMonth() // year10 monthly
|
||||
const seen: string[] = []
|
||||
for (let i = 0; i < 24; i++) {
|
||||
if (w.state.pendingEvent) {
|
||||
if (w.state.pendingEvent.startsWith('ev-tournament-')) seen.push(w.state.pendingEvent)
|
||||
applyEventChoice(w, w.state.pendingEvent, 0)
|
||||
w.state.pendingEvent = undefined
|
||||
}
|
||||
w.advanceMonth()
|
||||
}
|
||||
expect(seen.filter((x) => x === 'ev-tournament-10').length).toBe(1)
|
||||
})
|
||||
|
||||
it('飞升离世累加 feishengCount(可入飞升之资档)', () => {
|
||||
const w = baseWorld('fs-count')
|
||||
const c = w.state.members['x4']
|
||||
c.realm = { major: 'spirit', minor: 1 }
|
||||
applyEventChoice(w, 'ev-feisheng', 1) // 云游
|
||||
expect(w.state.stats.feishengCount).toBe(1)
|
||||
expect(w.state.family.flag['fengFeiBless']).toBe(true)
|
||||
})
|
||||
|
||||
it('圣者护族对幼童有效(hasPatriarch 消费)', () => {
|
||||
const w = baseWorld('patriarch')
|
||||
const c = w.state.members['x5']
|
||||
c.bornYear = 1 // 1 岁
|
||||
const elder = w.state.members['x4']
|
||||
elder.bornYear = 1 - 70
|
||||
elder.realm = { major: 'foundation', minor: 1 }
|
||||
const rate = monthlyRate(w, c)
|
||||
expect(rate).toBeGreaterThan(0)
|
||||
// 幼童本身速率低(<8 → ×0.65),此处只保证不为负
|
||||
expect(rate).toBeGreaterThanOrEqual(0.1)
|
||||
})
|
||||
|
||||
it('年份 flag 3 年后清理(pruneYearFlags)', () => {
|
||||
const w = baseWorld('prune')
|
||||
w.state.year = 50
|
||||
w.state.family.flag['auction-45'] = true
|
||||
w.state.family.flag['auction-49'] = true
|
||||
w.advanceMonth()
|
||||
expect(w.state.family.flag['auction-45']).toBeUndefined()
|
||||
expect(w.state.family.flag['auction-49']).toBe(true)
|
||||
})
|
||||
|
||||
it('急事徽章 derived 正确(无继承人/重伤/瓶颈)', () => {
|
||||
const w = baseWorld('urgent')
|
||||
// 无继承人:除 x2 都死
|
||||
w.state.members['x1'].alive = false
|
||||
w.state.members['x3'].alive = false
|
||||
w.state.members['x5'].alive = false
|
||||
w.state.members['x4'].alive = false
|
||||
const u = computeUrgency(w)
|
||||
expect(u.noHeir).toBe(true)
|
||||
// 重伤
|
||||
const w2 = baseWorld('urgent2')
|
||||
w2.state.members['x3'].health = 20
|
||||
const u2 = computeUrgency(w2)
|
||||
expect(u2.injuredCount).toBeGreaterThanOrEqual(1)
|
||||
// 瓶颈
|
||||
const w3 = baseWorld('urgent3')
|
||||
w3.state.members['x3'].realmProgress = 100
|
||||
const u3 = computeUrgency(w3)
|
||||
expect(u3.blockedCount).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('王座 dead 成员不再显示活动 state(标签层修复)', () => {
|
||||
const w = baseWorld('deadlabel')
|
||||
const c = w.state.members['x3']
|
||||
c.alive = false
|
||||
// MemberCard 逻辑由 STATE_LABEL['dead'] 处理,引擎侧保证 alive=false 时 state 不变
|
||||
expect(c.state === 'idle' || c.state === 'wounded' || c.state === 'meditation').toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -82,7 +82,7 @@ describe('审计回归:P0 修复固化', () => {
|
||||
})
|
||||
|
||||
it('防御性修补后金钟罩不变(行为等价确认)', () => {
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a3223c77')
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('ffdd3fb1')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('195b8aaa')
|
||||
})
|
||||
})
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ import { World } from '../src/renderer/game/engine/world'
|
||||
// 0.1.10 基线:长线数值重建(修炼提速/寿元放宽/渡劫修复)后三档固化为 47/100/180 年。
|
||||
// 0.1.8 时修复批次后为 9af71ecb/63cede89/ebfec4a4;0.1.10 有意变更数值后按三档重算。
|
||||
const GOLDEN: Record<string, Record<number, string>> = {
|
||||
'bell-seed-1': { 560: 'a3223c77', 1200: '55aead92', 2160: 'ae2d9fb7' },
|
||||
'bell-seed-2': { 560: '3b8dcde4', 1200: '27becabd', 2160: '12d7e520' },
|
||||
'bell-seed-1': { 560: 'ffdd3fb1', 1200: '76787bd9', 2160: 'e3f0a1cd' },
|
||||
'bell-seed-2': { 560: '1dd432bb', 1200: '8ab8db6d', 2160: '2340e385' },
|
||||
'bell-seed-3': { 560: '195b8aaa', 1200: 'a19e1a90', 2160: '2d767a64' }
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ describe('GameFacade 门面', () => {
|
||||
const f = new GameFacade(w, 1)
|
||||
const info = f.about()
|
||||
expect(info.title).toBe('仙途家族志')
|
||||
expect(info.version).toContain('0.1.8')
|
||||
expect(info.version).toContain('0.1.11')
|
||||
expect(info.modules).toBeGreaterThanOrEqual(11)
|
||||
expect(info.systems).toBeGreaterThan(0)
|
||||
expect(info.plugins).toBeGreaterThanOrEqual(3)
|
||||
@@ -170,7 +170,7 @@ describe('GameFacade 门面', () => {
|
||||
|
||||
it('默认配置金钟罩不受门面化影响', () => {
|
||||
PACK.reset()
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a3223c77')
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('ffdd3fb1')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('195b8aaa')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -85,8 +85,8 @@ describe('PluginCore 插件协议', () => {
|
||||
})
|
||||
|
||||
it('默认管线金钟罩不受插件层影响', () => {
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('a3223c77')
|
||||
expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('3b8dcde4')
|
||||
expect(stateFingerprint(longRun('bell-seed-1').state)).toBe('ffdd3fb1')
|
||||
expect(stateFingerprint(longRun('bell-seed-2').state)).toBe('1dd432bb')
|
||||
expect(stateFingerprint(longRun('bell-seed-3').state)).toBe('195b8aaa')
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { World } from '../src/renderer/game/engine/world'
|
||||
import { computeUrgency } from '../src/renderer/game/core/urgency'
|
||||
import { computeGuide, GUIDE_STEPS, guideAdvance } from '../src/renderer/game/core/guide'
|
||||
import { fmt } from '../src/renderer/game/core/format'
|
||||
|
||||
function baseWorld(seed: string): World {
|
||||
return World.create({ seed, surname: '舒', familyName: '舒家', motto: 'm', difficulty: 'normal' })
|
||||
}
|
||||
|
||||
describe('急事徽章 derivedState(urgent)', () => {
|
||||
it('可突破≥1 时 badge 出现', () => {
|
||||
const w = baseWorld('urge-a')
|
||||
const c = w.state.members['x3']
|
||||
c.realmProgress = 100
|
||||
const u = computeUrgency(w)
|
||||
expect(u.blockedCount).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('重伤 count 反映', () => {
|
||||
const w = baseWorld('urge-b')
|
||||
const c = w.state.members['x3']
|
||||
c.health = 15
|
||||
const u = computeUrgency(w)
|
||||
expect(u.injuredCount).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('在外队伍 count 反映', () => {
|
||||
const w = baseWorld('urge-c')
|
||||
w.state.missions.push({ id: 'm1', defId: 'm-anmoku', memberIds: ['x3'], startYear: 1, startMonth: 1, stage: 0, stageMonth: 0, log: [], done: false })
|
||||
const u = computeUrgency(w)
|
||||
expect(u.missionCount).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('无继承人次条(危险信号)', () => {
|
||||
const w = baseWorld('urge-d')
|
||||
w.state.members['x1'].alive = false
|
||||
w.state.members['x3'].alive = false
|
||||
w.state.members['x5'].alive = false
|
||||
w.state.members['x4'].alive = false
|
||||
const u = computeUrgency(w)
|
||||
expect(u.noHeir).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('引导任务的 state machine', () => {
|
||||
it('开局处于第 1 步', () => {
|
||||
const w = baseWorld('guide-a')
|
||||
expect(computeGuide(w).step).toBe(0)
|
||||
expect(GUIDE_STEPS.length).toBe(4)
|
||||
})
|
||||
|
||||
it('完成任务推进且可跳步(不强制)', () => {
|
||||
const w = baseWorld('guide-b')
|
||||
expect(guideAdvance(w, 2)).toBe(2) // 跳到第 3 步(index 2)
|
||||
})
|
||||
|
||||
it('所有目标挂到状态', () => {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(GUIDE_STEPS[i].title.length).toBeGreaterThan(1)
|
||||
expect(GUIDE_STEPS[i].hint.length).toBeGreaterThan(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('数字格式化', () => {
|
||||
it.each([[1234, '1.2千'], [56789, '5.7万'], [123456, '12.3万'], [7890123, '789.0万']])('%i → %s', (n, s) => {
|
||||
expect(fmt(n)).toBe(s)
|
||||
})
|
||||
})
|
||||
|
||||
describe('0.1.11 编译 smoke', () => {
|
||||
it('engine 加载并推进数月无异常', () => {
|
||||
const w = baseWorld('smoke-011')
|
||||
for (let i = 0; i < 24; i++) w.advanceMonth()
|
||||
expect(w.state.year).toBeGreaterThan(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user