reqct-gamehub-module 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,2879 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/index.js
30
+ var index_exports = {};
31
+ __export(index_exports, {
32
+ GameHubNode: () => GameHubNode_default
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/components/GameHubNode/GameHubNode.js
37
+ var import_react7 = __toESM(require("react"));
38
+ var import_reactflow = require("reactflow");
39
+
40
+ // src/games/Snake/SnakeGame.js
41
+ var import_react = __toESM(require("react"));
42
+ var import_jsx_runtime = require("react/jsx-runtime");
43
+ var SnakeGame = () => {
44
+ const GRID_SIZES = {
45
+ easy: 15,
46
+ medium: 20,
47
+ hard: 25
48
+ };
49
+ const SPEEDS = {
50
+ easy: 200,
51
+ medium: 150,
52
+ hard: 100
53
+ };
54
+ const [difficulty, setDifficulty] = (0, import_react.useState)("medium");
55
+ const [gridSize, setGridSize] = (0, import_react.useState)(GRID_SIZES.medium);
56
+ const [snake, setSnake] = (0, import_react.useState)([]);
57
+ const [food, setFood] = (0, import_react.useState)({ x: 5, y: 5 });
58
+ const [direction, setDirection] = (0, import_react.useState)("RIGHT");
59
+ const [nextDirection, setNextDirection] = (0, import_react.useState)("RIGHT");
60
+ const [score, setScore] = (0, import_react.useState)(0);
61
+ const [gameOver, setGameOver] = (0, import_react.useState)(false);
62
+ const [gameStarted, setGameStarted] = (0, import_react.useState)(false);
63
+ const [speed, setSpeed] = (0, import_react.useState)(SPEEDS.medium);
64
+ const [highScore, setHighScore] = (0, import_react.useState)(0);
65
+ const [gamePaused, setGamePaused] = (0, import_react.useState)(false);
66
+ const [gameSpeed, setGameSpeed] = (0, import_react.useState)(1);
67
+ (0, import_react.useEffect)(() => {
68
+ const savedHighScore = localStorage.getItem("snakeHighScore");
69
+ if (savedHighScore) {
70
+ setHighScore(parseInt(savedHighScore));
71
+ }
72
+ initializeGame();
73
+ }, []);
74
+ const generateFood = (0, import_react.useCallback)(() => {
75
+ const emptyCells = [];
76
+ for (let y = 0; y < gridSize; y++) {
77
+ for (let x = 0; x < gridSize; x++) {
78
+ if (!snake.some((segment) => segment.x === x && segment.y === y)) {
79
+ emptyCells.push({ x, y });
80
+ }
81
+ }
82
+ }
83
+ if (emptyCells.length > 0) {
84
+ const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
85
+ return randomCell;
86
+ }
87
+ return { x: -1, y: -1 };
88
+ }, [snake, gridSize]);
89
+ const initializeGame = () => {
90
+ const size = GRID_SIZES[difficulty];
91
+ const initialSpeed = SPEEDS[difficulty];
92
+ const midPoint = Math.floor(size / 2);
93
+ const initialSnake = [{ x: midPoint, y: midPoint }];
94
+ const initialFood = {
95
+ x: Math.floor(Math.random() * size),
96
+ y: Math.floor(Math.random() * size)
97
+ };
98
+ if (initialSnake.some((segment) => segment.x === initialFood.x && segment.y === initialFood.y)) {
99
+ initialFood.x = (initialFood.x + 5) % size;
100
+ initialFood.y = (initialFood.y + 5) % size;
101
+ }
102
+ setGridSize(size);
103
+ setSnake(initialSnake);
104
+ setFood(initialFood);
105
+ setDirection("RIGHT");
106
+ setNextDirection("RIGHT");
107
+ setScore(0);
108
+ setGameOver(false);
109
+ setGameStarted(true);
110
+ setGamePaused(false);
111
+ setSpeed(initialSpeed);
112
+ setGameSpeed(1);
113
+ };
114
+ const changeDifficulty = (newDifficulty) => {
115
+ if (gameStarted && !gameOver) {
116
+ if (window.confirm("\u0421\u043C\u0435\u043D\u0438\u0442\u044C \u0441\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C? \u0422\u0435\u043A\u0443\u0449\u0430\u044F \u0438\u0433\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0431\u0440\u043E\u0448\u0435\u043D\u0430.")) {
117
+ setDifficulty(newDifficulty);
118
+ setTimeout(() => initializeGame(), 100);
119
+ }
120
+ } else {
121
+ setDifficulty(newDifficulty);
122
+ }
123
+ };
124
+ const checkCollision = (0, import_react.useCallback)((head) => {
125
+ if (head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize) {
126
+ return true;
127
+ }
128
+ for (let i = 1; i < snake.length; i++) {
129
+ if (head.x === snake[i].x && head.y === snake[i].y) {
130
+ return true;
131
+ }
132
+ }
133
+ return false;
134
+ }, [snake, gridSize]);
135
+ const moveSnake = (0, import_react.useCallback)(() => {
136
+ if (!gameStarted || gameOver || gamePaused) return;
137
+ setSnake((prevSnake) => {
138
+ const head = { ...prevSnake[0] };
139
+ const currentDirection = nextDirection;
140
+ setDirection(currentDirection);
141
+ switch (currentDirection) {
142
+ case "RIGHT":
143
+ head.x += 1;
144
+ break;
145
+ case "LEFT":
146
+ head.x -= 1;
147
+ break;
148
+ case "UP":
149
+ head.y -= 1;
150
+ break;
151
+ case "DOWN":
152
+ head.y += 1;
153
+ break;
154
+ default:
155
+ break;
156
+ }
157
+ if (checkCollision(head)) {
158
+ setGameOver(true);
159
+ if (score > highScore) {
160
+ const newHighScore = score;
161
+ setHighScore(newHighScore);
162
+ localStorage.setItem("snakeHighScore", newHighScore.toString());
163
+ }
164
+ return prevSnake;
165
+ }
166
+ const newSnake = [head, ...prevSnake];
167
+ if (head.x === food.x && head.y === food.y) {
168
+ const newScore = score + 10;
169
+ setScore(newScore);
170
+ if (newScore > 0 && newScore % 50 === 0) {
171
+ const newSpeed = Math.max(50, speed - 20);
172
+ setSpeed(newSpeed);
173
+ setGameSpeed(Math.round(SPEEDS[difficulty] / newSpeed));
174
+ }
175
+ const newFood = generateFood();
176
+ if (newFood.x !== -1 && newFood.y !== -1) {
177
+ setFood(newFood);
178
+ } else {
179
+ setGameOver(true);
180
+ }
181
+ } else {
182
+ newSnake.pop();
183
+ }
184
+ return newSnake;
185
+ });
186
+ }, [gameStarted, gameOver, gamePaused, nextDirection, checkCollision, food, score, highScore, speed, difficulty, generateFood]);
187
+ (0, import_react.useEffect)(() => {
188
+ const handleKeyDown = (e) => {
189
+ if (gameOver) return;
190
+ switch (e.key) {
191
+ case "ArrowUp":
192
+ if (direction !== "DOWN") {
193
+ e.preventDefault();
194
+ setNextDirection("UP");
195
+ }
196
+ break;
197
+ case "ArrowDown":
198
+ if (direction !== "UP") {
199
+ e.preventDefault();
200
+ setNextDirection("DOWN");
201
+ }
202
+ break;
203
+ case "ArrowLeft":
204
+ if (direction !== "RIGHT") {
205
+ e.preventDefault();
206
+ setNextDirection("LEFT");
207
+ }
208
+ break;
209
+ case "ArrowRight":
210
+ if (direction !== "LEFT") {
211
+ e.preventDefault();
212
+ setNextDirection("RIGHT");
213
+ }
214
+ break;
215
+ case " ":
216
+ e.preventDefault();
217
+ if (gameStarted && !gameOver) {
218
+ setGamePaused((prev) => !prev);
219
+ }
220
+ break;
221
+ case "Enter":
222
+ if (!gameStarted || gameOver) {
223
+ e.preventDefault();
224
+ initializeGame();
225
+ }
226
+ break;
227
+ default:
228
+ break;
229
+ }
230
+ };
231
+ window.addEventListener("keydown", handleKeyDown);
232
+ return () => window.removeEventListener("keydown", handleKeyDown);
233
+ }, [direction, gameStarted, gameOver, gamePaused]);
234
+ (0, import_react.useEffect)(() => {
235
+ if (!gameStarted || gameOver || gamePaused) return;
236
+ const gameInterval = setInterval(moveSnake, speed);
237
+ return () => clearInterval(gameInterval);
238
+ }, [gameStarted, gameOver, gamePaused, moveSnake, speed]);
239
+ (0, import_react.useEffect)(() => {
240
+ if (gameStarted) {
241
+ const event = new CustomEvent("gameScoreUpdate", {
242
+ detail: { game: "snake", score }
243
+ });
244
+ window.dispatchEvent(event);
245
+ }
246
+ }, [score, gameStarted]);
247
+ const handleButtonControl = (newDirection) => {
248
+ if (gamePaused || gameOver || !gameStarted) return;
249
+ if (newDirection === "UP" && direction !== "DOWN" || newDirection === "DOWN" && direction !== "UP" || newDirection === "LEFT" && direction !== "RIGHT" || newDirection === "RIGHT" && direction !== "LEFT") {
250
+ setNextDirection(newDirection);
251
+ }
252
+ };
253
+ const getDifficultyColor = (diff) => {
254
+ return diff === difficulty ? diff === "easy" ? "#00b09b" : diff === "medium" ? "#667eea" : "#ff5e62" : "#666";
255
+ };
256
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "snake-game", children: [
257
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "game-header", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "game-controls", children: !gameStarted || gameOver ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "control-btn start-btn", onClick: initializeGame, children: gameOver ? "\u{1F504} \u0418\u0433\u0440\u0430\u0442\u044C \u0441\u043D\u043E\u0432\u0430" : "\u25B6\uFE0F \u041D\u0430\u0447\u0430\u0442\u044C \u0438\u0433\u0440\u0443" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
258
+ "button",
259
+ {
260
+ className: "control-btn pause-btn",
261
+ onClick: () => setGamePaused(!gamePaused),
262
+ children: gamePaused ? "\u25B6\uFE0F \u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C" : "\u23F8\uFE0F \u041F\u0430\u0443\u0437\u0430"
263
+ }
264
+ ) }) }),
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "difficulty-selector", children: [
266
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "difficulty-label", children: "\u0421\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C:" }),
267
+ ["easy", "medium", "hard"].map((diff) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
268
+ "button",
269
+ {
270
+ className: `difficulty-btn ${difficulty === diff ? "active" : ""}`,
271
+ onClick: () => changeDifficulty(diff),
272
+ style: {
273
+ background: difficulty === diff ? getDifficultyColor(diff) : void 0,
274
+ color: difficulty === diff ? "white" : void 0
275
+ },
276
+ children: [
277
+ diff === "easy" && "\u{1F422} \u041B\u0435\u0433\u043A\u0430\u044F",
278
+ diff === "medium" && "\u26A1 \u0421\u0440\u0435\u0434\u043D\u044F\u044F",
279
+ diff === "hard" && "\u{1F525} \u0421\u043B\u043E\u0436\u043D\u0430\u044F"
280
+ ]
281
+ },
282
+ diff
283
+ ))
284
+ ] }),
285
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "difficulty-info", children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "info-item", children: [
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u0420\u0430\u0437\u043C\u0435\u0440 \u043F\u043E\u043B\u044F:" }),
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
289
+ gridSize,
290
+ "x",
291
+ gridSize
292
+ ] })
293
+ ] }),
294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "info-item", children: [
295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C:" }),
296
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
297
+ gameSpeed,
298
+ "x"
299
+ ] })
300
+ ] }),
301
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "info-item", children: [
302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u0411\u043E\u043D\u0443\u0441:" }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
304
+ "+",
305
+ difficulty === "hard" ? "20" : difficulty === "medium" ? "15" : "10",
306
+ "/\u0435\u0434\u0430"
307
+ ] })
308
+ ] })
309
+ ] }),
310
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "game-stats", children: [
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stat-box", children: [
312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-label", children: "\u0421\u0447\u0435\u0442" }),
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-value", children: score })
314
+ ] }),
315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stat-box", children: [
316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-label", children: "\u0420\u0435\u043A\u043E\u0440\u0434" }),
317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-value", children: highScore })
318
+ ] }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stat-box", children: [
320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-label", children: "\u0414\u043B\u0438\u043D\u0430" }),
321
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-value", children: snake.length })
322
+ ] }),
323
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stat-box", children: [
324
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-label", children: "\u0421\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C" }),
325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-value", children: difficulty === "easy" ? "\u{1F422}" : difficulty === "medium" ? "\u26A1" : "\u{1F525}" })
326
+ ] })
327
+ ] }),
328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "game-board-container", children: !gameStarted ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "start-screen", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "instructions", children: [
329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u041A\u0430\u043A \u0438\u0433\u0440\u0430\u0442\u044C:" }),
330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("ul", { children: [
331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u{1F4CD} \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0441\u0442\u0440\u0435\u043B\u043A\u0438 \u0434\u043B\u044F \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F" }),
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u{1F34E} \u0421\u043E\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u043A\u0440\u0430\u0441\u043D\u044B\u0435 \u044F\u0431\u043B\u043E\u043A\u0438" }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u{1F6AB} \u0418\u0437\u0431\u0435\u0433\u0430\u0439\u0442\u0435 \u0441\u0442\u0435\u043D \u0438 \u0441\u0435\u0431\u044F" }),
334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u{1F3AF} \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0441\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u0432\u044B\u0448\u0435" }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: "\u{1F3C6} \u041F\u043E\u0431\u0438\u0432\u0430\u0439\u0442\u0435 \u0441\u0432\u043E\u0439 \u0440\u0435\u043A\u043E\u0440\u0434!" })
336
+ ] }),
337
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "start-instruction-btn", onClick: initializeGame, children: "\u041D\u0410\u0427\u0410\u0422\u042C \u0418\u0413\u0420\u0423" })
338
+ ] }) }) : gamePaused ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "paused-screen", children: [
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u23F8\uFE0F \u0418\u0413\u0420\u0410 \u041D\u0410 \u041F\u0410\u0423\u0417\u0415" }),
340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: '\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043F\u0440\u043E\u0431\u0435\u043B \u0438\u043B\u0438 \u043A\u043D\u043E\u043F\u043A\u0443 "\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C"' })
341
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
342
+ "div",
343
+ {
344
+ className: "game-board",
345
+ style: {
346
+ gridTemplateColumns: `repeat(${gridSize}, 1fr)`,
347
+ gridTemplateRows: `repeat(${gridSize}, 1fr)`
348
+ },
349
+ children: [
350
+ Array.from({ length: gridSize * gridSize }).map((_, index) => {
351
+ const y = Math.floor(index / gridSize);
352
+ const x = index % gridSize;
353
+ const isSnakeHead = snake[0]?.x === x && snake[0]?.y === y;
354
+ const isSnakeBody = snake.slice(1).some((segment) => segment.x === x && segment.y === y);
355
+ const isFood = food.x === x && food.y === y;
356
+ let cellClass = "grid-cell";
357
+ if (isSnakeHead) cellClass += " snake-head";
358
+ else if (isSnakeBody) cellClass += " snake-body";
359
+ else if (isFood) cellClass += " food";
360
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
361
+ "div",
362
+ {
363
+ className: cellClass,
364
+ style: {
365
+ width: `${400 / gridSize}px`,
366
+ height: `${400 / gridSize}px`
367
+ }
368
+ },
369
+ `${x}-${y}`
370
+ );
371
+ }),
372
+ gameOver && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "game-over-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "game-over-content", children: [
373
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: "\u{1F480} \u0418\u0413\u0420\u0410 \u041E\u041A\u041E\u041D\u0427\u0415\u041D\u0410" }),
374
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "final-stats", children: [
375
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
376
+ "\u0412\u0430\u0448 \u0441\u0447\u0435\u0442: ",
377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: score })
378
+ ] }),
379
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
380
+ "\u0420\u0435\u043A\u043E\u0440\u0434: ",
381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: Math.max(score, highScore) })
382
+ ] }),
383
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
384
+ "\u0414\u043B\u0438\u043D\u0430 \u0437\u043C\u0435\u0439\u043A\u0438: ",
385
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: snake.length })
386
+ ] }),
387
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
388
+ "\u0421\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C: ",
389
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: difficulty === "easy" ? "\u041B\u0435\u0433\u043A\u0430\u044F" : difficulty === "medium" ? "\u0421\u0440\u0435\u0434\u043D\u044F\u044F" : "\u0421\u043B\u043E\u0436\u043D\u0430\u044F" })
390
+ ] })
391
+ ] }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
393
+ "button",
394
+ {
395
+ className: "play-again-btn",
396
+ onClick: initializeGame,
397
+ children: "\u{1F3AE} \u0418\u0433\u0440\u0430\u0442\u044C \u0441\u043D\u043E\u0432\u0430"
398
+ }
399
+ )
400
+ ] }) })
401
+ ]
402
+ }
403
+ ) }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mobile-controls", children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
406
+ "button",
407
+ {
408
+ className: "mobile-btn up-btn",
409
+ onClick: () => handleButtonControl("UP"),
410
+ children: "\u2191"
411
+ }
412
+ ),
413
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "horizontal-controls", children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
415
+ "button",
416
+ {
417
+ className: "mobile-btn left-btn",
418
+ onClick: () => handleButtonControl("LEFT"),
419
+ children: "\u2190"
420
+ }
421
+ ),
422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "center-space" }),
423
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
424
+ "button",
425
+ {
426
+ className: "mobile-btn right-btn",
427
+ onClick: () => handleButtonControl("RIGHT"),
428
+ children: "\u2192"
429
+ }
430
+ )
431
+ ] }),
432
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
433
+ "button",
434
+ {
435
+ className: "mobile-btn down-btn",
436
+ onClick: () => handleButtonControl("DOWN"),
437
+ children: "\u2193"
438
+ }
439
+ )
440
+ ] }),
441
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "game-instructions", children: [
442
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "key-instructions", children: [
443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435:" }),
445
+ " \u2190\u2191\u2193\u2192 \u0438\u043B\u0438 WASD"
446
+ ] }),
447
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { children: [
448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "\u041F\u0430\u0443\u0437\u0430:" }),
449
+ " \u041F\u0440\u043E\u0431\u0435\u043B \u2022 ",
450
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "\u0420\u0435\u0441\u0442\u0430\u0440\u0442:" }),
451
+ " Enter"
452
+ ] })
453
+ ] }),
454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "legend", children: [
455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "legend-item", children: [
456
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "legend-color snake-head" }),
457
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u0413\u043E\u043B\u043E\u0432\u0430 \u0437\u043C\u0435\u0439\u043A\u0438" })
458
+ ] }),
459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "legend-item", children: [
460
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "legend-color snake-body" }),
461
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u0422\u0435\u043B\u043E \u0437\u043C\u0435\u0439\u043A\u0438" })
462
+ ] }),
463
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "legend-item", children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "legend-color food" }),
465
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "\u0415\u0434\u0430 (+10 \u043E\u0447\u043A\u043E\u0432)" })
466
+ ] })
467
+ ] })
468
+ ] })
469
+ ] });
470
+ };
471
+ var SnakeGame_default = SnakeGame;
472
+
473
+ // src/games/Game2048/Game2048.js
474
+ var import_react2 = __toESM(require("react"));
475
+ var import_jsx_runtime2 = require("react/jsx-runtime");
476
+ var Game2048 = () => {
477
+ const [board, setBoard] = (0, import_react2.useState)([]);
478
+ const [score, setScore] = (0, import_react2.useState)(0);
479
+ const [gameOver, setGameOver] = (0, import_react2.useState)(false);
480
+ const [won, setWon] = (0, import_react2.useState)(false);
481
+ const [lastMove, setLastMove] = (0, import_react2.useState)(null);
482
+ const initializeBoard = () => {
483
+ const newBoard = Array(4).fill().map(() => Array(4).fill(0));
484
+ addRandomTile(newBoard);
485
+ addRandomTile(newBoard);
486
+ setBoard(newBoard);
487
+ setScore(0);
488
+ setGameOver(false);
489
+ setWon(false);
490
+ setLastMove(null);
491
+ };
492
+ const addRandomTile = (boardCopy) => {
493
+ const emptyCells = [];
494
+ for (let i = 0; i < 4; i++) {
495
+ for (let j = 0; j < 4; j++) {
496
+ if (boardCopy[i][j] === 0) {
497
+ emptyCells.push({ i, j });
498
+ }
499
+ }
500
+ }
501
+ if (emptyCells.length > 0) {
502
+ const { i, j } = emptyCells[Math.floor(Math.random() * emptyCells.length)];
503
+ boardCopy[i][j] = Math.random() < 0.9 ? 2 : 4;
504
+ }
505
+ return boardCopy;
506
+ };
507
+ const moveLeft = (0, import_react2.useCallback)((boardCopy) => {
508
+ let moved = false;
509
+ let newScore = score;
510
+ for (let i = 0; i < 4; i++) {
511
+ let row = boardCopy[i].filter((cell) => cell !== 0);
512
+ for (let j = 0; j < row.length - 1; j++) {
513
+ if (row[j] === row[j + 1]) {
514
+ row[j] *= 2;
515
+ newScore += row[j];
516
+ row.splice(j + 1, 1);
517
+ moved = true;
518
+ if (row[j] === 2048) {
519
+ setWon(true);
520
+ }
521
+ }
522
+ }
523
+ while (row.length < 4) {
524
+ row.push(0);
525
+ }
526
+ if (JSON.stringify(boardCopy[i]) !== JSON.stringify(row)) {
527
+ moved = true;
528
+ }
529
+ boardCopy[i] = row;
530
+ }
531
+ if (moved) {
532
+ setScore(newScore);
533
+ }
534
+ return moved;
535
+ }, [score]);
536
+ const rotateBoard = (boardToRotate, times = 1) => {
537
+ let result = boardToRotate.map((row) => [...row]);
538
+ for (let t = 0; t < times; t++) {
539
+ const newBoard = Array(4).fill().map(() => Array(4).fill(0));
540
+ for (let i = 0; i < 4; i++) {
541
+ for (let j = 0; j < 4; j++) {
542
+ newBoard[j][3 - i] = result[i][j];
543
+ }
544
+ }
545
+ result = newBoard;
546
+ }
547
+ return result;
548
+ };
549
+ const move = (0, import_react2.useCallback)((direction) => {
550
+ if (gameOver) return;
551
+ let boardCopy = board.map((row) => [...row]);
552
+ let moved = false;
553
+ const oldBoard = JSON.parse(JSON.stringify(boardCopy));
554
+ switch (direction) {
555
+ case "left":
556
+ moved = moveLeft(boardCopy);
557
+ setLastMove({ direction: "left", oldBoard, newBoard: boardCopy });
558
+ break;
559
+ case "right":
560
+ boardCopy = boardCopy.map((row) => [...row].reverse());
561
+ moved = moveLeft(boardCopy);
562
+ boardCopy = boardCopy.map((row) => [...row].reverse());
563
+ setLastMove({ direction: "right", oldBoard, newBoard: boardCopy });
564
+ break;
565
+ case "up":
566
+ boardCopy = rotateBoard(boardCopy, 3);
567
+ moved = moveLeft(boardCopy);
568
+ boardCopy = rotateBoard(boardCopy, 1);
569
+ setLastMove({ direction: "up", oldBoard, newBoard: boardCopy });
570
+ break;
571
+ case "down":
572
+ boardCopy = rotateBoard(boardCopy, 1);
573
+ moved = moveLeft(boardCopy);
574
+ boardCopy = rotateBoard(boardCopy, 3);
575
+ setLastMove({ direction: "down", oldBoard, newBoard: boardCopy });
576
+ break;
577
+ default:
578
+ break;
579
+ }
580
+ if (moved) {
581
+ addRandomTile(boardCopy);
582
+ setBoard(boardCopy);
583
+ const event = new CustomEvent("gameScoreUpdate", {
584
+ detail: { game: "2048", score }
585
+ });
586
+ window.dispatchEvent(event);
587
+ if (isGameOver(boardCopy)) {
588
+ setGameOver(true);
589
+ }
590
+ }
591
+ }, [board, gameOver, moveLeft]);
592
+ const isGameOver = (boardCopy) => {
593
+ for (let i = 0; i < 4; i++) {
594
+ for (let j = 0; j < 4; j++) {
595
+ if (boardCopy[i][j] === 0) return false;
596
+ }
597
+ }
598
+ for (let i = 0; i < 4; i++) {
599
+ for (let j = 0; j < 4; j++) {
600
+ if (j < 3 && boardCopy[i][j] === boardCopy[i][j + 1]) return false;
601
+ if (i < 3 && boardCopy[i][j] === boardCopy[i + 1][j]) return false;
602
+ }
603
+ }
604
+ return true;
605
+ };
606
+ (0, import_react2.useEffect)(() => {
607
+ initializeBoard();
608
+ }, []);
609
+ (0, import_react2.useEffect)(() => {
610
+ const handleKeyDown = (e) => {
611
+ e.preventDefault();
612
+ if (gameOver || won) return;
613
+ switch (e.key.toLowerCase()) {
614
+ case "arrowleft":
615
+ case "a":
616
+ move("left");
617
+ break;
618
+ case "arrowright":
619
+ case "d":
620
+ move("right");
621
+ break;
622
+ case "arrowup":
623
+ case "w":
624
+ move("up");
625
+ break;
626
+ case "arrowdown":
627
+ case "s":
628
+ move("down");
629
+ break;
630
+ case "r":
631
+ initializeBoard();
632
+ break;
633
+ default:
634
+ break;
635
+ }
636
+ };
637
+ window.addEventListener("keydown", handleKeyDown);
638
+ return () => window.removeEventListener("keydown", handleKeyDown);
639
+ }, [move, gameOver, won]);
640
+ const getTileColor = (value) => {
641
+ const colors = {
642
+ 2: "#eee4da",
643
+ 4: "#ede0c8",
644
+ 8: "#f2b179",
645
+ 16: "#f59563",
646
+ 32: "#f67c5f",
647
+ 64: "#f65e3b",
648
+ 128: "#edcf72",
649
+ 256: "#edcc61",
650
+ 512: "#edc850",
651
+ 1024: "#edc53f",
652
+ 2048: "#edc22e",
653
+ 4096: "#edc22e",
654
+ 8192: "#edc22e"
655
+ };
656
+ return colors[value] || "#3c3a32";
657
+ };
658
+ const getTileFontSize = (value) => {
659
+ if (value < 100) return "18px";
660
+ if (value < 1e3) return "16px";
661
+ if (value < 1e4) return "14px";
662
+ return "12px";
663
+ };
664
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "game-2048", children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "game-header", children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "header-left", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "game-subtitle", children: "\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u044F\u0439\u0442\u0435 \u043F\u043B\u0438\u0442\u043A\u0438, \u0447\u0442\u043E\u0431\u044B \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C 2048!" }) }),
667
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "header-right", children: [
668
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "score-display", children: [
669
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "score-label", children: "\u0421\u0447\u0435\u0442" }),
670
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "score-value", children: score })
671
+ ] }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "game-controls", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
673
+ "button",
674
+ {
675
+ className: "control-btn new-game-btn",
676
+ onClick: initializeBoard,
677
+ children: "\u041D\u043E\u0432\u0430\u044F \u0438\u0433\u0440\u0430"
678
+ }
679
+ ) })
680
+ ] })
681
+ ] }),
682
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "instructions", children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
684
+ "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 ",
685
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "\u0441\u0442\u0440\u0435\u043B\u043A\u0438" }),
686
+ " \u0438\u043B\u0438 ",
687
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "WASD" }),
688
+ " \u0434\u043B\u044F \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u044F"
689
+ ] }),
690
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
691
+ "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 ",
692
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: "R" }),
693
+ " \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430"
694
+ ] })
695
+ ] }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "game-area", children: [
697
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "board-container", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "board", children: board.map((row, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "row", children: row.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "cell", children: cell !== 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
698
+ "div",
699
+ {
700
+ className: `tile tile-${cell}`,
701
+ style: {
702
+ backgroundColor: getTileColor(cell),
703
+ color: cell <= 4 ? "#776e65" : "#f9f6f2",
704
+ fontSize: getTileFontSize(cell)
705
+ },
706
+ "data-value": cell,
707
+ children: cell
708
+ }
709
+ ) }, `${i}-${j}`)) }, i)) }) }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mobile-controls", children: [
711
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "control-row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
712
+ "button",
713
+ {
714
+ className: "mobile-control-btn up",
715
+ onClick: () => move("up"),
716
+ children: "\u2191"
717
+ }
718
+ ) }),
719
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "control-row", children: [
720
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
721
+ "button",
722
+ {
723
+ className: "mobile-control-btn left",
724
+ onClick: () => move("left"),
725
+ children: "\u2190"
726
+ }
727
+ ),
728
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "spacer" }),
729
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
730
+ "button",
731
+ {
732
+ className: "mobile-control-btn right",
733
+ onClick: () => move("right"),
734
+ children: "\u2192"
735
+ }
736
+ )
737
+ ] }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "control-row", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
739
+ "button",
740
+ {
741
+ className: "mobile-control-btn down",
742
+ onClick: () => move("down"),
743
+ children: "\u2193"
744
+ }
745
+ ) })
746
+ ] })
747
+ ] }),
748
+ gameOver && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "game-over-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "game-over-content", children: [
749
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: "\u{1F3AE} \u0418\u0433\u0440\u0430 \u043E\u043A\u043E\u043D\u0447\u0435\u043D\u0430!" }),
750
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "final-score", children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
752
+ "\u0412\u0430\u0448 \u0441\u0447\u0435\u0442: ",
753
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: score })
754
+ ] }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { children: [
756
+ "\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u043F\u043B\u0438\u0442\u043A\u0430: ",
757
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { children: Math.max(...board.flat()) })
758
+ ] })
759
+ ] }),
760
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
761
+ "button",
762
+ {
763
+ className: "play-again-btn",
764
+ onClick: initializeBoard,
765
+ children: "\u0418\u0433\u0440\u0430\u0442\u044C \u0441\u043D\u043E\u0432\u0430"
766
+ }
767
+ )
768
+ ] }) }),
769
+ won && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "win-overlay", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "win-content", children: [
770
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "confetti", children: "\u{1F389}" }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: "\u{1F3C6} \u041F\u043E\u0431\u0435\u0434\u0430!" }),
772
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: "\u0412\u044B \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0438 \u043F\u043B\u0438\u0442\u043A\u0443 2048!" }),
773
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "win-buttons", children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
775
+ "button",
776
+ {
777
+ className: "continue-btn",
778
+ onClick: () => setWon(false),
779
+ children: "\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C"
780
+ }
781
+ ),
782
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
783
+ "button",
784
+ {
785
+ className: "restart-btn",
786
+ onClick: initializeBoard,
787
+ children: "\u041D\u043E\u0432\u0430\u044F \u0438\u0433\u0440\u0430"
788
+ }
789
+ )
790
+ ] })
791
+ ] }) })
792
+ ] });
793
+ };
794
+ var Game2048_default = Game2048;
795
+
796
+ // src/games/Memory/MemoryGame.js
797
+ var import_react3 = __toESM(require("react"));
798
+ var import_jsx_runtime3 = require("react/jsx-runtime");
799
+ var MemoryGame = () => {
800
+ const [difficulty, setDifficulty] = (0, import_react3.useState)("medium");
801
+ const [cards, setCards] = (0, import_react3.useState)([]);
802
+ const [flipped, setFlipped] = (0, import_react3.useState)([]);
803
+ const [solved, setSolved] = (0, import_react3.useState)([]);
804
+ const [disabled, setDisabled] = (0, import_react3.useState)(false);
805
+ const [score, setScore] = (0, import_react3.useState)(0);
806
+ const [moves, setMoves] = (0, import_react3.useState)(0);
807
+ const difficulties = {
808
+ easy: { pairs: 8, gridSize: 4 },
809
+ medium: { pairs: 12, gridSize: 4 },
810
+ hard: { pairs: 16, gridSize: 4 }
811
+ };
812
+ const emojis = ["\u{1F436}", "\u{1F431}", "\u{1F42D}", "\u{1F439}", "\u{1F430}", "\u{1F98A}", "\u{1F43B}", "\u{1F43C}", "\u{1F428}", "\u{1F42F}", "\u{1F981}", "\u{1F42E}", "\u{1F437}", "\u{1F438}", "\u{1F435}", "\u{1F414}"];
813
+ (0, import_react3.useEffect)(() => {
814
+ initializeGame();
815
+ }, [difficulty]);
816
+ (0, import_react3.useEffect)(() => {
817
+ if (score > 0) {
818
+ const event = new CustomEvent("gameScoreUpdate", {
819
+ detail: { game: "memory", score }
820
+ });
821
+ window.dispatchEvent(event);
822
+ }
823
+ }, [score]);
824
+ const initializeGame = () => {
825
+ const { pairs } = difficulties[difficulty];
826
+ const selectedEmojis = emojis.slice(0, pairs);
827
+ const cardPairs = [...selectedEmojis, ...selectedEmojis];
828
+ const shuffledCards = cardPairs.sort(() => Math.random() - 0.5).map((emoji, index) => ({
829
+ id: index,
830
+ emoji,
831
+ flipped: false
832
+ }));
833
+ setCards(shuffledCards);
834
+ setFlipped([]);
835
+ setSolved([]);
836
+ setScore(0);
837
+ setMoves(0);
838
+ };
839
+ const handleCardClick = (id) => {
840
+ if (disabled || flipped.length === 2 || solved.includes(id) || flipped.includes(id)) {
841
+ return;
842
+ }
843
+ const newFlipped = [...flipped, id];
844
+ setFlipped(newFlipped);
845
+ setMoves(moves + 1);
846
+ if (newFlipped.length === 2) {
847
+ setDisabled(true);
848
+ const [firstId, secondId] = newFlipped;
849
+ const firstCard = cards.find((card) => card.id === firstId);
850
+ const secondCard = cards.find((card) => card.id === secondId);
851
+ if (firstCard.emoji === secondCard.emoji) {
852
+ const bonus = difficulty === "hard" ? 15 : difficulty === "medium" ? 10 : 5;
853
+ setSolved([...solved, firstId, secondId]);
854
+ setScore(score + bonus);
855
+ setFlipped([]);
856
+ setDisabled(false);
857
+ } else {
858
+ setTimeout(() => {
859
+ setFlipped([]);
860
+ setDisabled(false);
861
+ }, 1e3);
862
+ }
863
+ }
864
+ };
865
+ const changeDifficulty = (newDifficulty) => {
866
+ if (window.confirm("\u0421\u043C\u0435\u043D\u0438\u0442\u044C \u0441\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C? \u0422\u0435\u043A\u0443\u0449\u0430\u044F \u0438\u0433\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0431\u0440\u043E\u0448\u0435\u043D\u0430.")) {
867
+ setDifficulty(newDifficulty);
868
+ }
869
+ };
870
+ const getCardSize = () => {
871
+ switch (difficulty) {
872
+ case "easy":
873
+ return "large";
874
+ case "medium":
875
+ return "medium";
876
+ case "hard":
877
+ return "small";
878
+ default:
879
+ return "medium";
880
+ }
881
+ };
882
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "memory-game", children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "difficulty-selector", children: [
884
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "difficulty-label", children: "\u0421\u043B\u043E\u0436\u043D\u043E\u0441\u0442\u044C:" }),
885
+ ["easy", "medium", "hard"].map((diff) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
886
+ "button",
887
+ {
888
+ className: `difficulty-btn ${difficulty === diff ? "active" : ""}`,
889
+ onClick: () => changeDifficulty(diff),
890
+ children: [
891
+ diff === "easy" && "\u{1F422} \u041B\u0435\u0433\u043A\u0430\u044F (8 \u043F\u0430\u0440)",
892
+ diff === "medium" && "\u26A1 \u0421\u0440\u0435\u0434\u043D\u044F\u044F (12 \u043F\u0430\u0440)",
893
+ diff === "hard" && "\u{1F525} \u0421\u043B\u043E\u0436\u043D\u0430\u044F (16 \u043F\u0430\u0440)"
894
+ ]
895
+ },
896
+ diff
897
+ ))
898
+ ] }),
899
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "game-stats", children: [
900
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "stat", children: [
901
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u0421\u0447\u0435\u0442:" }),
902
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: score })
903
+ ] }),
904
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "stat", children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u0425\u043E\u0434\u044B:" }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: moves })
907
+ ] }),
908
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "stat", children: [
909
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u041D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u0430\u0440:" }),
910
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: solved.length / 2 })
911
+ ] })
912
+ ] }),
913
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "memory-board-container", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `memory-board ${difficulty}`, children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
914
+ "div",
915
+ {
916
+ className: `memory-card ${getCardSize()} ${flipped.includes(card.id) || solved.includes(card.id) ? "flipped" : ""} ${solved.includes(card.id) ? "matched" : ""}`,
917
+ onClick: () => handleCardClick(card.id),
918
+ children: [
919
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "card-front", children: "?" }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "card-back", children: card.emoji })
921
+ ]
922
+ },
923
+ card.id
924
+ )) }) }),
925
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "game-controls", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "restart-btn", onClick: initializeGame, children: "\u041D\u043E\u0432\u0430\u044F \u0438\u0433\u0440\u0430" }) }),
926
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "game-instructions", children: [
927
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: "\u041D\u0430\u0439\u0434\u0438\u0442\u0435 \u0432\u0441\u0435 \u043F\u0430\u0440\u044B \u043E\u0434\u0438\u043D\u0430\u043A\u043E\u0432\u044B\u0445 \u044D\u043C\u043E\u0434\u0437\u0438!" }),
928
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { children: [
929
+ "\u0411\u043E\u043D\u0443\u0441 \u0437\u0430 \u043F\u0430\u0440\u0443: ",
930
+ difficulty === "hard" ? "15" : difficulty === "medium" ? "10" : "5",
931
+ " \u043E\u0447\u043A\u043E\u0432"
932
+ ] })
933
+ ] })
934
+ ] });
935
+ };
936
+ var MemoryGame_default = MemoryGame;
937
+
938
+ // src/games/Clicker/ClickerGame.js
939
+ var import_react4 = __toESM(require("react"));
940
+ var import_jsx_runtime4 = require("react/jsx-runtime");
941
+ var ClickerGame = () => {
942
+ const [clicks, setClicks] = (0, import_react4.useState)(0);
943
+ const [clickPower, setClickPower] = (0, import_react4.useState)(1);
944
+ const [autoClickerLevel, setAutoClickerLevel] = (0, import_react4.useState)(0);
945
+ const [autoClickerRate, setAutoClickerRate] = (0, import_react4.useState)(0);
946
+ const [multiplier, setMultiplier] = (0, import_react4.useState)(1);
947
+ const [upgrades, setUpgrades] = (0, import_react4.useState)([
948
+ { id: 1, name: "\u{1F4AA} \u0411\u043E\u043B\u0435\u0435 \u0441\u0438\u043B\u044C\u043D\u044B\u0439 \u043F\u0430\u043B\u0435\u0446", cost: 50, power: 2, purchased: false },
949
+ { id: 2, name: "\u26A1 \u042D\u043D\u0435\u0440\u0433\u0435\u0442\u0438\u043A", cost: 200, power: 5, purchased: false },
950
+ { id: 3, name: "\u{1F680} \u041A\u043E\u0441\u043C\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u043A\u043B\u0438\u043A\u0435\u0440", cost: 1e3, power: 10, purchased: false },
951
+ { id: 4, name: "\u{1F916} \u0410\u0432\u0442\u043E\u043A\u043B\u0438\u043A\u0435\u0440 Lv1", cost: 500, rate: 1, purchased: false },
952
+ { id: 5, name: "\u{1F3ED} \u0410\u0432\u0442\u043E\u043A\u043B\u0438\u043A\u0435\u0440 Lv2", cost: 2e3, rate: 5, purchased: false },
953
+ { id: 6, name: "\u{1F680} \u0410\u0432\u0442\u043E\u043A\u043B\u0438\u043A\u0435\u0440 Lv3", cost: 1e4, rate: 10, purchased: false },
954
+ { id: 7, name: "\u2728 \u041C\u043D\u043E\u0436\u0438\u0442\u0435\u043B\u044C x2", cost: 1500, mult: 2, purchased: false },
955
+ { id: 8, name: "\u{1F308} \u041C\u043D\u043E\u0436\u0438\u0442\u0435\u043B\u044C x5", cost: 7500, mult: 5, purchased: false }
956
+ ]);
957
+ const [abilities, setAbilities] = (0, import_react4.useState)([
958
+ { id: 1, name: "\u{1F4A5} \u0412\u0437\u0440\u044B\u0432\u043D\u043E\u0439 \u043A\u043B\u0438\u043A", cost: 100, cooldown: 30, currentCd: 0, power: 100 },
959
+ { id: 2, name: "\u23F1\uFE0F \u0423\u0441\u043A\u043E\u0440\u0435\u043D\u0438\u0435 \u0432\u0440\u0435\u043C\u0435\u043D\u0438", cost: 300, cooldown: 60, currentCd: 0, effect: "double" },
960
+ { id: 3, name: "\u{1F4B0} \u0417\u043E\u043B\u043E\u0442\u0430\u044F \u043B\u0438\u0445\u043E\u0440\u0430\u0434\u043A\u0430", cost: 1e3, cooldown: 120, currentCd: 0, effect: "bonus" }
961
+ ]);
962
+ const [achievements, setAchievements] = (0, import_react4.useState)([
963
+ { id: 1, name: "\u{1F3AF} \u041F\u0435\u0440\u0432\u044B\u0435 100 \u043A\u043B\u0438\u043A\u043E\u0432", target: 100, achieved: false },
964
+ { id: 2, name: "\u{1F4AA} 1000 \u0441\u0438\u043B\u044B", target: 1e3, achieved: false },
965
+ { id: 3, name: "\u{1F680} \u0410\u0432\u0442\u043E\u043A\u043B\u0438\u043A\u0435\u0440 \u043C\u0430\u0441\u0442\u0435\u0440", target: 5, achieved: false },
966
+ { id: 4, name: "\u{1F4B0} \u041C\u0438\u043B\u043B\u0438\u043E\u043D\u0435\u0440", target: 1e6, achieved: false }
967
+ ]);
968
+ const [shopOpen, setShopOpen] = (0, import_react4.useState)(false);
969
+ (0, import_react4.useEffect)(() => {
970
+ if (autoClickerRate > 0) {
971
+ const interval = setInterval(() => {
972
+ setClicks((prev) => prev + autoClickerRate * multiplier);
973
+ }, 1e3);
974
+ return () => clearInterval(interval);
975
+ }
976
+ }, [autoClickerRate, multiplier]);
977
+ (0, import_react4.useEffect)(() => {
978
+ const abilityInterval = setInterval(() => {
979
+ setAbilities((prev) => prev.map((ability) => ({
980
+ ...ability,
981
+ currentCd: ability.currentCd > 0 ? ability.currentCd - 1 : 0
982
+ })));
983
+ }, 1e3);
984
+ return () => clearInterval(abilityInterval);
985
+ }, []);
986
+ (0, import_react4.useEffect)(() => {
987
+ checkAchievements();
988
+ const event = new CustomEvent("gameScoreUpdate", {
989
+ detail: { game: "clicker", score: clicks }
990
+ });
991
+ window.dispatchEvent(event);
992
+ }, [clicks, clickPower, autoClickerLevel]);
993
+ const handleClick = () => {
994
+ setClicks((prev) => prev + clickPower * multiplier);
995
+ };
996
+ const buyUpgrade = (upgrade) => {
997
+ if (clicks >= upgrade.cost && !upgrade.purchased) {
998
+ setClicks((prev) => prev - upgrade.cost);
999
+ if (upgrade.power) {
1000
+ setClickPower((prev) => prev + upgrade.power);
1001
+ } else if (upgrade.rate) {
1002
+ setAutoClickerLevel((prev) => prev + 1);
1003
+ setAutoClickerRate((prev) => prev + upgrade.rate);
1004
+ } else if (upgrade.mult) {
1005
+ setMultiplier((prev) => prev * upgrade.mult);
1006
+ }
1007
+ setUpgrades((prev) => prev.map(
1008
+ (u) => u.id === upgrade.id ? { ...u, purchased: true } : u
1009
+ ));
1010
+ }
1011
+ };
1012
+ const activateAbility = (ability) => {
1013
+ if (clicks >= ability.cost && ability.currentCd === 0) {
1014
+ setClicks((prev) => prev - ability.cost);
1015
+ if (ability.power) {
1016
+ setClicks((prev) => prev + ability.power * multiplier);
1017
+ } else if (ability.effect === "double") {
1018
+ const oldMultiplier = multiplier;
1019
+ setMultiplier((prev) => prev * 2);
1020
+ setTimeout(() => setMultiplier(oldMultiplier), 1e4);
1021
+ } else if (ability.effect === "bonus") {
1022
+ setClicks((prev) => prev + 5e3 * multiplier);
1023
+ }
1024
+ setAbilities((prev) => prev.map(
1025
+ (a) => a.id === ability.id ? { ...a, currentCd: a.cooldown } : a
1026
+ ));
1027
+ }
1028
+ };
1029
+ const checkAchievements = () => {
1030
+ const newAchievements = achievements.map((achievement) => {
1031
+ let achieved = false;
1032
+ switch (achievement.id) {
1033
+ case 1:
1034
+ achieved = clicks >= achievement.target;
1035
+ break;
1036
+ case 2:
1037
+ achieved = clickPower >= achievement.target;
1038
+ break;
1039
+ case 3:
1040
+ achieved = autoClickerLevel >= achievement.target;
1041
+ break;
1042
+ case 4:
1043
+ achieved = clicks >= achievement.target;
1044
+ break;
1045
+ default:
1046
+ break;
1047
+ }
1048
+ return { ...achievement, achieved };
1049
+ });
1050
+ setAchievements(newAchievements);
1051
+ };
1052
+ const formatNumber = (num) => {
1053
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
1054
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}K`;
1055
+ return num.toString();
1056
+ };
1057
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "clicker-game", children: [
1058
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "game-header", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "header-controls", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1059
+ "button",
1060
+ {
1061
+ className: `shop-toggle ${shopOpen ? "active" : ""}`,
1062
+ onClick: () => setShopOpen(!shopOpen),
1063
+ children: shopOpen ? "\u2716\uFE0F \u0417\u0430\u043A\u0440\u044B\u0442\u044C \u043C\u0430\u0433\u0430\u0437\u0438\u043D" : "\u{1F6D2} \u041C\u0430\u0433\u0430\u0437\u0438\u043D"
1064
+ }
1065
+ ) }) }),
1066
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "game-stats", children: [
1067
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "stat-box", children: [
1068
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "stat-label", children: "\u041A\u043B\u0438\u043A\u043E\u0432" }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "stat-value click-count", children: formatNumber(clicks) })
1070
+ ] }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "stat-box", children: [
1072
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "stat-label", children: "\u0421\u0438\u043B\u0430 \u043A\u043B\u0438\u043A\u0430" }),
1073
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "stat-value", children: clickPower })
1074
+ ] }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "stat-box", children: [
1076
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "stat-label", children: "\u041C\u043D\u043E\u0436\u0438\u0442\u0435\u043B\u044C" }),
1077
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "stat-value multiplier", children: [
1078
+ "x",
1079
+ multiplier
1080
+ ] })
1081
+ ] }),
1082
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "stat-box", children: [
1083
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "stat-label", children: "\u0410\u0432\u0442\u043E\u043A\u043B\u0438\u043A\u0435\u0440" }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "stat-value", children: [
1085
+ autoClickerRate,
1086
+ "/\u0441\u0435\u043A"
1087
+ ] })
1088
+ ] })
1089
+ ] }),
1090
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "main-game-area", children: [
1091
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "clicker-container", children: [
1092
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1093
+ "button",
1094
+ {
1095
+ className: "click-button",
1096
+ onClick: handleClick,
1097
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "click-button-text", children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "click-power", children: [
1099
+ "+",
1100
+ clickPower * multiplier
1101
+ ] }),
1102
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "click-label", children: "\u041A\u041B\u0418\u041A\u0410\u0419!" }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "click-hint", children: "\u0417\u0430\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0439 \u043E\u0447\u043A\u0438" })
1104
+ ] })
1105
+ }
1106
+ ),
1107
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "abilities", children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: "\u{1F52E} \u0421\u043F\u043E\u0441\u043E\u0431\u043D\u043E\u0441\u0442\u0438" }),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "abilities-grid", children: abilities.map((ability) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1110
+ "button",
1111
+ {
1112
+ className: `ability-btn ${ability.currentCd > 0 ? "cooldown" : ""}`,
1113
+ onClick: () => activateAbility(ability),
1114
+ disabled: ability.currentCd > 0 || clicks < ability.cost,
1115
+ children: [
1116
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ability-name", children: ability.name }),
1117
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ability-cost", children: [
1118
+ "\u{1F4B0} ",
1119
+ formatNumber(ability.cost)
1120
+ ] }),
1121
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ability-cooldown", children: ability.currentCd > 0 ? `${ability.currentCd}\u0441` : "\u0413\u043E\u0442\u043E\u0432\u043E!" })
1122
+ ]
1123
+ },
1124
+ ability.id
1125
+ )) })
1126
+ ] })
1127
+ ] }),
1128
+ shopOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "shop-panel", children: [
1129
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: "\u{1F6D2} \u041C\u0430\u0433\u0430\u0437\u0438\u043D \u0443\u043B\u0443\u0447\u0448\u0435\u043D\u0438\u0439" }),
1130
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "upgrades-grid", children: upgrades.map((upgrade) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1131
+ "div",
1132
+ {
1133
+ className: `upgrade-item ${upgrade.purchased ? "purchased" : ""} ${clicks >= upgrade.cost ? "affordable" : ""}`,
1134
+ children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "upgrade-content", children: [
1136
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "upgrade-name", children: upgrade.name }),
1137
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "upgrade-stats", children: [
1138
+ upgrade.power && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
1139
+ "+",
1140
+ upgrade.power,
1141
+ " \u0441\u0438\u043B\u044B"
1142
+ ] }),
1143
+ upgrade.rate && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
1144
+ "+",
1145
+ upgrade.rate,
1146
+ "/\u0441\u0435\u043A"
1147
+ ] }),
1148
+ upgrade.mult && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
1149
+ "x",
1150
+ upgrade.mult,
1151
+ " \u043C\u043D\u043E\u0436\u0438\u0442\u0435\u043B\u044C"
1152
+ ] })
1153
+ ] }),
1154
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "upgrade-cost", children: [
1155
+ "\u{1F4B0} ",
1156
+ formatNumber(upgrade.cost)
1157
+ ] })
1158
+ ] }),
1159
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1160
+ "button",
1161
+ {
1162
+ className: "buy-btn",
1163
+ onClick: () => buyUpgrade(upgrade),
1164
+ disabled: upgrade.purchased || clicks < upgrade.cost,
1165
+ children: upgrade.purchased ? "\u041A\u0443\u043F\u043B\u0435\u043D\u043E" : "\u041A\u0443\u043F\u0438\u0442\u044C"
1166
+ }
1167
+ )
1168
+ ]
1169
+ },
1170
+ upgrade.id
1171
+ )) })
1172
+ ] })
1173
+ ] }),
1174
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "achievements", children: [
1175
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: "\u{1F3C6} \u0414\u043E\u0441\u0442\u0438\u0436\u0435\u043D\u0438\u044F" }),
1176
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "achievements-grid", children: achievements.map((achievement) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1177
+ "div",
1178
+ {
1179
+ className: `achievement ${achievement.achieved ? "achieved" : ""}`,
1180
+ children: [
1181
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "achievement-icon", children: achievement.achieved ? "\u2705" : "\u{1F512}" }),
1182
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "achievement-info", children: [
1183
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "achievement-name", children: achievement.name }),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "achievement-progress", children: [
1185
+ "\u041F\u0440\u043E\u0433\u0440\u0435\u0441\u0441: ",
1186
+ formatNumber(achievement.target)
1187
+ ] })
1188
+ ] })
1189
+ ]
1190
+ },
1191
+ achievement.id
1192
+ )) })
1193
+ ] }),
1194
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "game-instructions", children: [
1195
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { children: [
1196
+ "\u{1F4A1} ",
1197
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "\u0421\u043E\u0432\u0435\u0442:" }),
1198
+ " \u041F\u043E\u043A\u0443\u043F\u0430\u0439\u0442\u0435 \u0430\u0432\u0442\u043E\u043A\u043B\u0438\u043A\u0435\u0440\u044B \u0447\u0442\u043E\u0431\u044B \u0437\u0430\u0440\u0430\u0431\u0430\u0442\u044B\u0432\u0430\u0442\u044C \u043F\u0430\u0441\u0441\u0438\u0432\u043D\u043E!"
1199
+ ] }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "\u{1F3AF} \u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0441\u043F\u043E\u0441\u043E\u0431\u043D\u043E\u0441\u0442\u0438 \u0434\u043B\u044F \u0431\u043E\u043B\u044C\u0448\u0438\u0445 \u0440\u044B\u0432\u043A\u043E\u0432!" })
1201
+ ] })
1202
+ ] });
1203
+ };
1204
+ var ClickerGame_default = ClickerGame;
1205
+
1206
+ // src/games/Pong/Pong.js
1207
+ var import_react5 = __toESM(require("react"));
1208
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1209
+ var Pong = () => {
1210
+ const [gameActive, setGameActive] = (0, import_react5.useState)(false);
1211
+ const [playerScore, setPlayerScore] = (0, import_react5.useState)(0);
1212
+ const [computerScore, setComputerScore] = (0, import_react5.useState)(0);
1213
+ const [roundSpeed, setRoundSpeed] = (0, import_react5.useState)(1);
1214
+ const [winner, setWinner] = (0, import_react5.useState)(null);
1215
+ const [highScore, setHighScore] = (0, import_react5.useState)(0);
1216
+ const [winStreak, setWinStreak] = (0, import_react5.useState)(0);
1217
+ const [roundTime, setRoundTime] = (0, import_react5.useState)(0);
1218
+ const canvasRef = (0, import_react5.useRef)(null);
1219
+ const animationRef = (0, import_react5.useRef)(null);
1220
+ const roundStartTimeRef = (0, import_react5.useRef)(0);
1221
+ const timeIntervalRef = (0, import_react5.useRef)(null);
1222
+ const gameStateRef = (0, import_react5.useRef)({
1223
+ player: { x: 0, y: 180, width: 10, height: 60, score: 0 },
1224
+ computer: { x: 390, y: 180, width: 10, height: 60, score: 0 },
1225
+ ball: { x: 200, y: 200, radius: 8, speed: 2, velocityX: 2, velocityY: 2 },
1226
+ // Уменьшена начальная скорость
1227
+ canvasWidth: 400,
1228
+ canvasHeight: 300
1229
+ });
1230
+ (0, import_react5.useEffect)(() => {
1231
+ const saved = localStorage.getItem("pongHighScore");
1232
+ if (saved) setHighScore(parseInt(saved));
1233
+ }, []);
1234
+ (0, import_react5.useEffect)(() => {
1235
+ const event = new CustomEvent("gameScoreUpdate", {
1236
+ detail: { game: "pong", score: playerScore }
1237
+ });
1238
+ window.dispatchEvent(event);
1239
+ }, [playerScore]);
1240
+ (0, import_react5.useEffect)(() => {
1241
+ if (gameActive) {
1242
+ roundStartTimeRef.current = Date.now();
1243
+ timeIntervalRef.current = setInterval(() => {
1244
+ const elapsed = Math.floor((Date.now() - roundStartTimeRef.current) / 1e3);
1245
+ setRoundTime(elapsed);
1246
+ const newSpeed = 1 + elapsed / 10 * 0.15;
1247
+ setRoundSpeed(Math.min(newSpeed, 1.8));
1248
+ }, 1e3);
1249
+ } else {
1250
+ if (timeIntervalRef.current) {
1251
+ clearInterval(timeIntervalRef.current);
1252
+ timeIntervalRef.current = null;
1253
+ }
1254
+ setRoundTime(0);
1255
+ setRoundSpeed(1);
1256
+ }
1257
+ return () => {
1258
+ if (timeIntervalRef.current) {
1259
+ clearInterval(timeIntervalRef.current);
1260
+ }
1261
+ };
1262
+ }, [gameActive]);
1263
+ const handleGameEnd = (0, import_react5.useCallback)((winner2) => {
1264
+ setWinner(winner2);
1265
+ setGameActive(false);
1266
+ if (winner2 === "player") {
1267
+ const newStreak = winStreak + 1;
1268
+ setWinStreak(newStreak);
1269
+ if (newStreak > highScore) {
1270
+ setHighScore(newStreak);
1271
+ localStorage.setItem("pongHighScore", newStreak.toString());
1272
+ }
1273
+ } else if (winner2 === "computer") {
1274
+ setWinStreak(0);
1275
+ }
1276
+ }, [winStreak, highScore]);
1277
+ const resetHighScore = (0, import_react5.useCallback)(() => {
1278
+ localStorage.removeItem("pongHighScore");
1279
+ setHighScore(0);
1280
+ setWinStreak(0);
1281
+ alert("\u0420\u0435\u043A\u043E\u0440\u0434 \u0441\u0431\u0440\u043E\u0448\u0435\u043D!");
1282
+ }, []);
1283
+ (0, import_react5.useEffect)(() => {
1284
+ return () => {
1285
+ if (animationRef.current) cancelAnimationFrame(animationRef.current);
1286
+ if (timeIntervalRef.current) clearInterval(timeIntervalRef.current);
1287
+ };
1288
+ }, []);
1289
+ const drawRect = (0, import_react5.useCallback)((ctx, x, y, w, h, color) => {
1290
+ ctx.fillStyle = color;
1291
+ ctx.fillRect(x, y, w, h);
1292
+ }, []);
1293
+ const drawCircle = (0, import_react5.useCallback)((ctx, x, y, r, color) => {
1294
+ ctx.fillStyle = color;
1295
+ ctx.beginPath();
1296
+ ctx.arc(x, y, r, 0, Math.PI * 2);
1297
+ ctx.closePath();
1298
+ ctx.fill();
1299
+ }, []);
1300
+ const drawText = (0, import_react5.useCallback)((ctx, text, x, y, color, size = "24px") => {
1301
+ ctx.fillStyle = color;
1302
+ ctx.font = `bold ${size} 'Courier New', monospace`;
1303
+ ctx.fillText(text, x, y);
1304
+ }, []);
1305
+ const drawNet = (0, import_react5.useCallback)((ctx, canvasWidth, canvasHeight) => {
1306
+ for (let i = 0; i <= canvasHeight; i += 15) {
1307
+ drawRect(ctx, canvasWidth / 2 - 1, i, 2, 8, "#667eea");
1308
+ }
1309
+ }, [drawRect]);
1310
+ const collision = (0, import_react5.useCallback)((ball, paddle) => {
1311
+ const b = {
1312
+ top: ball.y - ball.radius,
1313
+ bottom: ball.y + ball.radius,
1314
+ left: ball.x - ball.radius,
1315
+ right: ball.x + ball.radius
1316
+ };
1317
+ const p = {
1318
+ top: paddle.y,
1319
+ bottom: paddle.y + paddle.height,
1320
+ left: paddle.x,
1321
+ right: paddle.x + paddle.width
1322
+ };
1323
+ const isColliding = b.right > p.left && b.bottom > p.top && b.left < p.right && b.top < p.bottom;
1324
+ if (isColliding) {
1325
+ const randomAngle = (Math.random() - 0.5) * 0.3;
1326
+ return randomAngle;
1327
+ }
1328
+ return null;
1329
+ }, []);
1330
+ const resetBallForRound = (0, import_react5.useCallback)(() => {
1331
+ const state = gameStateRef.current;
1332
+ state.ball.x = state.canvasWidth / 2;
1333
+ state.ball.y = state.canvasHeight / 2;
1334
+ const direction = Math.random() > 0.5 ? 1 : -1;
1335
+ const angle = Math.random() * Math.PI / 3 - Math.PI / 6;
1336
+ state.ball.speed = 2;
1337
+ state.ball.velocityX = direction * state.ball.speed * Math.cos(angle);
1338
+ state.ball.velocityY = state.ball.speed * Math.sin(angle);
1339
+ }, []);
1340
+ const update = (0, import_react5.useCallback)(() => {
1341
+ const state = gameStateRef.current;
1342
+ const { ball, player, computer, canvasWidth, canvasHeight } = state;
1343
+ if (ball.x - ball.radius < 0) {
1344
+ computer.score++;
1345
+ setComputerScore(computer.score);
1346
+ resetBallForRound();
1347
+ setRoundSpeed(1);
1348
+ setRoundTime(0);
1349
+ roundStartTimeRef.current = Date.now();
1350
+ if (computer.score >= 5) {
1351
+ handleGameEnd("computer");
1352
+ }
1353
+ return;
1354
+ } else if (ball.x + ball.radius > canvasWidth) {
1355
+ player.score++;
1356
+ setPlayerScore(player.score);
1357
+ resetBallForRound();
1358
+ setRoundSpeed(1);
1359
+ setRoundTime(0);
1360
+ roundStartTimeRef.current = Date.now();
1361
+ if (player.score >= 5) {
1362
+ handleGameEnd("player");
1363
+ }
1364
+ return;
1365
+ }
1366
+ ball.x += ball.velocityX * roundSpeed;
1367
+ ball.y += ball.velocityY * roundSpeed;
1368
+ const computerCenter = computer.y + computer.height / 2;
1369
+ const predictionY = ball.y + ball.velocityY * 5;
1370
+ const targetY = predictionY - computer.height / 2;
1371
+ const diff = targetY - computer.y;
1372
+ let moveAmount = diff * 0.07 * Math.min(roundSpeed, 1.3);
1373
+ const maxComputerSpeed = 1.5;
1374
+ if (Math.abs(moveAmount) > maxComputerSpeed) {
1375
+ moveAmount = Math.sign(moveAmount) * maxComputerSpeed;
1376
+ }
1377
+ computer.y += moveAmount;
1378
+ computer.y = Math.max(0, Math.min(canvasHeight - computer.height, computer.y));
1379
+ player.y = Math.max(0, Math.min(canvasHeight - player.height, player.y));
1380
+ if (ball.y - ball.radius < 0) {
1381
+ ball.y = ball.radius + 1;
1382
+ ball.velocityY = Math.abs(ball.velocityY) * 0.9;
1383
+ ball.velocityY += Math.random() * 0.15;
1384
+ } else if (ball.y + ball.radius > canvasHeight) {
1385
+ ball.y = canvasHeight - ball.radius - 1;
1386
+ ball.velocityY = -Math.abs(ball.velocityY) * 0.9;
1387
+ ball.velocityY -= Math.random() * 0.15;
1388
+ }
1389
+ const minVerticalSpeed = 0.4;
1390
+ if (Math.abs(ball.velocityY) < minVerticalSpeed) {
1391
+ const sign = ball.velocityY >= 0 ? 1 : -1;
1392
+ ball.velocityY = sign * minVerticalSpeed;
1393
+ }
1394
+ const isHorizontalStuck = Math.abs(ball.velocityY) < 0.2 && Math.abs(ball.velocityX) > 1;
1395
+ if (isHorizontalStuck) {
1396
+ ball.velocityY += (Math.random() - 0.5) * 0.5;
1397
+ }
1398
+ const playerCollision = collision(ball, player);
1399
+ const computerCollision = collision(ball, computer);
1400
+ let collisionAngle = null;
1401
+ let paddle = null;
1402
+ if (playerCollision !== null && ball.velocityX < 0) {
1403
+ collisionAngle = playerCollision;
1404
+ paddle = player;
1405
+ } else if (computerCollision !== null && ball.velocityX > 0) {
1406
+ collisionAngle = computerCollision;
1407
+ paddle = computer;
1408
+ }
1409
+ if (collisionAngle !== null && paddle) {
1410
+ let collidePoint = (ball.y - (paddle.y + paddle.height / 2)) / (paddle.height / 2);
1411
+ collidePoint = Math.max(-0.8, Math.min(0.8, collidePoint));
1412
+ let angleRad = Math.PI / 4 * collidePoint;
1413
+ angleRad += collisionAngle;
1414
+ let direction = ball.x < canvasWidth / 2 ? 1 : -1;
1415
+ const currentSpeed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY);
1416
+ const newSpeed = Math.min(currentSpeed * 1.03, 5);
1417
+ ball.speed = newSpeed;
1418
+ ball.velocityX = direction * ball.speed * Math.cos(angleRad);
1419
+ ball.velocityY = ball.speed * Math.sin(angleRad);
1420
+ if (direction > 0) {
1421
+ ball.x = paddle.x + paddle.width + ball.radius + 1;
1422
+ } else {
1423
+ ball.x = paddle.x - ball.radius - 1;
1424
+ }
1425
+ }
1426
+ }, [roundSpeed, collision, resetBallForRound, handleGameEnd]);
1427
+ const render = (0, import_react5.useCallback)(() => {
1428
+ const canvas = canvasRef.current;
1429
+ if (!canvas) return;
1430
+ const ctx = canvas.getContext("2d");
1431
+ const state = gameStateRef.current;
1432
+ const { canvasWidth, canvasHeight, player, computer, ball } = state;
1433
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
1434
+ drawRect(ctx, 0, 0, canvasWidth, canvasHeight, "#1a1a2e");
1435
+ drawNet(ctx, canvasWidth, canvasHeight);
1436
+ drawText(ctx, player.score.toString(), canvasWidth / 4, 40, "#667eea", "32px");
1437
+ drawText(ctx, computer.score.toString(), 3 * canvasWidth / 4, 40, "#ff5e62", "32px");
1438
+ drawText(ctx, `${formatTime(roundTime)}`, canvasWidth / 2 - 40, 25, "#ff9966", "14px");
1439
+ if (roundSpeed > 1.1) {
1440
+ drawText(ctx, `x${roundSpeed.toFixed(1)}`, canvasWidth / 2 + 20, 25, "#00b09b", "14px");
1441
+ }
1442
+ drawRect(ctx, player.x, player.y, player.width, player.height, "#667eea");
1443
+ drawRect(ctx, computer.x, computer.y, computer.width, computer.height, "#ff5e62");
1444
+ drawCircle(ctx, ball.x, ball.y, ball.radius, "#00b09b");
1445
+ ctx.strokeStyle = "rgba(255,255,255,0.1)";
1446
+ ctx.setLineDash([5, 10]);
1447
+ ctx.beginPath();
1448
+ ctx.moveTo(canvasWidth / 2, 0);
1449
+ ctx.lineTo(canvasWidth / 2, canvasHeight);
1450
+ ctx.stroke();
1451
+ ctx.setLineDash([]);
1452
+ }, [drawRect, drawCircle, drawText, drawNet, roundTime, roundSpeed]);
1453
+ const formatTime = (0, import_react5.useCallback)((seconds) => {
1454
+ const mins = Math.floor(seconds / 60);
1455
+ const secs = seconds % 60;
1456
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
1457
+ }, []);
1458
+ const gameLoop = (0, import_react5.useCallback)(() => {
1459
+ if (!gameActive) return;
1460
+ update();
1461
+ render();
1462
+ animationRef.current = requestAnimationFrame(gameLoop);
1463
+ }, [gameActive, update, render]);
1464
+ (0, import_react5.useEffect)(() => {
1465
+ if (gameActive) {
1466
+ animationRef.current = requestAnimationFrame(gameLoop);
1467
+ } else {
1468
+ if (animationRef.current) {
1469
+ cancelAnimationFrame(animationRef.current);
1470
+ }
1471
+ }
1472
+ return () => {
1473
+ if (animationRef.current) {
1474
+ cancelAnimationFrame(animationRef.current);
1475
+ }
1476
+ };
1477
+ }, [gameActive, gameLoop]);
1478
+ const handleMouseMove = (0, import_react5.useCallback)((e) => {
1479
+ if (!gameActive || !canvasRef.current) return;
1480
+ const canvas = canvasRef.current;
1481
+ const rect = canvas.getBoundingClientRect();
1482
+ const state = gameStateRef.current;
1483
+ const y = e.clientY - rect.top - state.player.height / 2;
1484
+ state.player.y = Math.max(0, Math.min(state.canvasHeight - state.player.height, y));
1485
+ }, [gameActive]);
1486
+ const handleTouchMove = (0, import_react5.useCallback)((e) => {
1487
+ if (!gameActive || !canvasRef.current) return;
1488
+ const touch = e.touches[0];
1489
+ handleMouseMove(touch);
1490
+ }, [gameActive, handleMouseMove]);
1491
+ const startGame = (0, import_react5.useCallback)(() => {
1492
+ setGameActive(true);
1493
+ setPlayerScore(0);
1494
+ setComputerScore(0);
1495
+ setWinner(null);
1496
+ setRoundSpeed(1);
1497
+ setRoundTime(0);
1498
+ gameStateRef.current = {
1499
+ player: { x: 0, y: 180, width: 10, height: 60, score: 0 },
1500
+ computer: { x: 390, y: 180, width: 10, height: 60, score: 0 },
1501
+ ball: { x: 200, y: 200, radius: 8, speed: 2, velocityX: 2, velocityY: 0 },
1502
+ // Уменьшена начальная скорость
1503
+ canvasWidth: 400,
1504
+ canvasHeight: 300
1505
+ };
1506
+ resetBallForRound();
1507
+ }, [resetBallForRound]);
1508
+ const resetGame = (0, import_react5.useCallback)(() => {
1509
+ setGameActive(false);
1510
+ setPlayerScore(0);
1511
+ setComputerScore(0);
1512
+ setWinner(null);
1513
+ setWinStreak(0);
1514
+ setRoundSpeed(1);
1515
+ setRoundTime(0);
1516
+ if (animationRef.current) {
1517
+ cancelAnimationFrame(animationRef.current);
1518
+ }
1519
+ }, []);
1520
+ (0, import_react5.useEffect)(() => {
1521
+ const canvas = canvasRef.current;
1522
+ if (!canvas) return;
1523
+ canvas.addEventListener("mousemove", handleMouseMove);
1524
+ canvas.addEventListener("touchmove", handleTouchMove, { passive: false });
1525
+ return () => {
1526
+ canvas.removeEventListener("mousemove", handleMouseMove);
1527
+ canvas.removeEventListener("touchmove", handleTouchMove);
1528
+ };
1529
+ }, [handleMouseMove, handleTouchMove]);
1530
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pong-game", children: [
1531
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "game-header", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "game-controls", children: [
1532
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1533
+ "button",
1534
+ {
1535
+ className: "control-btn",
1536
+ onClick: gameActive ? resetGame : startGame,
1537
+ children: gameActive ? "\u{1F504} \u041D\u0430\u0447\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E" : "\u25B6\uFE0F \u041D\u0430\u0447\u0430\u0442\u044C \u0438\u0433\u0440\u0443"
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1541
+ "button",
1542
+ {
1543
+ className: "control-btn reset-btn",
1544
+ onClick: resetHighScore,
1545
+ disabled: gameActive,
1546
+ children: "\u{1F5D1}\uFE0F \u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u0440\u0435\u043A\u043E\u0440\u0434"
1547
+ }
1548
+ )
1549
+ ] }) }),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "game-stats", children: [
1551
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "stat-box", children: [
1552
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-label", children: "\u0418\u0433\u0440\u043E\u043A" }),
1553
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-value player-score", children: playerScore })
1554
+ ] }),
1555
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "stat-box", children: [
1556
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-label", children: "\u041A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440" }),
1557
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-value computer-score", children: computerScore })
1558
+ ] }),
1559
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "stat-box", children: [
1560
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-label", children: "\u0420\u0435\u043A\u043E\u0440\u0434" }),
1561
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "stat-value", children: [
1562
+ highScore,
1563
+ " \u{1F525}"
1564
+ ] })
1565
+ ] }),
1566
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "stat-box", children: [
1567
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-label", children: "\u0420\u0430\u0443\u043D\u0434" }),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "stat-value time-value", children: formatTime(roundTime) })
1569
+ ] })
1570
+ ] }),
1571
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "game-area", children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pong-container", children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1574
+ "canvas",
1575
+ {
1576
+ ref: canvasRef,
1577
+ width: 400,
1578
+ height: 300,
1579
+ className: "pong-canvas"
1580
+ }
1581
+ ),
1582
+ !gameActive && winner && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "game-over-screen", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "game-over-message", children: [
1583
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { children: winner === "player" ? "\u{1F3C6} \u041F\u043E\u0431\u0435\u0434\u0430!" : "\u{1F480} \u041F\u043E\u0440\u0430\u0436\u0435\u043D\u0438\u0435" }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "final-score", children: [
1585
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1586
+ "\u0421\u0447\u0435\u0442: ",
1587
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("strong", { children: [
1588
+ playerScore,
1589
+ ":",
1590
+ computerScore
1591
+ ] })
1592
+ ] }),
1593
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1594
+ "\u0421\u0435\u0440\u0438\u044F \u043F\u043E\u0431\u0435\u0434: ",
1595
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: winStreak })
1596
+ ] }),
1597
+ winner === "player" && winStreak > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "streak-message", children: [
1598
+ "\u{1F525} \u0412\u044B\u0438\u0433\u0440\u0430\u043D\u043E ",
1599
+ winStreak,
1600
+ " \u043F\u0430\u0440\u0442\u0438\u0439 \u043F\u043E\u0434\u0440\u044F\u0434!"
1601
+ ] })
1602
+ ] }),
1603
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "play-again-btn", onClick: startGame, children: "\u{1F3AE} \u041D\u043E\u0432\u0430\u044F \u043F\u0430\u0440\u0442\u0438\u044F" })
1604
+ ] }) }),
1605
+ !gameActive && !winner && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "start-screen", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "start-message", children: [
1606
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { children: "\u{1F3D3} \u041F\u0438\u043D\u0433-\u041F\u043E\u043D\u0433" }),
1607
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "\u0414\u0432\u0438\u0433\u0430\u0439\u0442\u0435 \u043C\u044B\u0448\u044C\u044E \u0434\u043B\u044F \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0440\u0430\u043A\u0435\u0442\u043A\u043E\u0439" }),
1608
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "\u041F\u0430\u0440\u0442\u0438\u044F: \u043F\u0435\u0440\u0432\u044B\u0439 \u0434\u043E 5 \u043E\u0447\u043A\u043E\u0432" }),
1609
+ highScore > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "record-info", children: [
1610
+ "\u{1F3C6} \u0420\u0435\u043A\u043E\u0440\u0434: ",
1611
+ highScore,
1612
+ " \u043F\u043E\u0431\u0435\u0434 \u043F\u043E\u0434\u0440\u044F\u0434"
1613
+ ] }),
1614
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "start-btn-big", onClick: startGame, children: "\u041D\u0410\u0427\u0410\u0422\u042C \u041F\u0410\u0420\u0422\u0418\u042E" })
1615
+ ] }) })
1616
+ ] }),
1617
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "speed-info", children: [
1618
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "speed-display", children: [
1619
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "speed-label", children: "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C \u0440\u0430\u0443\u043D\u0434\u0430: " }),
1620
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "speed-value", children: [
1621
+ "x",
1622
+ roundSpeed.toFixed(1)
1623
+ ] })
1624
+ ] }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "speed-hint", children: "\u26A1 \u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C \u043C\u0435\u0434\u043B\u0435\u043D\u043D\u043E \u0440\u0430\u0441\u0442\u0435\u0442 \u0438 \u0441\u0431\u0440\u0430\u0441\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0433\u043E\u043B\u0435" })
1626
+ ] })
1627
+ ] }),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "game-instructions", children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "instructions-row", children: [
1630
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1631
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435:" }),
1632
+ " \u0414\u0432\u0438\u0433\u0430\u0439\u0442\u0435 \u043C\u044B\u0448\u044C\u044E \u0432\u0432\u0435\u0440\u0445/\u0432\u043D\u0438\u0437"
1633
+ ] }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: "\u0426\u0435\u043B\u044C:" }),
1636
+ " \u0412\u044B\u0438\u0433\u0440\u0430\u0439\u0442\u0435 5 \u0440\u0430\u0443\u043D\u0434\u043E\u0432"
1637
+ ] })
1638
+ ] }),
1639
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "legend", children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "legend-item", children: [
1641
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "legend-color player-color" }),
1642
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u0418\u0433\u0440\u043E\u043A (\u0441\u0438\u043D\u0438\u0439)" })
1643
+ ] }),
1644
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "legend-item", children: [
1645
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "legend-color computer-color" }),
1646
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u041A\u043E\u043C\u043F\u044C\u044E\u0442\u0435\u0440 (\u043A\u0440\u0430\u0441\u043D\u044B\u0439)" })
1647
+ ] }),
1648
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "legend-item", children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "legend-color ball-color" }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u041C\u044F\u0447" })
1651
+ ] })
1652
+ ] })
1653
+ ] })
1654
+ ] });
1655
+ };
1656
+ var Pong_default = Pong;
1657
+
1658
+ // src/games/Platformer/PlatformerGame.js
1659
+ var import_react6 = __toESM(require("react"));
1660
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1661
+ var PlatformerGame = () => {
1662
+ const [score, setScore] = (0, import_react6.useState)(0);
1663
+ const [gameActive, setGameActive] = (0, import_react6.useState)(false);
1664
+ const [level, setLevel] = (0, import_react6.useState)(0);
1665
+ const [lives, setLives] = (0, import_react6.useState)(3);
1666
+ const [gameStatus, setGameStatus] = (0, import_react6.useState)("ready");
1667
+ const [coinsCollected, setCoinsCollected] = (0, import_react6.useState)(0);
1668
+ const [totalCoins, setTotalCoins] = (0, import_react6.useState)(0);
1669
+ const gameContainerRef = (0, import_react6.useRef)(null);
1670
+ const canvasRef = (0, import_react6.useRef)(null);
1671
+ const animationRef = (0, import_react6.useRef)(null);
1672
+ const gameStateRef = (0, import_react6.useRef)(null);
1673
+ const keysRef = (0, import_react6.useRef)({ left: false, right: false, up: false });
1674
+ const deathAnimationRef = (0, import_react6.useRef)({ active: false, time: 0, scale: 1 });
1675
+ const LEVELS = [
1676
+ [
1677
+ " ",
1678
+ " ",
1679
+ " ",
1680
+ " ",
1681
+ " ",
1682
+ " ",
1683
+ " xxx ",
1684
+ " xx xx xx!xx ",
1685
+ " o o xx x!!!x ",
1686
+ " xx!xx ",
1687
+ " xxxxx xvx ",
1688
+ " xx ",
1689
+ " xx o o x ",
1690
+ " x o x ",
1691
+ " x xxxxx o x ",
1692
+ " x xxxx o x ",
1693
+ " x @ x x xxxxx x ",
1694
+ " xxxxxxxxxxxx xxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxx ",
1695
+ " x x x x ",
1696
+ " x!!!x x!!!!!x ",
1697
+ " x!!!x x!!!!!x ",
1698
+ " xxxxx xxxxxxx ",
1699
+ " ",
1700
+ " "
1701
+ ],
1702
+ [
1703
+ " x!!x xxxxxxx x!x ",
1704
+ " x!!x xxxx xxxx x!x ",
1705
+ " x!!xxxxxxxxxx xx xx x!x ",
1706
+ " xx!!!!!!!!!!xx xx xx x!x ",
1707
+ " xxxxxxxxxx!!x x o o o x!x ",
1708
+ " xx!x x o o xx!x ",
1709
+ " x!x x xxxxxxxxxxxxxxx!!x ",
1710
+ " xvx x x x !!!!!!!!!!!!!!xx ",
1711
+ " xx | | | xx xxxxxxxxxxxxxxxxxxxxx ",
1712
+ " xx!!!!!!!!!!!xx v ",
1713
+ " xxxx!!!!!xxxx ",
1714
+ " x x xxxxxxx xxx xxx ",
1715
+ " x x x x x x ",
1716
+ " x x x x ",
1717
+ " x x xx x ",
1718
+ " xx x x x ",
1719
+ " x x o o x x x x ",
1720
+ " xxxxxxx xxx xxx x x x x x x ",
1721
+ " xx xx x x x x xxxxxx x x xxxxxxxxx x ",
1722
+ " xx xx x o x x xx x x x x ",
1723
+ " @ x x x x x x x x x x ",
1724
+ " xxx x x x x x x x xxxxx xxxxxx x ",
1725
+ " x x x x xx o xx x x x o x x x ",
1726
+ "!!!!x x!!!!!!x x!!!!!!xx xx!!!!!!!!xx x!!!!!!!!!! x = x x x ",
1727
+ "!!!!x x!!!!!!x x!!!!!xx xxxxxxxxxx x!!!!!!!xx! xxxxxxxxxxxxx xx o o xx ",
1728
+ "!!!!x x!!!!!!x x!!!!!x o xx!!!!!!xx ! xx xx ",
1729
+ "!!!!x x!!!!!!x x!!!!!x xx!!!!!!xx ! xxxxxxx ",
1730
+ "!!!!x x!!!!!!x x!!!!!xx xxxxxxxxxxxxxx!!!!!!xx ! ",
1731
+ "!!!!x x!!!!!!x x!!!!!!xxxxxxxxx!!!!!!!!!!!!!!!!!!xx ! ",
1732
+ "!!!!x x!!!!!!x x!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!xx ! "
1733
+ ],
1734
+ // Третий уровень (прототип)
1735
+ [
1736
+ " ",
1737
+ " ",
1738
+ " ",
1739
+ " ",
1740
+ " ",
1741
+ " o ",
1742
+ " ",
1743
+ " x ",
1744
+ " x ",
1745
+ " x ",
1746
+ " x ",
1747
+ " xxx ",
1748
+ " x x !!! !!! xxx ",
1749
+ " x x !x! !x! ",
1750
+ " xxx xxx x x ",
1751
+ " x x x oooo x xxx ",
1752
+ " x x x x x!!!x ",
1753
+ " x x xxxxxxxxxxxx xxx ",
1754
+ " xx xx x x x ",
1755
+ " x xxxxxxxxx xxxxxxxx x x ",
1756
+ " x x x x!!!x ",
1757
+ " x x x xxx ",
1758
+ " xx xx x ",
1759
+ " x x= = = = x xxx ",
1760
+ " x x x x!!!x ",
1761
+ " x x = = = =x o xxx xxx ",
1762
+ " xx xx x x!!!x ",
1763
+ " o o x x x x xxv xxx ",
1764
+ " x x x x x!!!x ",
1765
+ " xxx xxx xxx xxx o o x!!!!!!!!!!!!!!x vx ",
1766
+ " x xxx x x xxx x x!!!!!!!!!!!!!!x ",
1767
+ " x x xxxxxxxxxxxxxxxxxxxxxxx ",
1768
+ " xx xx xxx ",
1769
+ " xxx x x x x!!!x xxx ",
1770
+ " x x x xxx x xxx x x ",
1771
+ " x x xxx xxxxxxx xxxxx x ",
1772
+ " x x x x x x ",
1773
+ " x xx x x x x x ",
1774
+ " x x |xxxx| |xxxx| xxx xxx x ",
1775
+ " x xxx o o x x xxx x ",
1776
+ " x xxxxx xx x xxx x!!!x x x ",
1777
+ " x oxxxo x xxx x x x xxx xxx x ",
1778
+ " x xxx xxxxxxxxxxxxx x oo x x oo x x oo xx xx xxx x ",
1779
+ " x @ x x x!!x x!!!!x x!!!!x xx xx x x ",
1780
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ",
1781
+ " ",
1782
+ " "
1783
+ ]
1784
+ ];
1785
+ class Vector {
1786
+ constructor(x, y) {
1787
+ this.x = x;
1788
+ this.y = y;
1789
+ }
1790
+ plus(other) {
1791
+ return new Vector(this.x + other.x, this.y + other.y);
1792
+ }
1793
+ times(scale) {
1794
+ return new Vector(this.x * scale, this.y * scale);
1795
+ }
1796
+ }
1797
+ class Player {
1798
+ constructor(pos) {
1799
+ this.pos = pos.plus(new Vector(0, -0.5));
1800
+ this.size = new Vector(0.5, 1);
1801
+ this.speed = new Vector(0, 0);
1802
+ this.type = "player";
1803
+ this.alive = true;
1804
+ this.canJump = false;
1805
+ this.jumpCooldown = 0;
1806
+ }
1807
+ moveX(step, level2, keys) {
1808
+ const playerXSpeed = 10;
1809
+ this.speed.x = 0;
1810
+ if (keys.left) this.speed.x -= playerXSpeed;
1811
+ if (keys.right) this.speed.x += playerXSpeed;
1812
+ const motion = new Vector(this.speed.x * step, 0);
1813
+ const newPos = this.pos.plus(motion);
1814
+ const obstacle = level2.obstacleAt(newPos, this.size);
1815
+ if (!obstacle || obstacle === "lava") {
1816
+ this.pos = newPos;
1817
+ }
1818
+ }
1819
+ moveY(step, level2, keys) {
1820
+ const gravity = 30;
1821
+ const jumpSpeed = 17;
1822
+ this.speed.y += step * gravity;
1823
+ const motion = new Vector(0, this.speed.y * step);
1824
+ const newPos = this.pos.plus(motion);
1825
+ const obstacle = level2.obstacleAt(newPos, this.size);
1826
+ if (obstacle) {
1827
+ if (obstacle === "lava") {
1828
+ this.alive = false;
1829
+ level2.status = "lost";
1830
+ this.speed.y = 0;
1831
+ return;
1832
+ }
1833
+ if (keys.up && this.canJump && this.jumpCooldown <= 0) {
1834
+ this.speed.y = -jumpSpeed;
1835
+ this.canJump = false;
1836
+ this.jumpCooldown = 0.2;
1837
+ } else {
1838
+ this.speed.y = 0;
1839
+ this.canJump = true;
1840
+ }
1841
+ } else {
1842
+ this.pos = newPos;
1843
+ this.canJump = false;
1844
+ }
1845
+ if (this.jumpCooldown > 0) {
1846
+ this.jumpCooldown -= step;
1847
+ }
1848
+ }
1849
+ act(step, level2, keys) {
1850
+ if (!this.alive) return;
1851
+ this.moveX(step, level2, keys);
1852
+ this.moveY(step, level2, keys);
1853
+ if (this.pos.y > level2.height) {
1854
+ this.alive = false;
1855
+ level2.status = "lost";
1856
+ }
1857
+ }
1858
+ }
1859
+ class Coin {
1860
+ constructor(pos) {
1861
+ this.basePos = this.pos = pos;
1862
+ this.size = new Vector(0.6, 0.6);
1863
+ this.wobble = Math.random() * Math.PI * 2;
1864
+ this.type = "coin";
1865
+ this.collected = false;
1866
+ }
1867
+ act(step) {
1868
+ if (this.collected) return;
1869
+ const wobbleSpeed = 8;
1870
+ const wobbleDist = 0.07;
1871
+ this.wobble += step * wobbleSpeed;
1872
+ const wobblePos = Math.sin(this.wobble) * wobbleDist;
1873
+ this.pos = this.basePos.plus(new Vector(0, wobblePos));
1874
+ }
1875
+ }
1876
+ class Lava {
1877
+ constructor(pos, ch) {
1878
+ this.pos = pos;
1879
+ this.size = new Vector(1, 1);
1880
+ this.type = "lava";
1881
+ this.char = ch;
1882
+ if (ch === "=") {
1883
+ this.speed = new Vector(2, 0);
1884
+ } else if (ch === "|") {
1885
+ this.speed = new Vector(0, 2);
1886
+ } else if (ch === "v") {
1887
+ this.speed = new Vector(0, 3);
1888
+ this.repeatPos = pos;
1889
+ } else if (ch === "!") {
1890
+ this.speed = new Vector(0, 0);
1891
+ }
1892
+ }
1893
+ act(step, level2) {
1894
+ if (this.char === "=" || this.char === "|" || this.char === "v") {
1895
+ const newPos = this.pos.plus(this.speed.times(step));
1896
+ if (!level2.obstacleAt(newPos, this.size)) {
1897
+ this.pos = newPos;
1898
+ } else if (this.repeatPos) {
1899
+ this.pos = this.repeatPos;
1900
+ } else {
1901
+ this.speed = this.speed.times(-1);
1902
+ }
1903
+ }
1904
+ }
1905
+ }
1906
+ class Level {
1907
+ constructor(plan) {
1908
+ this.width = plan[0].length;
1909
+ this.height = plan.length;
1910
+ this.grid = [];
1911
+ this.actors = [];
1912
+ this.coins = 0;
1913
+ this.coinsCollected = 0;
1914
+ const actorChars = {
1915
+ "@": Player,
1916
+ "o": Coin,
1917
+ "=": Lava,
1918
+ "|": Lava,
1919
+ "v": Lava,
1920
+ "!": Lava
1921
+ };
1922
+ for (let y = 0; y < this.height; y++) {
1923
+ const line = plan[y];
1924
+ const gridLine = [];
1925
+ for (let x = 0; x < this.width; x++) {
1926
+ const ch = line[x];
1927
+ const ActorClass = actorChars[ch];
1928
+ let fieldType = null;
1929
+ if (ActorClass) {
1930
+ const actor = new ActorClass(new Vector(x, y), ch);
1931
+ this.actors.push(actor);
1932
+ if (actor.type === "coin") this.coins++;
1933
+ } else if (ch === "x") {
1934
+ fieldType = "wall";
1935
+ } else if (ch === "!" || ch === "|" || ch === "=" || ch === "v") {
1936
+ fieldType = "lava";
1937
+ }
1938
+ gridLine.push(fieldType);
1939
+ }
1940
+ this.grid.push(gridLine);
1941
+ }
1942
+ this.player = this.actors.find((actor) => actor.type === "player");
1943
+ this.status = null;
1944
+ this.coinsTotal = this.coins;
1945
+ this.coinsCollected = 0;
1946
+ }
1947
+ obstacleAt(pos, size) {
1948
+ const xStart = Math.floor(pos.x);
1949
+ const xEnd = Math.ceil(pos.x + size.x);
1950
+ const yStart = Math.floor(pos.y);
1951
+ const yEnd = Math.ceil(pos.y + size.y);
1952
+ if (xStart < 0 || xEnd > this.width || yStart < 0) return "wall";
1953
+ if (yEnd > this.height) return "lava";
1954
+ for (let y = yStart; y < yEnd; y++) {
1955
+ for (let x = xStart; x < xEnd; x++) {
1956
+ const fieldType = this.grid[y][x];
1957
+ if (fieldType) return fieldType;
1958
+ }
1959
+ }
1960
+ return null;
1961
+ }
1962
+ actorAt(actor) {
1963
+ for (const other of this.actors) {
1964
+ if (other !== actor && other.type !== "lava" && actor.pos.x + actor.size.x > other.pos.x && actor.pos.x < other.pos.x + other.size.x && actor.pos.y + actor.size.y > other.pos.y && actor.pos.y < other.pos.y + other.size.y) {
1965
+ return other;
1966
+ }
1967
+ }
1968
+ return null;
1969
+ }
1970
+ checkCollisions() {
1971
+ if (!this.player || !this.player.alive) return;
1972
+ const playerGridX = Math.floor(this.player.pos.x);
1973
+ const playerGridY = Math.floor(this.player.pos.y);
1974
+ const playerEndX = Math.ceil(this.player.pos.x + this.player.size.x);
1975
+ const playerEndY = Math.ceil(this.player.pos.y + this.player.size.y);
1976
+ for (let y = playerGridY; y < playerEndY; y++) {
1977
+ if (y < 0 || y >= this.height) continue;
1978
+ for (let x = playerGridX; x < playerEndX; x++) {
1979
+ if (x < 0 || x >= this.width) continue;
1980
+ if (this.grid[y][x] === "lava") {
1981
+ this.player.alive = false;
1982
+ this.status = "lost";
1983
+ deathAnimationRef.current = { active: true, time: 0, scale: 1 };
1984
+ return;
1985
+ }
1986
+ }
1987
+ }
1988
+ for (const actor of this.actors) {
1989
+ if (actor.type === "lava" && this.player.pos.x + this.player.size.x > actor.pos.x && this.player.pos.x < actor.pos.x + actor.size.x && this.player.pos.y + this.player.size.y > actor.pos.y && this.player.pos.y < actor.pos.y + actor.size.y) {
1990
+ this.player.alive = false;
1991
+ this.status = "lost";
1992
+ deathAnimationRef.current = { active: true, time: 0, scale: 1 };
1993
+ return;
1994
+ }
1995
+ }
1996
+ for (const actor of this.actors) {
1997
+ if (actor.type === "coin" && !actor.collected && this.player.pos.x + this.player.size.x > actor.pos.x && this.player.pos.x < actor.pos.x + actor.size.x && this.player.pos.y + this.player.size.y > actor.pos.y && this.player.pos.y < actor.pos.y + actor.size.y) {
1998
+ actor.collected = true;
1999
+ this.coinsCollected++;
2000
+ setScore((prev) => prev + 100);
2001
+ setCoinsCollected(this.coinsCollected);
2002
+ if (this.coinsCollected >= this.coinsTotal) {
2003
+ this.status = "won";
2004
+ }
2005
+ break;
2006
+ }
2007
+ }
2008
+ }
2009
+ animate(step, keys) {
2010
+ const maxStep = 0.05;
2011
+ while (step > 0) {
2012
+ const thisStep = Math.min(step, maxStep);
2013
+ this.actors.forEach((actor) => {
2014
+ if (actor.type === "player") {
2015
+ actor.act(thisStep, this, keys);
2016
+ } else if (actor.type === "lava") {
2017
+ actor.act(thisStep, this);
2018
+ } else if (actor.type === "coin" && !actor.collected) {
2019
+ actor.act(thisStep);
2020
+ }
2021
+ });
2022
+ this.checkCollisions();
2023
+ if (deathAnimationRef.current.active) {
2024
+ deathAnimationRef.current.time += thisStep;
2025
+ deathAnimationRef.current.scale = Math.max(0, 1 - deathAnimationRef.current.time * 2);
2026
+ if (deathAnimationRef.current.time > 0.5) {
2027
+ deathAnimationRef.current.active = false;
2028
+ }
2029
+ }
2030
+ step -= thisStep;
2031
+ }
2032
+ }
2033
+ isFinished() {
2034
+ return this.status === "won" || this.status === "lost";
2035
+ }
2036
+ }
2037
+ const initGame = (0, import_react6.useCallback)(() => {
2038
+ if (!canvasRef.current) return;
2039
+ const currentLevel = new Level(LEVELS[level]);
2040
+ setTotalCoins(currentLevel.coinsTotal);
2041
+ setCoinsCollected(0);
2042
+ gameStateRef.current = {
2043
+ level: currentLevel,
2044
+ lastTime: null,
2045
+ gameRunning: true,
2046
+ cameraX: 0,
2047
+ cameraY: 0
2048
+ };
2049
+ deathAnimationRef.current = { active: false, time: 0, scale: 1 };
2050
+ setGameStatus("playing");
2051
+ setGameActive(true);
2052
+ }, [level]);
2053
+ const updateCamera = (0, import_react6.useCallback)((playerPos, canvasWidth, canvasHeight, levelWidth, levelHeight) => {
2054
+ if (!gameStateRef.current || !playerPos) return;
2055
+ const scale = 12;
2056
+ const viewportWidth = canvasWidth / scale;
2057
+ const viewportHeight = canvasHeight / scale;
2058
+ gameStateRef.current.cameraX = Math.max(0, Math.min(
2059
+ playerPos.x - viewportWidth / 2,
2060
+ levelWidth - viewportWidth
2061
+ ));
2062
+ gameStateRef.current.cameraY = Math.max(0, Math.min(
2063
+ playerPos.y - viewportHeight / 2,
2064
+ levelHeight - viewportHeight
2065
+ ));
2066
+ }, []);
2067
+ const gameLoop = (0, import_react6.useCallback)((timestamp) => {
2068
+ if (!gameActive || !gameStateRef.current) {
2069
+ return;
2070
+ }
2071
+ const state = gameStateRef.current;
2072
+ if (state.lastTime === null) {
2073
+ state.lastTime = timestamp;
2074
+ }
2075
+ const timeStep = Math.min(timestamp - state.lastTime, 100) / 1e3;
2076
+ state.lastTime = timestamp;
2077
+ if (state.level && state.gameRunning) {
2078
+ state.level.animate(timeStep, keysRef.current);
2079
+ if (state.level.player && state.level.player.alive) {
2080
+ updateCamera(
2081
+ state.level.player.pos,
2082
+ canvasRef.current.width,
2083
+ canvasRef.current.height,
2084
+ state.level.width,
2085
+ state.level.height
2086
+ );
2087
+ }
2088
+ if (state.level.isFinished()) {
2089
+ if (state.level.status === "won") {
2090
+ setGameStatus("won");
2091
+ if (level < LEVELS.length - 1) {
2092
+ setTimeout(() => {
2093
+ setLevel((prev) => prev + 1);
2094
+ setGameStatus("ready");
2095
+ setGameActive(false);
2096
+ }, 1500);
2097
+ }
2098
+ } else if (state.level.status === "lost") {
2099
+ setLives((prev) => {
2100
+ const newLives = prev - 1;
2101
+ if (newLives <= 0) {
2102
+ setGameStatus("lost");
2103
+ state.gameRunning = false;
2104
+ return 0;
2105
+ }
2106
+ setTimeout(() => {
2107
+ setGameStatus("ready");
2108
+ setGameActive(false);
2109
+ }, 1e3);
2110
+ return newLives;
2111
+ });
2112
+ }
2113
+ state.gameRunning = false;
2114
+ }
2115
+ drawGame(state.level, state.cameraX, state.cameraY);
2116
+ }
2117
+ if (gameActive) {
2118
+ animationRef.current = requestAnimationFrame(gameLoop);
2119
+ }
2120
+ }, [gameActive, level, updateCamera]);
2121
+ const drawGame = (0, import_react6.useCallback)((gameLevel, cameraX, cameraY) => {
2122
+ const canvas = canvasRef.current;
2123
+ if (!canvas) return;
2124
+ const ctx = canvas.getContext("2d");
2125
+ const scale = 12;
2126
+ ctx.fillStyle = "#222";
2127
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
2128
+ const viewStartX = Math.floor(cameraX);
2129
+ const viewEndX = Math.ceil(cameraX + canvas.width / scale);
2130
+ const viewStartY = Math.floor(cameraY);
2131
+ const viewEndY = Math.ceil(cameraY + canvas.height / scale);
2132
+ for (let y = viewStartY; y < viewEndY; y++) {
2133
+ if (y < 0 || y >= gameLevel.height) continue;
2134
+ for (let x = viewStartX; x < viewEndX; x++) {
2135
+ if (x < 0 || x >= gameLevel.width) continue;
2136
+ const cell = gameLevel.grid[y][x];
2137
+ const screenX = (x - cameraX) * scale;
2138
+ const screenY = (y - cameraY) * scale;
2139
+ if (cell === "wall") {
2140
+ ctx.fillStyle = "#444";
2141
+ ctx.fillRect(screenX, screenY, scale, scale);
2142
+ ctx.strokeStyle = "#333";
2143
+ ctx.lineWidth = 2;
2144
+ ctx.strokeRect(screenX, screenY, scale, scale);
2145
+ } else if (cell === "lava") {
2146
+ const pulse = Math.sin(Date.now() / 200) * 0.2 + 0.8;
2147
+ ctx.fillStyle = `rgb(${Math.floor(229 * pulse)}, ${Math.floor(85 * pulse)}, ${Math.floor(85 * pulse)})`;
2148
+ ctx.fillRect(screenX, screenY, scale, scale);
2149
+ if (Date.now() % 500 < 250) {
2150
+ ctx.fillStyle = "rgba(255, 100, 100, 0.3)";
2151
+ ctx.fillRect(screenX + 2, screenY + 2, scale - 4, scale - 4);
2152
+ }
2153
+ }
2154
+ }
2155
+ }
2156
+ gameLevel.actors.forEach((actor) => {
2157
+ if (actor.type === "coin" && actor.collected) return;
2158
+ const screenX = (actor.pos.x - cameraX) * scale;
2159
+ const screenY = (actor.pos.y - cameraY) * scale;
2160
+ if (screenX + actor.size.x * scale < 0 || screenX > canvas.width || screenY + actor.size.y * scale < 0 || screenY > canvas.height) {
2161
+ return;
2162
+ }
2163
+ if (actor.type === "player") {
2164
+ if (!actor.alive) {
2165
+ const deathScale = deathAnimationRef.current.scale;
2166
+ ctx.save();
2167
+ ctx.translate(screenX + actor.size.x * scale / 2, screenY + actor.size.y * scale / 2);
2168
+ ctx.scale(deathScale, deathScale);
2169
+ ctx.fillStyle = "#a04040";
2170
+ ctx.fillRect(
2171
+ -actor.size.x * scale / 2,
2172
+ -actor.size.y * scale / 2,
2173
+ actor.size.x * scale,
2174
+ actor.size.y * scale
2175
+ );
2176
+ ctx.strokeStyle = "#800000";
2177
+ ctx.lineWidth = 2;
2178
+ ctx.beginPath();
2179
+ ctx.moveTo(-actor.size.x * scale / 3, -actor.size.y * scale / 3);
2180
+ ctx.lineTo(actor.size.x * scale / 3, actor.size.y * scale / 3);
2181
+ ctx.moveTo(actor.size.x * scale / 3, -actor.size.y * scale / 3);
2182
+ ctx.lineTo(-actor.size.x * scale / 3, actor.size.y * scale / 3);
2183
+ ctx.stroke();
2184
+ ctx.restore();
2185
+ } else {
2186
+ ctx.fillStyle = gameLevel.status === "won" ? "#4CAF50" : "#335699";
2187
+ ctx.fillRect(screenX, screenY, actor.size.x * scale, actor.size.y * scale);
2188
+ ctx.fillStyle = "white";
2189
+ const eyeSize = scale * 0.2;
2190
+ const eyeOffset = actor.speed.x > 0 ? scale * 0.3 : scale * 0.1;
2191
+ ctx.fillRect(screenX + eyeOffset, screenY + scale * 0.2, eyeSize, eyeSize);
2192
+ ctx.fillRect(screenX + eyeOffset + scale * 0.2, screenY + scale * 0.2, eyeSize, eyeSize);
2193
+ ctx.strokeStyle = "white";
2194
+ ctx.lineWidth = 1;
2195
+ ctx.beginPath();
2196
+ ctx.arc(
2197
+ screenX + actor.size.x * scale / 2,
2198
+ screenY + scale * 0.6,
2199
+ scale * 0.2,
2200
+ 0.2,
2201
+ Math.PI - 0.2
2202
+ );
2203
+ ctx.stroke();
2204
+ }
2205
+ } else if (actor.type === "coin" && !actor.collected) {
2206
+ const wobble = Math.sin(actor.wobble) * 0.1;
2207
+ ctx.fillStyle = "#e2e838";
2208
+ ctx.beginPath();
2209
+ ctx.arc(
2210
+ screenX + actor.size.x * scale / 2,
2211
+ screenY + actor.size.y * scale / 2 + wobble * scale,
2212
+ actor.size.x * scale / 2,
2213
+ 0,
2214
+ Math.PI * 2
2215
+ );
2216
+ ctx.fill();
2217
+ ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
2218
+ ctx.beginPath();
2219
+ ctx.arc(
2220
+ screenX + actor.size.x * scale / 2 - scale * 0.15,
2221
+ screenY + actor.size.y * scale / 2 - scale * 0.15 + wobble * scale,
2222
+ scale * 0.1,
2223
+ 0,
2224
+ Math.PI * 2
2225
+ );
2226
+ ctx.fill();
2227
+ ctx.strokeStyle = "rgba(255, 255, 200, 0.6)";
2228
+ ctx.lineWidth = 1;
2229
+ for (let i = 0; i < 4; i++) {
2230
+ const angle = actor.wobble + i * Math.PI / 2;
2231
+ const length = scale * 0.4;
2232
+ ctx.beginPath();
2233
+ ctx.moveTo(
2234
+ screenX + actor.size.x * scale / 2,
2235
+ screenY + actor.size.y * scale / 2 + wobble * scale
2236
+ );
2237
+ ctx.lineTo(
2238
+ screenX + actor.size.x * scale / 2 + Math.cos(angle) * length,
2239
+ screenY + actor.size.y * scale / 2 + wobble * scale + Math.sin(angle) * length
2240
+ );
2241
+ ctx.stroke();
2242
+ }
2243
+ } else if (actor.type === "lava") {
2244
+ if (actor.char === "=" || actor.char === "|" || actor.char === "v") {
2245
+ const pulse = Math.sin(Date.now() / 150) * 0.3 + 0.7;
2246
+ ctx.fillStyle = `rgb(${Math.floor(255 * pulse)}, ${Math.floor(100 * pulse)}, ${Math.floor(100 * pulse)})`;
2247
+ } else {
2248
+ const pulse = Math.sin(Date.now() / 300) * 0.2 + 0.8;
2249
+ ctx.fillStyle = `rgb(${Math.floor(229 * pulse)}, ${Math.floor(85 * pulse)}, ${Math.floor(85 * pulse)})`;
2250
+ }
2251
+ ctx.fillRect(screenX, screenY, actor.size.x * scale, actor.size.y * scale);
2252
+ if (Math.random() < 0.02) {
2253
+ const bubbleX = screenX + Math.random() * actor.size.x * scale;
2254
+ const bubbleY = screenY + Math.random() * actor.size.y * scale;
2255
+ const bubbleSize = Math.random() * 3 + 1;
2256
+ ctx.fillStyle = "rgba(255, 200, 200, 0.6)";
2257
+ ctx.beginPath();
2258
+ ctx.arc(bubbleX, bubbleY, bubbleSize, 0, Math.PI * 2);
2259
+ ctx.fill();
2260
+ }
2261
+ }
2262
+ });
2263
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.1)";
2264
+ ctx.lineWidth = 1;
2265
+ ctx.strokeRect(-cameraX * scale, -cameraY * scale, gameLevel.width * scale, gameLevel.height * scale);
2266
+ }, []);
2267
+ (0, import_react6.useEffect)(() => {
2268
+ const handleKeyDown = (e) => {
2269
+ if (e.key.toLowerCase() === "r") {
2270
+ e.preventDefault();
2271
+ resetGame();
2272
+ return;
2273
+ }
2274
+ if (!gameActive && e.key === "Enter") {
2275
+ startGame();
2276
+ return;
2277
+ }
2278
+ if (!gameActive) return;
2279
+ switch (e.key.toLowerCase()) {
2280
+ case "arrowleft":
2281
+ case "a":
2282
+ keysRef.current.left = true;
2283
+ break;
2284
+ case "arrowright":
2285
+ case "d":
2286
+ keysRef.current.right = true;
2287
+ break;
2288
+ case "arrowup":
2289
+ case "w":
2290
+ case " ":
2291
+ keysRef.current.up = true;
2292
+ break;
2293
+ }
2294
+ };
2295
+ const handleKeyUp = (e) => {
2296
+ if (!gameActive) return;
2297
+ switch (e.key.toLowerCase()) {
2298
+ case "arrowleft":
2299
+ case "a":
2300
+ keysRef.current.left = false;
2301
+ break;
2302
+ case "arrowright":
2303
+ case "d":
2304
+ keysRef.current.right = false;
2305
+ break;
2306
+ case "arrowup":
2307
+ case "w":
2308
+ case " ":
2309
+ keysRef.current.up = false;
2310
+ break;
2311
+ }
2312
+ };
2313
+ window.addEventListener("keydown", handleKeyDown);
2314
+ window.addEventListener("keyup", handleKeyUp);
2315
+ return () => {
2316
+ window.removeEventListener("keydown", handleKeyDown);
2317
+ window.removeEventListener("keyup", handleKeyUp);
2318
+ };
2319
+ }, [gameActive]);
2320
+ (0, import_react6.useEffect)(() => {
2321
+ if (gameActive) {
2322
+ animationRef.current = requestAnimationFrame(gameLoop);
2323
+ } else {
2324
+ if (animationRef.current) {
2325
+ cancelAnimationFrame(animationRef.current);
2326
+ }
2327
+ }
2328
+ return () => {
2329
+ if (animationRef.current) {
2330
+ cancelAnimationFrame(animationRef.current);
2331
+ }
2332
+ };
2333
+ }, [gameActive, gameLoop]);
2334
+ const resetGame = (0, import_react6.useCallback)(() => {
2335
+ setScore(0);
2336
+ setLevel(0);
2337
+ setLives(3);
2338
+ setCoinsCollected(0);
2339
+ setGameStatus("ready");
2340
+ setGameActive(false);
2341
+ if (animationRef.current) {
2342
+ cancelAnimationFrame(animationRef.current);
2343
+ }
2344
+ deathAnimationRef.current = { active: false, time: 0, scale: 1 };
2345
+ setTimeout(() => {
2346
+ if (canvasRef.current) {
2347
+ const ctx = canvasRef.current.getContext("2d");
2348
+ ctx.fillStyle = "#222";
2349
+ ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
2350
+ }
2351
+ }, 100);
2352
+ }, []);
2353
+ const startGame = () => {
2354
+ if (gameStatus === "lost") {
2355
+ resetGame();
2356
+ }
2357
+ initGame();
2358
+ };
2359
+ (0, import_react6.useEffect)(() => {
2360
+ if (score > 0) {
2361
+ const event = new CustomEvent("gameScoreUpdate", {
2362
+ detail: { game: "platformer", score }
2363
+ });
2364
+ window.dispatchEvent(event);
2365
+ }
2366
+ }, [score]);
2367
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "platformer-game", children: [
2368
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "game-header", children: [
2369
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { children: "\u{1F3AE} \u041F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0435\u0440" }),
2370
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "game-controls", children: [
2371
+ !gameActive ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "control-btn start-btn", onClick: startGame, children: gameStatus === "ready" ? "\u25B6\uFE0F \u041D\u0430\u0447\u0430\u0442\u044C \u0438\u0433\u0440\u0443" : gameStatus === "won" ? "\u{1F389} \u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C" : "\u{1F504} \u0418\u0433\u0440\u0430\u0442\u044C \u0441\u043D\u043E\u0432\u0430" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "control-btn pause-btn", onClick: () => setGameActive(false), children: "\u23F8\uFE0F \u041F\u0430\u0443\u0437\u0430" }),
2372
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "control-btn reset-btn", onClick: resetGame, children: "\u{1F504} \u0421\u0431\u0440\u043E\u0441 (R)" })
2373
+ ] })
2374
+ ] }),
2375
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "game-stats", children: [
2376
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "stat-box", children: [
2377
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "stat-label", children: "\u0421\u0447\u0435\u0442" }),
2378
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "stat-value", children: score })
2379
+ ] }),
2380
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "stat-box", children: [
2381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "stat-label", children: "\u0423\u0440\u043E\u0432\u0435\u043D\u044C" }),
2382
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "stat-value", children: [
2383
+ level + 1,
2384
+ " / ",
2385
+ LEVELS.length
2386
+ ] })
2387
+ ] }),
2388
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "stat-box", children: [
2389
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "stat-label", children: "\u0416\u0438\u0437\u043D\u0438" }),
2390
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "stat-value", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { color: lives <= 1 ? "#ff5e62" : "#667eea" }, children: "\u2764\uFE0F".repeat(Math.max(0, lives)) }) })
2391
+ ] }),
2392
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "stat-box", children: [
2393
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "stat-label", children: "\u041C\u043E\u043D\u0435\u0442\u044B" }),
2394
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "stat-value", children: [
2395
+ coinsCollected,
2396
+ " / ",
2397
+ totalCoins,
2398
+ " \u{1F7E1}"
2399
+ ] })
2400
+ ] })
2401
+ ] }),
2402
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "game-area", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "platformer-container", ref: gameContainerRef, children: [
2403
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2404
+ "canvas",
2405
+ {
2406
+ ref: canvasRef,
2407
+ width: 800,
2408
+ height: 400,
2409
+ className: "platformer-canvas"
2410
+ }
2411
+ ),
2412
+ !gameActive && gameStatus === "ready" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "start-screen", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "start-message", children: [
2413
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: "\u041F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0435\u0440" }),
2414
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "\u0421\u043E\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u043C\u043E\u043D\u0435\u0442\u044B \u{1F7E1}, \u0438\u0437\u0431\u0435\u0433\u0430\u0439\u0442\u0435 \u043B\u0430\u0432\u044B \u{1F534}" }),
2415
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435: \u2190 \u2192 (\u0438\u043B\u0438 A, D) \u0438 \u041F\u0420\u041E\u0411\u0415\u041B \u0434\u043B\u044F \u043F\u0440\u044B\u0436\u043A\u0430" }),
2416
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 ENTER \u0434\u043B\u044F \u0441\u0442\u0430\u0440\u0442\u0430, R \u0434\u043B\u044F \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430" }),
2417
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "start-btn-big", onClick: startGame, children: "\u041D\u0410\u0427\u0410\u0422\u042C \u0418\u0413\u0420\u0423" })
2418
+ ] }) }),
2419
+ gameStatus === "won" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "win-screen", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "win-message", children: [
2420
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: "\u{1F389} \u0423\u0420\u041E\u0412\u0415\u041D\u042C \u041F\u0420\u041E\u0419\u0414\u0415\u041D!" }),
2421
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2422
+ "\u041D\u0430\u0431\u0440\u0430\u043D\u043E \u043E\u0447\u043A\u043E\u0432: ",
2423
+ score
2424
+ ] }),
2425
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2426
+ "\u0421\u043E\u0431\u0440\u0430\u043D\u043E \u043C\u043E\u043D\u0435\u0442: ",
2427
+ coinsCollected,
2428
+ "/",
2429
+ totalCoins
2430
+ ] }),
2431
+ level < LEVELS.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "next-level-btn", onClick: startGame, children: "\u0421\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u0443\u0440\u043E\u0432\u0435\u043D\u044C \u2192" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "restart-btn", onClick: resetGame, children: "\u{1F3C6} \u041F\u043E\u0437\u0434\u0440\u0430\u0432\u043B\u044F\u0435\u043C! \u041D\u0430\u0447\u0430\u0442\u044C \u0437\u0430\u043D\u043E\u0432\u043E" })
2432
+ ] }) }),
2433
+ gameStatus === "lost" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "lose-screen", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "lose-message", children: [
2434
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: "\u{1F480} \u0418\u0413\u0420\u0410 \u041E\u041A\u041E\u041D\u0427\u0415\u041D\u0410" }),
2435
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2436
+ "\u0424\u0438\u043D\u0430\u043B\u044C\u043D\u044B\u0439 \u0441\u0447\u0435\u0442: ",
2437
+ score
2438
+ ] }),
2439
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2440
+ "\u0421\u043E\u0431\u0440\u0430\u043D\u043E \u043C\u043E\u043D\u0435\u0442: ",
2441
+ coinsCollected,
2442
+ "/",
2443
+ totalCoins
2444
+ ] }),
2445
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "restart-btn", onClick: resetGame, children: "\u0418\u0433\u0440\u0430\u0442\u044C \u0441\u043D\u043E\u0432\u0430" })
2446
+ ] }) })
2447
+ ] }) }),
2448
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "game-instructions", children: [
2449
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "instructions-row", children: [
2450
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435:" }),
2452
+ " \u2190 \u2192 (A, D) - \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u0435, \u041F\u0420\u041E\u0411\u0415\u041B - \u043F\u0440\u044B\u0436\u043E\u043A"
2453
+ ] }),
2454
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
2455
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: "\u0426\u0435\u043B\u044C:" }),
2456
+ " \u0421\u043E\u0431\u0440\u0430\u0442\u044C \u0432\u0441\u0435 \u043C\u043E\u043D\u0435\u0442\u044B, \u0438\u0437\u0431\u0435\u0433\u0430\u044F \u043B\u0430\u0432\u044B"
2457
+ ] })
2458
+ ] }),
2459
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "legend", children: [
2460
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "legend-item", children: [
2461
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "legend-color player-color" }),
2462
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u0418\u0433\u0440\u043E\u043A (\u0441\u0438\u043D\u0438\u0439)" })
2463
+ ] }),
2464
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "legend-item", children: [
2465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "legend-color coin-color" }),
2466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u041C\u043E\u043D\u0435\u0442\u0430 (+100 \u043E\u0447\u043A\u043E\u0432)" })
2467
+ ] }),
2468
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "legend-item", children: [
2469
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "legend-color lava-color" }),
2470
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u041B\u0430\u0432\u0430 (\u043E\u043F\u0430\u0441\u043D\u043E)" })
2471
+ ] }),
2472
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "legend-item", children: [
2473
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "legend-color wall-color" }),
2474
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "\u0421\u0442\u0435\u043D\u0430" })
2475
+ ] })
2476
+ ] })
2477
+ ] })
2478
+ ] });
2479
+ };
2480
+ var PlatformerGame_default = PlatformerGame;
2481
+
2482
+ // src/components/GameHubNode/GameHubNode.js
2483
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2484
+ var MENU_SIZE = { width: 560, height: 520 };
2485
+ var GAME_NODE_SIZES = {
2486
+ snake: { width: 700, height: 1e3 },
2487
+ "2048": { width: 560, height: 780 },
2488
+ memory: { width: 680, height: 890 },
2489
+ clicker: { width: 980, height: 760 },
2490
+ pong: { width: 760, height: 780 },
2491
+ platformer: { width: 980, height: 750 }
2492
+ };
2493
+ var STATS_API_BASE = process.env.REACT_APP_STATS_API_URL || "http://localhost:4000";
2494
+ function getAuthFromStorage() {
2495
+ const token = localStorage.getItem("jwt") || localStorage.getItem("token") || localStorage.getItem("access_token") || localStorage.getItem("accessToken") || "";
2496
+ let user = null;
2497
+ const userRaw = localStorage.getItem("user") || localStorage.getItem("userInfo") || localStorage.getItem("profile");
2498
+ if (userRaw) {
2499
+ try {
2500
+ user = JSON.parse(userRaw);
2501
+ } catch {
2502
+ user = null;
2503
+ }
2504
+ }
2505
+ const userId = user && (user.id || user.userId || user._id) || localStorage.getItem("userId") || localStorage.getItem("uid") || "";
2506
+ return { userId, token };
2507
+ }
2508
+ async function fetchUserStats(userId, token) {
2509
+ const res = await fetch(`${STATS_API_BASE}/api/stats/${encodeURIComponent(userId)}`, {
2510
+ headers: { ...token ? { Authorization: `Bearer ${token}` } : {} }
2511
+ });
2512
+ if (!res.ok) throw new Error(`GET stats failed: ${res.status}`);
2513
+ return res.json();
2514
+ }
2515
+ async function postGameStat({ userId, token, gameId, score }) {
2516
+ const res = await fetch(`${STATS_API_BASE}/api/stats`, {
2517
+ method: "POST",
2518
+ headers: {
2519
+ "Content-Type": "application/json",
2520
+ ...token ? { Authorization: `Bearer ${token}` } : {}
2521
+ },
2522
+ body: JSON.stringify({ userId, gameId, score })
2523
+ });
2524
+ if (!res.ok) {
2525
+ const text = await res.text().catch(() => "");
2526
+ throw new Error(`POST stats failed: ${res.status} ${text}`);
2527
+ }
2528
+ return res.json();
2529
+ }
2530
+ async function fetchLeaderboard(gameId, limit = 10) {
2531
+ const res = await fetch(
2532
+ `${STATS_API_BASE}/api/leaderboard/${encodeURIComponent(gameId)}?limit=${encodeURIComponent(limit)}`
2533
+ );
2534
+ if (!res.ok) throw new Error(`GET leaderboard failed: ${res.status}`);
2535
+ return res.json();
2536
+ }
2537
+ async function upsertNickname({ userId, nickname }) {
2538
+ const res = await fetch(`${STATS_API_BASE}/api/users`, {
2539
+ method: "POST",
2540
+ headers: { "Content-Type": "application/json" },
2541
+ body: JSON.stringify({ userId, nickname })
2542
+ });
2543
+ if (!res.ok) {
2544
+ const text = await res.text().catch(() => "");
2545
+ throw new Error(`POST /api/users failed: ${res.status} ${text}`);
2546
+ }
2547
+ return res.json();
2548
+ }
2549
+ async function fetchNickname(userId) {
2550
+ const res = await fetch(`${STATS_API_BASE}/api/users/${encodeURIComponent(userId)}`);
2551
+ if (!res.ok) throw new Error(`GET /api/users/:userId failed: ${res.status}`);
2552
+ return res.json();
2553
+ }
2554
+ var GameHubNode = (0, import_react7.memo)(({ id, data, selected, overlayMode = false, overlayGameId }) => {
2555
+ const games = (0, import_react7.useMemo)(
2556
+ () => [
2557
+ { id: "snake", title: "\u{1F40D} \u0417\u043C\u0435\u0439\u043A\u0430", desc: "\u041A\u043B\u0430\u0441\u0441\u0438\u043A\u0430 \u043D\u0430 \u0441\u0435\u0442\u043A\u0435", component: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SnakeGame_default, {}) },
2558
+ { id: "2048", title: "\u{1F522} 2048", desc: "\u0421\u043E\u0431\u0435\u0440\u0438 2048", component: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Game2048_default, {}) },
2559
+ { id: "memory", title: "\u{1F9E0} \u041F\u0430\u043C\u044F\u0442\u044C", desc: "\u041D\u0430\u0439\u0434\u0438 \u043F\u0430\u0440\u044B", component: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MemoryGame_default, {}) },
2560
+ { id: "clicker", title: "\u{1F5B1}\uFE0F \u041A\u043B\u0438\u043A\u0435\u0440", desc: "\u041A\u043B\u0438\u043A\u0430\u0439 \u0438 \u043F\u0440\u043E\u043A\u0430\u0447\u0438\u0432\u0430\u0439", component: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ClickerGame_default, {}) },
2561
+ { id: "pong", title: "\u{1F3D3} \u041F\u0438\u043D\u0433-\u041F\u043E\u043D\u0433", desc: "\u0420\u0430\u043A\u0435\u0442\u043A\u0430 \u0438 \u043C\u044F\u0447", component: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Pong_default, {}) },
2562
+ { id: "platformer", title: "\u{1F47E} \u041F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0435\u0440", desc: "\u041F\u0440\u044B\u0433\u0430\u0439 \u0438 \u0431\u0435\u0433\u0438", component: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PlatformerGame_default, {}) }
2563
+ ],
2564
+ []
2565
+ );
2566
+ const [view, setView] = (0, import_react7.useState)("menu");
2567
+ const [currentGame, setCurrentGame] = (0, import_react7.useState)(games[0].id);
2568
+ const [currentScore, setCurrentScore] = (0, import_react7.useState)(0);
2569
+ const [menuTab, setMenuTab] = (0, import_react7.useState)("games");
2570
+ const [userStats, setUserStats] = (0, import_react7.useState)([]);
2571
+ const [statsLoading, setStatsLoading] = (0, import_react7.useState)(false);
2572
+ const [statsError, setStatsError] = (0, import_react7.useState)("");
2573
+ const [lbGame, setLbGame] = (0, import_react7.useState)(games[0].id);
2574
+ const [leaderboard, setLeaderboard] = (0, import_react7.useState)([]);
2575
+ const [lbLoading, setLbLoading] = (0, import_react7.useState)(false);
2576
+ const [lbError, setLbError] = (0, import_react7.useState)("");
2577
+ const [nicknameInput, setNicknameInput] = (0, import_react7.useState)("");
2578
+ const [nickSaving, setNickSaving] = (0, import_react7.useState)(false);
2579
+ const [nickMsg, setNickMsg] = (0, import_react7.useState)("");
2580
+ (0, import_react7.useEffect)(() => {
2581
+ if (overlayMode && overlayGameId) {
2582
+ setView("game");
2583
+ setCurrentGame(overlayGameId);
2584
+ setCurrentScore(0);
2585
+ }
2586
+ }, [overlayMode, overlayGameId]);
2587
+ const active = (0, import_react7.useMemo)(() => games.find((g) => g.id === currentGame), [games, currentGame]);
2588
+ (0, import_react7.useEffect)(() => {
2589
+ const handleScoreUpdate = (e) => {
2590
+ const d = e?.detail;
2591
+ if (!d) return;
2592
+ if (d.game === currentGame) setCurrentScore(d.score ?? 0);
2593
+ };
2594
+ window.addEventListener("gameScoreUpdate", handleScoreUpdate);
2595
+ return () => window.removeEventListener("gameScoreUpdate", handleScoreUpdate);
2596
+ }, [currentGame]);
2597
+ (0, import_react7.useEffect)(() => {
2598
+ const size = overlayMode ? GAME_NODE_SIZES[currentGame] : view === "menu" ? MENU_SIZE : GAME_NODE_SIZES[currentGame] || MENU_SIZE;
2599
+ window.dispatchEvent(
2600
+ new CustomEvent("resizeGameHubNode", {
2601
+ detail: { nodeId: id, width: size.width, height: size.height }
2602
+ })
2603
+ );
2604
+ }, [id, view, currentGame, overlayMode]);
2605
+ const backToMenu = () => {
2606
+ setView("menu");
2607
+ setCurrentScore(0);
2608
+ };
2609
+ const startGame = (gameId) => {
2610
+ setCurrentGame(gameId);
2611
+ setCurrentScore(0);
2612
+ setView("game");
2613
+ };
2614
+ (0, import_react7.useEffect)(() => {
2615
+ if (overlayMode) return;
2616
+ if (view !== "game") return;
2617
+ const { userId, token } = getAuthFromStorage();
2618
+ if (!userId) {
2619
+ setStatsError("\u041D\u0435\u0442 userId \u0432 storage");
2620
+ setUserStats([]);
2621
+ return;
2622
+ }
2623
+ let cancelled = false;
2624
+ setStatsLoading(true);
2625
+ setStatsError("");
2626
+ fetchUserStats(userId, token).then((rows) => {
2627
+ if (cancelled) return;
2628
+ setUserStats(Array.isArray(rows) ? rows : []);
2629
+ }).catch((err) => {
2630
+ if (cancelled) return;
2631
+ setStatsError("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0443");
2632
+ setUserStats([]);
2633
+ console.warn(err);
2634
+ }).finally(() => {
2635
+ if (cancelled) return;
2636
+ setStatsLoading(false);
2637
+ });
2638
+ return () => {
2639
+ cancelled = true;
2640
+ };
2641
+ }, [view, currentGame, overlayMode]);
2642
+ const currentGameStat = (0, import_react7.useMemo)(() => {
2643
+ const row = userStats.find((r) => r?.game_id === currentGame);
2644
+ if (!row) return null;
2645
+ return {
2646
+ best: row.best_score ?? 0,
2647
+ last: row.last_score ?? 0,
2648
+ plays: row.plays ?? 0
2649
+ };
2650
+ }, [userStats, currentGame]);
2651
+ const saveScore = async () => {
2652
+ const { userId, token } = getAuthFromStorage();
2653
+ if (!userId) {
2654
+ setStatsError("\u041D\u0435\u0442 userId \u0432 storage");
2655
+ return;
2656
+ }
2657
+ try {
2658
+ setStatsError("");
2659
+ setStatsLoading(true);
2660
+ await postGameStat({
2661
+ userId,
2662
+ token,
2663
+ gameId: currentGame,
2664
+ score: Number(currentScore) || 0
2665
+ });
2666
+ const rows = await fetchUserStats(userId, token);
2667
+ setUserStats(Array.isArray(rows) ? rows : []);
2668
+ } catch (err) {
2669
+ setStatsError("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442");
2670
+ console.warn(err);
2671
+ } finally {
2672
+ setStatsLoading(false);
2673
+ }
2674
+ };
2675
+ const loadLeaderboard = async (gameId) => {
2676
+ try {
2677
+ setLbError("");
2678
+ setLbLoading(true);
2679
+ const rows = await fetchLeaderboard(gameId, 10);
2680
+ setLeaderboard(Array.isArray(rows) ? rows : []);
2681
+ } catch (err) {
2682
+ setLbError("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0442\u043E\u043F-10");
2683
+ setLeaderboard([]);
2684
+ console.warn(err);
2685
+ } finally {
2686
+ setLbLoading(false);
2687
+ }
2688
+ };
2689
+ (0, import_react7.useEffect)(() => {
2690
+ if (overlayMode) return;
2691
+ if (view !== "menu") return;
2692
+ if (menuTab !== "stats") return;
2693
+ loadLeaderboard(lbGame);
2694
+ const { userId } = getAuthFromStorage();
2695
+ if (!userId) return;
2696
+ fetchNickname(userId).then((u) => {
2697
+ if (u?.nickname) setNicknameInput(u.nickname);
2698
+ }).catch((e) => {
2699
+ console.warn(e);
2700
+ });
2701
+ }, [menuTab, lbGame, view, overlayMode]);
2702
+ const saveNickname = async () => {
2703
+ const { userId } = getAuthFromStorage();
2704
+ if (!userId) {
2705
+ setNickMsg("\u26A0\uFE0F \u041D\u0435\u0442 userId \u0432 storage");
2706
+ return;
2707
+ }
2708
+ const nick = (nicknameInput || "").trim();
2709
+ if (!nick) {
2710
+ setNickMsg("\u26A0\uFE0F \u0412\u0432\u0435\u0434\u0438 \u043D\u0438\u043A\u043D\u0435\u0439\u043C");
2711
+ return;
2712
+ }
2713
+ try {
2714
+ setNickMsg("");
2715
+ setNickSaving(true);
2716
+ await upsertNickname({ userId, nickname: nick });
2717
+ setNicknameInput(nick);
2718
+ setNickMsg("\u2705 \u041D\u0438\u043A \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D");
2719
+ await loadLeaderboard(lbGame);
2720
+ } catch (e) {
2721
+ console.warn(e);
2722
+ setNickMsg("\u26A0\uFE0F \u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043D\u0438\u043A");
2723
+ } finally {
2724
+ setNickSaving(false);
2725
+ }
2726
+ };
2727
+ if (overlayMode) {
2728
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "game-overlay-container nodrag", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "game-overlay-game nodrag", children: active?.component }) });
2729
+ }
2730
+ const subtitleText = view === "menu" ? menuTab === "stats" ? "\u041B\u0438\u0434\u0435\u0440\u0431\u043E\u0440\u0434\u044B: \u0442\u043E\u043F-10 \u043F\u043E \u0438\u0433\u0440\u0430\u043C" : "\u0412\u044B\u0431\u0435\u0440\u0438 \u0438\u0433\u0440\u0443" : `\u0422\u0435\u043A\u0443\u0449\u0430\u044F: ${active?.title || ""} \u2022 \u0421\u0447\u0451\u0442: ${currentScore}${currentGameStat ? ` \u2022 Best: ${currentGameStat.best} \u2022 Last: ${currentGameStat.last} \u2022 Plays: ${currentGameStat.plays}` : ""}`;
2731
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `gamehub-node ${selected ? "selected" : ""}`, children: [
2732
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_reactflow.Handle, { type: "target", position: import_reactflow.Position.Top }),
2733
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-drag-handle", children: [
2734
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-title", children: data?.label ?? "\u{1F3AE} \u0418\u0433\u0440\u043E\u0432\u043E\u0439 \u0445\u0430\u0431" }),
2735
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-subtitle", children: subtitleText })
2736
+ ] }),
2737
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-content nodrag", children: view === "menu" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-menu nodrag", children: [
2738
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-menu-head nodrag", children: [
2739
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-menu-title nodrag", children: menuTab === "games" ? "\u0412\u044B\u0431\u043E\u0440 \u0438\u0433\u0440\u044B" : "\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430 \u0438\u0433\u0440\u043E\u043A\u043E\u0432" }),
2740
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-menu-tabs nodrag", children: [
2741
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2742
+ "button",
2743
+ {
2744
+ type: "button",
2745
+ className: `gamehub-tab nodrag ${menuTab === "games" ? "active" : ""}`,
2746
+ onClick: () => setMenuTab("games"),
2747
+ children: "\u{1F3AE} \u0418\u0433\u0440\u044B"
2748
+ }
2749
+ ),
2750
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2751
+ "button",
2752
+ {
2753
+ type: "button",
2754
+ className: `gamehub-tab nodrag ${menuTab === "stats" ? "active" : ""}`,
2755
+ onClick: () => {
2756
+ setMenuTab("stats");
2757
+ setNickMsg("");
2758
+ },
2759
+ children: "\u{1F3C6} \u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043A\u0430"
2760
+ }
2761
+ )
2762
+ ] })
2763
+ ] }),
2764
+ menuTab === "games" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-menu-grid nodrag", children: games.map((g) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2765
+ "button",
2766
+ {
2767
+ type: "button",
2768
+ className: "gamehub-card nodrag",
2769
+ onClick: () => startGame(g.id),
2770
+ children: [
2771
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-card-title", children: g.title }),
2772
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-card-desc", children: g.desc }),
2773
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-card-cta", children: "\u0418\u0433\u0440\u0430\u0442\u044C \u2192" })
2774
+ ]
2775
+ },
2776
+ g.id
2777
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-stats nodrag", children: [
2778
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-nick-box nodrag", children: [
2779
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-nick-title nodrag", children: "\u0422\u0432\u043E\u0439 \u043D\u0438\u043A\u043D\u0435\u0439\u043C" }),
2780
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-nick-row nodrag", children: [
2781
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2782
+ "input",
2783
+ {
2784
+ className: "gamehub-nick-input nodrag",
2785
+ placeholder: "\u041D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: \u0410\u043B\u0435\u043A\u0441",
2786
+ value: nicknameInput,
2787
+ onChange: (e) => setNicknameInput(e.target.value)
2788
+ }
2789
+ ),
2790
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2791
+ "button",
2792
+ {
2793
+ type: "button",
2794
+ className: "gamehub-top-btn nodrag",
2795
+ onClick: saveNickname,
2796
+ disabled: nickSaving,
2797
+ title: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043D\u0438\u043A\u043D\u0435\u0439\u043C",
2798
+ children: "\u{1F4BE} \u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043D\u0438\u043A"
2799
+ }
2800
+ )
2801
+ ] }),
2802
+ nickMsg ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-nick-msg nodrag", children: nickMsg }) : null
2803
+ ] }),
2804
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-stats-controls nodrag", children: [
2805
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "gamehub-stats-label nodrag", children: [
2806
+ "\u0418\u0433\u0440\u0430:",
2807
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2808
+ "select",
2809
+ {
2810
+ className: "gamehub-stats-select nodrag",
2811
+ value: lbGame,
2812
+ onChange: (e) => setLbGame(e.target.value),
2813
+ children: games.map((g) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: g.id, children: g.title }, g.id))
2814
+ }
2815
+ )
2816
+ ] }),
2817
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2818
+ "button",
2819
+ {
2820
+ type: "button",
2821
+ className: "gamehub-top-btn nodrag",
2822
+ onClick: () => loadLeaderboard(lbGame),
2823
+ disabled: lbLoading,
2824
+ title: "\u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0442\u043E\u043F-10",
2825
+ children: "\u{1F504} \u041E\u0431\u043D\u043E\u0432\u0438\u0442\u044C"
2826
+ }
2827
+ )
2828
+ ] }),
2829
+ lbError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-stats-error nodrag", children: [
2830
+ "\u26A0\uFE0F ",
2831
+ lbError
2832
+ ] }) : lbLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-stats-loading nodrag", children: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026" }) : leaderboard.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-stats-empty nodrag", children: "(\u043F\u043E\u043A\u0430 \u043D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445)" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ol", { className: "gamehub-leaderboard nodrag", children: leaderboard.map((row, idx) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { className: "gamehub-leaderboard-item nodrag", children: [
2833
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "gamehub-lb-rank nodrag", children: [
2834
+ "#",
2835
+ idx + 1
2836
+ ] }),
2837
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gamehub-lb-user nodrag", children: row.nickname || row.user_id }),
2838
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gamehub-lb-score nodrag", children: row.best_score })
2839
+ ] }, `${row.user_id}-${idx}`)) })
2840
+ ] })
2841
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-game-screen nodrag", children: [
2842
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-game-topbar nodrag", children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", className: "gamehub-top-btn nodrag", onClick: backToMenu, children: "\u2190 \u041C\u0435\u043D\u044E" }),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-top-title nodrag", children: active?.title }),
2845
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "gamehub-top-right nodrag", children: [
2846
+ statsError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "gamehub-stats-text nodrag", children: [
2847
+ "\u26A0\uFE0F ",
2848
+ statsError
2849
+ ] }) : currentGameStat ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "gamehub-stats-text nodrag", children: [
2850
+ "Best ",
2851
+ currentGameStat.best,
2852
+ " \u2022 Last ",
2853
+ currentGameStat.last,
2854
+ " \u2022 Plays ",
2855
+ currentGameStat.plays
2856
+ ] }) : statsLoading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gamehub-stats-text nodrag", children: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "gamehub-stats-text nodrag", children: "(\u043D\u0435\u0442 \u0434\u0430\u043D\u043D\u044B\u0445)" }),
2857
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2858
+ "button",
2859
+ {
2860
+ type: "button",
2861
+ className: "gamehub-top-btn nodrag",
2862
+ onClick: saveScore,
2863
+ title: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u0442\u0435\u043A\u0443\u0449\u0438\u0439 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",
2864
+ disabled: statsLoading,
2865
+ children: "\u{1F4BE} \u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C"
2866
+ }
2867
+ )
2868
+ ] })
2869
+ ] }),
2870
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "gamehub-game-wrap nodrag", children: active?.component })
2871
+ ] }) }),
2872
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_reactflow.Handle, { type: "source", position: import_reactflow.Position.Bottom })
2873
+ ] });
2874
+ });
2875
+ var GameHubNode_default = GameHubNode;
2876
+ // Annotate the CommonJS export names for ESM import in node:
2877
+ 0 && (module.exports = {
2878
+ GameHubNode
2879
+ });