v0.1.0: 仙途家族志 · Chronicle of the Immortal Clan
家族模拟器首版:修仙/经营/战斗/外交/叙事全套系统 - Electron + React + TS + MetonaSqlark(aria+OPFS) - 水墨中国风 UI - 引擎种子随机、确定性可回放 - 19 项单元测试、端到端冒烟验证
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
import { useGameStore } from '../store'
|
||||
import { ITEMS } from '../../game/data/items'
|
||||
import { TECHNIQUES } from '../../game/data/techniques'
|
||||
import { TECHNIQUE_GRADE_NAMES } from '../../game/data/realms'
|
||||
import { marketPrice, buyItem, sellItem, buyTechnique } from '../../game/engine/market'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
export default function MarketPanel() {
|
||||
const world = useGameStore((s) => s.world)
|
||||
const bump = useGameStore((s) => s.bump)
|
||||
const revision = useGameStore((s) => s.revision)
|
||||
void revision
|
||||
if (!world) return null
|
||||
const w = world
|
||||
const fam = w.state.family
|
||||
const inv = fam.inventory
|
||||
const [tab, setTab] = useState<'goods' | 'tech'>('goods')
|
||||
|
||||
const goods = useMemo(() => Object.values(ITEMS).filter((i) => i.kind === 'resource' || i.kind === 'pill'), [])
|
||||
const artifacts = useMemo(() => Object.values(ITEMS).filter((i) => i.kind === 'artifact'), [])
|
||||
const marketTech = TECHNIQUES.filter((t) => t.grade <= 3)
|
||||
const cangshuLv = fam.buildings['cangshu'] ?? 0
|
||||
|
||||
const price = (id: string) => marketPrice(w, id)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="tabs" style={{ padding: 0, background: 'none', border: 'none', marginBottom: 10 }}>
|
||||
<div className={`tab ${tab === 'goods' ? 'sel' : ''}`} onClick={() => setTab('goods')}>货栈</div>
|
||||
<div className={`tab ${tab === 'tech' ? 'sel' : ''}`} onClick={() => setTab('tech')}>法帖</div>
|
||||
</div>
|
||||
{tab === 'goods' && (
|
||||
<>
|
||||
<div className="help-text">
|
||||
集市物价随季节波动(当前行情 {Math.round(((fam.flag['priceMult'] as number) ?? 1) * 100)}%)。
|
||||
仓库寄存的灵草、灵矿、兽核均可在此出手。
|
||||
</div>
|
||||
<table className="market-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>品名</th>
|
||||
<th>说明</th>
|
||||
<th>行价</th>
|
||||
<th>库存/持有</th>
|
||||
<th>买卖</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[...goods, ...artifacts].map((item) => {
|
||||
const have = item.id === 'lingcao' || item.id === 'lingkuang' || item.id === 'beastcore' || item.kind === 'pill' || item.kind === 'artifact'
|
||||
? inv[item.id] ?? 0
|
||||
: 0
|
||||
const stock = item.kind === 'artifact' ? 3 : item.kind === 'pill' ? 5 : 9999
|
||||
const count = item.kind === 'artifact' ? 1 : 5
|
||||
return (
|
||||
<tr key={item.id}>
|
||||
<td>
|
||||
<b><span className="s-icon">{item.icon}</span> {item.name}</b>
|
||||
</td>
|
||||
<td className="dim">{item.desc}</td>
|
||||
<td>{price(item.id)} 灵石</td>
|
||||
<td className="dim">{have}</td>
|
||||
<td>
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
disabled={fam.stones < price(item.id) * count}
|
||||
onClick={() => {
|
||||
buyItem(w, item.id, count)
|
||||
bump()
|
||||
}}
|
||||
>
|
||||
买{count}
|
||||
</button>{' '}
|
||||
{((item.kind === 'resource' || item.kind === 'pill') && have >= count) && (
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
onClick={() => {
|
||||
sellItem(w, item.id, count)
|
||||
bump()
|
||||
}}
|
||||
>
|
||||
卖{count}
|
||||
</button>
|
||||
)}
|
||||
{item.kind === 'artifact' && have > 0 && (
|
||||
<span className="dim2"> 已购藏</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
<tr>
|
||||
<td><b><span className="s-icon">炼</span> 丹房炼制</b></td>
|
||||
<td className="dim">灵草+辅矿 → 丹药(丹房等级越高成数越足)</td>
|
||||
<td className="dim">{fam.buildings['danfang'] ? `${fam.buildings['danfang']}级丹房` : '未建丹房'}</td>
|
||||
<td className="dim">灵草{inv['lingcao'] ?? 0}</td>
|
||||
<td>
|
||||
{fam.buildings['danfang'] && (
|
||||
<>
|
||||
<button className="btn btn-sm" onClick={() => { w.craftPill('qiyuan'); bump() }}>炼气丹</button>{' '}
|
||||
<button className="btn btn-sm" onClick={() => { w.craftPill('ningyuan'); bump() }}>炼元丹</button>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
)}
|
||||
{tab === 'tech' && (
|
||||
<>
|
||||
<div className="help-text">
|
||||
藏书阁录存法帖,族人可修习。藏书阁等级决定可在坊市搜购的品阶(当前 {cangshuLv} 级,可购至{TECHNIQUE_GRADE_NAMES[Math.min(3, Math.max(0, cangshuLv))]})。
|
||||
若藏书阁未建或等级不足,只可见基础法帖。
|
||||
</div>
|
||||
<table className="market-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>法帖</th>
|
||||
<th>品阶/路子</th>
|
||||
<th>妙用</th>
|
||||
<th>修为加成</th>
|
||||
<th>战力加成</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{marketTech
|
||||
.filter((t) => t.grade <= 1 + cangshuLv)
|
||||
.filter((t) => t.grade <= 3)
|
||||
.map((t) => {
|
||||
const owned = fam.techniques.includes(t.id)
|
||||
const p = t.grade <= 1 + cangshuLv
|
||||
const priceT = [120, 300, 700, 1600, 3600][t.grade] ?? 300
|
||||
return (
|
||||
<tr key={t.id}>
|
||||
<td><b>《{t.name}》</b></td>
|
||||
<td className="dim">{TECHNIQUE_GRADE_NAMES[t.grade]} · {t.path}</td>
|
||||
<td className="dim">{t.desc}</td>
|
||||
<td>+{Math.round((t.expBonus) * 100)}%</td>
|
||||
<td>+{Math.round((t.powerBonus) * 100)}%</td>
|
||||
<td>
|
||||
{owned ? (
|
||||
<span className="good">已录</span>
|
||||
) : (
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
disabled={!p || fam.stones < priceT}
|
||||
onClick={() => {
|
||||
buyTechnique(w, t.id, priceT)
|
||||
bump()
|
||||
}}
|
||||
>
|
||||
{priceT}灵石
|
||||
</button>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
)}
|
||||
<div className="dim2" style={{ marginTop: 10 }}>
|
||||
灵矿不足时可上坊市民间收购;妖丹于探秘与灵兽园中可得。
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user