Upload 11 files
Browse files- app.py +121 -4
- instance/typing_game.db +0 -0
- static/css/style.css +33 -0
- static/js/game.js +42 -0
- static/js/multiplayer.js +43 -13
- templates/game.html +2 -0
- templates/multiplayer.html +2 -1
app.py
CHANGED
|
@@ -32,7 +32,93 @@ typing_texts = [
|
|
| 32 |
"Хороший наборщик текста может печатать, не глядя на клавиатуру.",
|
| 33 |
"Скорость набора измеряется в количестве слов в минуту.",
|
| 34 |
"Точность набора так же важна, как и скорость.",
|
| 35 |
-
"Регулярные тренировки помогут вам улучшить навыки набора текста."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
]
|
| 37 |
|
| 38 |
# Комнаты для многопользовательской игры
|
|
@@ -67,9 +153,21 @@ def save_result():
|
|
| 67 |
wpm = data.get('wpm', 0)
|
| 68 |
accuracy = data.get('accuracy', 0)
|
| 69 |
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
return jsonify({'success': True})
|
| 75 |
|
|
@@ -102,6 +200,25 @@ def on_join(data):
|
|
| 102 |
print(f"Игрок {username} присоединился к комнате {room}")
|
| 103 |
print(f"Текущие игроки в комнате: {rooms[room]['players'].keys()}")
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
@socketio.on('start_game')
|
| 106 |
def on_start_game(data):
|
| 107 |
room = data['room']
|
|
|
|
| 32 |
"Хороший наборщик текста может печатать, не глядя на клавиатуру.",
|
| 33 |
"Скорость набора измеряется в количестве слов в минуту.",
|
| 34 |
"Точность набора так же важна, как и скорость.",
|
| 35 |
+
"Регулярные тренировки помогут вам улучшить навыки набора текста.",
|
| 36 |
+
"Все счастливые семьи похожи друг на друга, каждая несчастливая семья несчастлива по-своему. Все смешалось в доме Облонских.",
|
| 37 |
+
"Мой дядя самых честных правил, когда не в шутку занемог, он уважать себя заставил и лучше выдумать не мог.",
|
| 38 |
+
"В начале было Слово, и Слово было у Бога, и Слово было Бог. Оно было в начале у Бога. Все чрез Него начало быть, и без Него ничто не начало быть, что начало быть.",
|
| 39 |
+
"Двадцать лет назад никто не слыхал о капитане Немо, а теперь все знают, что он существует. Точно так же и я, профессор Пьер Аронакс, единственный спутник капитана Немо, расскажу вам то, что до сих пор было скрыто.",
|
| 40 |
+
"Над всем этим возвышался громадный замок с высокими башнями, с длинными флюгерами, развевающимися по ветру. Он был окружен глубоким рвом с водой, в которой плавали дикие лебеди и росли красивейшие тростники.",
|
| 41 |
+
"Человек, который смеется, не всегда смеется от радости. Иногда он смеется от горя, от отчаяния, от ненависти. Смех бывает разный.",
|
| 42 |
+
"Все мы родом из детства. Но не все помнят об этом. Взрослые забывают, что когда-то были детьми. А я помню.",
|
| 43 |
+
"Мы все глядим в Наполеоны; двуногих тварей миллионы для нас орудие одно; нам чувство дико и смешно.",
|
| 44 |
+
"Я памятник себе воздвиг нерукотворный, к нему не зарастет народная тропа, вознесся выше он главою непокорной Александрийского столпа.",
|
| 45 |
+
"Тот, кто не помнит своего прошлого, обречен на то, чтобы пережить его вновь. История учит лишь тому, что она никогда ничему не научила народы.",
|
| 46 |
+
"Мой дом - моя крепость. Но даже крепость можно взять штурмом, если найти в ней слабое место. Поэтому я всегда начеку.",
|
| 47 |
+
"Время - самая большая ценность, которая у нас есть. Его нельзя вернуть, его нельзя накопить, его можно только потратить. И от того, как мы его тратим, зависит наша жизнь.",
|
| 48 |
+
"Книги - корабли мысли, странствующие по волнам времени и бережно несущие свой драгоценный груз от поколения к поколению.",
|
| 49 |
+
"Жизнь - это то, что с тобой происходит, пока ты строишь планы на будущее. Не откладывай на завтра то, что можно сделать сегодня.",
|
| 50 |
+
"Утро туманное, утро седое, нивы печальные, снегом покрытые. Нехотя вспомнишь и время былое, вспомнишь и лица, давно позабытые.",
|
| 51 |
+
"Я пришел к тебе с приветом, рассказать, что солнце встало, что оно горячим светом по листам затрепетало.",
|
| 52 |
+
"Мороз и солнце; день чудесный! Еще ты дремлешь, друг прелестный - пора, красавица, проснись: открой сомкнуты негой взоры навстречу северной Авроры, звездою севера явись!",
|
| 53 |
+
"Белеет парус одинокий в тумане моря голубом. Что ищет о�� в стране далекой? Что кинул он в краю родном?",
|
| 54 |
+
"Люблю грозу в начале мая, когда весенний, первый гром, как бы резвяся и играя, грохочет в небе голубом.",
|
| 55 |
+
"Однажды в студеную зимнюю пору я из лесу вышел; был сильный мороз. Гляжу, поднимается медленно в гору лошадка, везущая хворосту воз.",
|
| 56 |
+
"Ночь, улица, фонарь, аптека, бессмысленный и тусклый свет. Живи еще хоть четверть века - все будет так. Исхода нет.",
|
| 57 |
+
"Я вас любил: любовь еще, быть может, в душе моей угасла не совсем; но пусть она вас больше не тревожит; я не хочу печалить вас ничем.",
|
| 58 |
+
"Я помню чудное мгновенье: передо мной явилась ты, как мимолетное виденье, как гений чистой красоты.",
|
| 59 |
+
"Мой первый друг, мой друг бесценный! И я судьбу благословил, когда мой двор уединенный, печальным снегом занесенный, твой колокольчик огласил.",
|
| 60 |
+
|
| 61 |
+
# Дополнительные фрагменты из русской классической литературы
|
| 62 |
+
"Герой нашего времени, милостивые государи мои, точно портрет, но не одного человека: это портрет, составленный из пороков всего нашего поколения, в полном их развитии.",
|
| 63 |
+
"Чичиков подъехал к дому, который был ни мал, ни велик, но опрятен и прочен. Две русские избы, перенесенные сюда бревно в бревно, заменили флигеля.",
|
| 64 |
+
"Тарас Бульба остановился перед крыльцом и увидел жену свою, которая только что успела его обнять, как уже вскрикнула, вглядываясь в него пристально.",
|
| 65 |
+
"Старик Хоттабыч сидел на диване, поджав под себя ноги, и с упоением читал какую-то толстую книгу. Он был в своем любимом белом костюме и красной феске.",
|
| 66 |
+
"Вечера на хуторе близ Диканьки. Как упоителен, как роскошен летний день в Малороссии! Как томительно жарки те часы, когда полдень блещет в тишине и зное!",
|
| 67 |
+
"Капитанская дочка. Отец мой Андрей Петрович Гринев в молодости своей служил при графе Минихе и вышел в отставку премьер-майором в 17.. году.",
|
| 68 |
+
"Война и мир. Ну, князь, Генуя и Лукка стали не больше, как поместьями фамилии Бонапарте. Нет, я вам вперед говорю, если вы мне не скажете, что у нас война, если вы еще позволите себе защищать все гадости, все ужасы этого антихриста (право, я верю, что он антихрист), — я вас больше не знаю, вы уж не друг мой, вы уж не мой верный раб, как вы говорите.",
|
| 69 |
+
"Преступление и наказание. В начале июля, в чрезвычайно жаркое время, под вечер, один молодой человек вышел из своей каморки, которую нанимал от жильцов в С-м переулке, на улицу и медленно, как бы в нерешимости, отправился к К-ну мосту.",
|
| 70 |
+
"Идиот. В конце ноября, в оттепель, часов в девять утра, поезд Петербургско-Варшавской железной дороги на всех парах подходил к Петербургу. Было так сыро и туманно, что насилу рассвело.",
|
| 71 |
+
"Мастер и Маргарита. Однажды весною, в час небывало жаркого заката, в Москве, на Патриарших прудах, появились два гражданина. Первый из них, одетый в летнюю серенькую пару, был маленького роста, упитан, лыс, свою приличную шляпу пирожком нес в руке, а на хорошо выбритом лице его помещались сверхъестественных размеров очки в черной роговой оправе.",
|
| 72 |
+
"Собачье сердце. На обоях золотые корзины, букеты роз, над ними вьются голубые птички. На высоком узком камине прислонен к стене кремовый пастушок. Перед камином на стеклянном столике лежат раскрытая громадная книга с пестрыми картинками и мертвая птица с распущенным хвостом.",
|
| 73 |
+
"Тихий Дон. Мелеховский двор - на самом краю хутора. Воротца со скотиньего база ведут на север к Дону. Крутой восьмисаженный спуск меж замшелых в прозелени меловых глыб, и вот берег: перламутровая россыпь ракушек, серая изломистая кайма нацелованной волнами гальки и дальше - перекипающее под ветром вороненой рябью стремя Дона.",
|
| 74 |
+
"Обломов. В Гороховой улице, в одном из больших домов, народонаселения которого стало бы на целый уездный город, лежал утром в постели, на своей квартире, Илья Ильич Обломов.",
|
| 75 |
+
"Отцы и дети. - Что, Петр, не видать еще? - спрашивал 20 мая 1859 года, выходя без шапки на низкое крылечко постоялого двора на *** шоссе, барин лет сорока с небольшим, в запыленном пальто и клетчатых панталонах, у своего слуги, молодого и щекастого малого с беловатым пухом на подбородке и маленькими тусклыми глазенками.",
|
| 76 |
+
"Анна Каренина. Степан Аркадьич Облонский - Стива, как его звали в свете, - проснулся в обычный час, то есть в восемь часов утра, не в спальне жены, а в своем кабинете, на сафьянном диване.",
|
| 77 |
+
"Евгений Онегин. Мой брат двоюродный, Буянов, в пуху, в картузе с козырьком (Как вам, конечно, он знаком), явился; мой поэт, Буянов мне друг и родственник.",
|
| 78 |
+
"Горе от ума. Чацкий. Ну вот и день прошел, и с ним все призраки, весь чад и дым надежд, насмешливых речей, невыполнимых планов, загадок и сомнений... Какое-то волненье в крови, а голове пустота.",
|
| 79 |
+
"Вишневый сад. Любовь Андреевна. Детская, милая моя, прекрасная комната... Я тут спала, когда была маленькой... И вот я как маленькая... А я родилась здесь, тут прошло мое детство, моя юность.",
|
| 80 |
+
"Ревизор. Городничий. Я пригласил вас, господа, с тем, чтобы сообщить вам пренеприятное известие: к нам едет ревизор.",
|
| 81 |
+
"Мертвые души. Чичиков. Эх, русский народец! Не любит умирать своею смертью!",
|
| 82 |
+
|
| 83 |
+
# Фрагменты из зарубежной литературы
|
| 84 |
+
"Гамлет. Быть или не быть - вот в чем вопрос. Достойно ль смиряться под ударами судьбы, иль надо оказать сопротивленье и в смертной схватке с целым морем бед покончить с ними?",
|
| 85 |
+
"Ромео и Джульетта. Но мягче свет! Что за краса в окне? То свет зари, и солнце - Джульетта! Встань, ясная заря, убей луну-завистницу: она и без того больна и зелена от горя, что ты ее прекрасней во сто крат.",
|
| 86 |
+
"Дон Кихот. В некоем селе Ламанчском, которого название у меня нет охоты припоминать, не так давно жил-был один из тех идальго, чье имущество заключается в фамильном копье, древнем щите, тощей кляче и борзой собаке.",
|
| 87 |
+
"Три мушкетера. В первый понедельник апреля 1625 года все население городка Менга, где некогда родился автор «Романа о розе», казалось взволнованным так, словно гугеноты собирались превратить его во вторую Ла-Рошель.",
|
| 88 |
+
"Граф Монте-Кристо. 24 февраля 1815 года дозорный Нотр-Дам де-ла-Гард дал знать о приближении трехмачтового корабля «Фараон», идущего из Смирны, Триеста и Неаполя.",
|
| 89 |
+
"Гордость и предубеждение. Все знают, что молодой человек, располагающий средствами, должен подыскивать себе жену.",
|
| 90 |
+
"Джейн Эйр. В тот день нельзя было и думать о прогулке. Правда, утром мы еще бродили часок по дорожкам облетевшего сада, но после обеда холодный зимний ветер нагнал угрюмые тучи и полил такой пронизывающий дождь, что и речи не могло быть ни о какой попытке выйти еще раз.",
|
| 91 |
+
"Портрет Дориана Грея. Густой аромат роз наполнял мастерскую художника, а когда в саду поднимался летний ветерок, он, влетая в открытую дверь, приносил с собой то пьянящий запах сирени, то нежное благоухание алых цветов шиповника.",
|
| 92 |
+
"Алиса в Стране чудес. Алисе наскучило сидеть с сестрой без дела на берегу реки; разок-другой она заглянула в книжку, которую читала сестра, но там не было ни картинок, ни разговоров. «Что толку в книжке, – подумала Алиса, – если в ней нет ни картинок, ни разговоров?»",
|
| 93 |
+
"Маленький принц. Когда мне было шесть лет, в книге под названием «Правдивые истории», где рассказывалось про девственные леса, я увидел однажды удивительную картинку.",
|
| 94 |
+
"Властелин колец. Когда господин Бильбо Бэггинс из Бэг-Энда объявил, что скоро отпразднует свое стоодиннадцатилетие весьма пышным угощением, весь Хоббитон загудел и заволновался.",
|
| 95 |
+
"Гарри Поттер и философский камень. Мистер и миссис Дурсль проживали в доме номер четыре по Тисовой улице и всегда с гордостью заявляли, что они, слава богу, абсолютно нормальные люди.",
|
| 96 |
+
"Хоббит, или Туда и обратно. В земле была нора, а в норе жил хоббит. Не мерзкая грязная сырая нора, где со всех сторон торчат хвосты червей и противно пахнет тиной, но и не сухая песчаная голая нора, где не на что сесть и нечего съесть.",
|
| 97 |
+
"Шерлок Холмс. Для Шерлока Холмса она всегда оставалась «той женщиной». Я редко слышал, чтобы он называл ее каким-либо другим именем. В его глазах она затмевала всех представительниц своего пола.",
|
| 98 |
+
"Великий Гэтсби. В юные и ранимые годы отец дал мне совет, который с тех пор не выходит у меня из головы. «Если тебе вдруг захочется осудить кого-то, – сказал он, – вспомни, что не все люди на свете обладают теми преимуществами, которыми обладал ты».",
|
| 99 |
+
"Над пропастью во ржи. Если вам на самом деле хочется услышать эту историю, вы, наверное, прежде всего захотите узнать, где я родился, как провел свое дурацкое детство, что делали мои родители до моего рождения, – словом, всю эту давид-копперфилдовскую муть.",
|
| 100 |
+
"Сто лет одиночества. Много лет спустя, перед самым расстрелом, полковник Аурелиано Буэндиа вспомнит тот далекий день, когда отец повел его поглядеть на лед.",
|
| 101 |
+
"1984. Был холодный ясный апрельский день, и часы пробили тринадцать. Уткнув подбородок в грудь, чтобы спастись от злого ветра, Уинстон Смит торопливо шмыгнул за стеклянную дверь жилого дома «Победа», но все-таки впустил за собой вихрь зернистой пыли.",
|
| 102 |
+
"Преступление и наказание. Раскольников. Я хотел Наполеоном сделаться, оттого и убил... Вошь ли я, как все, или человек? Смогу ли я переступить или не смогу?",
|
| 103 |
+
"Мастер и Маргарита. Рукописи не горят.",
|
| 104 |
+
|
| 105 |
+
# Современные тексты
|
| 106 |
+
"Метро 2033. Артём. Станция казалась обреченной. Еще немного - и она погибнет, ее жители будут убиты, а те, кому повезет умереть сразу, избегнут страшной участи оказаться пищей для мутантов.",
|
| 107 |
+
"Ночной дозор. Иные не умирают насовсем. Мы уходим в сумрак и становимся его частью.",
|
| 108 |
+
"Географ глобус пропил. Служкин сидел на подоконнике в школьном коридоре и курил, опасливо поглядывая на двери учительской.",
|
| 109 |
+
"Generation П. Вавилен Татарский. Когда-то в России и правда жило беспечальное юное поколение, которое улыбнулось лету, морю и солнцу – и выбрало «Пепси».",
|
| 110 |
+
"Лавр. Евгений Водолазкин. Прежде Лавр был Арсением. Когда он родился, никто не предполагал, что впоследствии его назовут Устином, Амвросием и, наконец, Лавром.",
|
| 111 |
+
"Текст. Глеб Янковский. Телефон – это вообще единственное, что у человека по-настоящему личное. Раньше, может, это была душа, но теперь – телефон.",
|
| 112 |
+
"Зулейха открывает глаза. Зулейха открывает глаза. Темно, как в погребе. Сонно вздыхают за тонкой занавеской гуси. Месяц скоро народится, а пока стоят самые темные ночи.",
|
| 113 |
+
"Теория бесконечности. Мы все живем в мире, где каждый день происходят миллионы событий, и каждое из них может изменить нашу жизнь навсегда.",
|
| 114 |
+
"Цифровая крепость. Сьюзан Флетчер бросила быстрый взгляд на ТРАНСТЕКСТ. Она всегда думала, что шифры весьма, весьма сложны. Но ведь для обычных пользователей они не должны быть сложнее обычных слов.",
|
| 115 |
+
"Код да Винчи. Роберт Лэнгдон. Знаменитый куратор Лувра Жак Соньер был найден мертвым в Большой галерее музея. Рядом с телом полиция обнаружила загадочное послание.",
|
| 116 |
+
"Облачный атлас. Тихоокеанский дневник Адама Юинга. Четверг, 7 ноября. За бортом Пропеллер, судна капитана Моллинью, показался остров Чатем.",
|
| 117 |
+
"Бойцовский клуб. Тайлер Дёрден. Первое правило Бойцовского клуба: никому не рассказывать о Бойцовском клубе. Второе правило Бойцовского клуба: никогда никому не рассказывать о Бойцовском клубе.",
|
| 118 |
+
"Голодные игры. Китнисс Эвердин. Когда я просыпаюсь, другая половина кровати уже остыла. Я протягиваю руку, ищу тепло Прим, но нахожу лишь грубую холщовую обивку матраса.",
|
| 119 |
+
"Дивергент. Беатрис Прайор. В нашем доме есть только одно зеркало, и оно спрятано за раздвижной панелью во втором этаже коридора. Наша фракция позволяет смотреться в него р��з в три месяца.",
|
| 120 |
+
"Марсианин. Марк Уотни. Я в полной заднице. Вот моя обдуманная оценка ситуации: я в полной заднице.",
|
| 121 |
+
"Игра престолов. Эддард Старк. Зима близко."
|
| 122 |
]
|
| 123 |
|
| 124 |
# Комнаты для многопользовательской игры
|
|
|
|
| 153 |
wpm = data.get('wpm', 0)
|
| 154 |
accuracy = data.get('accuracy', 0)
|
| 155 |
|
| 156 |
+
# Проверяем, есть ли уже результат с таким именем пользователя
|
| 157 |
+
existing_result = GameResult.query.filter_by(username=username).first()
|
| 158 |
+
|
| 159 |
+
# Если результат существует и новый результат лучше, обновляем его
|
| 160 |
+
if existing_result:
|
| 161 |
+
if wpm > existing_result.wpm:
|
| 162 |
+
existing_result.wpm = wpm
|
| 163 |
+
existing_result.accuracy = accuracy
|
| 164 |
+
existing_result.date = datetime.utcnow()
|
| 165 |
+
db.session.commit()
|
| 166 |
+
else:
|
| 167 |
+
# Если результата нет, создаем новый
|
| 168 |
+
result = GameResult(username=username, wpm=wpm, accuracy=accuracy)
|
| 169 |
+
db.session.add(result)
|
| 170 |
+
db.session.commit()
|
| 171 |
|
| 172 |
return jsonify({'success': True})
|
| 173 |
|
|
|
|
| 200 |
print(f"Игрок {username} присоединился к комнате {room}")
|
| 201 |
print(f"Текущие игроки в комнате: {rooms[room]['players'].keys()}")
|
| 202 |
|
| 203 |
+
@socketio.on('player_ready')
|
| 204 |
+
def on_player_ready(data):
|
| 205 |
+
room = data['room']
|
| 206 |
+
if room in rooms:
|
| 207 |
+
# Отмечаем игрока как готового
|
| 208 |
+
rooms[room]['players'][request.sid]['ready'] = True
|
| 209 |
+
|
| 210 |
+
# Проверяем, все ли игроки готовы
|
| 211 |
+
all_ready = True
|
| 212 |
+
for player_id in rooms[room]['players']:
|
| 213 |
+
if not rooms[room]['players'][player_id].get('ready', False):
|
| 214 |
+
all_ready = False
|
| 215 |
+
break
|
| 216 |
+
|
| 217 |
+
# Если все готовы, начинаем обратный отсчет
|
| 218 |
+
if all_ready and len(rooms[room]['players']) > 0:
|
| 219 |
+
emit('countdown_start', {}, to=room)
|
| 220 |
+
print(f"Все игроки в комнате {room} готовы. Начинаем обратный отсчет.")
|
| 221 |
+
|
| 222 |
@socketio.on('start_game')
|
| 223 |
def on_start_game(data):
|
| 224 |
room = data['room']
|
instance/typing_game.db
CHANGED
|
Binary files a/instance/typing_game.db and b/instance/typing_game.db differ
|
|
|
static/css/style.css
CHANGED
|
@@ -435,6 +435,10 @@ main {
|
|
| 435 |
color: var(--text-color);
|
| 436 |
}
|
| 437 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
.player-progress {
|
| 439 |
width: 60%;
|
| 440 |
height: 20px;
|
|
@@ -456,6 +460,35 @@ main {
|
|
| 456 |
gap: 15px;
|
| 457 |
}
|
| 458 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
.leaderboard-container {
|
| 460 |
background-color: var(--secondary-color);
|
| 461 |
border-radius: 10px;
|
|
|
|
| 435 |
color: var(--text-color);
|
| 436 |
}
|
| 437 |
|
| 438 |
+
.player-ready {
|
| 439 |
+
color: var(--success-color);
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
.player-progress {
|
| 443 |
width: 60%;
|
| 444 |
height: 20px;
|
|
|
|
| 460 |
gap: 15px;
|
| 461 |
}
|
| 462 |
|
| 463 |
+
/* Стили для кнопки готовности */
|
| 464 |
+
.ready {
|
| 465 |
+
background-color: var(--success-color);
|
| 466 |
+
color: var(--light-text);
|
| 467 |
+
}
|
| 468 |
+
|
| 469 |
+
/* Стили для обратного отсчёта */
|
| 470 |
+
.countdown {
|
| 471 |
+
display: none;
|
| 472 |
+
position: absolute;
|
| 473 |
+
top: 50%;
|
| 474 |
+
left: 50%;
|
| 475 |
+
transform: translate(-50%, -50%);
|
| 476 |
+
font-size: 5rem;
|
| 477 |
+
color: var(--text-color);
|
| 478 |
+
z-index: 100;
|
| 479 |
+
text-shadow: 0 0 10px rgba(233, 69, 96, 0.7);
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
.pulse {
|
| 483 |
+
animation: pulse 1s ease-in-out;
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
@keyframes pulse {
|
| 487 |
+
0% { transform: translate(-50%, -50%) scale(0.8); opacity: 0.7; }
|
| 488 |
+
50% { transform: translate(-50%, -50%) scale(1.2); opacity: 1; }
|
| 489 |
+
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.7; }
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
.leaderboard-container {
|
| 493 |
background-color: var(--secondary-color);
|
| 494 |
border-radius: 10px;
|
static/js/game.js
CHANGED
|
@@ -56,6 +56,45 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 56 |
function startGame() {
|
| 57 |
if (gameActive) return;
|
| 58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
fetchText();
|
| 60 |
inputArea.value = '';
|
| 61 |
inputArea.disabled = false;
|
|
@@ -76,6 +115,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 76 |
resultContainer.style.display = 'none';
|
| 77 |
overseer.classList.remove('show');
|
| 78 |
|
|
|
|
|
|
|
|
|
|
| 79 |
// Обновляем статистику
|
| 80 |
updateStats();
|
| 81 |
}
|
|
|
|
| 56 |
function startGame() {
|
| 57 |
if (gameActive) return;
|
| 58 |
|
| 59 |
+
// Скрываем кнопку старта во время обратного отсчёта
|
| 60 |
+
startBtn.disabled = true;
|
| 61 |
+
|
| 62 |
+
// Запускаем обратный отсчёт
|
| 63 |
+
startCountdown();
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
// Функция обратного отсчёта
|
| 67 |
+
function startCountdown() {
|
| 68 |
+
const countdown = document.getElementById('countdown');
|
| 69 |
+
countdown.style.display = 'block';
|
| 70 |
+
let count = 3;
|
| 71 |
+
|
| 72 |
+
countdown.textContent = count;
|
| 73 |
+
countdown.classList.add('pulse');
|
| 74 |
+
|
| 75 |
+
const countdownTimer = setInterval(() => {
|
| 76 |
+
count--;
|
| 77 |
+
|
| 78 |
+
if (count > 0) {
|
| 79 |
+
countdown.textContent = count;
|
| 80 |
+
countdown.classList.remove('pulse');
|
| 81 |
+
void countdown.offsetWidth; // Перезапуск анимации
|
| 82 |
+
countdown.classList.add('pulse');
|
| 83 |
+
} else if (count === 0) {
|
| 84 |
+
countdown.textContent = 'Старт!';
|
| 85 |
+
countdown.classList.remove('pulse');
|
| 86 |
+
void countdown.offsetWidth;
|
| 87 |
+
countdown.classList.add('pulse');
|
| 88 |
+
} else {
|
| 89 |
+
clearInterval(countdownTimer);
|
| 90 |
+
countdown.style.display = 'none';
|
| 91 |
+
initializeGame();
|
| 92 |
+
}
|
| 93 |
+
}, 1000);
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// Инициализация игры после обратного отсчёта
|
| 97 |
+
function initializeGame() {
|
| 98 |
fetchText();
|
| 99 |
inputArea.value = '';
|
| 100 |
inputArea.disabled = false;
|
|
|
|
| 115 |
resultContainer.style.display = 'none';
|
| 116 |
overseer.classList.remove('show');
|
| 117 |
|
| 118 |
+
// Разблокируем кнопку старта
|
| 119 |
+
startBtn.disabled = false;
|
| 120 |
+
|
| 121 |
// Обновляем статистику
|
| 122 |
updateStats();
|
| 123 |
}
|
static/js/multiplayer.js
CHANGED
|
@@ -4,11 +4,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 4 |
const usernameInput = document.getElementById('username');
|
| 5 |
const roomInput = document.getElementById('room');
|
| 6 |
const joinBtn = document.getElementById('joinBtn');
|
| 7 |
-
const
|
| 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;
|
|
@@ -20,6 +21,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 20 |
let mistakes = 0;
|
| 21 |
let totalChars = 0;
|
| 22 |
let gameActive = false;
|
|
|
|
| 23 |
|
| 24 |
// Инициализация Socket.IO
|
| 25 |
function initSocket() {
|
|
@@ -37,14 +39,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 37 |
if (data.started && !gameActive) {
|
| 38 |
startGame(data.text);
|
| 39 |
}
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
} else {
|
| 46 |
-
startBtn.disabled = true;
|
| 47 |
-
}
|
| 48 |
});
|
| 49 |
|
| 50 |
socket.on('game_started', (data) => {
|
|
@@ -108,6 +107,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 108 |
playerName.className = 'player-name';
|
| 109 |
playerName.textContent = player.username;
|
| 110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
const playerProgress = document.createElement('div');
|
| 112 |
playerProgress.className = 'player-progress';
|
| 113 |
|
|
@@ -149,6 +154,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 149 |
}
|
| 150 |
}
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
// Начало игры
|
| 153 |
function startGame(textContent) {
|
| 154 |
text = textContent;
|
|
@@ -265,10 +289,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 265 |
joinRoom();
|
| 266 |
});
|
| 267 |
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
});
|
| 273 |
|
| 274 |
// Инициализация
|
|
|
|
| 4 |
const usernameInput = document.getElementById('username');
|
| 5 |
const roomInput = document.getElementById('room');
|
| 6 |
const joinBtn = document.getElementById('joinBtn');
|
| 7 |
+
const readyBtn = document.getElementById('readyBtn');
|
| 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 |
+
const countdown = document.getElementById('countdown');
|
| 13 |
|
| 14 |
// Переменные для игры
|
| 15 |
let socket;
|
|
|
|
| 21 |
let mistakes = 0;
|
| 22 |
let totalChars = 0;
|
| 23 |
let gameActive = false;
|
| 24 |
+
let playerReady = false;
|
| 25 |
|
| 26 |
// Инициализация Socket.IO
|
| 27 |
function initSocket() {
|
|
|
|
| 39 |
if (data.started && !gameActive) {
|
| 40 |
startGame(data.text);
|
| 41 |
}
|
| 42 |
+
});
|
| 43 |
+
|
| 44 |
+
socket.on('countdown_start', () => {
|
| 45 |
+
// Начинаем обратный отсчет
|
| 46 |
+
startCountdown();
|
|
|
|
|
|
|
|
|
|
| 47 |
});
|
| 48 |
|
| 49 |
socket.on('game_started', (data) => {
|
|
|
|
| 107 |
playerName.className = 'player-name';
|
| 108 |
playerName.textContent = player.username;
|
| 109 |
|
| 110 |
+
// Добавляем индикатор готовности
|
| 111 |
+
if (player.ready) {
|
| 112 |
+
playerName.textContent += ' ✓';
|
| 113 |
+
playerName.classList.add('player-ready');
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
const playerProgress = document.createElement('div');
|
| 117 |
playerProgress.className = 'player-progress';
|
| 118 |
|
|
|
|
| 154 |
}
|
| 155 |
}
|
| 156 |
|
| 157 |
+
// Обратный отсчет перед началом игры
|
| 158 |
+
function startCountdown() {
|
| 159 |
+
readyBtn.disabled = true;
|
| 160 |
+
countdown.style.display = 'block';
|
| 161 |
+
let count = 3;
|
| 162 |
+
countdown.textContent = count;
|
| 163 |
+
|
| 164 |
+
const countdownInterval = setInterval(() => {
|
| 165 |
+
count--;
|
| 166 |
+
countdown.textContent = count;
|
| 167 |
+
|
| 168 |
+
if (count <= 0) {
|
| 169 |
+
clearInterval(countdownInterval);
|
| 170 |
+
countdown.style.display = 'none';
|
| 171 |
+
socket.emit('start_game', { room: room });
|
| 172 |
+
}
|
| 173 |
+
}, 1000);
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
// Начало игры
|
| 177 |
function startGame(textContent) {
|
| 178 |
text = textContent;
|
|
|
|
| 289 |
joinRoom();
|
| 290 |
});
|
| 291 |
|
| 292 |
+
readyBtn.addEventListener('click', function() {
|
| 293 |
+
if (!playerReady) {
|
| 294 |
+
playerReady = true;
|
| 295 |
+
readyBtn.textContent = 'Готов!';
|
| 296 |
+
readyBtn.classList.add('ready');
|
| 297 |
+
|
| 298 |
+
socket.emit('player_ready', {
|
| 299 |
+
room: room
|
| 300 |
+
});
|
| 301 |
+
}
|
| 302 |
});
|
| 303 |
|
| 304 |
// Инициализация
|
templates/game.html
CHANGED
|
@@ -39,6 +39,8 @@
|
|
| 39 |
<button id="startBtn" class="btn btn-primary">Начать игру</button>
|
| 40 |
</div>
|
| 41 |
|
|
|
|
|
|
|
| 42 |
<div class="result-container" id="resultContainer">
|
| 43 |
<h2>Результаты</h2>
|
| 44 |
<div class="result-details">
|
|
|
|
| 39 |
<button id="startBtn" class="btn btn-primary">Начать игру</button>
|
| 40 |
</div>
|
| 41 |
|
| 42 |
+
<div class="countdown" id="countdown"></div>
|
| 43 |
+
|
| 44 |
<div class="result-container" id="resultContainer">
|
| 45 |
<h2>Результаты</h2>
|
| 46 |
<div class="result-details">
|
templates/multiplayer.html
CHANGED
|
@@ -22,7 +22,8 @@
|
|
| 22 |
</div>
|
| 23 |
|
| 24 |
<div class="controls">
|
| 25 |
-
<button id="
|
|
|
|
| 26 |
</div>
|
| 27 |
|
| 28 |
<div class="players-list" id="playersList">
|
|
|
|
| 22 |
</div>
|
| 23 |
|
| 24 |
<div class="controls">
|
| 25 |
+
<button id="readyBtn" class="btn btn-primary">Я готов!</button>
|
| 26 |
+
<div id="countdown" class="countdown" style="display: none;">3</div>
|
| 27 |
</div>
|
| 28 |
|
| 29 |
<div class="players-list" id="playersList">
|