v0.1.19: 八方风雨 + 孤立系统归并(双主线,1002 全绿)
【主线一:世界自演进深化《八方风雨》】 - NPC 战略姿态 stance:守成/扩张/隐忍/结盟四态,年首评估(era>景气>power>relation) 全行为调制:raid 概率×2/×0.4/×0、互攻阈值 -40/-70/不攻、贸量 ×1.3/×0.7、 赠礼收益 ally×1.3、结盟门槛 ally 放宽至30——NPC 从统计物理到行为战略 - 超卖潮(谷贱伤农):池>1.8×base 世界产能回调×0.7 + 年度播报(防玩家玩崩市场) - 盟约连坐:结盟令世仇(rel<-50)对玩家-20;盟者世仇 raid 概率×2 - 盟友求援:互攻失利盟友告急(distress 旗标)——WorldPanel 响应(出资200/见死不救) - 干预天下:调停(50灵石令世仇-60→-25、声望+2)/ 资助盟友;恩怨网情报视图(各家关系展开+调停钮) 【主线二:孤立系统归并引擎】 - P0 兜底修复:empty() 收敛 makeWorldSimState 单源 + normalize 补 tideTicks/calamityLeft/ era/eraStartYear/prosperity/distress/overfluxYear(旧档 NaN 级联防死) - FxGate 从 engine/kernel/fxqueue.ts 并入 ui/fx.ts(kernel 清纯引擎;deltas 顶层导出, 3 处引用 10 行改动);engine 侧零 UI 依赖再下一城 - 音效语义补充:onFx→blade/pulse/spark→sWar/sGong/sBell;onLog kind 兜底保留(不静音) - store.advance drift/season 段左移 fx.ts(fxSeason 复活接管 season 检测) - 死码清除:timesense.tideOf/tintLabel、tideDir 全链(类型/初始/写入/domain) - domain.ts worldSim 局部类型补全 0.1.18/0.1.19 全部新字段(断言类型安全) 【测试】1002 全绿(41 套件):stance-world 7 例(兜底/姿态/raid/超卖/连坐/调停/求援)
This commit is contained in:
@@ -1,72 +0,0 @@
|
||||
export type FxKind = 'spark' | 'mist' | 'ripple' | 'blade' | 'pulse' | 'seasonShift' | 'drift'
|
||||
|
||||
export type FxMode = 'soft' | 'std' | 'full'
|
||||
|
||||
export interface FxEmit {
|
||||
kind: FxKind
|
||||
payload?: Record<string, number | string>
|
||||
at: number
|
||||
}
|
||||
|
||||
export interface FxBus {
|
||||
emit(e: FxEmit): void
|
||||
}
|
||||
|
||||
/** 竞态安全发射闸:单循环、队列不覆盖、档位过滤、reduced-motion 降级 */
|
||||
export class FxGate {
|
||||
private queue: FxEmit[] = []
|
||||
private draining = false
|
||||
mode: FxMode = 'std'
|
||||
preferReduced = false
|
||||
|
||||
constructor(private bus: FxBus) {}
|
||||
|
||||
setMode(m: FxMode): void {
|
||||
this.mode = m
|
||||
}
|
||||
|
||||
setReduced(r: boolean): void {
|
||||
this.preferReduced = r
|
||||
}
|
||||
|
||||
allowed(kind: FxKind): boolean {
|
||||
if (this.mode === 'soft') return kind === 'ripple' || kind === 'pulse' || kind === 'drift'
|
||||
if (this.mode === 'full') return true
|
||||
return kind !== 'blade' // std:火花/雾/涟漪/脉动,刀光仅 full
|
||||
}
|
||||
|
||||
emit(kind: FxKind, payload?: FxEmit['payload']): void {
|
||||
if (!this.allowed(kind) || this.preferReduced || this.draining) return
|
||||
this.queue.push({ kind, payload, at: Date.now() })
|
||||
this.drain()
|
||||
}
|
||||
|
||||
private drain(): void {
|
||||
if (this.draining) return
|
||||
this.draining = true
|
||||
// 单帧集中派发(不逐个 setTimeout,防止栈深+竞态覆盖)
|
||||
const batch = this.queue
|
||||
this.queue = []
|
||||
try {
|
||||
for (const e of batch) this.bus.emit(e)
|
||||
} finally {
|
||||
this.draining = false
|
||||
}
|
||||
}
|
||||
|
||||
pending(): number {
|
||||
return this.queue.length + (this.draining ? 1 : 0)
|
||||
}
|
||||
|
||||
/** 纯逻辑:资源差量(防负值与 NaN,供漂字用) */
|
||||
static deltas(prev: Record<string, number>, next: Record<string, number>): Record<string, number> {
|
||||
const out: Record<string, number> = {}
|
||||
const keys = new Set([...Object.keys(prev), ...Object.keys(next)])
|
||||
for (const k of keys) {
|
||||
const a = prev[k] ?? 0
|
||||
const b = next[k] ?? 0
|
||||
if (Number.isFinite(a) && Number.isFinite(b) && b !== a) out[k] = b - a
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,6 @@ export const TIDES: string[] = [
|
||||
'立冬', '小雪', '大雪', '冬至', '小寒', '大寒'
|
||||
]
|
||||
|
||||
/** 季节 → 节气(每月两个节气:tideOf(month, phase) 返回 0..1 ) */
|
||||
export function tideOf(month: number, phase: 0 | 1): string {
|
||||
const idx = Math.min(23, (month - 1) * 2 + phase)
|
||||
return TIDES[idx] ?? '立春'
|
||||
}
|
||||
|
||||
/** 年轮进度(0..1)——用于顶栏年环弧段 */
|
||||
export function yearRing(month: number): number {
|
||||
return (month - 1) / 12
|
||||
@@ -46,6 +40,3 @@ export function seasonCssVars(season: Season): Record<string, string> {
|
||||
}
|
||||
}
|
||||
|
||||
export function tintLabel(season: Season): string {
|
||||
return TINTS[season].name
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user