v0.1.0: 仙途家族志 · Chronicle of the Immortal Clan
家族模拟器首版:修仙/经营/战斗/外交/叙事全套系统 - Electron + React + TS + MetonaSqlark(aria+OPFS) - 水墨中国风 UI - 引擎种子随机、确定性可回放 - 19 项单元测试、端到端冒烟验证
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useGameStore } from '../store'
|
||||
import { SURNAME_POOL } from '../../game/core/names'
|
||||
|
||||
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(
|
||||
() => `${Date.now()}-${Math.floor(Math.random() * 1e9)}-${Math.floor(Math.random() * 1e9)}`,
|
||||
[randoming]
|
||||
)
|
||||
|
||||
const rollNames = () => {
|
||||
setRandoming((r) => !r)
|
||||
const s = SURNAME_POOL[Math.floor(Math.random() * SURNAME_POOL.length)]
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user