@scriptonita/chess-football-ui 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/chunk-J2TBQPPC.cjs +85 -0
- package/dist/chunk-J2TBQPPC.cjs.map +1 -0
- package/dist/chunk-YLNDQ4HG.js +82 -0
- package/dist/chunk-YLNDQ4HG.js.map +1 -0
- package/dist/index.cjs +1126 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +140 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +1088 -0
- package/dist/index.js.map +1 -0
- package/dist/store.cjs +16 -0
- package/dist/store.cjs.map +1 -0
- package/dist/store.d.cts +22 -0
- package/dist/store.d.ts +22 -0
- package/dist/store.js +3 -0
- package/dist/store.js.map +1 -0
- package/package.json +59 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkJ2TBQPPC_cjs = require('./chunk-J2TBQPPC.cjs');
|
|
4
|
+
var chessFootballEngine = require('@scriptonita/chess-football-engine');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var framerMotion = require('framer-motion');
|
|
8
|
+
var lucideReact = require('lucide-react');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
var React = require('react');
|
|
11
|
+
|
|
12
|
+
function _interopNamespace(e) {
|
|
13
|
+
if (e && e.__esModule) return e;
|
|
14
|
+
var n = Object.create(null);
|
|
15
|
+
if (e) {
|
|
16
|
+
Object.keys(e).forEach(function (k) {
|
|
17
|
+
if (k !== 'default') {
|
|
18
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
19
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return e[k]; }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
n.default = e;
|
|
27
|
+
return Object.freeze(n);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
31
|
+
|
|
32
|
+
function cn(...inputs) {
|
|
33
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
34
|
+
}
|
|
35
|
+
var PIECE_ICON = {
|
|
36
|
+
king: lucideReact.ChessKing,
|
|
37
|
+
queen: lucideReact.ChessQueen,
|
|
38
|
+
rook: lucideReact.ChessRook,
|
|
39
|
+
bishop: lucideReact.ChessBishop,
|
|
40
|
+
knight: lucideReact.ChessKnight
|
|
41
|
+
};
|
|
42
|
+
var PieceIcon = ({ type }) => {
|
|
43
|
+
const Icon = PIECE_ICON[type] ?? lucideReact.ShieldQuestion;
|
|
44
|
+
const style = {
|
|
45
|
+
width: "clamp(16px, 7.4cqw, 44px)",
|
|
46
|
+
height: "clamp(16px, 7.4cqw, 44px)",
|
|
47
|
+
filter: "drop-shadow(0 1px 1px rgba(0,0,0,0.35))"
|
|
48
|
+
};
|
|
49
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Icon, { style, strokeWidth: 1.75, "aria-hidden": "true" });
|
|
50
|
+
};
|
|
51
|
+
var WHITE_PIECE_STYLE = {
|
|
52
|
+
background: "radial-gradient(circle at 30% 25%, #ffffff 0%, #f4f4f5 55%, #d4d4d8 100%)",
|
|
53
|
+
boxShadow: [
|
|
54
|
+
"inset 0 1.5px 1px rgba(255,255,255,0.95)",
|
|
55
|
+
"inset 0 -2px 3px rgba(0,0,0,0.18)",
|
|
56
|
+
"0 4px 6px rgba(0,0,0,0.45)",
|
|
57
|
+
"0 1px 2px rgba(0,0,0,0.35)"
|
|
58
|
+
].join(", "),
|
|
59
|
+
border: "1px solid rgba(0,0,0,0.18)",
|
|
60
|
+
color: "#09090b"
|
|
61
|
+
};
|
|
62
|
+
var BLACK_PIECE_STYLE = {
|
|
63
|
+
background: "radial-gradient(circle at 30% 25%, #52525b 0%, #1c1c1f 55%, #050507 100%)",
|
|
64
|
+
boxShadow: [
|
|
65
|
+
"inset 0 1.5px 1px rgba(255,255,255,0.22)",
|
|
66
|
+
"inset 0 -2px 3px rgba(0,0,0,0.6)",
|
|
67
|
+
"0 4px 6px rgba(0,0,0,0.55)",
|
|
68
|
+
"0 1px 2px rgba(0,0,0,0.4)"
|
|
69
|
+
].join(", "),
|
|
70
|
+
border: "1px solid rgba(0,0,0,0.55)",
|
|
71
|
+
color: "#fafafa"
|
|
72
|
+
};
|
|
73
|
+
var MOVED_STYLE = {
|
|
74
|
+
filter: "saturate(0.15) brightness(0.75)"
|
|
75
|
+
};
|
|
76
|
+
function GamePiece({ piece, isSelected, hasBall, onClick }) {
|
|
77
|
+
const baseStyle = piece.side === "white" ? WHITE_PIECE_STYLE : BLACK_PIECE_STYLE;
|
|
78
|
+
const style = piece.hasMovedThisTurn ? { ...baseStyle, ...MOVED_STYLE } : baseStyle;
|
|
79
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
80
|
+
"div",
|
|
81
|
+
{
|
|
82
|
+
onClick,
|
|
83
|
+
style,
|
|
84
|
+
className: cn(
|
|
85
|
+
"relative w-[88%] h-[88%] flex items-center justify-center rounded-full cursor-pointer z-10 transition-transform",
|
|
86
|
+
isSelected && "ring-4 ring-primary scale-110 z-30",
|
|
87
|
+
piece.hasMovedThisTurn && "opacity-70"
|
|
88
|
+
),
|
|
89
|
+
children: [
|
|
90
|
+
/* @__PURE__ */ jsxRuntime.jsx(PieceIcon, { type: piece.type }),
|
|
91
|
+
hasBall && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full ring-2 ring-orange-400 pointer-events-none" }),
|
|
92
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full animate-pulse ring-2 ring-yellow-400 ring-offset-2 ring-offset-transparent" })
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
var Football = ({ className }) => {
|
|
98
|
+
const bodyId = React.useId();
|
|
99
|
+
const hiId = `${bodyId}-h`;
|
|
100
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
101
|
+
"svg",
|
|
102
|
+
{
|
|
103
|
+
viewBox: "0 0 100 100",
|
|
104
|
+
className: cn("w-full h-full drop-shadow-md", className),
|
|
105
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
106
|
+
"aria-hidden": "true",
|
|
107
|
+
children: [
|
|
108
|
+
/* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
|
|
109
|
+
/* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: bodyId, cx: "36%", cy: "32%", r: "68%", children: [
|
|
110
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#f0f9ff" }),
|
|
111
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "55%", stopColor: "#bae6fd" }),
|
|
112
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#38bdf8" })
|
|
113
|
+
] }),
|
|
114
|
+
/* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: hiId, cx: "32%", cy: "28%", r: "22%", children: [
|
|
115
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#ffffff", stopOpacity: "0.85" }),
|
|
116
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#ffffff", stopOpacity: "0" })
|
|
117
|
+
] })
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "50", cy: "50", r: "47", fill: `url(#${bodyId})`, stroke: "#18181b", strokeWidth: "1.5" }),
|
|
120
|
+
/* @__PURE__ */ jsxRuntime.jsxs("g", { stroke: "#18181b", strokeWidth: "1.4", strokeLinecap: "round", fill: "none", children: [
|
|
121
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "50", y1: "41", x2: "50", y2: "30" }),
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "41.44", y1: "47.22", x2: "30.97", y2: "43.82" }),
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "44.71", y1: "57.28", x2: "38.24", y2: "66.18" }),
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "55.29", y1: "57.28", x2: "61.76", y2: "66.18" }),
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "58.56", y1: "47.22", x2: "69.03", y2: "43.82" }),
|
|
126
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "50", y1: "15.5", x2: "50", y2: "4.5" }),
|
|
127
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "17.18", y1: "39.34", x2: "6.71", y2: "35.94" }),
|
|
128
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "29.72", y1: "77.91", x2: "23.25", y2: "86.81" }),
|
|
129
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "70.28", y1: "77.91", x2: "76.75", y2: "86.81" }),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "82.82", y1: "39.34", x2: "93.29", y2: "35.94" })
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsxRuntime.jsxs("g", { fill: "#18181b", stroke: "#18181b", strokeWidth: "0.6", strokeLinejoin: "round", children: [
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "50,41 41.44,47.22 44.71,57.28 55.29,57.28 58.56,47.22" }),
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "50,30 57.61,24.47 54.70,15.53 45.30,15.53 42.39,24.47" }),
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "30.97,43.82 28.06,34.88 18.66,34.88 15.75,43.82 23.36,49.35" }),
|
|
136
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "38.24,66.18 29.84,66.18 25.93,75.12 33.54,80.65 41.15,75.12" }),
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "61.76,66.18 58.85,75.12 66.46,80.65 74.07,75.12 71.16,66.18" }),
|
|
138
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "69.03,43.82 76.64,49.35 84.25,43.82 81.34,34.88 71.94,34.88" })
|
|
139
|
+
] }),
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "50", cy: "50", r: "47", fill: `url(#${hiId})`, pointerEvents: "none" })
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
var COLS = 9;
|
|
146
|
+
var ROWS = 12;
|
|
147
|
+
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
148
|
+
function GameBoard({ userSide, showCoordinates = false, keyboardNav = false }) {
|
|
149
|
+
const {
|
|
150
|
+
boardState,
|
|
151
|
+
selectedPieceId,
|
|
152
|
+
setSelectedPieceId,
|
|
153
|
+
interactionMode,
|
|
154
|
+
setInteractionMode,
|
|
155
|
+
movePiece,
|
|
156
|
+
passBall
|
|
157
|
+
} = chunkJ2TBQPPC_cjs.useGameStore();
|
|
158
|
+
const [cursor, setCursor] = React.useState(null);
|
|
159
|
+
const prevBallRef = React.useRef(boardState?.ball);
|
|
160
|
+
const prevBall = prevBallRef.current;
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
prevBallRef.current = boardState?.ball;
|
|
163
|
+
});
|
|
164
|
+
const validMoves = React.useMemo(() => {
|
|
165
|
+
if (!boardState) return [];
|
|
166
|
+
const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
167
|
+
if (sp && interactionMode === "move" && !sp.hasMovedThisTurn) {
|
|
168
|
+
return chessFootballEngine.getValidMoves(sp, boardState);
|
|
169
|
+
}
|
|
170
|
+
return [];
|
|
171
|
+
}, [boardState, selectedPieceId, interactionMode]);
|
|
172
|
+
const validPasses = React.useMemo(() => {
|
|
173
|
+
if (!boardState) return [];
|
|
174
|
+
const sp = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
175
|
+
if (sp && interactionMode === "pass") {
|
|
176
|
+
return chessFootballEngine.getValidPasses(sp, boardState);
|
|
177
|
+
}
|
|
178
|
+
return [];
|
|
179
|
+
}, [boardState, selectedPieceId, interactionMode]);
|
|
180
|
+
if (!boardState) return null;
|
|
181
|
+
const lastMove = boardState.lastMove;
|
|
182
|
+
const ball = boardState.ball;
|
|
183
|
+
const ballMoved = !!prevBall && (prevBall.pos.x !== ball.pos.x || prevBall.pos.y !== ball.pos.y);
|
|
184
|
+
const isPickupOnMove = !!prevBall && !prevBall.holderId && !!ball.holderId && lastMove?.type === "move" && lastMove?.playerId === ball.holderId && ballMoved;
|
|
185
|
+
const PICKUP_DURATION = 0.5;
|
|
186
|
+
const pickupCarrierId = isPickupOnMove ? ball.holderId : null;
|
|
187
|
+
let pickupProportion = 0.5;
|
|
188
|
+
if (isPickupOnMove && prevBall && lastMove?.from) {
|
|
189
|
+
const totalSteps = Math.max(
|
|
190
|
+
Math.abs(lastMove.to.x - lastMove.from.x),
|
|
191
|
+
Math.abs(lastMove.to.y - lastMove.from.y)
|
|
192
|
+
);
|
|
193
|
+
const stepsToBall = Math.max(
|
|
194
|
+
Math.abs(prevBall.pos.x - lastMove.from.x),
|
|
195
|
+
Math.abs(prevBall.pos.y - lastMove.from.y)
|
|
196
|
+
);
|
|
197
|
+
pickupProportion = totalSteps > 0 ? Math.min(0.85, Math.max(0.15, stepsToBall / totalSteps)) : 0.5;
|
|
198
|
+
}
|
|
199
|
+
const handleSquareClick = (x, y) => {
|
|
200
|
+
if (interactionMode === "move" && selectedPieceId) {
|
|
201
|
+
if (validMoves.some((m) => m.x === x && m.y === y)) {
|
|
202
|
+
movePiece(selectedPieceId, { x, y });
|
|
203
|
+
}
|
|
204
|
+
} else if (interactionMode === "pass" && selectedPieceId) {
|
|
205
|
+
if (validPasses.some((p) => p.x === x && p.y === y)) {
|
|
206
|
+
passBall({ x, y });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const handlePieceClick = (pieceId, x, y, e) => {
|
|
211
|
+
e.stopPropagation();
|
|
212
|
+
const pieceAt = boardState.pieces.find((p) => p.id === pieceId);
|
|
213
|
+
if (!pieceAt) return;
|
|
214
|
+
const isValidDest = validMoves.some((m) => m.x === x && m.y === y) || validPasses.some((p) => p.x === x && p.y === y);
|
|
215
|
+
if (isValidDest) {
|
|
216
|
+
handleSquareClick(x, y);
|
|
217
|
+
} else if (boardState.turn === pieceAt.side && userSide === pieceAt.side) {
|
|
218
|
+
if (selectedPieceId === pieceAt.id) {
|
|
219
|
+
if (boardState.ball.holderId === pieceAt.id) {
|
|
220
|
+
setInteractionMode(interactionMode === "move" ? "pass" : "move");
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
setSelectedPieceId(pieceAt.id);
|
|
224
|
+
setInteractionMode("move");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const defaultCursor = () => {
|
|
229
|
+
const sel = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
230
|
+
return sel ? { ...sel.pos } : { ...boardState.ball.pos };
|
|
231
|
+
};
|
|
232
|
+
const activate = (x, y) => {
|
|
233
|
+
const pieceAt = boardState.pieces.find((p) => p.pos.x === x && p.pos.y === y);
|
|
234
|
+
if (pieceAt) {
|
|
235
|
+
handlePieceClick(pieceAt.id, x, y, { stopPropagation() {
|
|
236
|
+
} });
|
|
237
|
+
} else {
|
|
238
|
+
handleSquareClick(x, y);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const handleKeyDown = (e) => {
|
|
242
|
+
if (!keyboardNav) return;
|
|
243
|
+
const step = (dx, dy) => {
|
|
244
|
+
e.preventDefault();
|
|
245
|
+
setCursor((c) => {
|
|
246
|
+
const base = c ?? defaultCursor();
|
|
247
|
+
return { x: clamp(base.x + dx, 0, COLS - 1), y: clamp(base.y + dy, 0, ROWS - 1) };
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
switch (e.key) {
|
|
251
|
+
case "ArrowUp":
|
|
252
|
+
step(0, 1);
|
|
253
|
+
break;
|
|
254
|
+
// visually up = higher rank (y grows toward the top)
|
|
255
|
+
case "ArrowDown":
|
|
256
|
+
step(0, -1);
|
|
257
|
+
break;
|
|
258
|
+
case "ArrowLeft":
|
|
259
|
+
step(-1, 0);
|
|
260
|
+
break;
|
|
261
|
+
case "ArrowRight":
|
|
262
|
+
step(1, 0);
|
|
263
|
+
break;
|
|
264
|
+
case "Enter":
|
|
265
|
+
case " ": {
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
const c = cursor ?? defaultCursor();
|
|
268
|
+
setCursor(c);
|
|
269
|
+
activate(c.x, c.y);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case "m":
|
|
273
|
+
case "M":
|
|
274
|
+
setInteractionMode("move");
|
|
275
|
+
break;
|
|
276
|
+
case "p":
|
|
277
|
+
case "P":
|
|
278
|
+
setInteractionMode("pass");
|
|
279
|
+
break;
|
|
280
|
+
case "Escape":
|
|
281
|
+
setSelectedPieceId(null);
|
|
282
|
+
setInteractionMode(null);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
const renderSquares = () => {
|
|
287
|
+
const squares = [];
|
|
288
|
+
for (let y = ROWS - 1; y >= 0; y--) {
|
|
289
|
+
for (let x = 0; x < COLS; x++) {
|
|
290
|
+
const isGoalArea = x >= 2 && x <= 6 && (y >= 0 && y <= 1 || y >= 10 && y <= 11);
|
|
291
|
+
const isEven = (x + y) % 2 === 0;
|
|
292
|
+
const isValidDest = validMoves.some((m) => m.x === x && m.y === y) || validPasses.some((p) => p.x === x && p.y === y);
|
|
293
|
+
const pieceAt = boardState.pieces.find((p) => p.pos.x === x && p.pos.y === y);
|
|
294
|
+
const isCursor = keyboardNav && cursor?.x === x && cursor?.y === y;
|
|
295
|
+
squares.push(
|
|
296
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
297
|
+
"div",
|
|
298
|
+
{
|
|
299
|
+
onClick: () => handleSquareClick(x, y),
|
|
300
|
+
className: cn(
|
|
301
|
+
"relative aspect-square w-full flex items-center justify-center cursor-pointer",
|
|
302
|
+
isEven ? "bg-[#2d5a27]" : "bg-[#356a2d]",
|
|
303
|
+
isGoalArea && "bg-[#1a3a18] ring-1 ring-inset ring-emerald-500/30",
|
|
304
|
+
isValidDest && (interactionMode === "move" ? "ring-2 ring-inset ring-yellow-400/50 bg-yellow-400/10" : "ring-2 ring-inset ring-sky-400/50 bg-sky-400/10")
|
|
305
|
+
),
|
|
306
|
+
children: [
|
|
307
|
+
isValidDest && !pieceAt && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(
|
|
308
|
+
"w-3 h-3 rounded-full",
|
|
309
|
+
interactionMode === "move" ? "bg-yellow-400/40" : "bg-sky-400/40"
|
|
310
|
+
) }),
|
|
311
|
+
isCursor && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 ring-2 ring-inset ring-white pointer-events-none z-20", "aria-hidden": "true" })
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
`${x}-${y}`
|
|
315
|
+
)
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return squares;
|
|
320
|
+
};
|
|
321
|
+
const board = /* @__PURE__ */ jsxRuntime.jsx(
|
|
322
|
+
"div",
|
|
323
|
+
{
|
|
324
|
+
className: cn(
|
|
325
|
+
"w-full max-w-[500px] mx-auto overflow-hidden rounded-xl shadow-2xl border-4 border-[#1a3317] bg-[#1a3317]",
|
|
326
|
+
keyboardNav && "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-green focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary"
|
|
327
|
+
),
|
|
328
|
+
...keyboardNav ? {
|
|
329
|
+
tabIndex: 0,
|
|
330
|
+
role: "application",
|
|
331
|
+
"aria-label": "Game board \u2014 arrow keys to move the cursor, Enter to act",
|
|
332
|
+
onKeyDown: handleKeyDown,
|
|
333
|
+
onFocus: () => setCursor((c) => c ?? defaultCursor())
|
|
334
|
+
} : {},
|
|
335
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", style: { containerType: "inline-size" }, children: [
|
|
336
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-9 gap-[1px]", children: renderSquares() }),
|
|
337
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
338
|
+
"svg",
|
|
339
|
+
{
|
|
340
|
+
viewBox: "0 0 9 12",
|
|
341
|
+
preserveAspectRatio: "none",
|
|
342
|
+
className: "absolute inset-0 w-full h-full pointer-events-none",
|
|
343
|
+
"aria-hidden": "true",
|
|
344
|
+
children: [
|
|
345
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "0", width: "5", height: "2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
346
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "10", width: "5", height: "2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
347
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "0", y1: "6", x2: "9", y2: "6", stroke: "white", strokeOpacity: "0.35", strokeWidth: "0.05" }),
|
|
348
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "6", r: "1.5", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
349
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "6", r: "0.12", fill: "white", fillOpacity: "0.5" }),
|
|
350
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "1.5", r: "0.1", fill: "white", fillOpacity: "0.5" }),
|
|
351
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "10.5", r: "0.1", fill: "white", fillOpacity: "0.5" }),
|
|
352
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 3.086,2 A 1.5,1.5 0 0 0 5.914,2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
353
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 3.086,10 A 1.5,1.5 0 0 1 5.914,10", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" })
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
),
|
|
357
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 pointer-events-none", children: [
|
|
358
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "sync", children: boardState.pieces.map((piece) => {
|
|
359
|
+
const isPickupCarrier = pickupCarrierId === piece.id;
|
|
360
|
+
const pieceTransition = isPickupCarrier ? { duration: PICKUP_DURATION, ease: "linear" } : {
|
|
361
|
+
type: "spring",
|
|
362
|
+
stiffness: 200,
|
|
363
|
+
damping: 25,
|
|
364
|
+
mass: 1
|
|
365
|
+
};
|
|
366
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
367
|
+
framerMotion.motion.div,
|
|
368
|
+
{
|
|
369
|
+
layout: true,
|
|
370
|
+
initial: false,
|
|
371
|
+
animate: {
|
|
372
|
+
left: `${piece.pos.x / COLS * 100}%`,
|
|
373
|
+
top: `${(ROWS - 1 - piece.pos.y) / ROWS * 100}%`
|
|
374
|
+
},
|
|
375
|
+
transition: pieceTransition,
|
|
376
|
+
style: {
|
|
377
|
+
position: "absolute",
|
|
378
|
+
width: `${100 / COLS}%`,
|
|
379
|
+
height: `${100 / ROWS}%`
|
|
380
|
+
},
|
|
381
|
+
className: "flex items-center justify-center pointer-events-auto",
|
|
382
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
383
|
+
GamePiece,
|
|
384
|
+
{
|
|
385
|
+
piece,
|
|
386
|
+
isSelected: selectedPieceId === piece.id,
|
|
387
|
+
hasBall: boardState.ball.holderId === piece.id,
|
|
388
|
+
onClick: (e) => handlePieceClick(piece.id, piece.pos.x, piece.pos.y, e)
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
},
|
|
392
|
+
piece.id
|
|
393
|
+
);
|
|
394
|
+
}) }),
|
|
395
|
+
(() => {
|
|
396
|
+
const targetX = ball.pos.x / COLS * 100;
|
|
397
|
+
const targetY = (ROWS - 1 - ball.pos.y) / ROWS * 100;
|
|
398
|
+
let animate;
|
|
399
|
+
let transition;
|
|
400
|
+
if (isPickupOnMove && prevBall) {
|
|
401
|
+
const prevX = prevBall.pos.x / COLS * 100;
|
|
402
|
+
const prevY = (ROWS - 1 - prevBall.pos.y) / ROWS * 100;
|
|
403
|
+
const pulseStart = Math.max(0.05, pickupProportion - 0.06);
|
|
404
|
+
animate = {
|
|
405
|
+
left: [`${prevX}%`, `${prevX}%`, `${prevX}%`, `${targetX}%`],
|
|
406
|
+
top: [`${prevY}%`, `${prevY}%`, `${prevY}%`, `${targetY}%`],
|
|
407
|
+
scale: [1, 1, 1.35, 1]
|
|
408
|
+
};
|
|
409
|
+
transition = {
|
|
410
|
+
duration: PICKUP_DURATION,
|
|
411
|
+
times: [0, pulseStart, pickupProportion, 1],
|
|
412
|
+
ease: "linear"
|
|
413
|
+
};
|
|
414
|
+
} else {
|
|
415
|
+
animate = {
|
|
416
|
+
left: `${targetX}%`,
|
|
417
|
+
top: `${targetY}%`,
|
|
418
|
+
scale: 1
|
|
419
|
+
};
|
|
420
|
+
transition = {
|
|
421
|
+
type: "spring",
|
|
422
|
+
stiffness: 300,
|
|
423
|
+
damping: 30,
|
|
424
|
+
mass: 0.5
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
428
|
+
framerMotion.motion.div,
|
|
429
|
+
{
|
|
430
|
+
initial: false,
|
|
431
|
+
animate,
|
|
432
|
+
transition,
|
|
433
|
+
style: {
|
|
434
|
+
position: "absolute",
|
|
435
|
+
width: `${100 / COLS}%`,
|
|
436
|
+
height: `${100 / ROWS}%`,
|
|
437
|
+
zIndex: 50
|
|
438
|
+
},
|
|
439
|
+
className: "flex items-center justify-center pointer-events-none",
|
|
440
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[60%] h-[60%]", children: /* @__PURE__ */ jsxRuntime.jsx(Football, {}) })
|
|
441
|
+
},
|
|
442
|
+
"ball"
|
|
443
|
+
);
|
|
444
|
+
})()
|
|
445
|
+
] })
|
|
446
|
+
] })
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
if (!showCoordinates) return board;
|
|
450
|
+
const ranksTopToBottom = [...chessFootballEngine.RANK_LABELS].reverse();
|
|
451
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full max-w-[540px] mx-auto", "aria-hidden": "false", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
452
|
+
"div",
|
|
453
|
+
{
|
|
454
|
+
className: "grid gap-1 select-none",
|
|
455
|
+
style: {
|
|
456
|
+
gridTemplateColumns: "20px 1fr 20px",
|
|
457
|
+
gridTemplateRows: "14px 1fr 14px"
|
|
458
|
+
},
|
|
459
|
+
children: [
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" }),
|
|
461
|
+
/* @__PURE__ */ jsxRuntime.jsx(FileRow, { files: chessFootballEngine.FILE_LABELS }),
|
|
462
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" }),
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsx(RankColumn, { ranks: ranksTopToBottom }),
|
|
464
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: board }),
|
|
465
|
+
/* @__PURE__ */ jsxRuntime.jsx(RankColumn, { ranks: ranksTopToBottom }),
|
|
466
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" }),
|
|
467
|
+
/* @__PURE__ */ jsxRuntime.jsx(FileRow, { files: chessFootballEngine.FILE_LABELS }),
|
|
468
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true" })
|
|
469
|
+
]
|
|
470
|
+
}
|
|
471
|
+
) });
|
|
472
|
+
}
|
|
473
|
+
function FileRow({ files }) {
|
|
474
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-9 gap-[1px] px-[2px]", children: files.map((f) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
475
|
+
"span",
|
|
476
|
+
{
|
|
477
|
+
"aria-hidden": "true",
|
|
478
|
+
className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 uppercase tracking-[0.5px]",
|
|
479
|
+
children: f
|
|
480
|
+
},
|
|
481
|
+
f
|
|
482
|
+
)) });
|
|
483
|
+
}
|
|
484
|
+
function RankColumn({ ranks }) {
|
|
485
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-rows-12 gap-[1px] py-[2px]", children: ranks.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
486
|
+
"span",
|
|
487
|
+
{
|
|
488
|
+
"aria-hidden": "true",
|
|
489
|
+
className: "flex items-center justify-center font-mono text-[9px] text-fg-muted/80 tabular-nums",
|
|
490
|
+
children: r
|
|
491
|
+
},
|
|
492
|
+
r
|
|
493
|
+
)) });
|
|
494
|
+
}
|
|
495
|
+
function Avatar({ className, children }) {
|
|
496
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative w-9 h-9 rounded-full overflow-hidden shrink-0", className), children });
|
|
497
|
+
}
|
|
498
|
+
function AvatarImage({ src, alt, className }) {
|
|
499
|
+
if (!src) return null;
|
|
500
|
+
return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: alt ?? "", className: cn("w-full h-full object-cover", className) });
|
|
501
|
+
}
|
|
502
|
+
function AvatarFallback({ className, children }) {
|
|
503
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("absolute inset-0 flex items-center justify-center text-xs font-semibold", className), children });
|
|
504
|
+
}
|
|
505
|
+
var fallback = (key) => key;
|
|
506
|
+
var GameI18nContext = React.createContext(fallback);
|
|
507
|
+
function GameI18nProvider({ t, children }) {
|
|
508
|
+
return /* @__PURE__ */ jsxRuntime.jsx(GameI18nContext.Provider, { value: t, children });
|
|
509
|
+
}
|
|
510
|
+
function useGameT() {
|
|
511
|
+
return React.useContext(GameI18nContext);
|
|
512
|
+
}
|
|
513
|
+
function ActionPoints({ total, remaining, size = 20, className, kingMustRelease }) {
|
|
514
|
+
const t = useGameT();
|
|
515
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex items-center gap-1.5", className), "aria-label": t("actionPointsAriaLabel", { remaining, total }), children: Array.from({ length: total }).map((_, i) => {
|
|
516
|
+
const isActive = i < remaining;
|
|
517
|
+
const showCrown = kingMustRelease && i === total - 1;
|
|
518
|
+
if (showCrown) {
|
|
519
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
520
|
+
lucideReact.Crown,
|
|
521
|
+
{
|
|
522
|
+
size,
|
|
523
|
+
strokeWidth: 2,
|
|
524
|
+
className: "text-yellow-400 fill-yellow-400 transition-colors duration-150",
|
|
525
|
+
"aria-hidden": "true"
|
|
526
|
+
},
|
|
527
|
+
i
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
531
|
+
lucideReact.Zap,
|
|
532
|
+
{
|
|
533
|
+
size,
|
|
534
|
+
strokeWidth: 2,
|
|
535
|
+
className: cn(
|
|
536
|
+
"transition-colors duration-150",
|
|
537
|
+
isActive ? "text-accent-green fill-accent-green" : "text-fg-muted fill-none"
|
|
538
|
+
),
|
|
539
|
+
"aria-hidden": "true"
|
|
540
|
+
},
|
|
541
|
+
i
|
|
542
|
+
);
|
|
543
|
+
}) });
|
|
544
|
+
}
|
|
545
|
+
function initials(name) {
|
|
546
|
+
return (name?.split(" ").map((w) => w[0]).join("").toUpperCase() || "?").slice(0, 2);
|
|
547
|
+
}
|
|
548
|
+
function Scoreboard({
|
|
549
|
+
creatorUsername,
|
|
550
|
+
opponentUsername,
|
|
551
|
+
creatorAvatarUrl,
|
|
552
|
+
opponentAvatarUrl,
|
|
553
|
+
userSide
|
|
554
|
+
}) {
|
|
555
|
+
const t = useGameT();
|
|
556
|
+
const { boardState } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
557
|
+
if (!boardState?.score) return null;
|
|
558
|
+
const { score, turn, actionPoints, kingMustRelease } = boardState;
|
|
559
|
+
const myScore = userSide === "white" ? score.white : score.black;
|
|
560
|
+
const rivalScore = userSide === "white" ? score.black : score.white;
|
|
561
|
+
const myName = userSide === "white" ? creatorUsername : opponentUsername;
|
|
562
|
+
const rivalName = userSide === "white" ? opponentUsername : creatorUsername;
|
|
563
|
+
const myAvatar = userSide === "white" ? creatorAvatarUrl : opponentAvatarUrl;
|
|
564
|
+
const rivalAvatar = userSide === "white" ? opponentAvatarUrl : creatorAvatarUrl;
|
|
565
|
+
const myTeam = userSide === "white" ? t("teamWhite") : t("teamBlack");
|
|
566
|
+
const rivalTeam = userSide === "white" ? t("teamBlack") : t("teamWhite");
|
|
567
|
+
const isMyTurn = turn === userSide;
|
|
568
|
+
const myKingMustRelease = isMyTurn && kingMustRelease === turn;
|
|
569
|
+
const rivalKingMustRelease = !isMyTurn && kingMustRelease === turn;
|
|
570
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-bg-secondary flex items-center justify-between px-5 py-2.5", children: [
|
|
571
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
572
|
+
PlayerInfo,
|
|
573
|
+
{
|
|
574
|
+
name: myName,
|
|
575
|
+
team: myTeam,
|
|
576
|
+
avatarUrl: myAvatar,
|
|
577
|
+
isActive: isMyTurn,
|
|
578
|
+
actionPoints: isMyTurn ? actionPoints : 0,
|
|
579
|
+
kingMustRelease: myKingMustRelease,
|
|
580
|
+
align: "left"
|
|
581
|
+
}
|
|
582
|
+
),
|
|
583
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-anton text-[32px] leading-none text-fg-primary tabular-nums", children: myScore }),
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-anton text-2xl text-fg-muted", children: "\u2014" }),
|
|
586
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-anton text-[32px] leading-none text-fg-primary tabular-nums", children: rivalScore })
|
|
587
|
+
] }),
|
|
588
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
589
|
+
PlayerInfo,
|
|
590
|
+
{
|
|
591
|
+
name: rivalName,
|
|
592
|
+
team: rivalTeam,
|
|
593
|
+
avatarUrl: rivalAvatar,
|
|
594
|
+
isActive: !isMyTurn,
|
|
595
|
+
actionPoints: !isMyTurn ? actionPoints : 0,
|
|
596
|
+
kingMustRelease: rivalKingMustRelease,
|
|
597
|
+
align: "right"
|
|
598
|
+
}
|
|
599
|
+
)
|
|
600
|
+
] });
|
|
601
|
+
}
|
|
602
|
+
function PlayerInfo({ name, team, avatarUrl, isActive, actionPoints, kingMustRelease, align }) {
|
|
603
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-2.5", align === "right" && "flex-row-reverse"), children: [
|
|
604
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: cn(
|
|
605
|
+
"w-9 h-9 border-2 transition-colors duration-300",
|
|
606
|
+
isActive ? "border-accent-green" : "border-border-subtle"
|
|
607
|
+
), children: [
|
|
608
|
+
/* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: avatarUrl, alt: name, className: "object-cover" }),
|
|
609
|
+
/* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "bg-bg-surface-elevated text-fg-secondary text-xs font-semibold", children: initials(name) })
|
|
610
|
+
] }),
|
|
611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1", align === "right" && "items-end"), children: [
|
|
612
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
613
|
+
"font-inter text-[13px] font-semibold leading-none transition-colors duration-300",
|
|
614
|
+
isActive ? "text-fg-primary" : "text-fg-muted"
|
|
615
|
+
), children: name }),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-1", align === "right" && "flex-row-reverse"), children: [
|
|
617
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[10px] text-fg-muted uppercase", children: team }),
|
|
618
|
+
isActive && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-accent-green animate-pulse", "aria-hidden": "true" })
|
|
619
|
+
] }),
|
|
620
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
621
|
+
ActionPoints,
|
|
622
|
+
{
|
|
623
|
+
total: 5,
|
|
624
|
+
remaining: actionPoints,
|
|
625
|
+
size: 12,
|
|
626
|
+
kingMustRelease,
|
|
627
|
+
className: "gap-0.5"
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
] })
|
|
631
|
+
] });
|
|
632
|
+
}
|
|
633
|
+
function MiniScoreboard({
|
|
634
|
+
creatorUsername,
|
|
635
|
+
opponentUsername,
|
|
636
|
+
userSide,
|
|
637
|
+
className
|
|
638
|
+
}) {
|
|
639
|
+
const t = useGameT();
|
|
640
|
+
const { boardState } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
641
|
+
if (!boardState?.score) return null;
|
|
642
|
+
const { score, turn } = boardState;
|
|
643
|
+
const myScore = userSide === "white" ? score.white : score.black;
|
|
644
|
+
const rivalScore = userSide === "white" ? score.black : score.white;
|
|
645
|
+
const myName = userSide === "white" ? creatorUsername : opponentUsername;
|
|
646
|
+
const rivalName = userSide === "white" ? opponentUsername : creatorUsername;
|
|
647
|
+
const myTeam = userSide === "white" ? t("teamWhite") : t("teamBlack");
|
|
648
|
+
const rivalTeam = userSide === "white" ? t("teamBlack") : t("teamWhite");
|
|
649
|
+
const isMyTurn = turn === userSide;
|
|
650
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
651
|
+
"div",
|
|
652
|
+
{
|
|
653
|
+
className: cn(
|
|
654
|
+
"w-full max-w-[540px] rounded-xl bg-bg-surface border border-border-subtle",
|
|
655
|
+
"flex items-center justify-between gap-4 px-5 py-3",
|
|
656
|
+
className
|
|
657
|
+
),
|
|
658
|
+
children: [
|
|
659
|
+
/* @__PURE__ */ jsxRuntime.jsx(SideCell, { name: myName, team: myTeam, score: myScore, isActive: isMyTurn, align: "left" }),
|
|
660
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-anton text-base text-fg-muted uppercase tracking-[2px]", "aria-hidden": "true", children: "vs" }),
|
|
661
|
+
/* @__PURE__ */ jsxRuntime.jsx(SideCell, { name: rivalName, team: rivalTeam, score: rivalScore, isActive: !isMyTurn, align: "right" })
|
|
662
|
+
]
|
|
663
|
+
}
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
function SideCell({ name, team, score, isActive, align }) {
|
|
667
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col min-w-0", align === "right" ? "items-end" : "items-start"), children: [
|
|
668
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn(
|
|
669
|
+
"font-mono text-[10px] uppercase tracking-[0.5px] truncate max-w-[160px]",
|
|
670
|
+
isActive ? "text-fg-secondary" : "text-fg-muted"
|
|
671
|
+
), children: [
|
|
672
|
+
name,
|
|
673
|
+
" \xB7 ",
|
|
674
|
+
team
|
|
675
|
+
] }),
|
|
676
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
677
|
+
"font-anton text-[32px] leading-none tabular-nums",
|
|
678
|
+
isActive ? "text-fg-primary" : "text-fg-secondary"
|
|
679
|
+
), children: score })
|
|
680
|
+
] });
|
|
681
|
+
}
|
|
682
|
+
var VARIANT = {
|
|
683
|
+
primary: "bg-accent-green text-fg-primary font-anton tracking-widest rounded-md hover:bg-accent-green-light active:bg-accent-green-dark",
|
|
684
|
+
secondary: "bg-bg-surface text-fg-primary font-inter font-semibold rounded-md border border-border-subtle hover:bg-bg-surface-elevated active:bg-bg-secondary",
|
|
685
|
+
ghost: "bg-transparent text-fg-secondary font-inter font-medium rounded-md hover:text-fg-primary active:text-fg-muted",
|
|
686
|
+
destructive: "bg-danger/10 text-danger font-inter font-semibold rounded-md border border-danger hover:bg-danger/20 active:bg-danger/30"
|
|
687
|
+
};
|
|
688
|
+
var SIZE = {
|
|
689
|
+
default: "h-12 min-h-[44px] px-6 text-base",
|
|
690
|
+
sm: "h-9 min-h-[36px] px-4 text-sm",
|
|
691
|
+
lg: "h-13 min-h-[52px] px-8 text-lg",
|
|
692
|
+
icon: "h-11 w-11 min-h-[44px]"
|
|
693
|
+
};
|
|
694
|
+
var Button = React__namespace.forwardRef(
|
|
695
|
+
({ className, variant = "primary", size = "default", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
696
|
+
"button",
|
|
697
|
+
{
|
|
698
|
+
ref,
|
|
699
|
+
className: cn(
|
|
700
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap transition-all duration-150",
|
|
701
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-green focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary",
|
|
702
|
+
"disabled:pointer-events-none disabled:opacity-40",
|
|
703
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
704
|
+
VARIANT[variant],
|
|
705
|
+
SIZE[size],
|
|
706
|
+
className
|
|
707
|
+
),
|
|
708
|
+
...props
|
|
709
|
+
}
|
|
710
|
+
)
|
|
711
|
+
);
|
|
712
|
+
Button.displayName = "Button";
|
|
713
|
+
function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, onConfirm, onCancel }) {
|
|
714
|
+
if (!open) return null;
|
|
715
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
716
|
+
"div",
|
|
717
|
+
{
|
|
718
|
+
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",
|
|
719
|
+
role: "dialog",
|
|
720
|
+
"aria-modal": "true",
|
|
721
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
722
|
+
"div",
|
|
723
|
+
{
|
|
724
|
+
className: cn(
|
|
725
|
+
"bg-bg-surface border border-border-subtle rounded-xl p-6 mx-4 max-w-sm w-full",
|
|
726
|
+
"flex flex-col gap-4 shadow-2xl"
|
|
727
|
+
),
|
|
728
|
+
children: [
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "font-anton text-lg text-fg-primary uppercase tracking-wide", children: title }),
|
|
730
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-inter text-sm text-fg-secondary", children: description }),
|
|
731
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 pt-1", children: [
|
|
732
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "secondary", className: "flex-1", onClick: onCancel, children: cancelLabel }),
|
|
733
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", className: "flex-1", onClick: onConfirm, children: confirmLabel })
|
|
734
|
+
] })
|
|
735
|
+
]
|
|
736
|
+
}
|
|
737
|
+
)
|
|
738
|
+
}
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
function GameControls({ isMyTurn }) {
|
|
742
|
+
const {
|
|
743
|
+
boardState,
|
|
744
|
+
selectedPieceId,
|
|
745
|
+
interactionMode,
|
|
746
|
+
setInteractionMode,
|
|
747
|
+
endTurn
|
|
748
|
+
} = chunkJ2TBQPPC_cjs.useGameStore();
|
|
749
|
+
const t = useGameT();
|
|
750
|
+
const [showEndTurnConfirm, setShowEndTurnConfirm] = React.useState(false);
|
|
751
|
+
if (!boardState) return null;
|
|
752
|
+
const actionPoints = boardState.actionPoints;
|
|
753
|
+
const selectedPiece = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
754
|
+
const hasBall = selectedPiece && boardState.ball.holderId === selectedPiece.id;
|
|
755
|
+
const canPass = hasBall && actionPoints > 0;
|
|
756
|
+
const canMove = actionPoints > 0 && !selectedPiece?.hasMovedThisTurn;
|
|
757
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-bg-secondary flex flex-col gap-3 px-5 pt-3 pb-5", children: [
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isMyTurn && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
759
|
+
framerMotion.motion.div,
|
|
760
|
+
{
|
|
761
|
+
initial: { opacity: 0, y: 8 },
|
|
762
|
+
animate: { opacity: 1, y: 0 },
|
|
763
|
+
exit: { opacity: 0, y: 8 },
|
|
764
|
+
className: "flex gap-2",
|
|
765
|
+
children: [
|
|
766
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
767
|
+
"button",
|
|
768
|
+
{
|
|
769
|
+
onClick: () => setInteractionMode("move"),
|
|
770
|
+
disabled: !canMove,
|
|
771
|
+
className: cn(
|
|
772
|
+
"flex-1 flex items-center justify-center gap-2 h-11 rounded-md border font-inter text-sm font-semibold",
|
|
773
|
+
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-move-highlight",
|
|
774
|
+
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
775
|
+
interactionMode === "move" ? "bg-move-highlight/10 border-move-highlight text-move-highlight" : "bg-bg-surface border-border-subtle text-move-highlight hover:bg-move-highlight/5"
|
|
776
|
+
),
|
|
777
|
+
"aria-pressed": interactionMode === "move",
|
|
778
|
+
children: [
|
|
779
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Move, { size: 16, strokeWidth: 2 }),
|
|
780
|
+
t("move")
|
|
781
|
+
]
|
|
782
|
+
}
|
|
783
|
+
),
|
|
784
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
785
|
+
"button",
|
|
786
|
+
{
|
|
787
|
+
onClick: () => setInteractionMode("pass"),
|
|
788
|
+
disabled: !canPass,
|
|
789
|
+
className: cn(
|
|
790
|
+
"flex-1 flex items-center justify-center gap-2 h-11 rounded-md border font-inter text-sm font-semibold",
|
|
791
|
+
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-pass-highlight",
|
|
792
|
+
"disabled:opacity-40 disabled:cursor-not-allowed",
|
|
793
|
+
interactionMode === "pass" ? "bg-pass-highlight/10 border-pass-highlight text-pass-highlight" : "bg-bg-surface border-border-subtle text-pass-highlight hover:bg-pass-highlight/5"
|
|
794
|
+
),
|
|
795
|
+
"aria-pressed": interactionMode === "pass",
|
|
796
|
+
children: [
|
|
797
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(Football, {}) }),
|
|
798
|
+
t("pass")
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
)
|
|
802
|
+
]
|
|
803
|
+
},
|
|
804
|
+
"action-btns"
|
|
805
|
+
) }),
|
|
806
|
+
isMyTurn && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
807
|
+
Button,
|
|
808
|
+
{
|
|
809
|
+
variant: "primary",
|
|
810
|
+
size: "default",
|
|
811
|
+
className: "w-full gap-2 tracking-[1.5px]",
|
|
812
|
+
onClick: () => setShowEndTurnConfirm(true),
|
|
813
|
+
children: [
|
|
814
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Flag, { size: 16, strokeWidth: 2 }),
|
|
815
|
+
t("endTurn")
|
|
816
|
+
]
|
|
817
|
+
}
|
|
818
|
+
),
|
|
819
|
+
!isMyTurn && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-11 rounded-md bg-bg-surface border border-border-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-inter text-sm text-fg-muted", children: t("waitingRival") }) }),
|
|
820
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
821
|
+
ConfirmDialog,
|
|
822
|
+
{
|
|
823
|
+
open: showEndTurnConfirm,
|
|
824
|
+
title: t("endTurnConfirm"),
|
|
825
|
+
description: t("endTurnConfirmDescription", { count: actionPoints }),
|
|
826
|
+
confirmLabel: t("endTurnConfirmYes"),
|
|
827
|
+
cancelLabel: t("endTurnKeepPlaying"),
|
|
828
|
+
onConfirm: () => {
|
|
829
|
+
endTurn();
|
|
830
|
+
setShowEndTurnConfirm(false);
|
|
831
|
+
},
|
|
832
|
+
onCancel: () => setShowEndTurnConfirm(false)
|
|
833
|
+
}
|
|
834
|
+
)
|
|
835
|
+
] });
|
|
836
|
+
}
|
|
837
|
+
var PIECE_ICON2 = {
|
|
838
|
+
king: lucideReact.ChessKing,
|
|
839
|
+
queen: lucideReact.ChessQueen,
|
|
840
|
+
rook: lucideReact.ChessRook,
|
|
841
|
+
bishop: lucideReact.ChessBishop,
|
|
842
|
+
knight: lucideReact.ChessKnight
|
|
843
|
+
};
|
|
844
|
+
function SelectedPieceDetail({ isMyTurn, className }) {
|
|
845
|
+
const t = useGameT();
|
|
846
|
+
const { boardState, selectedPieceId } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
847
|
+
if (!boardState) return null;
|
|
848
|
+
const piece = boardState.pieces.find((p) => p.id === selectedPieceId);
|
|
849
|
+
if (!piece) {
|
|
850
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
851
|
+
"div",
|
|
852
|
+
{
|
|
853
|
+
className: cn(
|
|
854
|
+
"rounded-xl bg-bg-surface/40 border border-dashed border-border-subtle",
|
|
855
|
+
"px-4 py-3 flex items-center gap-3",
|
|
856
|
+
className
|
|
857
|
+
),
|
|
858
|
+
children: [
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ShieldQuestion, { size: 20, className: "text-fg-muted shrink-0", "aria-hidden": "true" }),
|
|
860
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px] text-fg-muted", children: t("selectedPiece.empty") })
|
|
861
|
+
]
|
|
862
|
+
}
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
const Icon = PIECE_ICON2[piece.type] ?? lucideReact.ShieldQuestion;
|
|
866
|
+
const longName = t(`pieces.${chessFootballEngine.LONG_KEY[piece.type]}`);
|
|
867
|
+
const square = chessFootballEngine.squareName(piece.pos);
|
|
868
|
+
const hasBall = boardState.ball.holderId === piece.id;
|
|
869
|
+
const movedThisTurn = piece.hasMovedThisTurn && isMyTurn;
|
|
870
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
871
|
+
"div",
|
|
872
|
+
{
|
|
873
|
+
className: cn(
|
|
874
|
+
"rounded-xl bg-bg-surface border border-border-subtle",
|
|
875
|
+
"px-4 py-3 flex items-center gap-4",
|
|
876
|
+
className
|
|
877
|
+
),
|
|
878
|
+
role: "status",
|
|
879
|
+
"aria-live": "polite",
|
|
880
|
+
"aria-label": `${longName} ${square}`,
|
|
881
|
+
children: [
|
|
882
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
883
|
+
"div",
|
|
884
|
+
{
|
|
885
|
+
className: cn(
|
|
886
|
+
"w-12 h-12 shrink-0 rounded-full flex items-center justify-center",
|
|
887
|
+
piece.side === "white" ? "bg-white text-black" : "bg-fg-primary/90 text-bg-primary"
|
|
888
|
+
),
|
|
889
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 26, strokeWidth: 1.75, "aria-hidden": "true" })
|
|
890
|
+
}
|
|
891
|
+
),
|
|
892
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
|
|
893
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-anton text-base text-fg-primary leading-none uppercase tracking-[0.5px]", children: longName }),
|
|
894
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[11px] text-fg-muted mt-1", children: t("selectedPiece.atSquare", { square }) })
|
|
895
|
+
] }),
|
|
896
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-end gap-1 shrink-0", children: [
|
|
897
|
+
hasBall && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5 font-mono text-[10px] text-pass-highlight uppercase tracking-[0.5px]", children: [
|
|
898
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-3 h-3 inline-flex", children: /* @__PURE__ */ jsxRuntime.jsx(Football, {}) }),
|
|
899
|
+
t("selectedPiece.hasBall")
|
|
900
|
+
] }),
|
|
901
|
+
movedThisTurn && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[10px] text-fg-muted uppercase tracking-[0.5px]", children: t("selectedPiece.alreadyMoved") })
|
|
902
|
+
] })
|
|
903
|
+
]
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
function MoveHistory({ scrollable = true, limit, className }) {
|
|
908
|
+
const t = useGameT();
|
|
909
|
+
const { boardState } = chunkJ2TBQPPC_cjs.useGameStore();
|
|
910
|
+
const scrollRef = React.useRef(null);
|
|
911
|
+
const history = boardState?.moveHistory ?? [];
|
|
912
|
+
const visible = limit ? history.slice(-limit) : history;
|
|
913
|
+
const ordered = [...visible].reverse();
|
|
914
|
+
React.useEffect(() => {
|
|
915
|
+
if (!scrollable) return;
|
|
916
|
+
if (scrollRef.current) scrollRef.current.scrollTop = 0;
|
|
917
|
+
}, [history.length, scrollable]);
|
|
918
|
+
if (ordered.length === 0) {
|
|
919
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
920
|
+
"p",
|
|
921
|
+
{
|
|
922
|
+
className: cn(
|
|
923
|
+
"font-mono text-[11px] text-fg-muted text-center py-3 px-3",
|
|
924
|
+
"bg-bg-surface/40 rounded-md",
|
|
925
|
+
className
|
|
926
|
+
),
|
|
927
|
+
children: t("history.empty")
|
|
928
|
+
}
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
932
|
+
"ol",
|
|
933
|
+
{
|
|
934
|
+
ref: scrollRef,
|
|
935
|
+
"aria-label": t("history.title"),
|
|
936
|
+
className: cn(
|
|
937
|
+
"flex flex-col gap-1",
|
|
938
|
+
scrollable && "h-full overflow-y-auto pr-1",
|
|
939
|
+
className
|
|
940
|
+
),
|
|
941
|
+
children: ordered.map((entry, i) => {
|
|
942
|
+
const isLatest = i === 0;
|
|
943
|
+
const prev = ordered[i - 1];
|
|
944
|
+
const showTurnDivider = !prev || prev.turnNumber !== entry.turnNumber;
|
|
945
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-col gap-1", children: [
|
|
946
|
+
showTurnDivider && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
947
|
+
"div",
|
|
948
|
+
{
|
|
949
|
+
className: "flex items-center gap-2 pt-1",
|
|
950
|
+
"aria-hidden": prev ? "true" : void 0,
|
|
951
|
+
children: [
|
|
952
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-[9px] tracking-[1px] text-fg-muted uppercase", children: t("history.turn", { n: entry.turnNumber }) }),
|
|
953
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 h-px bg-border-subtle/60" })
|
|
954
|
+
]
|
|
955
|
+
}
|
|
956
|
+
),
|
|
957
|
+
/* @__PURE__ */ jsxRuntime.jsx(HistoryRow, { entry, isLatest })
|
|
958
|
+
] }, `${entry.at}-${i}`);
|
|
959
|
+
})
|
|
960
|
+
}
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
function HistoryRow({ entry, isLatest }) {
|
|
964
|
+
const t = useGameT();
|
|
965
|
+
const fromSq = entry.from ? chessFootballEngine.squareName(entry.from) : "\u2014";
|
|
966
|
+
const toSq = chessFootballEngine.squareName(entry.to);
|
|
967
|
+
const pieceShort = t(`pieces.${chessFootballEngine.SHORT_KEY[entry.pieceType]}`);
|
|
968
|
+
let label;
|
|
969
|
+
switch (entry.type) {
|
|
970
|
+
case "pass":
|
|
971
|
+
label = t("history.pass", { to: toSq });
|
|
972
|
+
break;
|
|
973
|
+
case "tackle":
|
|
974
|
+
label = t("history.tackle", { to: toSq });
|
|
975
|
+
break;
|
|
976
|
+
case "interception":
|
|
977
|
+
label = t("history.interception", { to: toSq });
|
|
978
|
+
break;
|
|
979
|
+
case "goal":
|
|
980
|
+
label = t("history.goal", { from: fromSq, to: toSq });
|
|
981
|
+
break;
|
|
982
|
+
case "offside":
|
|
983
|
+
label = t("history.offside", { from: fromSq });
|
|
984
|
+
break;
|
|
985
|
+
case "move":
|
|
986
|
+
default:
|
|
987
|
+
label = t("history.move", { piece: pieceShort, from: fromSq, to: toSq });
|
|
988
|
+
}
|
|
989
|
+
const dotColor = entry.type === "goal" ? "bg-accent-green" : entry.type === "pass" ? "bg-pass-highlight" : entry.type === "interception" ? "bg-danger" : entry.type === "tackle" ? "bg-danger/80" : entry.type === "offside" ? "bg-warning" : "bg-move-highlight";
|
|
990
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
991
|
+
"div",
|
|
992
|
+
{
|
|
993
|
+
className: cn(
|
|
994
|
+
"flex items-center gap-2.5 min-h-[32px] px-3 py-1.5 rounded-md",
|
|
995
|
+
"font-mono text-[11px] leading-tight",
|
|
996
|
+
isLatest ? "bg-bg-surface-elevated text-fg-primary" : "bg-bg-surface/60 text-fg-secondary",
|
|
997
|
+
entry.pieceSide === "black" && "border-l-2 border-l-fg-primary/30",
|
|
998
|
+
entry.pieceSide === "white" && "border-l-2 border-l-fg-primary/80"
|
|
999
|
+
),
|
|
1000
|
+
children: [
|
|
1001
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1002
|
+
"span",
|
|
1003
|
+
{
|
|
1004
|
+
"aria-hidden": "true",
|
|
1005
|
+
className: cn("w-1.5 h-1.5 rounded-full shrink-0", dotColor)
|
|
1006
|
+
}
|
|
1007
|
+
),
|
|
1008
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate flex-1", children: label }),
|
|
1009
|
+
entry.type === "goal" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-anton text-[10px] tracking-[1px] uppercase text-accent-green", children: "GOL" })
|
|
1010
|
+
]
|
|
1011
|
+
}
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
var COLS2 = 9;
|
|
1015
|
+
var ROWS2 = 12;
|
|
1016
|
+
function StaticGameBoard() {
|
|
1017
|
+
const boardState = React.useMemo(() => chunkJ2TBQPPC_cjs.getInitialBoardState("white"), []);
|
|
1018
|
+
const renderSquares = () => {
|
|
1019
|
+
const squares = [];
|
|
1020
|
+
for (let y = ROWS2 - 1; y >= 0; y--) {
|
|
1021
|
+
for (let x = 0; x < COLS2; x++) {
|
|
1022
|
+
const isGoalArea = x >= 2 && x <= 6 && (y >= 0 && y <= 1 || y >= 10 && y <= 11);
|
|
1023
|
+
const isEven = (x + y) % 2 === 0;
|
|
1024
|
+
squares.push(
|
|
1025
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1026
|
+
"div",
|
|
1027
|
+
{
|
|
1028
|
+
className: cn(
|
|
1029
|
+
"relative aspect-square w-full",
|
|
1030
|
+
isEven ? "bg-[#2d5a27]" : "bg-[#356a2d]",
|
|
1031
|
+
isGoalArea && "bg-[#1a3a18] ring-1 ring-inset ring-emerald-500/30"
|
|
1032
|
+
)
|
|
1033
|
+
},
|
|
1034
|
+
`${x}-${y}`
|
|
1035
|
+
)
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return squares;
|
|
1040
|
+
};
|
|
1041
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full overflow-hidden rounded-xl shadow-2xl border-4 border-[#1a3317] bg-[#1a3317]", children: [
|
|
1042
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", style: { containerType: "inline-size" }, children: [
|
|
1043
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-9 gap-[1px]", children: renderSquares() }),
|
|
1044
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1045
|
+
"svg",
|
|
1046
|
+
{
|
|
1047
|
+
viewBox: "0 0 9 12",
|
|
1048
|
+
preserveAspectRatio: "none",
|
|
1049
|
+
className: "absolute inset-0 w-full h-full pointer-events-none",
|
|
1050
|
+
"aria-hidden": "true",
|
|
1051
|
+
children: [
|
|
1052
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "0", width: "5", height: "2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
1053
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "10", width: "5", height: "2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
1054
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "0", y1: "6", x2: "9", y2: "6", stroke: "white", strokeOpacity: "0.35", strokeWidth: "0.05" }),
|
|
1055
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "6", r: "1.5", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
1056
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "6", r: "0.12", fill: "white", fillOpacity: "0.5" }),
|
|
1057
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "1.5", r: "0.1", fill: "white", fillOpacity: "0.5" }),
|
|
1058
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4.5", cy: "10.5", r: "0.1", fill: "white", fillOpacity: "0.5" }),
|
|
1059
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 3.086,2 A 1.5,1.5 0 0 0 5.914,2", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" }),
|
|
1060
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M 3.086,10 A 1.5,1.5 0 0 1 5.914,10", fill: "none", stroke: "white", strokeOpacity: "0.3", strokeWidth: "0.05" })
|
|
1061
|
+
]
|
|
1062
|
+
}
|
|
1063
|
+
),
|
|
1064
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 pointer-events-none", children: boardState.pieces.map((piece) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1065
|
+
"div",
|
|
1066
|
+
{
|
|
1067
|
+
style: {
|
|
1068
|
+
position: "absolute",
|
|
1069
|
+
left: `${piece.pos.x / COLS2 * 100}%`,
|
|
1070
|
+
top: `${(ROWS2 - 1 - piece.pos.y) / ROWS2 * 100}%`,
|
|
1071
|
+
width: `${100 / COLS2}%`,
|
|
1072
|
+
height: `${100 / ROWS2}%`
|
|
1073
|
+
},
|
|
1074
|
+
className: "flex items-center justify-center",
|
|
1075
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1076
|
+
GamePiece,
|
|
1077
|
+
{
|
|
1078
|
+
piece,
|
|
1079
|
+
isSelected: false,
|
|
1080
|
+
hasBall: boardState.ball.holderId === piece.id,
|
|
1081
|
+
onClick: () => {
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
)
|
|
1085
|
+
},
|
|
1086
|
+
piece.id
|
|
1087
|
+
)) })
|
|
1088
|
+
] }),
|
|
1089
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1090
|
+
"div",
|
|
1091
|
+
{
|
|
1092
|
+
style: {
|
|
1093
|
+
position: "absolute",
|
|
1094
|
+
left: `${boardState.ball.pos.x / COLS2 * 100}%`,
|
|
1095
|
+
top: `${(ROWS2 - 1 - boardState.ball.pos.y) / ROWS2 * 100}%`,
|
|
1096
|
+
width: `${100 / COLS2}%`,
|
|
1097
|
+
height: `${100 / ROWS2}%`,
|
|
1098
|
+
zIndex: 50
|
|
1099
|
+
},
|
|
1100
|
+
className: "flex items-center justify-center pointer-events-none",
|
|
1101
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[60%] h-[60%]", children: /* @__PURE__ */ jsxRuntime.jsx(Football, {}) })
|
|
1102
|
+
}
|
|
1103
|
+
)
|
|
1104
|
+
] });
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
exports.ActionPoints = ActionPoints;
|
|
1108
|
+
exports.Avatar = Avatar;
|
|
1109
|
+
exports.AvatarFallback = AvatarFallback;
|
|
1110
|
+
exports.AvatarImage = AvatarImage;
|
|
1111
|
+
exports.Button = Button;
|
|
1112
|
+
exports.ConfirmDialog = ConfirmDialog;
|
|
1113
|
+
exports.Football = Football;
|
|
1114
|
+
exports.GameBoard = GameBoard;
|
|
1115
|
+
exports.GameControls = GameControls;
|
|
1116
|
+
exports.GameI18nProvider = GameI18nProvider;
|
|
1117
|
+
exports.GamePiece = GamePiece;
|
|
1118
|
+
exports.MiniScoreboard = MiniScoreboard;
|
|
1119
|
+
exports.MoveHistory = MoveHistory;
|
|
1120
|
+
exports.Scoreboard = Scoreboard;
|
|
1121
|
+
exports.SelectedPieceDetail = SelectedPieceDetail;
|
|
1122
|
+
exports.StaticGameBoard = StaticGameBoard;
|
|
1123
|
+
exports.cn = cn;
|
|
1124
|
+
exports.useGameT = useGameT;
|
|
1125
|
+
//# sourceMappingURL=index.cjs.map
|
|
1126
|
+
//# sourceMappingURL=index.cjs.map
|