first commit

This commit is contained in:
2026-07-16 17:37:25 +02:00
commit 61054537c5
40 changed files with 8211 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
import type { WorldPlayer } from "../types";
type Props = {
width: number;
height: number;
players: WorldPlayer[];
portal: { x: number; y: number };
selfId: string;
onTileClick: (x: number, y: number) => void;
onPortalClick: () => void;
};
export default function MapView({
width,
height,
players,
portal,
selfId,
onTileClick,
onPortalClick,
}: Props) {
const cells = [];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const isPortal = x === portal.x && y === portal.y;
const here = players.filter((p) => p.x === x && p.y === y);
const terrain =
(x + y) % 7 === 0 ? "mire" : (x * 3 + y) % 11 === 0 ? "bone" : "soil";
cells.push(
<button
key={`${x}-${y}`}
type="button"
className={`tile ${terrain} ${isPortal ? "portal" : ""}`}
style={{ gridColumn: x + 1, gridRow: y + 1 }}
onClick={() => {
if (isPortal) onPortalClick();
else onTileClick(x, y);
}}
title={isPortal ? "Dungeon Portal" : `(${x}, ${y})`}
>
{isPortal && <span className="portal-glyph" />}
{here.map((p) => (
<span
key={p.characterId}
className={`avatar ${p.specialty.toLowerCase()} ${
p.characterId === selfId ? "self" : ""
}`}
title={p.name}
>
{p.name.slice(0, 1).toUpperCase()}
</span>
))}
</button>
);
}
}
return (
<div className="map-shell">
<div
className="map-grid"
style={{
gridTemplateColumns: `repeat(${width}, var(--tile))`,
gridTemplateRows: `repeat(${height}, var(--tile))`,
}}
>
{cells}
</div>
</div>
);
}