Starchik1 commited on
Commit
753f279
·
verified ·
1 Parent(s): 56afd28

Delete static

Browse files
static/css/style.css DELETED
@@ -1,542 +0,0 @@
1
- :root {
2
- --primary-color: #1a1a2e;
3
- --secondary-color: #16213e;
4
- --accent-color: #0f3460;
5
- --text-color: #e94560;
6
- --light-text: #f8f8f8;
7
- --error-color: #ff0000;
8
- --success-color: #00ff00;
9
- }
10
-
11
- body {
12
- font-family: 'Press Start 2P', cursive, sans-serif;
13
- background-color: var(--primary-color);
14
- color: var(--light-text);
15
- margin: 0;
16
- padding: 0;
17
- display: flex;
18
- flex-direction: column;
19
- min-height: 100vh;
20
- overflow-x: hidden;
21
- }
22
-
23
- .container {
24
- width: 90%;
25
- max-width: 1200px;
26
- margin: 0 auto;
27
- padding: 20px;
28
- }
29
-
30
- header {
31
- background-color: var(--secondary-color);
32
- padding: 20px 0;
33
- text-align: center;
34
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
35
- }
36
-
37
- header h1 {
38
- margin: 0;
39
- color: var(--text-color);
40
- font-size: 2.5rem;
41
- text-transform: uppercase;
42
- letter-spacing: 3px;
43
- text-shadow: 3px 3px 0px rgba(0, 0, 0, 0.5);
44
- }
45
-
46
- nav {
47
- display: flex;
48
- justify-content: center;
49
- margin-top: 20px;
50
- }
51
-
52
- nav a {
53
- color: var(--light-text);
54
- text-decoration: none;
55
- margin: 0 15px;
56
- padding: 10px 15px;
57
- border: 2px solid var(--accent-color);
58
- border-radius: 5px;
59
- transition: all 0.3s ease;
60
- }
61
-
62
- nav a:hover {
63
- background-color: var(--accent-color);
64
- transform: translateY(-3px);
65
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
66
- }
67
-
68
- main {
69
- flex: 1;
70
- padding: 40px 0;
71
- }
72
-
73
- .game-container {
74
- background-color: var(--secondary-color);
75
- border-radius: 10px;
76
- padding: 30px;
77
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
78
- margin-bottom: 30px;
79
- }
80
-
81
- .text-display {
82
- font-size: 1.5rem;
83
- line-height: 2;
84
- margin-bottom: 30px;
85
- padding: 20px;
86
- background-color: var(--primary-color);
87
- border-radius: 8px;
88
- border: 2px solid var(--accent-color);
89
- white-space: pre-wrap;
90
- }
91
-
92
- .text-display span {
93
- position: relative;
94
- }
95
-
96
- .text-display span.correct {
97
- color: var(--success-color);
98
- }
99
-
100
- .text-display span.incorrect {
101
- color: var(--error-color);
102
- text-decoration: underline;
103
- }
104
-
105
- .text-display span.current {
106
- background-color: var(--accent-color);
107
- }
108
-
109
- .input-area {
110
- width: 100%;
111
- margin-bottom: 20px;
112
- }
113
-
114
- .input-area textarea {
115
- width: 100%;
116
- padding: 15px;
117
- font-size: 1.2rem;
118
- background-color: var(--primary-color);
119
- color: var(--light-text);
120
- border: 2px solid var(--accent-color);
121
- border-radius: 8px;
122
- resize: none;
123
- font-family: 'Courier New', monospace;
124
- }
125
-
126
- .input-area textarea:focus {
127
- outline: none;
128
- border-color: var(--text-color);
129
- box-shadow: 0 0 10px rgba(233, 69, 96, 0.5);
130
- }
131
-
132
- .stats {
133
- display: flex;
134
- justify-content: space-around;
135
- margin-top: 20px;
136
- padding: 15px;
137
- background-color: var(--primary-color);
138
- border-radius: 8px;
139
- border: 2px solid var(--accent-color);
140
- }
141
-
142
- .stat-box {
143
- text-align: center;
144
- padding: 10px;
145
- }
146
-
147
- .stat-box h3 {
148
- margin: 0;
149
- color: var(--text-color);
150
- font-size: 1rem;
151
- }
152
-
153
- .stat-box p {
154
- font-size: 1.5rem;
155
- margin: 10px 0 0;
156
- }
157
-
158
- .btn {
159
- background-color: var(--accent-color);
160
- color: var(--light-text);
161
- border: none;
162
- padding: 12px 25px;
163
- font-size: 1rem;
164
- border-radius: 5px;
165
- cursor: pointer;
166
- transition: all 0.3s ease;
167
- font-family: 'Press Start 2P', cursive, sans-serif;
168
- text-transform: uppercase;
169
- letter-spacing: 1px;
170
- }
171
-
172
- .btn:hover {
173
- background-color: var(--text-color);
174
- transform: translateY(-2px);
175
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
176
- }
177
-
178
- .btn:active {
179
- transform: translateY(0);
180
- box-shadow: none;
181
- }
182
-
183
- .btn-primary {
184
- background-color: var(--text-color);
185
- }
186
-
187
- .btn-secondary {
188
- background-color: var(--accent-color);
189
- }
190
-
191
- .result-container {
192
- text-align: center;
193
- padding: 30px;
194
- background-color: var(--secondary-color);
195
- border-radius: 10px;
196
- display: none;
197
- }
198
-
199
- .result-container h2 {
200
- color: var(--text-color);
201
- margin-bottom: 20px;
202
- }
203
-
204
- .result-details {
205
- display: flex;
206
- justify-content: space-around;
207
- margin-bottom: 30px;
208
- }
209
-
210
- .result-box {
211
- text-align: center;
212
- padding: 15px;
213
- background-color: var(--primary-color);
214
- border-radius: 8px;
215
- width: 30%;
216
- }
217
-
218
- .result-box h3 {
219
- margin: 0;
220
- color: var(--text-color);
221
- }
222
-
223
- .result-box p {
224
- font-size: 2rem;
225
- margin: 10px 0 0;
226
- }
227
-
228
- .overseer {
229
- position: fixed;
230
- top: 50%;
231
- left: 50%;
232
- transform: translate(-50%, -50%);
233
- width: 300px;
234
- height: 450px;
235
- background-image: url('/static/images/overseer.svg');
236
- background-size: contain;
237
- background-repeat: no-repeat;
238
- display: none;
239
- z-index: 100;
240
- filter: drop-shadow(0 0 10px rgba(255, 0, 0, 0.7));
241
- }
242
-
243
- .overseer.show {
244
- display: block;
245
- animation: overseerAppear 1.5s ease-in-out forwards;
246
- }
247
-
248
- @keyframes overseerAppear {
249
- 0% {
250
- opacity: 0;
251
- transform: translate(-50%, -50%) scale(0.5);
252
- }
253
- 50% {
254
- opacity: 1;
255
- transform: translate(-50%, -50%) scale(1.2);
256
- }
257
- 70% {
258
- transform: translate(-50%, -50%) scale(1);
259
- }
260
- 85% {
261
- transform: translate(-50%, -50%) rotate(-5deg);
262
- }
263
- 100% {
264
- transform: translate(-50%, -50%) rotate(0deg);
265
- }
266
- }
267
-
268
- .gunshot {
269
- position: fixed;
270
- top: 0;
271
- left: 0;
272
- width: 100%;
273
- height: 100%;
274
- background-color: rgba(255, 0, 0, 0.7);
275
- display: none;
276
- z-index: 99;
277
- animation: bloodFlash 0.5s ease-out;
278
- }
279
-
280
- @keyframes bloodFlash {
281
- 0% {
282
- opacity: 0;
283
- }
284
- 50% {
285
- opacity: 1;
286
- }
287
- 100% {
288
- opacity: 0;
289
- }
290
- }
291
-
292
- .blood-splatter {
293
- position: fixed;
294
- width: 100%;
295
- height: 100%;
296
- top: 0;
297
- left: 0;
298
- z-index: 98;
299
- pointer-events: none;
300
- display: none;
301
- }
302
-
303
- .blood-splatter.show {
304
- display: block;
305
- }
306
-
307
- .blood-drop {
308
- position: absolute;
309
- background-color: #800;
310
- border-radius: 50%;
311
- animation: drip 2s ease-in infinite;
312
- }
313
-
314
- @keyframes drip {
315
- 0% {
316
- transform: translateY(0) scale(1);
317
- }
318
- 80% {
319
- transform: translateY(300px) scale(1.5);
320
- opacity: 1;
321
- }
322
- 100% {
323
- transform: translateY(350px) scale(1);
324
- opacity: 0;
325
- }
326
- }
327
-
328
- .muzzle-flash, .muzzle-flash-inner {
329
- opacity: 0;
330
- }
331
-
332
- .overseer.firing .muzzle-flash {
333
- opacity: 1;
334
- animation: flash 0.3s ease-out;
335
- }
336
-
337
- .overseer.firing .muzzle-flash-inner {
338
- opacity: 1;
339
- animation: flash 0.2s ease-out;
340
- }
341
-
342
- @keyframes flash {
343
- 0% {
344
- opacity: 0;
345
- transform: scale(0.5);
346
- }
347
- 50% {
348
- opacity: 1;
349
- transform: scale(1.5);
350
- }
351
- 100% {
352
- opacity: 0;
353
- transform: scale(0.8);
354
- }
355
- }
356
-
357
- .death-message {
358
- position: fixed;
359
- top: 40%;
360
- left: 50%;
361
- transform: translate(-50%, -50%);
362
- font-size: 3rem;
363
- color: #ff0000;
364
- text-shadow: 0 0 10px #000;
365
- z-index: 101;
366
- opacity: 0;
367
- font-family: 'Press Start 2P', cursive;
368
- text-transform: uppercase;
369
- display: none;
370
- }
371
-
372
- .death-message.show {
373
- display: block;
374
- animation: messageAppear 2s ease-in-out forwards;
375
- }
376
-
377
- @keyframes messageAppear {
378
- 0% {
379
- opacity: 0;
380
- transform: translate(-50%, -50%) scale(0.5);
381
- }
382
- 50% {
383
- opacity: 1;
384
- transform: translate(-50%, -50%) scale(1.2);
385
- }
386
- 100% {
387
- opacity: 1;
388
- transform: translate(-50%, -50%) scale(1);
389
- }
390
- }
391
-
392
- .multiplayer-container {
393
- background-color: var(--secondary-color);
394
- border-radius: 10px;
395
- padding: 30px;
396
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
397
- }
398
-
399
- .room-form {
400
- margin-bottom: 30px;
401
- display: flex;
402
- flex-direction: column;
403
- align-items: center;
404
- }
405
-
406
- .room-form input {
407
- width: 100%;
408
- max-width: 400px;
409
- padding: 12px;
410
- margin-bottom: 15px;
411
- background-color: var(--primary-color);
412
- color: var(--light-text);
413
- border: 2px solid var(--accent-color);
414
- border-radius: 5px;
415
- font-family: 'Press Start 2P', cursive, sans-serif;
416
- }
417
-
418
- .players-list {
419
- margin-top: 30px;
420
- }
421
-
422
- .player-item {
423
- display: flex;
424
- justify-content: space-between;
425
- align-items: center;
426
- padding: 15px;
427
- margin-bottom: 10px;
428
- background-color: var(--primary-color);
429
- border-radius: 5px;
430
- border-left: 5px solid var(--accent-color);
431
- }
432
-
433
- .player-name {
434
- font-weight: bold;
435
- color: var(--text-color);
436
- }
437
-
438
- .player-progress {
439
- width: 60%;
440
- height: 20px;
441
- background-color: var(--primary-color);
442
- border: 2px solid var(--accent-color);
443
- border-radius: 10px;
444
- overflow: hidden;
445
- }
446
-
447
- .progress-bar {
448
- height: 100%;
449
- background-color: var(--text-color);
450
- width: 0%;
451
- transition: width 0.3s ease;
452
- }
453
-
454
- .player-stats {
455
- display: flex;
456
- gap: 15px;
457
- }
458
-
459
- .leaderboard-container {
460
- background-color: var(--secondary-color);
461
- border-radius: 10px;
462
- padding: 30px;
463
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
464
- }
465
-
466
- .leaderboard-table {
467
- width: 100%;
468
- border-collapse: collapse;
469
- margin-top: 20px;
470
- }
471
-
472
- .leaderboard-table th, .leaderboard-table td {
473
- padding: 15px;
474
- text-align: left;
475
- border-bottom: 2px solid var(--accent-color);
476
- }
477
-
478
- .leaderboard-table th {
479
- background-color: var(--accent-color);
480
- color: var(--light-text);
481
- text-transform: uppercase;
482
- }
483
-
484
- .leaderboard-table tr:nth-child(even) {
485
- background-color: var(--primary-color);
486
- }
487
-
488
- .leaderboard-table tr:hover {
489
- background-color: var(--accent-color);
490
- }
491
-
492
- footer {
493
- background-color: var(--secondary-color);
494
- text-align: center;
495
- padding: 20px 0;
496
- margin-top: auto;
497
- }
498
-
499
- footer p {
500
- margin: 0;
501
- color: var(--light-text);
502
- font-size: 0.8rem;
503
- }
504
-
505
- @media (max-width: 768px) {
506
- header h1 {
507
- font-size: 1.8rem;
508
- }
509
-
510
- nav {
511
- flex-direction: column;
512
- align-items: center;
513
- }
514
-
515
- nav a {
516
- margin: 5px 0;
517
- width: 80%;
518
- text-align: center;
519
- }
520
-
521
- .text-display {
522
- font-size: 1.2rem;
523
- }
524
-
525
- .stats {
526
- flex-direction: column;
527
- }
528
-
529
- .stat-box {
530
- margin-bottom: 10px;
531
- }
532
-
533
- .result-details {
534
- flex-direction: column;
535
- align-items: center;
536
- }
537
-
538
- .result-box {
539
- width: 80%;
540
- margin-bottom: 15px;
541
- }
542
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/images/overseer.svg DELETED
static/js/game.js DELETED
@@ -1,260 +0,0 @@
1
- document.addEventListener('DOMContentLoaded', function() {
2
- // Элементы DOM
3
- const textDisplay = document.getElementById('textDisplay');
4
- const inputArea = document.getElementById('inputArea');
5
- const wpmDisplay = document.getElementById('wpm');
6
- const accuracyDisplay = document.getElementById('accuracy');
7
- const timeDisplay = document.getElementById('time');
8
- const startBtn = document.getElementById('startBtn');
9
- const resultContainer = document.getElementById('resultContainer');
10
- const finalWpm = document.getElementById('finalWpm');
11
- const finalAccuracy = document.getElementById('finalAccuracy');
12
- const finalTime = document.getElementById('finalTime');
13
- const restartBtn = document.getElementById('restartBtn');
14
- const overseer = document.getElementById('overseer');
15
- const gunshot = document.getElementById('gunshot');
16
- const usernameInput = document.getElementById('username');
17
-
18
- // Переменные для игры
19
- let text = '';
20
- let startTime = null;
21
- let endTime = null;
22
- let timer = null;
23
- let mistakes = 0;
24
- let totalChars = 0;
25
- let currentPosition = 0;
26
- let gameActive = false;
27
- let timeLimit = 60; // Время в секундах
28
- let timeRemaining = timeLimit;
29
-
30
- // Получение текста с сервера
31
- function fetchText() {
32
- fetch('/get_text')
33
- .then(response => response.json())
34
- .then(data => {
35
- text = data.text;
36
- displayText();
37
- })
38
- .catch(error => console.error('Error fetching text:', error));
39
- }
40
-
41
- // Отображение текста
42
- function displayText() {
43
- textDisplay.innerHTML = '';
44
- for (let i = 0; i < text.length; i++) {
45
- const span = document.createElement('span');
46
- span.textContent = text[i];
47
- textDisplay.appendChild(span);
48
- }
49
- // Выделяем первый символ как текущий
50
- if (textDisplay.firstChild) {
51
- textDisplay.firstChild.classList.add('current');
52
- }
53
- }
54
-
55
- // Начало игры
56
- function startGame() {
57
- if (gameActive) return;
58
-
59
- fetchText();
60
- inputArea.value = '';
61
- inputArea.disabled = false;
62
- inputArea.focus();
63
-
64
- mistakes = 0;
65
- totalChars = 0;
66
- currentPosition = 0;
67
- timeRemaining = timeLimit;
68
-
69
- startTime = new Date();
70
- gameActive = true;
71
-
72
- // Запускаем таймер
73
- timer = setInterval(updateTimer, 1000);
74
-
75
- // Скрываем результаты и надсмотрщика
76
- resultContainer.style.display = 'none';
77
- overseer.classList.remove('show');
78
-
79
- // Обновляем статистику
80
- updateStats();
81
- }
82
-
83
- // Обновление таймера
84
- function updateTimer() {
85
- timeRemaining--;
86
- timeDisplay.textContent = timeRemaining;
87
-
88
- if (timeRemaining <= 0) {
89
- endGame(false);
90
- }
91
- }
92
-
93
- // Обновление статистики
94
- function updateStats() {
95
- if (!gameActive) return;
96
-
97
- const currentTime = new Date();
98
- const timeElapsed = (currentTime - startTime) / 1000; // в секундах
99
-
100
- // Расчет WPM (слов в минуту)
101
- // Считаем, что среднее слово - 5 символов
102
- const wpm = Math.round((currentPosition / 5) / (timeElapsed / 60));
103
-
104
- // Расчет точности
105
- const accuracy = totalChars > 0 ? Math.round(((totalChars - mistakes) / totalChars) * 100) : 100;
106
-
107
- wpmDisplay.textContent = wpm;
108
- accuracyDisplay.textContent = accuracy + '%';
109
- }
110
-
111
- // Завершение игры
112
- function endGame(completed = true) {
113
- clearInterval(timer);
114
- gameActive = false;
115
- endTime = new Date();
116
-
117
- inputArea.disabled = true;
118
-
119
- const timeElapsed = Math.round((endTime - startTime) / 1000);
120
- const wpm = Math.round((currentPosition / 5) / (timeElapsed / 60));
121
- const accuracy = totalChars > 0 ? Math.round(((totalChars - mistakes) / totalChars) * 100) : 100;
122
-
123
- finalWpm.textContent = wpm;
124
- finalAccuracy.textContent = accuracy + '%';
125
- finalTime.textContent = timeElapsed + 's';
126
-
127
- resultContainer.style.display = 'block';
128
-
129
- // Если игра не завершена успешно или точность ниже 70%, показываем надсмотрщика
130
- if (!completed || accuracy < 70) {
131
- showOverseer();
132
- }
133
-
134
- // Сохраняем результат
135
- if (usernameInput && usernameInput.value) {
136
- saveResult(usernameInput.value, wpm, accuracy);
137
- }
138
- }
139
-
140
- // Показ надсмотрщика и анимация выстрела
141
- function showOverseer() {
142
- overseer.classList.add('show');
143
-
144
- // Создаем капли крови для разбрызгивания
145
- createBloodSplatters();
146
-
147
- // Через 2 секунды после появления надсмотрщика - "выстрел"
148
- setTimeout(() => {
149
- overseer.classList.add('firing');
150
- gunshot.style.display = 'block';
151
- bloodSplatter.classList.add('show');
152
-
153
- // Показываем сообщение о смерти
154
- deathMessage.classList.add('show');
155
-
156
- // Звук выстрела
157
- const audio = new Audio('/static/sounds/gunshot.mp3');
158
- audio.play().catch(e => console.log('Audio play failed:', e));
159
-
160
- // Эффект тряски экрана
161
- document.body.classList.add('shake');
162
- setTimeout(() => {
163
- document.body.classList.remove('shake');
164
- }, 500);
165
-
166
- // Скрываем эффект выстрела через 500мс
167
- setTimeout(() => {
168
- gunshot.style.display = 'none';
169
- }, 500);
170
- }, 2000);
171
- }
172
-
173
- // Создание брызг крови
174
- function createBloodSplatters() {
175
- const bloodSplatter = document.getElementById('bloodSplatter');
176
- bloodSplatter.innerHTML = '';
177
-
178
- // Создаем 30 капель крови разного размера
179
- for (let i = 0; i < 30; i++) {
180
- const drop = document.createElement('div');
181
- drop.classList.add('blood-drop');
182
-
183
- // Случайный размер
184
- const size = Math.random() * 20 + 5;
185
- drop.style.width = `${size}px`;
186
- drop.style.height = `${size}px`;
187
-
188
- // Случайное положение
189
- drop.style.left = `${Math.random() * 100}%`;
190
- drop.style.top = `${Math.random() * 50}%`;
191
-
192
- // Случайная задержка анимации
193
- drop.style.animationDelay = `${Math.random() * 1.5}s`;
194
-
195
- bloodSplatter.appendChild(drop);
196
- }
197
- }
198
-
199
- // Сохранение результата
200
- function saveResult(username, wpm, accuracy) {
201
- fetch('/save_result', {
202
- method: 'POST',
203
- headers: {
204
- 'Content-Type': 'application/json',
205
- },
206
- body: JSON.stringify({
207
- username: username,
208
- wpm: wpm,
209
- accuracy: accuracy
210
- })
211
- })
212
- .then(response => response.json())
213
- .then(data => console.log('Result saved:', data))
214
- .catch(error => console.error('Error saving result:', error));
215
- }
216
-
217
- // Обработка ввода
218
- inputArea.addEventListener('input', function(e) {
219
- if (!gameActive) return;
220
-
221
- const inputValue = e.target.value;
222
- const currentChar = text[currentPosition];
223
-
224
- // Проверяем, правильно ли введен символ
225
- if (inputValue.charAt(inputValue.length - 1) === currentChar) {
226
- // Правильный символ
227
- const spans = textDisplay.querySelectorAll('span');
228
- spans[currentPosition].classList.remove('current');
229
- spans[currentPosition].classList.add('correct');
230
-
231
- currentPosition++;
232
- totalChars++;
233
-
234
- // Если есть следующий символ, делаем его текущим
235
- if (currentPosition < text.length) {
236
- spans[currentPosition].classList.add('current');
237
- } else {
238
- // Текст закончился, завершаем игру
239
- endGame(true);
240
- }
241
- } else {
242
- // Неправильный символ
243
- mistakes++;
244
- totalChars++;
245
-
246
- const spans = textDisplay.querySelectorAll('span');
247
- spans[currentPosition].classList.add('incorrect');
248
- }
249
-
250
- // Обновляем статистику
251
- updateStats();
252
- });
253
-
254
- // Обработчики кнопок
255
- startBtn.addEventListener('click', startGame);
256
- restartBtn.addEventListener('click', startGame);
257
-
258
- // Инициализация
259
- timeDisplay.textContent = timeLimit;
260
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/js/multiplayer.js DELETED
@@ -1,276 +0,0 @@
1
- document.addEventListener('DOMContentLoaded', function() {
2
- // Элементы DOM
3
- const roomForm = document.getElementById('roomForm');
4
- const usernameInput = document.getElementById('username');
5
- const roomInput = document.getElementById('room');
6
- const joinBtn = document.getElementById('joinBtn');
7
- const startBtn = document.getElementById('startBtn');
8
- const gameContainer = document.getElementById('gameContainer');
9
- const textDisplay = document.getElementById('textDisplay');
10
- const inputArea = document.getElementById('inputArea');
11
- const playersList = document.getElementById('playersList');
12
-
13
- // Переменные для игры
14
- let socket;
15
- let username = '';
16
- let room = '';
17
- let text = '';
18
- let startTime = null;
19
- let currentPosition = 0;
20
- let mistakes = 0;
21
- let totalChars = 0;
22
- let gameActive = false;
23
-
24
- // Инициализация Socket.IO
25
- function initSocket() {
26
- socket = io();
27
-
28
- // Обработчики событий Socket.IO
29
- socket.on('connect', () => {
30
- console.log('Connected to server');
31
- });
32
-
33
- socket.on('room_update', (data) => {
34
- updatePlayersList(data.players);
35
-
36
- // Если игра началась, но текст еще не отображен
37
- if (data.started && !gameActive) {
38
- startGame(data.text);
39
- }
40
-
41
- // Активируем кнопку старта только для первого игрока
42
- const playerIds = Object.keys(data.players);
43
- if (playerIds.length > 0 && playerIds[0] === socket.id) {
44
- startBtn.disabled = false;
45
- } else {
46
- startBtn.disabled = true;
47
- }
48
- });
49
-
50
- socket.on('game_started', (data) => {
51
- startGame(data.text);
52
- });
53
-
54
- socket.on('progress_update', (players) => {
55
- updatePlayersProgress(players);
56
-
57
- // Проверяем, закончили ли все игроки
58
- let allFinished = true;
59
- for (const id in players) {
60
- if (!players[id].finished) {
61
- allFinished = false;
62
- break;
63
- }
64
- }
65
-
66
- // Если все закончили, показываем результаты
67
- if (allFinished) {
68
- showResults(players);
69
- }
70
- });
71
-
72
- socket.on('disconnect', () => {
73
- console.log('Disconnected from server');
74
- });
75
- }
76
-
77
- // Присоединение к комнате
78
- function joinRoom() {
79
- username = usernameInput.value.trim();
80
- room = roomInput.value.trim();
81
-
82
- if (!username || !room) {
83
- alert('Пожалуйста, введите имя пользователя и название комнаты');
84
- return;
85
- }
86
-
87
- socket.emit('join', {
88
- username: username,
89
- room: room
90
- });
91
-
92
- // Скрываем форму и показываем игровой контейнер
93
- roomForm.style.display = 'none';
94
- gameContainer.style.display = 'block';
95
- }
96
-
97
- // Обновление списка игроков
98
- function updatePlayersList(players) {
99
- playersList.innerHTML = '';
100
-
101
- for (const id in players) {
102
- const player = players[id];
103
- const playerItem = document.createElement('div');
104
- playerItem.className = 'player-item';
105
- playerItem.id = 'player-' + id;
106
-
107
- const playerName = document.createElement('div');
108
- playerName.className = 'player-name';
109
- playerName.textContent = player.username;
110
-
111
- const playerProgress = document.createElement('div');
112
- playerProgress.className = 'player-progress';
113
-
114
- const progressBar = document.createElement('div');
115
- progressBar.className = 'progress-bar';
116
- progressBar.style.width = player.progress + '%';
117
-
118
- const playerStats = document.createElement('div');
119
- playerStats.className = 'player-stats';
120
- playerStats.innerHTML = `
121
- <span>WPM: ${player.wpm || 0}</span>
122
- <span>Точность: ${player.accuracy || 0}%</span>
123
- `;
124
-
125
- playerProgress.appendChild(progressBar);
126
- playerItem.appendChild(playerName);
127
- playerItem.appendChild(playerProgress);
128
- playerItem.appendChild(playerStats);
129
- playersList.appendChild(playerItem);
130
- }
131
- }
132
-
133
- // Обновление прогресса игроков
134
- function updatePlayersProgress(players) {
135
- for (const id in players) {
136
- const player = players[id];
137
- const playerItem = document.getElementById('player-' + id);
138
-
139
- if (playerItem) {
140
- const progressBar = playerItem.querySelector('.progress-bar');
141
- progressBar.style.width = player.progress + '%';
142
-
143
- const playerStats = playerItem.querySelector('.player-stats');
144
- playerStats.innerHTML = `
145
- <span>WPM: ${player.wpm || 0}</span>
146
- <span>Точность: ${player.accuracy || 0}%</span>
147
- `;
148
- }
149
- }
150
- }
151
-
152
- // Начало игры
153
- function startGame(textContent) {
154
- text = textContent;
155
- displayText();
156
-
157
- inputArea.value = '';
158
- inputArea.disabled = false;
159
- inputArea.focus();
160
-
161
- mistakes = 0;
162
- totalChars = 0;
163
- currentPosition = 0;
164
-
165
- startTime = new Date();
166
- gameActive = true;
167
- }
168
-
169
- // Отображение текста
170
- function displayText() {
171
- textDisplay.innerHTML = '';
172
- for (let i = 0; i < text.length; i++) {
173
- const span = document.createElement('span');
174
- span.textContent = text[i];
175
- textDisplay.appendChild(span);
176
- }
177
- // Выделяем первый символ как текущий
178
- if (textDisplay.firstChild) {
179
- textDisplay.firstChild.classList.add('current');
180
- }
181
- }
182
-
183
- // Показ результатов
184
- function showResults(players) {
185
- let winner = null;
186
- let maxWpm = 0;
187
-
188
- for (const id in players) {
189
- const player = players[id];
190
- if (player.wpm > maxWpm) {
191
- maxWpm = player.wpm;
192
- winner = player;
193
- }
194
- }
195
-
196
- if (winner) {
197
- alert(`Игра окончена! Победитель: ${winner.username} со скоростью ${winner.wpm} WPM и точностью ${winner.accuracy}%`);
198
- }
199
- }
200
-
201
- // Обработка ввода
202
- inputArea.addEventListener('input', function(e) {
203
- if (!gameActive) return;
204
-
205
- const inputValue = e.target.value;
206
- const currentChar = text[currentPosition];
207
-
208
- // Проверяем, правильно ли введен символ
209
- if (inputValue.charAt(inputValue.length - 1) === currentChar) {
210
- // Правильный символ
211
- const spans = textDisplay.querySelectorAll('span');
212
- spans[currentPosition].classList.remove('current');
213
- spans[currentPosition].classList.add('correct');
214
-
215
- currentPosition++;
216
- totalChars++;
217
-
218
- // Если есть следующий символ, делаем его текущим
219
- if (currentPosition < text.length) {
220
- spans[currentPosition].classList.add('current');
221
- } else {
222
- // Текст закончился, завершаем игру
223
- gameActive = false;
224
- inputArea.disabled = true;
225
- }
226
- } else {
227
- // Неправильный символ
228
- mistakes++;
229
- totalChars++;
230
- }
231
-
232
- // Обновляем статистику и отправляем на сервер
233
- updateStats();
234
- });
235
-
236
- // Обновление статистики
237
- function updateStats() {
238
- if (!gameActive && currentPosition < text.length) return;
239
-
240
- const currentTime = new Date();
241
- const timeElapsed = (currentTime - startTime) / 1000; // в секундах
242
-
243
- // Расчет WPM (слов в минуту)
244
- // Считаем, что среднее слово - 5 символов
245
- const wpm = Math.round((currentPosition / 5) / (timeElapsed / 60));
246
-
247
- // Расчет точности
248
- const accuracy = totalChars > 0 ? Math.round(((totalChars - mistakes) / totalChars) * 100) : 100;
249
-
250
- // Расчет прогресса
251
- const progress = Math.round((currentPosition / text.length) * 100);
252
-
253
- // Отправляем данные на сервер
254
- socket.emit('update_progress', {
255
- room: room,
256
- progress: progress,
257
- wpm: wpm,
258
- accuracy: accuracy
259
- });
260
- }
261
-
262
- // Обработчики кнопок
263
- joinBtn.addEventListener('click', function(e) {
264
- e.preventDefault();
265
- joinRoom();
266
- });
267
-
268
- startBtn.addEventListener('click', function() {
269
- socket.emit('start_game', {
270
- room: room
271
- });
272
- });
273
-
274
- // Инициализация
275
- initSocket();
276
- });