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

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>
);
}