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 { useGameStore } from '../store'
|
||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState, memo } from 'react'
|
||||||
|
|
||||||
const CAT_NAME: Record<string, string> = {
|
const CAT_NAME: Record<string, string> = {
|
||||||
birth: '诞庆',
|
birth: '诞庆',
|
||||||
@@ -61,6 +61,7 @@ export default function ChroniclePanel() {
|
|||||||
if (!world) return null
|
if (!world) return null
|
||||||
const battles = world.state.battles.slice().reverse()
|
const battles = world.state.battles.slice().reverse()
|
||||||
const battle = battles.find((b) => b.id === battleId)
|
const battle = battles.find((b) => b.id === battleId)
|
||||||
|
const groupedEntries = useMemo(() => grouped(entries), [entries])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -99,7 +100,7 @@ export default function ChroniclePanel() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="chronicle-list">
|
<div className="chronicle-list">
|
||||||
{grouped(entries).map((g) => (
|
{groupedEntries.map((g) => (
|
||||||
<div key={g.year}>
|
<div key={g.year}>
|
||||||
<div className="ch-yearbar">{g.year}年 <span className="dim2">{g.items.length}条 · {statOf(g)}</span></div>
|
<div className="ch-yearbar">{g.year}年 <span className="dim2">{g.items.length}条 · {statOf(g)}</span></div>
|
||||||
{g.items.map((e) => (
|
{g.items.map((e) => (
|
||||||
|
|||||||
@@ -1,35 +1,21 @@
|
|||||||
import { useGameStore } from '../store'
|
import { useGameStore } from '../store'
|
||||||
import { npcById } from '../../game/data/npcs'
|
import { npcById } from '../../game/data/npcs'
|
||||||
import { giftNpc, makePeace, marryNpcFamily, calcGiftGain, GIFT_TIERS } from '../../game/engine/runtime/Systems/diplomacy'
|
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 { 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) =>
|
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']
|
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 NpcRow = memo(function NpcRow({ npc, world, bump }: { npc: NpcFamilyState; world: World; bump: () => void }) {
|
||||||
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 def = npcById(npc.id)
|
const def = npcById(npc.id)
|
||||||
if (!def) return null
|
if (!def) return null
|
||||||
const [relLabel, relCls] = REL_TYPE(npc.relation)
|
const [relLabel, relCls] = REL_TYPE(npc.relation)
|
||||||
const married = npc.allied
|
const married = npc.allied
|
||||||
return (
|
return (
|
||||||
<div key={npc.id} className="npc-row">
|
<div className="npc-row">
|
||||||
<div className="npc-name">
|
<div className="npc-name">
|
||||||
<div style={{ fontSize: '1.15rem' }}>{npc.name}</div>
|
<div style={{ fontSize: '1.15rem' }}>{npc.name}</div>
|
||||||
<div className="dim2" style={{ fontSize: '0.78rem' }}>{def.style} · {npc.region}</div>
|
<div className="dim2" style={{ fontSize: '0.78rem' }}>{def.style} · {npc.region}</div>
|
||||||
@@ -55,9 +41,9 @@ export default function DiplomacyPanel() {
|
|||||||
<button
|
<button
|
||||||
key={t}
|
key={t}
|
||||||
className="btn btn-sm"
|
className="btn btn-sm"
|
||||||
disabled={w.state.family.stones < t}
|
disabled={world.state.family.stones < t}
|
||||||
title={`关系 +${calcGiftGain(t)}`}
|
title={`关系 +${calcGiftGain(t)}`}
|
||||||
onClick={() => { giftNpc(w, npc.id, t); bump() }}
|
onClick={() => { giftNpc(world, npc.id, t); bump() }}
|
||||||
>
|
>
|
||||||
赠礼{t}(+{calcGiftGain(t)})
|
赠礼{t}(+{calcGiftGain(t)})
|
||||||
</button>
|
</button>
|
||||||
@@ -67,7 +53,7 @@ export default function DiplomacyPanel() {
|
|||||||
className="btn btn-sm"
|
className="btn btn-sm"
|
||||||
disabled={npc.relation < 25 || married}
|
disabled={npc.relation < 25 || married}
|
||||||
title={npc.relation < 25 ? `需关系≥25(当前${npc.relation})` : married ? '一姓已结亲' : '关系≥25方可联姻'}
|
title={npc.relation < 25 ? `需关系≥25(当前${npc.relation})` : married ? '一姓已结亲' : '关系≥25方可联姻'}
|
||||||
onClick={() => { marryNpcFamily(w, npc.id); bump() }}
|
onClick={() => { marryNpcFamily(world, npc.id); bump() }}
|
||||||
>
|
>
|
||||||
{married ? '已联姻' : '联姻'}
|
{married ? '已联姻' : '联姻'}
|
||||||
</button>
|
</button>
|
||||||
@@ -76,7 +62,7 @@ export default function DiplomacyPanel() {
|
|||||||
title="关系-20,可能招致劫掠;每年一次"
|
title="关系-20,可能招致劫掠;每年一次"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (window.confirm(`向${npc.name}寻衅?关系将下降 20,可能招致劫掠。`)) {
|
if (window.confirm(`向${npc.name}寻衅?关系将下降 20,可能招致劫掠。`)) {
|
||||||
w.tauntNpc(npc.id)
|
world.tauntNpc(npc.id)
|
||||||
bump()
|
bump()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -86,9 +72,9 @@ export default function DiplomacyPanel() {
|
|||||||
{npc.relation < -30 && (
|
{npc.relation < -30 && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm"
|
className="btn btn-sm"
|
||||||
disabled={w.state.family.stones < 200}
|
disabled={world.state.family.stones < 200}
|
||||||
title={w.state.family.stones < 200 ? '需灵石 200' : '与对方结下和约'}
|
title={world.state.family.stones < 200 ? '需灵石 200' : '与对方结下和约'}
|
||||||
onClick={() => { makePeace(w, npc.id); bump() }}
|
onClick={() => { makePeace(world, npc.id); bump() }}
|
||||||
>
|
>
|
||||||
议和
|
议和
|
||||||
</button>
|
</button>
|
||||||
@@ -96,7 +82,27 @@ export default function DiplomacyPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</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 className="dim2" style={{ marginTop: 12 }}>
|
||||||
示好请用灵石铺路;寻衅会立刻恶化关系,且可能招致劫掠或围庄。
|
示好请用灵石铺路;寻衅会立刻恶化关系,且可能招致劫掠或围庄。
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useGameStore } from '../store'
|
import { useGameStore } from '../store'
|
||||||
import { useMemo } from 'react'
|
import { useMemo, memo } from 'react'
|
||||||
import { npcById } from '../../game/data/npcs'
|
import { npcById } from '../../game/data/npcs'
|
||||||
import { MAJOR_NAMES } from '../../game/data/realms'
|
import { MAJOR_NAMES } from '../../game/data/realms'
|
||||||
import type { WorldSimState } from '../../game/engine/sim/worldsim-data'
|
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 { combatPowerOf } from '../../game/engine/runtime/Systems/combat'
|
||||||
import { sellItem, buyItem, marketPrice } from '../../game/engine/sim/Market'
|
import { sellItem, buyItem, marketPrice } from '../../game/engine/sim/Market'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import type { World } from '../../game/engine/runtime/World'
|
||||||
|
|
||||||
const RES_LABEL: Record<string, string> = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核' }
|
const RES_LABEL: Record<string, string> = { lingcao: '灵草', lingkuang: '灵矿', beastcore: '兽核' }
|
||||||
const REALM_IDX = ['mortal', 'qi', 'foundation', 'core', 'nascent', 'spirit'] as const
|
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() {
|
export default function WorldPanel() {
|
||||||
const world = useGameStore((s) => s.world)
|
const world = useGameStore((s) => s.world)
|
||||||
const bump = useGameStore((s) => s.bump)
|
const bump = useGameStore((s) => s.bump)
|
||||||
@@ -123,40 +166,7 @@ export default function WorldPanel() {
|
|||||||
<div>
|
<div>
|
||||||
<h4 className="dim" style={{ margin: '4px 0 6px' }}>天下快讯</h4>
|
<h4 className="dim" style={{ margin: '4px 0 6px' }}>天下快讯</h4>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 380, overflowY: 'auto' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, maxHeight: 380, overflowY: 'auto' }}>
|
||||||
{news.length === 0 && <div className="dim2">风平浪静,尚无消息。</div>}
|
<NewsList news={news} world={w} bump={bump} />
|
||||||
{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>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user