结构(旧 game/core+engine → 新 engine/ 域): - engine/kernel/ 时钟/随机/插件协议/fxqueue/timesense/format/urgency/guide/names(原 core) - engine/narrative/ legacy/报告/列传/谱系/年轴(原 core 叙事族) - engine/runtime/ World/creation/pcgen/ApiFacade/capabilities/pluginManager/boot/clocks + Systems/*(12 系统) - engine/sim/ Market(未来 WorldSim 同行) - 旧 game/core、engine/systems、engine/world.ts 等路径全部废弃(无 re-export 兼容层) 验证:35 套件/967 测试全绿(金钟罩三档零漂移=纯搬迁无行为变化) typecheck 0 error
93 lines
3.6 KiB
TypeScript
93 lines
3.6 KiB
TypeScript
import { useMemo, useState } from 'react'
|
|
import { useGameStore } from '../store'
|
|
import { SURNAME_POOL } from '../../game/engine/kernel/names'
|
|
import { RngHub } from '../../game/engine/kernel/rng'
|
|
|
|
export default function NewGame() {
|
|
const go = useGameStore((s) => s.go)
|
|
const startNewGame = useGameStore((s) => s.startNewGame)
|
|
const [surname, setSurname] = useState('林')
|
|
const [familyName, setFamilyName] = useState('林家')
|
|
const [motto, setMotto] = useState('耕读传家,术法继世')
|
|
const [difficulty, setDifficulty] = useState<'easy' | 'normal' | 'hard'>('normal')
|
|
const [slot, setSlot] = useState(1)
|
|
const [randoming, setRandoming] = useState(false)
|
|
|
|
const seed = useMemo(() => RngHub.rollSeed(), [randoming])
|
|
|
|
const rollNames = () => {
|
|
setRandoming((r) => !r)
|
|
const idx = Math.floor(RngHub.audioNoise01() * SURNAME_POOL.length)
|
|
const s = SURNAME_POOL[Math.min(SURNAME_POOL.length - 1, idx)]
|
|
setSurname(s)
|
|
setFamilyName(`${s}氏`)
|
|
}
|
|
|
|
return (
|
|
<div className="newgame">
|
|
<div className="newgame-box">
|
|
<div className="card-title" style={{ textAlign: 'center', fontSize: '1.4rem' }}>
|
|
— 创建家族 —
|
|
</div>
|
|
<div className="field">
|
|
<label>姓氏</label>
|
|
<input value={surname} onChange={(e) => setSurname(e.target.value)} maxLength={2} />
|
|
<button className="btn btn-sm" onClick={rollNames}>随机</button>
|
|
</div>
|
|
<div className="field">
|
|
<label>家号</label>
|
|
<input value={familyName} onChange={(e) => setFamilyName(e.target.value)} maxLength={10} placeholder="例如:林氏" />
|
|
</div>
|
|
<div className="field">
|
|
<label>祖训</label>
|
|
<input value={motto} onChange={(e) => setMotto(e.target.value)} maxLength={20} />
|
|
</div>
|
|
<div className="field">
|
|
<label>难度</label>
|
|
<div className="diff-grid">
|
|
{(
|
|
[
|
|
['easy', '顺境', '灵石丰足,时来运转'],
|
|
['normal', '中庸', '中规中矩,全靠经营'],
|
|
['hard', '逆境', '部曲拮据,波折不断']
|
|
] as const
|
|
).map(([k, label, desc]) => (
|
|
<div
|
|
key={k}
|
|
className={`diff-opt ${difficulty === k ? 'sel' : ''}`}
|
|
onClick={() => setDifficulty(k)}
|
|
>
|
|
<div>{label}</div>
|
|
<div className="dim2" style={{ fontSize: '0.78rem', marginTop: 2 }}>{desc}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="field">
|
|
<label>存档位</label>
|
|
<select value={slot} onChange={(e) => setSlot(Number(e.target.value))}>
|
|
<option value={1}>第一档</option>
|
|
<option value={2}>第二档</option>
|
|
<option value={3}>第三档</option>
|
|
<option value={4}>第四档</option>
|
|
</select>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: 12, justifyContent: 'center', marginTop: 8 }}>
|
|
<button className="btn" onClick={() => go('boot')}>返回</button>
|
|
<button
|
|
className="btn btn-primary btn-lg"
|
|
onClick={() => {
|
|
void startNewGame({ seed, surname, familyName, motto, difficulty }, slot)
|
|
}}
|
|
>
|
|
缔 造 家 族
|
|
</button>
|
|
</div>
|
|
<div className="dim2" style={{ textAlign: 'center' }}>
|
|
你将扮演家主,经营一族兴衰:修炼突破、内外经营、结交应敌,直至百年之后。
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|