fix(v2): v1-faithful character window + improved inventory/radar
Character Window — now matches v1 exactly: - Navy blue background (#000022) with gold/bronze borders (#af7a30) - Two side-by-side 320px tab containers - Left tabs: Attributes (vital bars with gold borders + attribute table with green/blue cell backgrounds + vitals base + skill credits) | Skills (specialized=purple gradient, trained=teal gradient, grouped and sorted) | Titles - Right tabs: Augmentations (with auras section) | Ratings | Other (allegiance with followers) - Active tab: green tint background with gold top/side borders - Header: large name + level (gold, right-floated) + race/gender - XP grid: total, unassigned, luminance earned/total, deaths - Live vital bars from WebSocket vitals data - Augmentation/aura/rating property ID maps from v1 Radar — passes full radarData message (not just objects array) so canvas can render map background + entity positions properly WindowRenderer — passes live vitals to CharacterWindow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cf078b7765
commit
3cb8768dc1
5 changed files with 310 additions and 254 deletions
|
|
@ -2,171 +2,226 @@ import React, { useEffect, useState } from 'react';
|
|||
import { DraggableWindow } from './DraggableWindow';
|
||||
import { apiFetch } from '../../api/client';
|
||||
|
||||
interface Props { id: string; charName: string; zIndex: number; }
|
||||
interface Props { id: string; charName: string; zIndex: number; vitals?: any; }
|
||||
|
||||
export const CharacterWindow: React.FC<Props> = ({ id, charName, zIndex }) => {
|
||||
// Property ID maps matching v1's TS_AUGMENTATIONS, TS_AURAS, TS_RATINGS, etc.
|
||||
const TS_AUGMENTATIONS: Record<number, string> = {
|
||||
369:'Blade Turner',370:'Arrow Turner',371:'Mace Turner',
|
||||
372:'Caustic Enhancement',373:'Fiery Enhancement',374:'Icy Enhancement',375:'Lightning Enhancement',
|
||||
376:'Critical Protection',377:'Frenzy',362:'Iron Skin',363:'Eye of the Remorseless',364:'Hand of the Remorseless',
|
||||
365:'Ciandra\'s Essence',366:'Yoshi\'s Essence',367:'Jibril\'s Essence',368:'Celdiseth\'s Essence',
|
||||
};
|
||||
const TS_AURAS: Record<number, string> = { 378:'Valor',379:'Protection',380:'Glory',381:'Temperance',382:'Aetheric Vision',383:'Mana Flow',384:'Mana Infusion',385:'Purity',386:'Craftsman',387:'Specialization',388:'World' };
|
||||
const TS_RATINGS: Record<number, string> = { 354:'Damage Rating',355:'Damage Resist Rating',356:'Crit Rating',357:'Crit Resist Rating',358:'Crit Damage Rating',359:'Crit Damage Resist Rating',360:'Heal Boost Rating',361:'Vitality Rating' };
|
||||
|
||||
const gold = '#af7a30';
|
||||
const navy = '#000022';
|
||||
|
||||
export const CharacterWindow: React.FC<Props> = ({ id, charName, zIndex, vitals }) => {
|
||||
const [data, setData] = useState<any>(null);
|
||||
const [tab, setTab] = useState<'attr' | 'skills' | 'titles'>('attr');
|
||||
const [rightTab, setRightTab] = useState<'augs' | 'ratings' | 'other'>('augs');
|
||||
const [leftTab, setLeftTab] = useState(0);
|
||||
const [rightTab, setRightTab] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
apiFetch<any>(`/character-stats/${encodeURIComponent(charName)}`)
|
||||
.then(d => setData(d)).catch(() => {});
|
||||
apiFetch<any>(`/character-stats/${encodeURIComponent(charName)}`).then(setData).catch(() => {});
|
||||
}, [charName]);
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
<DraggableWindow id={id} title={`Character: ${charName}`} zIndex={zIndex} width={600} height={500}>
|
||||
<div style={{ padding: 20, color: '#666' }}>Loading character data...</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
}
|
||||
|
||||
const sd = data.stats_data || data;
|
||||
const fmt = (n: any) => n != null ? Number(n).toLocaleString() : '\u2014';
|
||||
const sd = data?.stats_data || data || {};
|
||||
const attrs = sd.attributes || {};
|
||||
const skills = sd.skills || {};
|
||||
const vitals = sd.vitals || {};
|
||||
const vit = sd.vitals || {};
|
||||
const titles = sd.titles || [];
|
||||
const properties = sd.properties || {};
|
||||
const props = sd.properties || {};
|
||||
|
||||
const specSkills = Object.entries(skills).filter(([, v]: [string, any]) => v?.training === 'Specialized');
|
||||
const trainedSkills = Object.entries(skills).filter(([, v]: [string, any]) => v?.training === 'Trained');
|
||||
// Group skills
|
||||
const specSkills = Object.entries(skills).filter(([,v]:any) => v?.training === 'Specialized').sort(([a],[b]) => a.localeCompare(b));
|
||||
const trainedSkills = Object.entries(skills).filter(([,v]:any) => v?.training === 'Trained').sort(([a],[b]) => a.localeCompare(b));
|
||||
|
||||
// Augmentations
|
||||
const augs = Object.entries(props).filter(([id,v]) => TS_AUGMENTATIONS[parseInt(id)] && Number(v) > 0).map(([id,v]) => ({ name: TS_AUGMENTATIONS[parseInt(id)], uses: Number(v) }));
|
||||
const auras = Object.entries(props).filter(([id,v]) => TS_AURAS[parseInt(id)] && Number(v) > 0).map(([id,v]) => ({ name: TS_AURAS[parseInt(id)], uses: Number(v) }));
|
||||
const ratings = Object.entries(props).filter(([id,v]) => TS_RATINGS[parseInt(id)] && Number(v) > 0).map(([id,v]) => ({ name: TS_RATINGS[parseInt(id)], value: Number(v) }));
|
||||
|
||||
const tabStyle = (active: boolean): React.CSSProperties => ({
|
||||
padding: '5px 8px', fontSize: 12, fontWeight: 'bold', color: '#fff', cursor: 'pointer', userSelect: 'none',
|
||||
borderTop: `2px solid ${active ? gold : navy}`, borderLeft: `2px solid ${active ? gold : navy}`, borderRight: `2px solid ${active ? gold : navy}`,
|
||||
background: active ? 'rgba(0,100,0,0.4)' : 'transparent',
|
||||
});
|
||||
const boxStyle: React.CSSProperties = { background: '#000', border: `2px solid ${gold}`, maxHeight: 400, overflowY: 'auto', overflowX: 'hidden' };
|
||||
const colNameStyle: React.CSSProperties = { background: '#222', fontWeight: 'bold', fontSize: 12, padding: '2px 6px' };
|
||||
const cellL: React.CSSProperties = { padding: '2px 6px', background: 'rgba(0,100,0,0.4)', whiteSpace: 'nowrap' };
|
||||
const cellR: React.CSSProperties = { padding: '2px 6px', background: 'rgba(0,0,100,0.4)', textAlign: 'right', whiteSpace: 'nowrap' };
|
||||
const cellCreation: React.CSSProperties = { padding: '2px 6px', color: '#ccc' };
|
||||
|
||||
return (
|
||||
<DraggableWindow id={id} title={`Character: ${charName}`} zIndex={zIndex} width={620} height={520}>
|
||||
<div style={{ flex: 1, overflowY: 'auto', fontSize: '0.75rem', color: '#ccc' }}>
|
||||
{/* Header: Level + Race + XP */}
|
||||
<div style={{ padding: '8px 10px', borderBottom: '1px solid #333', display: 'flex', flexWrap: 'wrap', gap: '12px', fontSize: '0.78rem' }}>
|
||||
{data.level && <span><strong>Lv {data.level}</strong></span>}
|
||||
{data.race && <span>{data.race}</span>}
|
||||
{data.gender && <span>{data.gender}</span>}
|
||||
{data.total_xp != null && <span>XP: {Number(data.total_xp).toLocaleString()}</span>}
|
||||
{data.unassigned_xp != null && <span>Unasgn: {Number(data.unassigned_xp).toLocaleString()}</span>}
|
||||
{data.luminance_earned != null && <span>Lum: {Number(data.luminance_earned).toLocaleString()}</span>}
|
||||
{data.deaths != null && <span>Deaths: {data.deaths}</span>}
|
||||
{sd.skill_credits != null && <span>Skill Credits: {sd.skill_credits}</span>}
|
||||
<DraggableWindow id={id} title={`Character: ${charName}`} zIndex={zIndex} width={740} height={600}>
|
||||
<div style={{ background: navy, color: '#fff', font: '14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif', overflowY: 'auto', padding: '10px 15px 15px', flex: 1 }}>
|
||||
{/* Header */}
|
||||
<div style={{ marginBottom: 10 }}>
|
||||
<h1 style={{ margin: '0 0 2px', fontSize: 28, fontWeight: 'bold' }}>
|
||||
{charName}
|
||||
<span style={{ fontSize: '200%', color: '#fff27f', float: 'right' }}>{data?.level || ''}</span>
|
||||
</h1>
|
||||
<div style={{ fontSize: '85%', color: 'gold' }}>
|
||||
{[data?.gender, data?.race].filter(Boolean).join(' ') || 'Awaiting character data...'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', minHeight: 350 }}>
|
||||
{/* Left panel */}
|
||||
<div style={{ flex: 1, borderRight: '1px solid #333' }}>
|
||||
<div style={{ display: 'flex', gap: 2, padding: '4px 6px', borderBottom: '1px solid #333' }}>
|
||||
{(['attr', 'skills', 'titles'] as const).map(t => (
|
||||
<button key={t} className={`ml-stats-range-btn ${tab === t ? 'active' : ''}`}
|
||||
onClick={() => setTab(t)} style={{ flex: 1 }}>
|
||||
{t === 'attr' ? 'Attributes' : t === 'skills' ? 'Skills' : 'Titles'}
|
||||
</button>
|
||||
{/* XP / Luminance */}
|
||||
<div style={{ fontSize: '85%', margin: '6px 0 10px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 20px' }}>
|
||||
<div>Total XP: {fmt(data?.total_xp)}</div>
|
||||
<div style={{ textAlign: 'right' }}>Unassigned XP: {fmt(data?.unassigned_xp)}</div>
|
||||
<div>Luminance: {data?.luminance_earned != null ? `${fmt(data.luminance_earned)} / ${fmt(data.luminance_total)}` : '\u2014'}</div>
|
||||
<div style={{ textAlign: 'right' }}>Deaths: {fmt(data?.deaths)}</div>
|
||||
</div>
|
||||
|
||||
{/* Tab row: two side-by-side containers */}
|
||||
<div style={{ display: 'flex', gap: 13, flexWrap: 'wrap' }}>
|
||||
{/* Left tabs */}
|
||||
<div style={{ width: 320 }}>
|
||||
<div style={{ height: 30, display: 'flex' }}>
|
||||
{['Attributes', 'Skills', 'Titles'].map((t, i) => (
|
||||
<div key={t} style={tabStyle(leftTab === i)} onClick={() => setLeftTab(i)}>{t}</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div style={{ padding: 8 }}>
|
||||
{tab === 'attr' && (
|
||||
<div style={boxStyle}>
|
||||
{leftTab === 0 && (
|
||||
<>
|
||||
{/* Vital bars */}
|
||||
{Object.entries(vitals).map(([k, v]: [string, any]) => (
|
||||
<div key={k} style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
|
||||
<span style={{ width: 55, color: '#888', fontSize: '0.7rem' }}>{k}</span>
|
||||
<div style={{ flex: 1, height: 6, background: '#222', borderRadius: 3, overflow: 'hidden' }}>
|
||||
<div style={{ width: '100%', height: '100%', background: k === 'health' ? '#c44' : k === 'stamina' ? '#ca0' : '#48f', borderRadius: 3 }} />
|
||||
{/* Vitals bars */}
|
||||
<div style={{ padding: '6px 8px', display: 'flex', flexDirection: 'column', gap: 8, borderBottom: `2px solid ${gold}` }}>
|
||||
{[
|
||||
{ label: 'Health', pct: vitals?.health_percentage ?? 0, cur: vitals?.health_current, max: vitals?.health_max, bg: '#cc3333' },
|
||||
{ label: 'Stamina', pct: vitals?.stamina_percentage ?? 0, cur: vitals?.stamina_current, max: vitals?.stamina_max, bg: '#ccaa33' },
|
||||
{ label: 'Mana', pct: vitals?.mana_percentage ?? 0, cur: vitals?.mana_current, max: vitals?.mana_max, bg: '#3366cc' },
|
||||
].map(v => (
|
||||
<div key={v.label} style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ width: 55, fontSize: 12, color: '#ccc' }}>{v.label}</span>
|
||||
<div style={{ flex: 1, height: 14, overflow: 'hidden', position: 'relative', border: `1px solid ${gold}` }}>
|
||||
<div style={{ height: '100%', width: `${v.pct}%`, background: v.bg, transition: 'width 0.5s ease' }} />
|
||||
</div>
|
||||
<span style={{ width: 80, textAlign: 'right', fontSize: 12, color: '#ccc' }}>{v.cur ?? '\u2014'} / {v.max ?? '\u2014'}</span>
|
||||
</div>
|
||||
<span style={{ fontSize: '0.68rem', color: '#aaa', width: 40, textAlign: 'right' }}>{v?.base ?? v}</span>
|
||||
</div>
|
||||
))}
|
||||
{/* Attributes table */}
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<div style={{ fontWeight: 600, color: '#6aadff', marginBottom: 4, fontSize: '0.72rem' }}>Attributes</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1px 16px' }}>
|
||||
{Object.entries(attrs).map(([k, v]: [string, any]) => (
|
||||
<div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '1px 0' }}>
|
||||
<span style={{ color: '#888', textTransform: 'capitalize' }}>{k}</span>
|
||||
<span>{v?.base ?? v} {v?.creation != null ? `(${v.creation})` : ''}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Attributes table */}
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<thead><tr><td style={colNameStyle}>Attribute</td><td style={colNameStyle}>Creation</td><td style={colNameStyle}>Base</td></tr></thead>
|
||||
<tbody>
|
||||
{['strength','endurance','coordination','quickness','focus','self'].map(a => (
|
||||
<tr key={a}><td style={cellL}>{a.charAt(0).toUpperCase() + a.slice(1)}</td><td style={cellCreation}>{attrs[a]?.creation ?? '\u2014'}</td><td style={cellR}>{attrs[a]?.base ?? '\u2014'}</td></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{/* Vitals base table */}
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<thead><tr><td style={colNameStyle}>Vital</td><td style={colNameStyle}>Base</td></tr></thead>
|
||||
<tbody>
|
||||
{['health','stamina','mana'].map(v => (
|
||||
<tr key={v}><td style={cellL}>{v.charAt(0).toUpperCase() + v.slice(1)}</td><td style={cellR}>{vit[v]?.base ?? '\u2014'}</td></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<tbody><tr><td style={cellL}>Skill Credits</td><td style={cellR}>{fmt(sd.skill_credits)}</td></tr></tbody>
|
||||
</table>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tab === 'skills' && (
|
||||
<>
|
||||
{specSkills.length > 0 && (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<div style={{ fontWeight: 600, color: '#6aadff', marginBottom: 3, fontSize: '0.7rem' }}>Specialized</div>
|
||||
{specSkills.sort(([a], [b]) => a.localeCompare(b)).map(([k, v]: [string, any]) => (
|
||||
<div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '1px 0' }}>
|
||||
<span style={{ color: '#ccc' }}>{k}</span>
|
||||
<span style={{ color: '#8f8' }}>{v?.base ?? v}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{trainedSkills.length > 0 && (
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, color: '#888', marginBottom: 3, fontSize: '0.7rem' }}>Trained</div>
|
||||
{trainedSkills.sort(([a], [b]) => a.localeCompare(b)).map(([k, v]: [string, any]) => (
|
||||
<div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '1px 0' }}>
|
||||
<span style={{ color: '#999' }}>{k}</span>
|
||||
<span>{v?.base ?? v}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{specSkills.length === 0 && trainedSkills.length === 0 && (
|
||||
<div style={{ color: '#555' }}>No skill data available</div>
|
||||
)}
|
||||
</>
|
||||
{leftTab === 1 && (
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<thead><tr><td style={colNameStyle}>Skill</td><td style={colNameStyle}>Level</td></tr></thead>
|
||||
<tbody>
|
||||
{specSkills.map(([k, v]: any) => (
|
||||
<tr key={k}><td style={{ padding: '2px 6px', background: 'linear-gradient(to right, #392067, #392067, black)' }}>{k.replace(/_/g,' ').replace(/\b\w/g, (c:string) => c.toUpperCase())}</td>
|
||||
<td style={{ ...cellR, background: 'linear-gradient(to right, #392067, #392067, black)' }}>{v.base}</td></tr>
|
||||
))}
|
||||
{trainedSkills.map(([k, v]: any) => (
|
||||
<tr key={k}><td style={{ padding: '2px 6px', background: 'linear-gradient(to right, #0f3c3e, #0f3c3e, black)' }}>{k.replace(/_/g,' ').replace(/\b\w/g, (c:string) => c.toUpperCase())}</td>
|
||||
<td style={{ ...cellR, background: 'linear-gradient(to right, #0f3c3e, #0f3c3e, black)' }}>{v.base}</td></tr>
|
||||
))}
|
||||
{specSkills.length === 0 && trainedSkills.length === 0 && <tr><td colSpan={2} style={{ padding: 10, color: '#666', fontStyle: 'italic', textAlign: 'center' }}>No skill data</td></tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
|
||||
{tab === 'titles' && (
|
||||
titles.length > 0 ? (
|
||||
<ul style={{ paddingLeft: 16, margin: 0 }}>
|
||||
{titles.map((t: string, i: number) => <li key={i} style={{ padding: '1px 0' }}>{t}</li>)}
|
||||
</ul>
|
||||
) : <div style={{ color: '#555' }}>No titles</div>
|
||||
{leftTab === 2 && (
|
||||
<div style={{ padding: '6px 10px', fontSize: 13 }}>
|
||||
{titles.length > 0 ? titles.map((t: string, i: number) => <div key={i} style={{ padding: '1px 0' }}>{t}</div>) :
|
||||
<div style={{ color: '#666', fontStyle: 'italic', textAlign: 'center', padding: 10 }}>No titles</div>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right panel */}
|
||||
<div style={{ width: 220 }}>
|
||||
<div style={{ display: 'flex', gap: 2, padding: '4px 4px', borderBottom: '1px solid #333' }}>
|
||||
{(['augs', 'ratings', 'other'] as const).map(t => (
|
||||
<button key={t} className={`ml-stats-range-btn ${rightTab === t ? 'active' : ''}`}
|
||||
onClick={() => setRightTab(t)} style={{ flex: 1, fontSize: '0.6rem' }}>
|
||||
{t === 'augs' ? 'Augs' : t === 'ratings' ? 'Ratings' : 'Other'}
|
||||
</button>
|
||||
{/* Right tabs */}
|
||||
<div style={{ width: 320 }}>
|
||||
<div style={{ height: 30, display: 'flex' }}>
|
||||
{['Augmentations', 'Ratings', 'Other'].map((t, i) => (
|
||||
<div key={t} style={tabStyle(rightTab === i)} onClick={() => setRightTab(i)}>{t}</div>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ padding: 6, fontSize: '0.7rem' }}>
|
||||
{rightTab === 'augs' && (
|
||||
Object.keys(properties).length > 0 ? (
|
||||
Object.entries(properties).slice(0, 20).map(([k, v]) => (
|
||||
<div key={k} style={{ display: 'flex', justifyContent: 'space-between', padding: '1px 0' }}>
|
||||
<span style={{ color: '#888' }}>{k}</span>
|
||||
<span>{String(v)}</span>
|
||||
</div>
|
||||
))
|
||||
) : <div style={{ color: '#555' }}>No augmentation data</div>
|
||||
<div style={boxStyle}>
|
||||
{rightTab === 0 && (
|
||||
augs.length || auras.length ? (
|
||||
<>
|
||||
{augs.length > 0 && (
|
||||
<><div style={{ background: '#222', padding: '4px 8px', fontWeight: 'bold', fontSize: 13, borderBottom: `1px solid ${gold}` }}>Augmentations</div>
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<thead><tr><td style={colNameStyle}>Name</td><td style={colNameStyle}>Uses</td></tr></thead>
|
||||
<tbody>{augs.map(a => <tr key={a.name}><td style={{ padding: '2px 6px' }}>{a.name}</td><td style={{ padding: '2px 6px', textAlign: 'right' }}>{a.uses}</td></tr>)}</tbody>
|
||||
</table></>
|
||||
)}
|
||||
{auras.length > 0 && (
|
||||
<><div style={{ background: '#222', padding: '4px 8px', fontWeight: 'bold', fontSize: 13, borderBottom: `1px solid ${gold}` }}>Auras</div>
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<thead><tr><td style={colNameStyle}>Name</td><td style={colNameStyle}>Uses</td></tr></thead>
|
||||
<tbody>{auras.map(a => <tr key={a.name}><td style={{ padding: '2px 6px' }}>{a.name}</td><td style={{ padding: '2px 6px', textAlign: 'right' }}>{a.uses}</td></tr>)}</tbody>
|
||||
</table></>
|
||||
)}
|
||||
</>
|
||||
) : <div style={{ color: '#666', fontStyle: 'italic', textAlign: 'center', padding: 10 }}>No augmentation data</div>
|
||||
)}
|
||||
{rightTab === 'ratings' && (
|
||||
<div style={{ color: '#555' }}>Rating data will appear here from character_stats events</div>
|
||||
{rightTab === 1 && (
|
||||
ratings.length > 0 ? (
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<thead><tr><td style={colNameStyle}>Rating</td><td style={colNameStyle}>Value</td></tr></thead>
|
||||
<tbody>{ratings.map(r => <tr key={r.name}><td style={{ padding: '2px 6px' }}>{r.name}</td><td style={{ padding: '2px 6px', textAlign: 'right' }}>{r.value}</td></tr>)}</tbody>
|
||||
</table>
|
||||
) : <div style={{ color: '#666', fontStyle: 'italic', textAlign: 'center', padding: 10 }}>No rating data</div>
|
||||
)}
|
||||
{rightTab === 'other' && (
|
||||
<>
|
||||
{data.allegiance && (
|
||||
<div>
|
||||
<div style={{ fontWeight: 600, color: '#6aadff', marginBottom: 3 }}>Allegiance</div>
|
||||
{data.allegiance.name && <div>Name: {data.allegiance.name}</div>}
|
||||
{data.allegiance.monarch?.name && <div>Monarch: {data.allegiance.monarch.name}</div>}
|
||||
{data.allegiance.patron?.name && <div>Patron: {data.allegiance.patron.name}</div>}
|
||||
{data.allegiance.rank != null && <div>Rank: {data.allegiance.rank}</div>}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
{rightTab === 2 && (
|
||||
<div style={{ padding: 6 }}>
|
||||
{data?.allegiance ? (
|
||||
<><div style={{ background: '#222', padding: '4px 8px', fontWeight: 'bold', fontSize: 13, borderBottom: `1px solid ${gold}` }}>Allegiance</div>
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<tbody>
|
||||
{data.allegiance.name && <tr><td style={{ padding: '2px 6px', color: '#ccc', width: 100 }}>Name</td><td style={{ padding: '2px 6px' }}>{data.allegiance.name}</td></tr>}
|
||||
{data.allegiance.monarch?.name && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Monarch</td><td style={{ padding: '2px 6px' }}>{data.allegiance.monarch.name}</td></tr>}
|
||||
{data.allegiance.patron?.name && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Patron</td><td style={{ padding: '2px 6px' }}>{data.allegiance.patron.name}</td></tr>}
|
||||
{data.allegiance.rank != null && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Rank</td><td style={{ padding: '2px 6px' }}>{data.allegiance.rank}</td></tr>}
|
||||
</tbody>
|
||||
</table></>
|
||||
) : <div style={{ color: '#666', fontStyle: 'italic', textAlign: 'center', padding: 10 }}>No additional data</div>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Allegiance section */}
|
||||
{data?.allegiance && (
|
||||
<div style={{ marginTop: 5, border: `2px solid ${gold}`, background: '#000' }}>
|
||||
<div style={{ background: '#222', padding: '4px 8px', fontWeight: 'bold', fontSize: 13, borderBottom: `1px solid ${gold}` }}>Allegiance</div>
|
||||
<table style={{ width: '100%', fontSize: 13, borderCollapse: 'collapse' }}>
|
||||
<tbody>
|
||||
{data.allegiance.name && <tr><td style={{ padding: '2px 6px', color: '#ccc', width: 100 }}>Name</td><td style={{ padding: '2px 6px' }}>{data.allegiance.name}</td></tr>}
|
||||
{data.allegiance.monarch?.name && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Monarch</td><td style={{ padding: '2px 6px' }}>{data.allegiance.monarch.name}</td></tr>}
|
||||
{data.allegiance.patron?.name && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Patron</td><td style={{ padding: '2px 6px' }}>{data.allegiance.patron.name}</td></tr>}
|
||||
{data.allegiance.rank != null && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Rank</td><td style={{ padding: '2px 6px' }}>{data.allegiance.rank}</td></tr>}
|
||||
{data.allegiance.followers != null && <tr><td style={{ padding: '2px 6px', color: '#ccc' }}>Followers</td><td style={{ padding: '2px 6px' }}>{data.allegiance.followers}</td></tr>}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ export const WindowRenderer: React.FC<Props> = ({ characters, chatMessages, near
|
|||
case 'stats':
|
||||
return <StatsWindow key={w.id} id={w.id} charName={charName} zIndex={w.zIndex} />;
|
||||
case 'char':
|
||||
return <CharacterWindow key={w.id} id={w.id} charName={charName} zIndex={w.zIndex} />;
|
||||
return <CharacterWindow key={w.id} id={w.id} charName={charName} zIndex={w.zIndex}
|
||||
vitals={characters.get(charName)?.vitals ?? undefined} />;
|
||||
case 'inv':
|
||||
return <InventoryWindow key={w.id} id={w.id} charName={charName} zIndex={w.zIndex} />;
|
||||
case 'radar':
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue