You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
import { type CSSProperties } from 'react';
|
|
import type { ActiveBuff } from '../game/types';
|
|
import { BUFF_META } from './buffMeta';
|
|
|
|
interface BuffStatusStripProps {
|
|
buffs: ActiveBuff[];
|
|
nowMs: number;
|
|
}
|
|
|
|
const rowStyle: CSSProperties = {
|
|
display: 'flex',
|
|
gap: 4,
|
|
flexWrap: 'wrap',
|
|
alignItems: 'center',
|
|
width: '100%',
|
|
maxWidth: '100%',
|
|
minWidth: 0,
|
|
pointerEvents: 'none',
|
|
};
|
|
|
|
const chipStyle: CSSProperties = {
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
gap: 3,
|
|
padding: '2px 6px',
|
|
borderRadius: 6,
|
|
fontSize: 10,
|
|
fontWeight: 700,
|
|
color: '#fff',
|
|
textShadow: '0 1px 2px rgba(0,0,0,0.85)',
|
|
flexShrink: 0,
|
|
};
|
|
|
|
function liveRemainingMs(buff: ActiveBuff, nowMs: number): number {
|
|
if (buff.expiresAtMs != null) {
|
|
return Math.max(0, buff.expiresAtMs - nowMs);
|
|
}
|
|
return Math.max(0, buff.remainingMs);
|
|
}
|
|
|
|
/** Read-only active buff indicators (no activation controls). */
|
|
export function BuffStatusStrip({ buffs, nowMs }: BuffStatusStripProps) {
|
|
const live = buffs
|
|
.map((b) => ({ ...b, remainingMs: liveRemainingMs(b, nowMs) }))
|
|
.filter((b) => b.remainingMs > 0);
|
|
|
|
if (live.length === 0) return null;
|
|
|
|
return (
|
|
<div style={rowStyle} role="status" aria-label="Active buffs">
|
|
{live.map((buff) => {
|
|
const meta = BUFF_META[buff.type];
|
|
if (!meta) return null;
|
|
const sec = Math.ceil(buff.remainingMs / 1000);
|
|
const justApplied =
|
|
buff.durationMs > 0 && buff.remainingMs / buff.durationMs > 0.8;
|
|
const style: CSSProperties = {
|
|
...chipStyle,
|
|
backgroundColor: `${meta.color}44`,
|
|
border: `1px solid ${meta.color}aa`,
|
|
animation: justApplied ? 'buff-status-pulse 0.55s ease-out' : undefined,
|
|
};
|
|
return (
|
|
<span key={buff.type} style={style} title={`${meta.label} — ${meta.desc}`}>
|
|
<span style={{ fontSize: 14, lineHeight: 1 }}>{meta.icon}</span>
|
|
<span>{sec}s</span>
|
|
</span>
|
|
);
|
|
})}
|
|
<style>{`
|
|
@keyframes buff-status-pulse {
|
|
0% { transform: scale(1); opacity: 0.75; }
|
|
50% { transform: scale(1.08); opacity: 1; }
|
|
100% { transform: scale(1); opacity: 1; }
|
|
}
|
|
`}</style>
|
|
</div>
|
|
);
|
|
}
|