0.1.40 P1-2: UI渲染效率memo化4处 (NpcRow/NewsList/groupedEntries/specialtyId)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useGameStore } from '../store'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useMemo, useState, memo } from 'react'
|
||||
|
||||
const CAT_NAME: Record<string, string> = {
|
||||
birth: '诞庆',
|
||||
@@ -61,6 +61,7 @@ export default function ChroniclePanel() {
|
||||
if (!world) return null
|
||||
const battles = world.state.battles.slice().reverse()
|
||||
const battle = battles.find((b) => b.id === battleId)
|
||||
const groupedEntries = useMemo(() => grouped(entries), [entries])
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -99,7 +100,7 @@ export default function ChroniclePanel() {
|
||||
))}
|
||||
</div>
|
||||
<div className="chronicle-list">
|
||||
{grouped(entries).map((g) => (
|
||||
{groupedEntries.map((g) => (
|
||||
<div key={g.year}>
|
||||
<div className="ch-yearbar">{g.year}年 <span className="dim2">{g.items.length}条 · {statOf(g)}</span></div>
|
||||
{g.items.map((e) => (
|
||||
|
||||
@@ -1,35 +1,21 @@
|
||||
import { useGameStore } from '../store'
|
||||
import { npcById } from '../../game/data/npcs'
|
||||
import { giftNpc, makePeace, marryNpcFamily, calcGiftGain, GIFT_TIERS } from '../../game/engine/runtime/Systems/diplomacy'
|
||||
import { useMemo } from 'react'
|
||||
import { useMemo, memo } from 'react'
|
||||
import { MAJOR_NAMES } from '../../game/data/realms'
|
||||
import type { World } from '../../game/engine/runtime/World'
|
||||
import type { NpcFamilyState } from '../../game/types/domain'
|
||||
|
||||
const REL_TYPE = (r: number) =>
|
||||
r >= 80 ? ['死生之交', 'good'] : r >= 50 ? ['通好之势', 'good'] : r >= 25 ? ['友善', 'warn'] : r >= 10 ? ['温和', 'warn'] : r > -10 ? ['中立', ''] : r > -30 ? ['嫌隙', ''] : r > -50 ? ['敌对', 'bad'] : ['仇雠', 'bad']
|
||||
|
||||
export default function DiplomacyPanel() {
|
||||
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 npcs = Object.values(w.state.npcFamilies)
|
||||
|
||||
const sorted = useMemo(() => npcs, [npcs])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="help-text">
|
||||
外交之道:送礼结好、联姻通好;仇恨过深的老对家会遣队劫掠。家族声望同样影响诸家态度。
|
||||
</div>
|
||||
{sorted.map((npc) => {
|
||||
const NpcRow = memo(function NpcRow({ npc, world, bump }: { npc: NpcFamilyState; world: World; bump: () => void }) {
|
||||
const def = npcById(npc.id)
|
||||
if (!def) return null
|
||||
const [relLabel, relCls] = REL_TYPE(npc.relation)
|
||||
const married = npc.allied
|
||||
return (
|
||||
<div key={npc.id} className="npc-row">
|
||||
<div className="npc-row">
|
||||
<div className="npc-name">
|
||||
<div style={{ fontSize: '1.15rem' }}>{npc.name}</div>
|
||||
<div className="dim2" style={{ fontSize: '0.78rem' }}>{def.style} · {npc.region}</div>
|
||||
@@ -55,9 +41,9 @@ export default function DiplomacyPanel() {
|
||||
<button
|
||||
key={t}
|
||||
className="btn btn-sm"
|
||||
disabled={w.state.family.stones < t}
|
||||
disabled={world.state.family.stones < t}
|
||||
title={`关系 +${calcGiftGain(t)}`}
|
||||
onClick={() => { giftNpc(w, npc.id, t); bump() }}
|
||||
onClick={() => { giftNpc(world, npc.id, t); bump() }}
|
||||
>
|
||||
赠礼{t}(+{calcGiftGain(t)})
|
||||
</button>
|
||||
@@ -67,7 +53,7 @@ export default function DiplomacyPanel() {
|
||||
className="btn btn-sm"
|
||||
disabled={npc.relation < 25 || married}
|
||||
title={npc.relation < 25 ? `需关系≥25(当前${npc.relation})` : married ? '一姓已结亲' : '关系≥25方可联姻'}
|
||||
onClick={() => { marryNpcFamily(w, npc.id); bump() }}
|
||||
onClick={() => { marryNpcFamily(world, npc.id); bump() }}
|
||||
>
|
||||
{married ? '已联姻' : '联姻'}
|
||||
</button>
|
||||
@@ -76,7 +62,7 @@ export default function DiplomacyPanel() {
|
||||
title="关系-20,可能招致劫掠;每年一次"
|
||||
onClick={() => {
|
||||
if (window.confirm(`向${npc.name}寻衅?关系将下降 20,可能招致劫掠。`)) {
|
||||
w.tauntNpc(npc.id)
|
||||
world.tauntNpc(npc.id)
|
||||
bump()
|
||||
}
|
||||
}}
|
||||
@@ -86,9 +72,9 @@ export default function DiplomacyPanel() {
|
||||
{npc.relation < -30 && (
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
disabled={w.state.family.stones < 200}
|
||||
title={w.state.family.stones < 200 ? '需灵石 200' : '与对方结下和约'}
|
||||
onClick={() => { makePeace(w, npc.id); bump() }}
|
||||
disabled={world.state.family.stones < 200}
|
||||
title={world.state.family.stones < 200 ? '需灵石 200' : '与对方结下和约'}
|
||||
onClick={() => { makePeace(world, npc.id); bump() }}
|
||||
>
|
||||
议和
|
||||
</button>
|
||||
@@ -96,7 +82,27 @@ export default function DiplomacyPanel() {
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
})
|
||||
|
||||
export default function DiplomacyPanel() {
|
||||
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 npcs = Object.values(w.state.npcFamilies)
|
||||
|
||||
const sorted = useMemo(() => npcs, [npcs])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="help-text">
|
||||
外交之道:送礼结好、联姻通好;仇恨过深的老对家会遣队劫掠。家族声望同样影响诸家态度。
|
||||
</div>
|
||||
{sorted.map((npc) => (
|
||||
<NpcRow key={npc.id} npc={npc} world={w} bump={bump} />
|
||||
))}
|
||||
<div className="dim2" style={{ marginTop: 12 }}>
|
||||
示好请用灵石铺路;寻衅会立刻恶化关系,且可能招致劫掠或围庄。
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useGameStore } from '../store'
|
||||
import { useMemo } from 'react'
|
||||
import { useMemo, memo } from 'react'
|
||||
import { npcById } from '../../game/data/npcs'
|
||||
import { MAJOR_NAMES } from '../../game/data/realms'
|
||||
import type { WorldSimState } from '../../game/engine/sim/worldsim-data'
|
||||
@@ -7,10 +7,53 @@ import { POOL_BASE, WORLDSIM, ERA_CONF } from '../../game/engine/sim/worldsim-da
|
||||
import { combatPowerOf } from '../../game/engine/runtime/Systems/combat'
|
||||
import { sellItem, buyItem, marketPrice } from '../../game/engine/sim/Market'
|
||||
import { useState } from 'react'
|
||||
import type { World } from '../../game/engine/runtime/World'
|
||||
|
||||
const RES_LABEL: Record<string, string> = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核' }
|
||||
const REALM_IDX = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] as const
|
||||
|
||||
/** 0.1.40 memo 化天下快讯——120 条 newsFeed 不随主面板其他状态变动重渲染 */
|
||||
const NewsList = memo(function NewsList({ news, world, bump }: { news: { year: number; month: number; src: string; text: string; itemId?: string; kind?: string }[]; world: World; bump: () => void }) {
|
||||
return (
|
||||
<>
|
||||
{news.length === 0 && <div className="dim2">风平浪静,尚无消息。</div>}
|
||||
{news.map((row, i) => {
|
||||
const canBuy = row.kind === 'quote' && row.itemId && world.state.family.stones >= marketPrice(world, row.itemId) * 5
|
||||
const canSell = row.kind === 'quote' && row.itemId && (world.state.family.inventory[row.itemId] ?? 0) >= 5
|
||||
const isCrisis = row.kind === 'calamity'
|
||||
return (
|
||||
<div key={i} className="ch-item" style={{ padding: '5px 8px' }}>
|
||||
<span className="ch-month">{row.year}年{row.month}月</span>
|
||||
<span>{row.text}</span>
|
||||
{isCrisis && world.state.family.stones >= 50 && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
title="引粥施药,换两家之谊"
|
||||
onClick={() => {
|
||||
world.reliefCalamity(50)
|
||||
bump()
|
||||
}}
|
||||
>救济</button>
|
||||
</span>
|
||||
)}
|
||||
{canBuy && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<button className="btn btn-sm" title="行情见底,出手吃进(买5)" onClick={() => { buyItem(world, row.itemId!, 5); bump() }}>吃进5</button>
|
||||
</span>
|
||||
)}
|
||||
{canSell && !canBuy && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<button className="btn btn-sm" title="行情见顶,集市抛售(卖5)" onClick={() => { sellItem(world, row.itemId!, 5); bump() }}>抛售5</button>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default function WorldPanel() {
|
||||
const world = useGameStore((s) => s.world)
|
||||
const bump = useGameStore((s) => s.bump)
|
||||
@@ -123,40 +166,7 @@ export default function WorldPanel() {
|
||||
<div>
|
||||
<h4 className="dim" style={{ margin: '4px 0 6px' }}>天下快讯</h4>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 380, overflowY: 'auto' }}>
|
||||
{news.length === 0 && <div className="dim2">风平浪静,尚无消息。</div>}
|
||||
{news.map((row, i) => {
|
||||
const canBuy = row.kind === 'quote' && row.itemId && w.state.family.stones >= marketPrice(w, row.itemId) * 5
|
||||
const canSell = row.kind === 'quote' && row.itemId && (w.state.family.inventory[row.itemId] ?? 0) >= 5
|
||||
const isCrisis = row.kind === 'calamity'
|
||||
return (
|
||||
<div key={i} className="ch-item" style={{ padding: '5px 8px' }}>
|
||||
<span className="ch-month">{row.year}年{row.month}月</span>
|
||||
<span>{row.text}</span>
|
||||
{isCrisis && w.state.family.stones >= 50 && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<button
|
||||
className="btn btn-sm"
|
||||
title="引粥施药,换两家之谊"
|
||||
onClick={() => {
|
||||
w.reliefCalamity(50)
|
||||
bump()
|
||||
}}
|
||||
>救济</button>
|
||||
</span>
|
||||
)}
|
||||
{canBuy && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<button className="btn btn-sm" title="行情见底,出手吃进(买5)" onClick={() => { buyItem(w, row.itemId!, 5); bump() }}>吃进5</button>
|
||||
</span>
|
||||
)}
|
||||
{canSell && !canBuy && (
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
<button className="btn btn-sm" title="行情见顶,集市抛售(卖5)" onClick={() => { sellItem(w, row.itemId!, 5); bump() }}>抛售5</button>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<NewsList news={news} world={w} bump={bump} />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user