added blackout timer
|
Before Width: | Height: | Size: 377 B After Width: | Height: | Size: 377 B |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 930 B After Width: | Height: | Size: 930 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,46 @@
|
||||
.timer {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 6px;
|
||||
border: 3px solid;
|
||||
background: #000;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.small {
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
gap: 3px;
|
||||
padding: 2px 3px;
|
||||
border-width: 2px;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.small .icon {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
/* Blink during the last second so the return of the board isn't a surprise. */
|
||||
.ending {
|
||||
animation: blink 0.25s steps(2) infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { itemVisual } from '../theme/assets';
|
||||
import styles from './BlackoutTimer.module.css';
|
||||
|
||||
/** Seconds left on a player's blackout, in the corner of their board. */
|
||||
export function BlackoutTimer({ remaining, small = false }: { remaining: number; small?: boolean }) {
|
||||
if (remaining <= 0) return null;
|
||||
const visual = itemVisual('blind');
|
||||
return (
|
||||
<div
|
||||
className={[styles.timer, small ? styles.small : '', remaining < 1 ? styles.ending : ''].join(' ')}
|
||||
style={{ color: visual.color, borderColor: visual.color }}
|
||||
title={visual.name}
|
||||
>
|
||||
<img className={styles.icon} src={visual.icon} alt="" />
|
||||
{remaining.toFixed(1)}s
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
import type { PlayerState } from '../net/types';
|
||||
import type { ItemFlash } from '../net/useGameConnection';
|
||||
import { BlackoutTimer } from './BlackoutTimer';
|
||||
import { BoardCanvas } from './BoardCanvas';
|
||||
import { ItemBar } from './ItemBar';
|
||||
import { ItemFlashOverlay } from './ItemFlashOverlay';
|
||||
@@ -29,6 +30,7 @@ export const MiniBoard = memo(function MiniBoard({ state, width, height, slot, s
|
||||
</div>
|
||||
<BoardCanvas state={state} width={width} height={height} cell={cell} highlighted={selected}>
|
||||
<ItemFlashOverlay flashes={flashes} small />
|
||||
<BlackoutTimer remaining={state.alive ? state.invisibleRemaining : 0} small />
|
||||
</BoardCanvas>
|
||||
<div className={styles.items} style={{ width: width * cell + 6 }}>
|
||||
<ItemBar items={state.inventory} size={Math.max(10, cell)} />
|
||||
|
||||