【资源物品大扩容】 - ItemKind 6 值:resource/material/pill/talisman/brew/artifact——市场分群/背包/售卖/modSchema 全链 - 新材料七种:灵玉/灵木/兽皮/灵果(POOL_BASE+MARKET_IDS+worldgen 偏移四同步入世界池)、 灵露/丹砂/符纸(道具料) - 循环接入:新建筑灵果园(灵果)/灵木坊(灵木);productionTick 泛化逐建筑读 produceTable 全键; 秘境掉落材料化(灵玉/兽皮/灵木);材料类同向微漂(物产同源生态) 【新用途】 - 符箓:war 战斗开战武备自动消耗(talismanWarPrep 零 rng) - 灵酿:渡劫伤后自动回气 +5 - 新丹方和灵丹/虚元丹 + 玉澜刃/灵酿铸器配方 【测试 5009】86 套件(+1000:matrix-treasure-a/b 950 + 断链修整); 【金钟罩】0.1.32 基线重算(物品/池/生产泛化受控变更);build 通过
231 lines
10 KiB
TypeScript
231 lines
10 KiB
TypeScript
import { useGameStore } from '../store'
|
||
import { ITEMS } from '../../game/data/items'
|
||
import { TECHNIQUES } from '../../game/data/techniques'
|
||
import { techniqueGradeName } from '../../game/data/realms'
|
||
import { marketPrice, buyItem, sellItem, buyTechnique, techniquePrice } from '../../game/engine/sim/Market'
|
||
import { PILL_RECIPES, FORGE_RECIPES } from '../../game/data/items'
|
||
import { bonusOf } from '../../game/data/buildings'
|
||
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
|
||
const [tab, setTab] = useState<'goods' | 'tech' | 'craft'>('goods')
|
||
if (!world) return null
|
||
const w = world
|
||
const fam = w.state.family
|
||
const inv = fam.inventory
|
||
|
||
const goods = useMemo(
|
||
() => Object.values(ITEMS).filter((i) => ['resource', 'material', 'pill', 'talisman', 'brew', 'artifact'].includes(i.kind)),
|
||
[]
|
||
)
|
||
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 className={`tab ${tab === 'craft' ? 'sel' : ''}`} onClick={() => setTab('craft')}>定制</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 = ['resource', 'material', 'pill', 'talisman', 'brew', 'artifact'].includes(item.kind)
|
||
? inv[item.id] ?? 0
|
||
: 0
|
||
const stock = item.kind === 'artifact' ? 3 : ['pill', 'talisman', 'brew'].includes(item.kind) ? 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}
|
||
title={fam.stones < price(item.id) * count ? '灵石不足' : ''}
|
||
onClick={() => {
|
||
buyItem(w, item.id, count)
|
||
bump()
|
||
}}
|
||
>
|
||
买{count}
|
||
</button>{' '}
|
||
{(['resource', 'material', 'pill'].includes(item.kind) && 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>
|
||
)
|
||
})}
|
||
|
||
</tbody>
|
||
</table>
|
||
</>
|
||
)}
|
||
{tab === 'tech' && (
|
||
<>
|
||
<div className="help-text">
|
||
藏书阁录存法帖,族人可修习。藏书阁等级决定可在坊市搜购的品阶(当前 {cangshuLv} 级,可购至{techniqueGradeName(Math.min(4, Math.max(0, cangshuLv + 1)))})。
|
||
若藏书阁未建或等级不足,只可见基础法帖。
|
||
</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 = techniquePrice(t.id)
|
||
return (
|
||
<tr key={t.id}>
|
||
<td><b>《{t.name}》</b></td>
|
||
<td className="dim">{techniqueGradeName(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>
|
||
</>
|
||
)}
|
||
{tab === 'craft' && (
|
||
<>
|
||
<div className="help-text">
|
||
丹房依「灵草+兽核+灵石」配比丹药,成数随丹房等级;破境丹需丹房三级。炼器以灵矿兽核+灵石相合,无建筑门槛,只看家底。
|
||
</div>
|
||
<h4 className="dim" style={{ margin: '8px 0 4px' }}>丹房炼制(当前 {fam.buildings['danfang'] ?? 0} 级 · 成率 {Math.round(bonusOf('danfang', 'craftChance', fam.buildings['danfang'] ?? 0) * 100)}%)</h4>
|
||
<table className="market-table">
|
||
<thead>
|
||
<tr><th>丹方</th><th>药性</th><th>配方</th><th>门槛</th><th></th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{PILL_RECIPES.map((r) => {
|
||
const lvl = fam.buildings['danfang'] ?? 0
|
||
const enough = (inv['lingcao'] ?? 0) >= r.lingcao && (inv['beastcore'] ?? 0) >= r.beastcore && fam.stones >= r.stones
|
||
return (
|
||
<tr key={r.output}>
|
||
<td><b><span className="s-icon">丹</span> {r.name}</b></td>
|
||
<td className="dim">{r.desc}</td>
|
||
<td className="dim">草{r.lingcao}·核{r.beastcore}·灵石{r.stones}</td>
|
||
<td className="dim">{r.danfangLevel}级丹房</td>
|
||
<td>
|
||
<button
|
||
className="btn btn-sm"
|
||
disabled={lvl < r.danfangLevel || !enough}
|
||
title={lvl < r.danfangLevel ? `丹房需升到 ${r.danfangLevel} 级` : enough ? '炼制(失败折半退料)' : '材料不足'}
|
||
onClick={() => { w.craftPill(r.output.replace('pill-', '') as 'qiyuan' | 'ningyuan' | 'pojing'); bump() }}
|
||
>炼</button>
|
||
</td>
|
||
</tr>
|
||
)
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
<h4 className="dim" style={{ margin: '14px 0 4px' }}>铸器坊(灵矿+兽核+灵石 → 兵刃)</h4>
|
||
<table className="market-table">
|
||
<thead>
|
||
<tr><th>兵刃</th><th>妙用</th><th>配方</th><th></th></tr>
|
||
</thead>
|
||
<tbody>
|
||
{FORGE_RECIPES.map((r) => {
|
||
const enough = (inv['lingkuang'] ?? 0) >= r.lingkuang && (inv['beastcore'] ?? 0) >= r.beastcore && fam.stones >= r.stones
|
||
return (
|
||
<tr key={r.output}>
|
||
<td><b><span className="s-icon">器</span> {r.name}</b></td>
|
||
<td className="dim">{r.desc}</td>
|
||
<td className="dim">矿{r.lingkuang}·核{r.beastcore}·灵石{r.stones}</td>
|
||
<td>
|
||
<button
|
||
className="btn btn-sm"
|
||
disabled={!enough}
|
||
title={enough ? '淬火铸成' : '材料不足'}
|
||
onClick={() => { w.forgeArtifact(r.output as 'weapon-qi' | 'weapon-ling' | 'weapon-fa'); bump() }}
|
||
>铸</button>
|
||
</td>
|
||
</tr>
|
||
)
|
||
})}
|
||
</tbody>
|
||
</table>
|
||
{(fam.buildings['danfang'] ?? 0) > 0 && (
|
||
<div className="dim2" style={{ marginTop: 8 }}>
|
||
破境丹亦可等拍卖会落槌(需要时留神百年拍卖)。
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
<div className="dim2" style={{ marginTop: 10 }}>
|
||
灵矿不足时可上坊市民间收购;妖丹于探秘与灵兽园中可得。
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|