v0.1.5: 人物志(志向/渡劫/列传/契合/回声)
- 志向系统:七向(求道/镇族/承宗/商略/兵略/耕读/云游)成年礼自动定志, 量化加成入修炼/战力/坊市/灵田/添丁/声望,云游减修但际遇更频 - 大境界渡劫:炼气→筑基起,晋升改走劫难事件三选——硬渡/请护法(100灵石)/ 压制来年;护法失败或受牵连;渡劫成败与心性、气血挂勾,高阶有小陨落率 - 灵根本命契合:功法元素×主灵根,契合者修炼+6%/战力+4%,详情页「本命」徽章 - 人物列传:自动列传生成器(生平/修行脉络/历险/姻缘/子嗣/大比/墓志铭), 成员详情小传分页 + 春秋原列传长卷 - 四邻回声:每两年一桩——友好赠礼/敌视暗桩/中立传闻三态动态事件 - 修复:回声事件 npcId 解析错误、事件优先级(惯例>传闻) - 测试 176→197(志向加成/契合/渡劫三轨/压制通道/列传结构/回声三态)
This commit is contained in:
@@ -8,6 +8,24 @@ import { ITEMS, ARTIFACT_POWER } from '../../game/data/items'
|
||||
import { combatPowerOf } from '../../game/engine/systems/combat'
|
||||
import { lifespanOf } from '../../game/engine/systems/lifecycle'
|
||||
import { POSTS, POST_ORDER } from '../../game/data/posts'
|
||||
import { buildBiography } from '../../game/core/biography'
|
||||
|
||||
function aspName(c: Character): string {
|
||||
const map: Record<string, string> = {
|
||||
dao: '志向·求道', zhen: '志向·镇族', cheng: '志向·承宗', shang: '志向·商略', bing: '志向·兵略', geng: '志向·耕读', yun: '志向·云游'
|
||||
}
|
||||
return map[c.aspiration ?? ''] ?? '志向·未定'
|
||||
}
|
||||
|
||||
function fitTag(c: Character): string | null {
|
||||
const t = TECHNIQUES.find((x) => x.id === c.techniqueId)
|
||||
if (!t) return null
|
||||
return t.element === c.roots.primary ? `本命·${t.element}契合` : null
|
||||
}
|
||||
|
||||
function deadish(c: Character): boolean {
|
||||
return !c.alive
|
||||
}
|
||||
|
||||
function pillCount(s: GameState, id: string): number {
|
||||
return s.family.inventory[id] ?? 0
|
||||
@@ -232,6 +250,28 @@ export function MemberModal({ member }: { member: Character }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{member.alive && (
|
||||
<div className="mm-sec">
|
||||
<div className="dim" style={{ marginBottom: 4 }}>志向与契合</div>
|
||||
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
|
||||
<span className="tag">{aspName(member)}</span>
|
||||
{fitTag(member) && <span className="tag gold-t">{fitTag(member)}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mm-sec">
|
||||
<div className="dim" style={{ marginBottom: 6 }}>小传(春秋原记录)</div>
|
||||
<div className="bio-box">
|
||||
{buildBiography(w.state, member.id).map((l, i) => (
|
||||
<div key={i} className={deadish(member) && l.label === '墓志' ? 'gold' : ''}>
|
||||
{l.label && <b className="bio-label">{l.label} · </b>}
|
||||
{l.text}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{member.alive && w.canMarry(member.id) && (
|
||||
<div className="mm-sec">
|
||||
<div className="dim" style={{ marginBottom: 6 }}>指婚(寻配族内共居者,远亲无碍)</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useGameStore } from '../store'
|
||||
import { computeLegacy, resolveLegacy } from '../../game/core/legacy'
|
||||
import { resolveLegacy as doesIt } from '../../game/core/legacy'
|
||||
import { ResolveModal } from '../components/ResolveModal'
|
||||
import { buildBiography } from '../../game/core/biography'
|
||||
|
||||
void doesIt
|
||||
|
||||
@@ -11,7 +12,7 @@ export default function LegacyPanel() {
|
||||
const revision = useGameStore((s) => s.revision)
|
||||
void revision
|
||||
const [showResolve, setShowResolve] = useState(false)
|
||||
const [view, setView] = useState<'dims' | 'chronicle' | 'report'>('dims')
|
||||
const [view, setView] = useState<'dims' | 'chronicle' | 'report' | 'scroll'>('dims')
|
||||
if (!world) return null
|
||||
const w = world
|
||||
const s = w.state
|
||||
@@ -52,6 +53,7 @@ export default function LegacyPanel() {
|
||||
<button className="btn" onClick={() => setView('dims')}>四维</button>
|
||||
<button className="btn" onClick={() => setView('report')}>年报</button>
|
||||
<button className="btn" onClick={() => setView('chronicle')}>年表</button>
|
||||
<button className="btn" onClick={() => setView('scroll')}>长卷</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -108,6 +110,8 @@ export default function LegacyPanel() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{view === 'scroll' && <ScrollScroll />}
|
||||
|
||||
{view === 'chronicle' && (
|
||||
<div className="card">
|
||||
<div className="card-title">生卒与突破年表</div>
|
||||
@@ -129,6 +133,28 @@ export default function LegacyPanel() {
|
||||
)
|
||||
}
|
||||
|
||||
function ScrollScroll() {
|
||||
const world = useGameStore((s) => s.world)
|
||||
if (!world) return null
|
||||
const mem = Object.values(world.state.members).sort((a, b) => b.generation - a.generation || b.bornYear - a.bornYear)
|
||||
return (
|
||||
<div className="card">
|
||||
<div className="card-title">列传长卷</div>
|
||||
{mem.slice(0, 40).map((c) => {
|
||||
const bio = buildBiography(world.state, c.id)
|
||||
return (
|
||||
<div key={c.id} className="bio-row">
|
||||
<b>{c.name}</b>
|
||||
<span className="dim" style={{ marginLeft: 8 }}>
|
||||
{bio.map((l) => l.text).join(' ').slice(0, 90)}{bio.map((l) => l.text).join(' ').length > 90 ? '……' : ''}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function dimName(k: string): string {
|
||||
const map: Record<string, string> = { renXing: '人兴', daoXing: '道兴', weiMing: '威名', xiangHuo: '香火' }
|
||||
return map[k] ?? k
|
||||
|
||||
@@ -1407,6 +1407,29 @@ body {
|
||||
text-shadow: 0 4px 18px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* ---------- 列传 ---------- */
|
||||
.bio-box {
|
||||
max-height: 240px;
|
||||
overflow-y: auto;
|
||||
background: rgba(20, 15, 9, 0.5);
|
||||
border: 1px solid rgba(120, 98, 55, 0.35);
|
||||
border-radius: 3px;
|
||||
padding: 8px 12px;
|
||||
line-height: 1.8;
|
||||
font-size: 0.9rem;
|
||||
color: #e4d6b4;
|
||||
}
|
||||
.bio-label {
|
||||
color: #cdaa63;
|
||||
font-weight: 400;
|
||||
}
|
||||
.bio-row {
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dashed rgba(120, 98, 55, 0.3);
|
||||
line-height: 1.7;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* ---------- 春秋原 & 结局 ---------- */
|
||||
.legacy-peak {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user