Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -12,7 +12,7 @@ socketio = SocketIO(app)
|
|
12 |
|
13 |
ROOMS_DB = os.path.join(app.root_path, 'rooms.json')
|
14 |
USERS_DB = os.path.join(app.root_path, 'users.json')
|
15 |
-
GAMES_DB = os.path.join(app.root_path, 'games.json')
|
16 |
|
17 |
|
18 |
def load_json(file_path, default={}):
|
@@ -37,26 +37,33 @@ def save_json(file_path, data):
|
|
37 |
rooms = load_json(ROOMS_DB)
|
38 |
users = load_json(USERS_DB)
|
39 |
games_data = load_json(GAMES_DB, default={
|
40 |
-
"
|
41 |
-
"name": "Угадай число",
|
42 |
-
"description": "Админ загадывает число от 1 до 100. Участники по очереди пытаются угадать число. Админ даёт подсказки 'больше' или 'меньше'.",
|
43 |
-
"min_players": 1,
|
44 |
-
"max_players": 5,
|
45 |
-
"state": {} # Состояние игры для каждой комнаты
|
46 |
-
},
|
47 |
-
"game2": {
|
48 |
"name": "Крокодил",
|
49 |
"description": "Один игрок (ведущий) получает слово и должен показать его жестами остальным игрокам, не произнося ни слова. Остальные игроки пытаются угадать слово.",
|
50 |
"min_players": 2,
|
51 |
"max_players": 5,
|
52 |
-
|
53 |
},
|
54 |
-
"
|
55 |
-
"name": "
|
56 |
-
"description": "
|
57 |
-
"min_players":
|
58 |
"max_players": 5,
|
59 |
"state": {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
}
|
61 |
})
|
62 |
save_json(GAMES_DB, games_data)
|
@@ -100,7 +107,7 @@ def index():
|
|
100 |
<meta charset="UTF-8">
|
101 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
102 |
<title>Видеоконференция</title>
|
103 |
-
|
104 |
:root {
|
105 |
--primary-color: #6200ee;
|
106 |
--secondary-color: #3700b3;
|
@@ -218,7 +225,7 @@ def dashboard():
|
|
218 |
<meta charset="UTF-8">
|
219 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
220 |
<title>Панель управления</title>
|
221 |
-
|
222 |
:root {
|
223 |
--primary-color: #6200ee;
|
224 |
--secondary-color: #3700b3;
|
@@ -346,7 +353,7 @@ def room(token):
|
|
346 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
347 |
<title>Комната {{ token }}</title>
|
348 |
<style>
|
349 |
-
|
350 |
--primary-color: #4CAF50;
|
351 |
--secondary-color: #388E3C;
|
352 |
--background-color: #f0f0f0;
|
@@ -554,39 +561,127 @@ def room(token):
|
|
554 |
background-color: var(--secondary-color);
|
555 |
}
|
556 |
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
566 |
|
567 |
-
|
568 |
-
|
569 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
570 |
|
571 |
-
#game-display p {
|
572 |
-
margin-bottom: 10px;
|
573 |
-
}
|
574 |
|
575 |
-
#game-display button {
|
576 |
-
background-color: var(--primary-color);
|
577 |
-
color: white;
|
578 |
-
border: none;
|
579 |
-
border-radius: var(--border-radius);
|
580 |
-
padding: 5px 10px;
|
581 |
-
cursor: pointer;
|
582 |
-
transition: background-color 0.2s;
|
583 |
-
margin-top: 10px;
|
584 |
-
}
|
585 |
-
#game-display button:hover{
|
586 |
-
background-color: var(--secondary-color);
|
587 |
-
}
|
588 |
-
|
589 |
-
|
590 |
@media (max-width: 768px) {
|
591 |
.main-container {
|
592 |
flex-direction: column;
|
@@ -943,132 +1038,36 @@ def room(token):
|
|
943 |
gameContent.innerHTML = '';
|
944 |
|
945 |
// Инициализация игры в зависимости от gameId
|
946 |
-
if(gameId === '
|
947 |
-
initGuessNumber(gameId, gameInfo, gameContent);
|
948 |
-
} else if (gameId === 'game2'){
|
949 |
initCrocodile(gameId, gameInfo, gameContent);
|
950 |
-
}else if (gameId === '
|
951 |
-
|
952 |
-
}
|
953 |
-
|
954 |
-
|
955 |
-
|
956 |
-
function initGuessNumber(gameId, gameInfo, gameContent) {
|
957 |
-
if (isAdmin) {
|
958 |
-
const input = document.createElement('input');
|
959 |
-
input.type = 'number';
|
960 |
-
input.id = 'admin-guess-number';
|
961 |
-
input.placeholder = 'Загадайте число (1-100)';
|
962 |
-
gameContent.appendChild(input);
|
963 |
-
|
964 |
-
const button = document.createElement('button');
|
965 |
-
button.textContent = 'Загадать';
|
966 |
-
button.onclick = () => {
|
967 |
-
const number = parseInt(input.value, 10);
|
968 |
-
if (number >= 1 && number <= 100) {
|
969 |
-
socket.emit('set_game_state', { token, game_id: gameId, state: { number: number, guesses: [] } });
|
970 |
-
} else {
|
971 |
-
alert('Пожалуйста, введите число от 1 до 100.');
|
972 |
-
}
|
973 |
-
};
|
974 |
-
gameContent.appendChild(button);
|
975 |
-
}
|
976 |
-
|
977 |
-
const guessInput = document.createElement('input');
|
978 |
-
guessInput.type = 'number';
|
979 |
-
guessInput.id = 'user-guess-input';
|
980 |
-
guessInput.placeholder = 'Ваша догадка';
|
981 |
-
guessInput.disabled = !isAdmin; // Изначально отключено для всех, кроме админа
|
982 |
-
gameContent.appendChild(guessInput);
|
983 |
-
|
984 |
-
const guessButton = document.createElement('button');
|
985 |
-
guessButton.textContent = 'Угадать';
|
986 |
-
guessButton.id = 'user-guess-button';
|
987 |
-
guessButton.disabled = !isAdmin;
|
988 |
-
guessButton.onclick = () => {
|
989 |
-
const guess = parseInt(guessInput.value, 10);
|
990 |
-
if (!isNaN(guess)) {
|
991 |
-
socket.emit('game_action', { token, game_id: gameId, action: 'guess', value: guess, user: username });
|
992 |
-
guessInput.value = ''; //очистка
|
993 |
-
}
|
994 |
-
};
|
995 |
-
gameContent.appendChild(guessButton);
|
996 |
-
|
997 |
-
const resultDiv = document.createElement('div');
|
998 |
-
resultDiv.id = 'guess-result';
|
999 |
-
gameContent.appendChild(resultDiv);
|
1000 |
-
}
|
1001 |
-
|
1002 |
-
|
1003 |
-
socket.on('update_game_state', (data) => {
|
1004 |
-
const gameId = data.game_id;
|
1005 |
-
const gameState = data.state;
|
1006 |
-
|
1007 |
-
if(gameId === 'game1'){
|
1008 |
-
updateGuessNumberState(gameId, gameState);
|
1009 |
-
}else if (gameId === 'game2'){
|
1010 |
-
updateCrocodileState(gameId,gameState);
|
1011 |
-
}else if (gameId === 'game3'){
|
1012 |
-
updateQuizState(gameId, gameState);
|
1013 |
-
}
|
1014 |
-
|
1015 |
-
});
|
1016 |
-
|
1017 |
-
function updateGuessNumberState(gameId, gameState){
|
1018 |
-
const resultDiv = document.getElementById('guess-result');
|
1019 |
-
if (!resultDiv) return; // Если элемента нет (игра не "Угадай число")
|
1020 |
-
|
1021 |
-
resultDiv.innerHTML = ''; // Очищаем предыдущие результаты
|
1022 |
-
|
1023 |
-
// Включаем ввод для всех, когда число загадано
|
1024 |
-
if (gameState.number) {
|
1025 |
-
document.getElementById('user-guess-input').disabled = false;
|
1026 |
-
document.getElementById('user-guess-button').disabled = false;
|
1027 |
-
|
1028 |
-
}
|
1029 |
-
if(gameState.guesses && gameState.guesses.length > 0){
|
1030 |
-
const lastGuess = gameState.guesses[gameState.guesses.length - 1];
|
1031 |
-
const p = document.createElement('p');
|
1032 |
-
p.textContent = `${lastGuess.user}: ${lastGuess.value} - ${lastGuess.result}`;
|
1033 |
-
resultDiv.appendChild(p);
|
1034 |
-
|
1035 |
-
if (lastGuess.result === 'Угадано!') {
|
1036 |
-
const winMessage = document.createElement('p');
|
1037 |
-
winMessage.textContent = `Победил ${lastGuess.user}!`;
|
1038 |
-
resultDiv.appendChild(winMessage);
|
1039 |
-
document.getElementById('user-guess-input').disabled = true; // Отключаем после победы
|
1040 |
-
document.getElementById('user-guess-button').disabled = true;
|
1041 |
-
|
1042 |
-
}
|
1043 |
-
}
|
1044 |
-
}
|
1045 |
-
|
1046 |
-
socket.on('game_action_result', (data) => {
|
1047 |
-
const gameId = data.game_id;
|
1048 |
-
const action = data.action;
|
1049 |
-
|
1050 |
-
if (gameId === 'game1' && action === 'guess') {
|
1051 |
-
// Обработка результата уже происходит в update_game_state
|
1052 |
}
|
1053 |
});
|
1054 |
|
1055 |
-
|
1056 |
-
// Игра "Крокодил"
|
1057 |
function initCrocodile(gameId, gameInfo, gameContent){
|
1058 |
const wordInput = document.createElement('input');
|
1059 |
wordInput.type = 'text';
|
1060 |
wordInput.id = 'crocodile-word-input';
|
1061 |
wordInput.placeholder = 'Введите слово';
|
|
|
1062 |
wordInput.disabled = true; // По умолчанию отключен
|
1063 |
gameContent.appendChild(wordInput);
|
1064 |
|
1065 |
const startTurnButton = document.createElement('button');
|
1066 |
startTurnButton.textContent = 'Начать ход';
|
|
|
1067 |
startTurnButton.id = 'start-turn-button';
|
1068 |
gameContent.appendChild(startTurnButton);
|
1069 |
|
1070 |
const guessInput = document.createElement('input');
|
1071 |
guessInput.type = 'text';
|
|
|
1072 |
guessInput.id = 'crocodile-guess-input';
|
1073 |
guessInput.placeholder = 'Ваша догадка';
|
1074 |
guessInput.disabled = true;
|
@@ -1076,6 +1075,7 @@ def room(token):
|
|
1076 |
|
1077 |
const guessButton = document.createElement('button');
|
1078 |
guessButton.textContent = 'Угадать';
|
|
|
1079 |
guessButton.id = 'crocodile-guess-button';
|
1080 |
guessButton.disabled = true;
|
1081 |
gameContent.appendChild(guessButton);
|
@@ -1172,153 +1172,523 @@ def room(token):
|
|
1172 |
}
|
1173 |
}
|
1174 |
}
|
1175 |
-
|
1176 |
-
|
1177 |
-
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
1185 |
-
|
1186 |
-
|
1187 |
-
|
1188 |
-
|
1189 |
-
|
1190 |
-
|
1191 |
-
|
1192 |
-
|
1193 |
-
|
1194 |
-
|
1195 |
-
|
1196 |
-
|
1197 |
-
|
1198 |
-
|
1199 |
-
|
1200 |
-
|
1201 |
-
|
1202 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
1206 |
-
userAnswerInput.disabled = true;
|
1207 |
-
gameContent.appendChild(userAnswerInput);
|
1208 |
-
|
1209 |
-
const answerButton = document.createElement('button');
|
1210 |
-
answerButton.textContent = 'Ответить';
|
1211 |
-
answerButton.id = 'answer-button';
|
1212 |
-
answerButton.disabled = true;
|
1213 |
-
gameContent.appendChild(answerButton);
|
1214 |
|
1215 |
const resultDiv = document.createElement('div');
|
1216 |
-
resultDiv.id = '
|
1217 |
gameContent.appendChild(resultDiv);
|
1218 |
|
1219 |
-
|
1220 |
-
|
1221 |
-
|
1222 |
-
|
1223 |
-
|
1224 |
-
|
1225 |
-
|
1226 |
-
|
1227 |
-
|
1228 |
-
|
1229 |
-
|
1230 |
-
|
1231 |
-
|
1232 |
-
|
1233 |
-
|
1234 |
-
|
1235 |
-
|
1236 |
-
|
1237 |
-
|
1238 |
-
|
1239 |
-
|
1240 |
-
|
1241 |
-
|
1242 |
-
answerButton.onclick = () => {
|
1243 |
-
const userAnswer = userAnswerInput.value.trim();
|
1244 |
-
if (userAnswer) {
|
1245 |
-
socket.emit('game_action', { token, game_id: gameId, action: 'answer', value: userAnswer, user: username });
|
1246 |
-
userAnswerInput.value = '';
|
1247 |
}
|
1248 |
-
|
1249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1250 |
}
|
1251 |
|
1252 |
-
function
|
1253 |
-
|
1254 |
-
const
|
1255 |
-
const
|
1256 |
-
const
|
1257 |
-
const
|
1258 |
|
1259 |
-
|
1260 |
|
1261 |
-
|
|
|
|
|
|
|
1262 |
|
1263 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1264 |
|
1265 |
-
|
1266 |
-
|
1267 |
-
questionText.textContent = `Вопрос: ${gameState.question}`;
|
1268 |
-
resultDiv.appendChild(questionText);
|
1269 |
-
}
|
1270 |
|
1271 |
-
|
1272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1273 |
|
1274 |
-
|
1275 |
-
|
1276 |
|
1277 |
-
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
1282 |
-
|
1283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1284 |
}
|
1285 |
|
1286 |
-
|
1287 |
-
|
1288 |
-
|
1289 |
-
|
1290 |
-
|
1291 |
-
|
1292 |
-
|
1293 |
-
|
1294 |
-
|
1295 |
-
|
1296 |
-
|
1297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1298 |
}
|
|
|
|
|
|
|
|
|
|
|
1299 |
|
1300 |
-
|
1301 |
-
|
|
|
1302 |
|
1303 |
-
|
1304 |
|
1305 |
-
|
1306 |
-
|
1307 |
-
|
1308 |
-
|
1309 |
}
|
|
|
1310 |
|
1311 |
-
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1316 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1317 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1318 |
}
|
1319 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1320 |
}
|
1321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1322 |
function leaveRoom() {
|
1323 |
socket.emit('leave', { token, username });
|
1324 |
if (localStream) {
|
@@ -1339,7 +1709,7 @@ def room(token):
|
|
1339 |
def handle_join(data):
|
1340 |
token = data['token']
|
1341 |
username = data['username']
|
1342 |
-
print(f"User {username} joining room {token}")
|
1343 |
if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']:
|
1344 |
join_room(token)
|
1345 |
if username not in rooms[token]['users']:
|
@@ -1377,14 +1747,7 @@ def handle_leave(data):
|
|
1377 |
if rooms[token].get('current_game'): # Если игра в процессе
|
1378 |
game_id = rooms[token]['current_game']
|
1379 |
if games_data[game_id]['state'].get(token):
|
1380 |
-
|
1381 |
-
if game_id == 'game1':
|
1382 |
-
if(games_data[game_id]['state'][token].get('guesses')):
|
1383 |
-
new_guesses = [guess for guess in games_data[game_id]['state'][token]['guesses'] if guess['user'] != username]
|
1384 |
-
games_data[game_id]['state'][token]['guesses'] = new_guesses
|
1385 |
-
|
1386 |
-
#Если "крокодил"
|
1387 |
-
elif game_id == 'game2':
|
1388 |
if games_data[game_id]['state'][token].get('presenter') == username:
|
1389 |
# Сброс игры, если уходит ведущий
|
1390 |
del games_data[game_id]['state'][token]
|
@@ -1394,22 +1757,35 @@ def handle_leave(data):
|
|
1394 |
#Удаление догадок
|
1395 |
if games_data[game_id]['state'][token].get('guesses'):
|
1396 |
games_data[game_id]['state'][token]['guesses'] = [guess for guess in games_data[game_id]['state'][token]['guesses'] if guess['user'] != username]
|
1397 |
-
|
1398 |
-
elif game_id == '
|
1399 |
-
if games_data[game_id]['state'][token].get('
|
1400 |
-
|
1401 |
-
|
1402 |
-
|
1403 |
-
|
1404 |
-
|
1405 |
-
|
1406 |
-
|
1407 |
-
|
1408 |
-
|
1409 |
-
|
1410 |
-
|
1411 |
-
|
1412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1413 |
save_json(GAMES_DB, games_data); # Сохраняем изменения в любом случае
|
1414 |
emit('update_game_state', {'game_id': game_id, 'state': games_data[game_id]['state'][token]}, room=token);
|
1415 |
|
@@ -1445,6 +1821,7 @@ def handle_set_game_state(data):
|
|
1445 |
token = data['token']
|
1446 |
game_id = data['game_id']
|
1447 |
state = data['state']
|
|
|
1448 |
|
1449 |
if token in rooms:
|
1450 |
if rooms[token]['admin'] == session.get('username'):
|
@@ -1454,7 +1831,7 @@ def handle_set_game_state(data):
|
|
1454 |
save_json(GAMES_DB, games_data)
|
1455 |
emit('update_game_state', {'game_id': game_id, 'state': state}, room=token)
|
1456 |
|
1457 |
-
if game_id == '
|
1458 |
start_timer(token, game_id)
|
1459 |
|
1460 |
@socketio.on('game_action')
|
@@ -1462,31 +1839,14 @@ def handle_game_action(data):
|
|
1462 |
token = data['token']
|
1463 |
game_id = data['game_id']
|
1464 |
action = data['action']
|
1465 |
-
value = data
|
1466 |
user = data['user']
|
|
|
1467 |
|
1468 |
if token in rooms and game_id == rooms[token]['current_game']:
|
1469 |
current_state = games_data[game_id]['state'].get(token, {})
|
1470 |
|
1471 |
-
if game_id == '
|
1472 |
-
if action == 'guess' and current_state.get('number') is not None:
|
1473 |
-
result = ""
|
1474 |
-
if value < current_state['number']:
|
1475 |
-
result = "Больше"
|
1476 |
-
elif value > current_state['number']:
|
1477 |
-
result = "Меньше"
|
1478 |
-
else:
|
1479 |
-
result = "Угадано!"
|
1480 |
-
|
1481 |
-
if 'guesses' not in current_state:
|
1482 |
-
current_state['guesses'] = []
|
1483 |
-
|
1484 |
-
current_state['guesses'].append({'user': user, 'value': value, 'result': result})
|
1485 |
-
games_data[game_id]['state'][token] = current_state # Сохраняем обновленное состояние
|
1486 |
-
save_json(GAMES_DB, games_data) # Сохраняем в файл
|
1487 |
-
emit('update_game_state', { 'game_id': game_id, 'state': current_state}, room=token)
|
1488 |
-
|
1489 |
-
elif game_id == 'game2': # Крокодил
|
1490 |
if action == 'guess' and current_state.get('isRunning'):
|
1491 |
result = "Не угадано"
|
1492 |
if value.lower() == current_state.get('word', '').lower():
|
@@ -1500,30 +1860,124 @@ def handle_game_action(data):
|
|
1500 |
save_json(GAMES_DB, games_data)
|
1501 |
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1502 |
|
1503 |
-
elif game_id == '
|
1504 |
-
if action == '
|
1505 |
-
|
|
|
1506 |
|
1507 |
-
|
1508 |
-
current_state['answers'] = []
|
1509 |
-
current_state['answers'].append({'user':user, 'value': value, 'isCorrect': is_correct})
|
1510 |
|
1511 |
-
|
1512 |
-
|
1513 |
-
|
1514 |
-
|
1515 |
-
|
1516 |
-
# Переходим к следующему игроку
|
1517 |
-
users_in_room = rooms.get(token,{}).get('users',[])
|
1518 |
-
current_state['currentPlayerIndex'] = (current_state.get('currentPlayerIndex', 0) + 1) % len(users_in_room)
|
1519 |
|
1520 |
games_data[game_id]['state'][token] = current_state
|
1521 |
-
save_json(GAMES_DB, games_data)
|
1522 |
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1523 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1524 |
|
1525 |
def start_timer(token, game_id):
|
1526 |
-
if game_id != '
|
1527 |
return
|
1528 |
|
1529 |
def timer_loop():
|
|
|
12 |
|
13 |
ROOMS_DB = os.path.join(app.root_path, 'rooms.json')
|
14 |
USERS_DB = os.path.join(app.root_path, 'users.json')
|
15 |
+
GAMES_DB = os.path.join(app.root_path, 'games.json')
|
16 |
|
17 |
|
18 |
def load_json(file_path, default={}):
|
|
|
37 |
rooms = load_json(ROOMS_DB)
|
38 |
users = load_json(USERS_DB)
|
39 |
games_data = load_json(GAMES_DB, default={
|
40 |
+
"crocodile": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
"name": "Крокодил",
|
42 |
"description": "Один игрок (ведущий) получает слово и должен показать его жестами остальным игрокам, не произнося ни слова. Остальные игроки пытаются угадать слово.",
|
43 |
"min_players": 2,
|
44 |
"max_players": 5,
|
45 |
+
"state": {}
|
46 |
},
|
47 |
+
"alias": {
|
48 |
+
"name": "Alias",
|
49 |
+
"description": "Один игрок (ведущий) получает слово и должен объяснить его другими словами, не используя однокоренные. Остальные игроки пытаются угадать слово.",
|
50 |
+
"min_players": 2,
|
51 |
"max_players": 5,
|
52 |
"state": {}
|
53 |
+
},
|
54 |
+
"mafia": {
|
55 |
+
"name": "Мафия",
|
56 |
+
"description": "Игроки делятся на две команды: мафию и мирных жителей. Мафия пытается тайно убивать мирных жителей, а мирные жители пытаются вычислить и казнить мафию.",
|
57 |
+
"min_players": 4,
|
58 |
+
"max_players": 5, # Можно увеличить, но для теста оставим так
|
59 |
+
"state": {}
|
60 |
+
},
|
61 |
+
"durak": {
|
62 |
+
"name": "Дурак",
|
63 |
+
"description": "Карточная игра, в которой игроки стараются избавиться от всех своих карт.",
|
64 |
+
"min_players": 2,
|
65 |
+
"max_players": 5,
|
66 |
+
"state": {} # Состояние игры
|
67 |
}
|
68 |
})
|
69 |
save_json(GAMES_DB, games_data)
|
|
|
107 |
<meta charset="UTF-8">
|
108 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
109 |
<title>Видеоконференция</title>
|
110 |
+
<style>
|
111 |
:root {
|
112 |
--primary-color: #6200ee;
|
113 |
--secondary-color: #3700b3;
|
|
|
225 |
<meta charset="UTF-8">
|
226 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
227 |
<title>Панель управления</title>
|
228 |
+
<style>
|
229 |
:root {
|
230 |
--primary-color: #6200ee;
|
231 |
--secondary-color: #3700b3;
|
|
|
353 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
354 |
<title>Комната {{ token }}</title>
|
355 |
<style>
|
356 |
+
:root {
|
357 |
--primary-color: #4CAF50;
|
358 |
--secondary-color: #388E3C;
|
359 |
--background-color: #f0f0f0;
|
|
|
561 |
background-color: var(--secondary-color);
|
562 |
}
|
563 |
|
564 |
+
#game-display {
|
565 |
+
margin-top: 20px;
|
566 |
+
padding: 20px;
|
567 |
+
background-color: var(--surface-color);
|
568 |
+
border-radius: var(--border-radius);
|
569 |
+
box-shadow: var(--box-shadow);
|
570 |
+
text-align: center;
|
571 |
+
display: none; /* Скрыто по умолчанию */
|
572 |
+
width: 80%; /* Ширина */
|
573 |
+
max-width: 800px; /* Максимальная ширина */
|
574 |
+
margin-left: auto; /* Центрирование по горизонтали */
|
575 |
+
margin-right: auto;
|
576 |
+
}
|
577 |
+
#game-display h2 {
|
578 |
+
color: var(--primary-color);
|
579 |
+
margin-bottom: 10px;
|
580 |
+
}
|
581 |
+
#game-description {
|
582 |
+
margin-bottom: 15px;
|
583 |
+
font-size: 1.1rem;
|
584 |
+
color: var(--text-color);
|
585 |
+
}
|
586 |
+
#game-content {
|
587 |
+
display: flex;
|
588 |
+
flex-direction: column;
|
589 |
+
align-items: center; /* Центрирование элементов */
|
590 |
+
gap: 10px; /* Расстояние между элементами */
|
591 |
+
}
|
592 |
+
.game-input {
|
593 |
+
padding: 8px;
|
594 |
+
border: 1px solid #ccc;
|
595 |
+
border-radius: var(--border-radius);
|
596 |
+
font-size: 1rem;
|
597 |
+
width: 60%; /* Ширина инпутов */
|
598 |
+
max-width: 300px; /* Максимальная ширина */
|
599 |
+
}
|
600 |
|
601 |
+
.game-button {
|
602 |
+
background-color: var(--primary-color);
|
603 |
+
color: white;
|
604 |
+
border: none;
|
605 |
+
border-radius: var(--border-radius);
|
606 |
+
padding: 10px 15px;
|
607 |
+
cursor: pointer;
|
608 |
+
transition: background-color 0.2s;
|
609 |
+
font-size: 1rem;
|
610 |
+
}
|
611 |
+
.game-button:hover {
|
612 |
+
background-color: var(--secondary-color);
|
613 |
+
}
|
614 |
+
|
615 |
+
#game-result {
|
616 |
+
margin-top: 15px;
|
617 |
+
font-size: 1rem;
|
618 |
+
color: var(--text-color);
|
619 |
+
width: 100%; /* Ширина */
|
620 |
+
}
|
621 |
+
#game-result p{
|
622 |
+
word-break: break-word;
|
623 |
+
}
|
624 |
+
#crocodile-timer {
|
625 |
+
font-size: 1.2rem;
|
626 |
+
font-weight: bold;
|
627 |
+
margin-top: 10px;
|
628 |
+
color: var(--primary-color);
|
629 |
+
}
|
630 |
+
/*Стили для карт*/
|
631 |
+
.card {
|
632 |
+
width: 60px;
|
633 |
+
height: 90px;
|
634 |
+
border: 1px solid black;
|
635 |
+
border-radius: 5px;
|
636 |
+
display: inline-block; /* Чтобы карты шли в ряд */
|
637 |
+
margin: 2px;
|
638 |
+
text-align: center;
|
639 |
+
font-size: 1rem;
|
640 |
+
background-color: white;
|
641 |
+
user-select: none; /* Предотвращает выделение текста */
|
642 |
+
}
|
643 |
+
.card.selected {
|
644 |
+
border-color: blue;
|
645 |
+
box-shadow: 0 0 5px blue;
|
646 |
+
}
|
647 |
+
.card.back {
|
648 |
+
background-color: #ddd; /* Цвет рубашки */
|
649 |
+
color: transparent; /* Скрываем текст на рубашке */
|
650 |
+
}
|
651 |
+
|
652 |
+
.card-container{
|
653 |
+
display: flex;
|
654 |
+
flex-wrap: wrap;
|
655 |
+
justify-content: center;
|
656 |
+
gap: 5px;
|
657 |
+
margin-bottom: 10px;
|
658 |
+
|
659 |
+
}
|
660 |
+
#durak-buttons{
|
661 |
+
margin-top: 10px;
|
662 |
+
display: flex;
|
663 |
+
justify-content: center;
|
664 |
+
gap: 10px;
|
665 |
+
|
666 |
+
}
|
667 |
+
#durak-table{
|
668 |
+
display: flex;
|
669 |
+
flex-wrap: wrap;
|
670 |
+
justify-content: center;
|
671 |
+
gap: 10px;
|
672 |
+
min-height: 100px;
|
673 |
+
border: 2px dashed #ccc;
|
674 |
+
padding: 10px;
|
675 |
+
margin-top: 10px;
|
676 |
+
margin-bottom: 10px;
|
677 |
+
|
678 |
+
}
|
679 |
+
#durak-info{
|
680 |
+
font-size: 1rem;
|
681 |
+
font-weight: bold;
|
682 |
+
}
|
683 |
|
|
|
|
|
|
|
684 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
685 |
@media (max-width: 768px) {
|
686 |
.main-container {
|
687 |
flex-direction: column;
|
|
|
1038 |
gameContent.innerHTML = '';
|
1039 |
|
1040 |
// Инициализация игры в зависимости от gameId
|
1041 |
+
if (gameId === 'crocodile'){
|
|
|
|
|
1042 |
initCrocodile(gameId, gameInfo, gameContent);
|
1043 |
+
} else if (gameId === 'alias'){
|
1044 |
+
initAlias(gameId, gameInfo, gameContent);
|
1045 |
+
} else if(gameId === 'mafia'){
|
1046 |
+
initMafia(gameId, gameInfo, gameContent);
|
1047 |
+
}else if(gameId === 'durak'){
|
1048 |
+
initDurak(gameId, gameInfo, gameContent);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1049 |
}
|
1050 |
});
|
1051 |
|
1052 |
+
// Игра "Крокодил"
|
|
|
1053 |
function initCrocodile(gameId, gameInfo, gameContent){
|
1054 |
const wordInput = document.createElement('input');
|
1055 |
wordInput.type = 'text';
|
1056 |
wordInput.id = 'crocodile-word-input';
|
1057 |
wordInput.placeholder = 'Введите слово';
|
1058 |
+
wordInput.classList.add('game-input');
|
1059 |
wordInput.disabled = true; // По умолчанию отключен
|
1060 |
gameContent.appendChild(wordInput);
|
1061 |
|
1062 |
const startTurnButton = document.createElement('button');
|
1063 |
startTurnButton.textContent = 'Начать ход';
|
1064 |
+
startTurnButton.classList.add('game-button');
|
1065 |
startTurnButton.id = 'start-turn-button';
|
1066 |
gameContent.appendChild(startTurnButton);
|
1067 |
|
1068 |
const guessInput = document.createElement('input');
|
1069 |
guessInput.type = 'text';
|
1070 |
+
guessInput.classList.add('game-input');
|
1071 |
guessInput.id = 'crocodile-guess-input';
|
1072 |
guessInput.placeholder = 'Ваша догадка';
|
1073 |
guessInput.disabled = true;
|
|
|
1075 |
|
1076 |
const guessButton = document.createElement('button');
|
1077 |
guessButton.textContent = 'Угадать';
|
1078 |
+
guessButton.classList.add('game-button');
|
1079 |
guessButton.id = 'crocodile-guess-button';
|
1080 |
guessButton.disabled = true;
|
1081 |
gameContent.appendChild(guessButton);
|
|
|
1172 |
}
|
1173 |
}
|
1174 |
}
|
1175 |
+
|
1176 |
+
//игра "Alias"
|
1177 |
+
function initAlias(gameId, gameInfo, gameContent){
|
1178 |
+
const wordInput = document.createElement('input');
|
1179 |
+
wordInput.type = 'text';
|
1180 |
+
wordInput.id = 'alias-word-input';
|
1181 |
+
wordInput.placeholder = 'Введите слово';
|
1182 |
+
wordInput.classList.add('game-input');
|
1183 |
+
wordInput.disabled = true; // По умолчанию отключен
|
1184 |
+
gameContent.appendChild(wordInput);
|
1185 |
+
|
1186 |
+
const startTurnButton = document.createElement('button');
|
1187 |
+
startTurnButton.textContent = 'Начать ход';
|
1188 |
+
startTurnButton.classList.add('game-button');
|
1189 |
+
startTurnButton.id = 'start-turn-button';
|
1190 |
+
gameContent.appendChild(startTurnButton);
|
1191 |
+
|
1192 |
+
const guessInput = document.createElement('input');
|
1193 |
+
guessInput.type = 'text';
|
1194 |
+
guessInput.id = 'alias-guess-input';
|
1195 |
+
guessInput.classList.add('game-input');
|
1196 |
+
guessInput.placeholder = 'Ваша догадка';
|
1197 |
+
guessInput.disabled = true;
|
1198 |
+
gameContent.appendChild(guessInput);
|
1199 |
+
|
1200 |
+
const guessButton = document.createElement('button');
|
1201 |
+
guessButton.textContent = 'Угадать';
|
1202 |
+
guessButton.classList.add('game-button');
|
1203 |
+
guessButton.id = 'alias-guess-button';
|
1204 |
+
guessButton.disabled = true;
|
1205 |
+
gameContent.appendChild(guessButton);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1206 |
|
1207 |
const resultDiv = document.createElement('div');
|
1208 |
+
resultDiv.id = 'alias-result';
|
1209 |
gameContent.appendChild(resultDiv);
|
1210 |
|
1211 |
+
const timerDisplay = document.createElement('div');
|
1212 |
+
timerDisplay.id = 'alias-timer';
|
1213 |
+
gameContent.appendChild(timerDisplay);
|
1214 |
+
|
1215 |
+
|
1216 |
+
if(isAdmin){
|
1217 |
+
wordInput.disabled = false;
|
1218 |
+
startTurnButton.onclick = () => {
|
1219 |
+
const word = wordInput.value.trim();
|
1220 |
+
if(word){
|
1221 |
+
const users = {{ rooms[token]['users']|tojson }};
|
1222 |
+
const nonAdminUsers = users.filter(u => u !== username);
|
1223 |
+
const presenter = nonAdminUsers.length > 0 ? nonAdminUsers[Math.floor(Math.random() * nonAdminUsers.length)] : null;
|
1224 |
+
|
1225 |
+
if(presenter){
|
1226 |
+
socket.emit('set_game_state', {token, game_id: gameId, state: {word: word, presenter: presenter, guesses: [], timer: 60, isRunning: true}});
|
1227 |
+
}else{
|
1228 |
+
alert("Нет игроков для выбора ведущего");
|
1229 |
+
}
|
1230 |
+
}else{
|
1231 |
+
alert("Введите слово");
|
1232 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1233 |
}
|
1234 |
+
}
|
1235 |
|
1236 |
+
guessButton.onclick = () => {
|
1237 |
+
const guess = guessInput.value.trim();
|
1238 |
+
if(guess){
|
1239 |
+
socket.emit('game_action', {token, game_id: gameId, action: 'guess', value: guess, user: username});
|
1240 |
+
guessInput.value = '';
|
1241 |
+
}
|
1242 |
+
}
|
1243 |
}
|
1244 |
|
1245 |
+
function updateAliasState(gameId, gameState){
|
1246 |
+
const resultDiv = document.getElementById('alias-result');
|
1247 |
+
const wordInput = document.getElementById('alias-word-input');
|
1248 |
+
const guessInput = document.getElementById('alias-guess-input');
|
1249 |
+
const guessButton = document.getElementById('alias-guess-button');
|
1250 |
+
const timerDisplay = document.getElementById('alias-timer');
|
1251 |
|
1252 |
+
if(!resultDiv || !wordInput || !guessInput || !guessButton || !timerDisplay) return;
|
1253 |
|
1254 |
+
resultDiv.innerHTML = '';
|
1255 |
+
|
1256 |
+
if(gameState.isRunning){
|
1257 |
+
timerDisplay.textContent = `Время: ${gameState.timer}`;
|
1258 |
|
1259 |
+
if(username === gameState.presenter){
|
1260 |
+
wordInput.value = gameState.word;
|
1261 |
+
wordInput.disabled = true;
|
1262 |
+
}else{
|
1263 |
+
wordInput.value = '';
|
1264 |
+
wordInput.disabled = true;
|
1265 |
+
}
|
1266 |
|
1267 |
+
guessInput.disabled = username === gameState.presenter;
|
1268 |
+
guessButton.disabled = username === gameState.presenter;
|
|
|
|
|
|
|
1269 |
|
1270 |
+
if(gameState.guesses && gameState.guesses.length > 0){
|
1271 |
+
gameState.guesses.forEach(guess => {
|
1272 |
+
const p = document.createElement('p');
|
1273 |
+
p.textContent = `${guess.user}: ${guess.value} - ${guess.result}`;
|
1274 |
+
resultDiv.appendChild(p);
|
1275 |
+
});
|
1276 |
+
}
|
1277 |
+
if (gameState.guesses.length>0 && gameState.guesses[gameState.guesses.length - 1].result === "Угадано!"){
|
1278 |
+
const winMessage = document.createElement('p');
|
1279 |
+
winMessage.textContent = `Победил ${gameState.guesses[gameState.guesses.length - 1].user}!`;
|
1280 |
+
resultDiv.appendChild(winMessage);
|
1281 |
+
guessInput.disabled = true;
|
1282 |
+
guessButton.disabled = true;
|
1283 |
+
timerDisplay.textContent = '';
|
1284 |
+
gameState.isRunning = false; // Остановка игры
|
1285 |
|
1286 |
+
//Очистка
|
1287 |
+
wordInput.value = '';
|
1288 |
|
1289 |
+
}
|
1290 |
+
}
|
1291 |
+
}
|
1292 |
+
|
1293 |
+
//Игра "Мафия"
|
1294 |
+
function initMafia(gameId, gameInfo, gameContent) {
|
1295 |
+
const startMafiaButton = document.createElement('button');
|
1296 |
+
startMafiaButton.textContent = 'Начать игру';
|
1297 |
+
startMafiaButton.classList.add('game-button');
|
1298 |
+
startMafiaButton.id = 'start-mafia-button';
|
1299 |
+
gameContent.appendChild(startMafiaButton);
|
1300 |
+
|
1301 |
+
const resultDiv = document.createElement('div');
|
1302 |
+
resultDiv.id = 'mafia-result';
|
1303 |
+
gameContent.appendChild(resultDiv);
|
1304 |
+
|
1305 |
+
const voteInput = document.createElement('input');
|
1306 |
+
voteInput.type = 'text';
|
1307 |
+
voteInput.id = 'mafia-vote-input';
|
1308 |
+
voteInput.placeholder = 'За кого голосуете?';
|
1309 |
+
voteInput.classList.add('game-input');
|
1310 |
+
voteInput.disabled = true;
|
1311 |
+
gameContent.appendChild(voteInput);
|
1312 |
+
|
1313 |
+
const voteButton = document.createElement('button');
|
1314 |
+
voteButton.textContent = 'Голосовать';
|
1315 |
+
voteButton.classList.add('game-button');
|
1316 |
+
voteButton.id = 'mafia-vote-button';
|
1317 |
+
voteButton.disabled = true;
|
1318 |
+
gameContent.appendChild(voteButton);
|
1319 |
+
|
1320 |
+
if (isAdmin) {
|
1321 |
+
startMafiaButton.onclick = () => {
|
1322 |
+
const users = {{ rooms[token]['users']|tojson }};
|
1323 |
+
// Раздача ролей
|
1324 |
+
const roles = assignMafiaRoles(users);
|
1325 |
+
socket.emit('set_game_state', { token, game_id: gameId, state: { roles: roles, phase: 'night', votes: {}, isRunning: true, killed: null, checked: null } });
|
1326 |
+
};
|
1327 |
}
|
1328 |
|
1329 |
+
voteButton.onclick = () => {
|
1330 |
+
const vote = voteInput.value.trim();
|
1331 |
+
if (vote) {
|
1332 |
+
socket.emit('game_action', { token, game_id: gameId, action: 'vote', value: vote, user: username });
|
1333 |
+
voteInput.value = ''; //Очищаем поле
|
1334 |
+
}
|
1335 |
+
};
|
1336 |
+
}
|
1337 |
+
|
1338 |
+
function assignMafiaRoles(users) {
|
1339 |
+
const numPlayers = users.length;
|
1340 |
+
let numMafia = 1;
|
1341 |
+
if (numPlayers >= 5) {
|
1342 |
+
numMafia = 2;
|
1343 |
+
}
|
1344 |
+
// Создаем массив ролей: сначала мафия, потом мирные
|
1345 |
+
const roles = Array(numMafia).fill('mafia').concat(Array(numPlayers - numMafia).fill('civilian'));
|
1346 |
+
|
1347 |
+
// Перемешиваем роли
|
1348 |
+
for (let i = roles.length - 1; i > 0; i--) {
|
1349 |
+
const j = Math.floor(Math.random() * (i + 1));
|
1350 |
+
[roles[i], roles[j]] = [roles[j], roles[i]]; // ES6 деструктуризация для обмена
|
1351 |
+
}
|
1352 |
+
|
1353 |
+
// Назначаем роли пользователям
|
1354 |
+
const assignedRoles = {};
|
1355 |
+
for (let i = 0; i < numPlayers; i++) {
|
1356 |
+
assignedRoles[users[i]] = roles[i];
|
1357 |
+
}
|
1358 |
+
return assignedRoles;
|
1359 |
+
}
|
1360 |
+
|
1361 |
+
function updateMafiaState(gameId, gameState) {
|
1362 |
+
|
1363 |
+
const resultDiv = document.getElementById('mafia-result');
|
1364 |
+
const voteInput = document.getElementById('mafia-vote-input');
|
1365 |
+
const voteButton = document.getElementById('mafia-vote-button');
|
1366 |
+
|
1367 |
+
|
1368 |
+
if (!resultDiv || !voteInput || !voteButton) return;
|
1369 |
+
|
1370 |
+
resultDiv.innerHTML = ''; // Очищаем
|
1371 |
+
|
1372 |
+
// Ночь
|
1373 |
+
if (gameState.isRunning && gameState.phase === 'night') {
|
1374 |
+
voteInput.disabled = true; // Отключаем голосование ночью
|
1375 |
+
voteButton.disabled = true;
|
1376 |
+
if (gameState.roles[username] === 'mafia') {
|
1377 |
+
resultDiv.innerHTML = '<p>Вы мафия. Обсудите с другими мафиози, кого убить.</p>';
|
1378 |
+
} else {
|
1379 |
+
resultDiv.innerHTML = '<p>Ночь. Все спят.</p>';
|
1380 |
}
|
1381 |
+
}
|
1382 |
+
// День
|
1383 |
+
else if(gameState.isRunning && gameState.phase === 'day'){
|
1384 |
+
voteInput.disabled = false;
|
1385 |
+
voteButton.disabled = false;
|
1386 |
|
1387 |
+
if (gameState.killed) {
|
1388 |
+
resultDiv.innerHTML += `<p>Ночью был убит ${gameState.killed}.</p>`;
|
1389 |
+
}
|
1390 |
|
1391 |
+
resultDiv.innerHTML += '<p>День. Обсуждение и голосование.</p>';
|
1392 |
|
1393 |
+
if (gameState.votes && Object.keys(gameState.votes).length > 0) {
|
1394 |
+
resultDiv.innerHTML += '<p>Голоса:</p>';
|
1395 |
+
for (const voter in gameState.votes) {
|
1396 |
+
resultDiv.innerHTML += `<p>${voter}: ${gameState.votes[voter]}</p>`;
|
1397 |
}
|
1398 |
+
}
|
1399 |
|
1400 |
+
// Подсчет голосов и определение, кого казнят
|
1401 |
+
if(Object.keys(gameState.votes).length === {{ rooms[token]['users']|length }}){
|
1402 |
+
const voteCounts = {};
|
1403 |
+
let maxVotes = 0;
|
1404 |
+
let votedOut = null;
|
1405 |
+
let tied = false; // Флаг ничьей
|
1406 |
+
|
1407 |
+
for(const voter in gameState.votes){
|
1408 |
+
const votedFor = gameState.votes[voter];
|
1409 |
+
voteCounts[votedFor] = (voteCounts[votedFor] || 0) + 1;
|
1410 |
+
|
1411 |
+
if(voteCounts[votedFor] > maxVotes){
|
1412 |
+
maxVotes = voteCounts[votedFor];
|
1413 |
+
votedOut = votedFor;
|
1414 |
+
tied = false; // Сбрасываем ничью, если новый лидер
|
1415 |
+
}else if(voteCounts[votedFor] === maxVotes){
|
1416 |
+
tied = true; // Ничья
|
1417 |
+
}
|
1418 |
+
}
|
1419 |
+
|
1420 |
+
if (tied) {
|
1421 |
+
resultDiv.innerHTML += '<p>Голосование завершилось ничьей. Никто не казнен.</p>';
|
1422 |
+
}else{
|
1423 |
+
resultDiv.innerHTML += `<p>По итогам голосования казнен ${votedOut}.</p>`;
|
1424 |
+
// Проверяем, была ли это мафия
|
1425 |
+
if (gameState.roles[votedOut] === 'mafia') {
|
1426 |
+
resultDiv.innerHTML += '<p>Мирные жители победили!</p>';
|
1427 |
+
gameState.isRunning = false;
|
1428 |
+
}
|
1429 |
+
}
|
1430 |
+
// Проверяем, остались ли мафиози
|
1431 |
+
const remainingMafia = Object.values(gameState.roles).filter(role => role === 'mafia').length;
|
1432 |
+
if (remainingMafia === 0) {
|
1433 |
+
resultDiv.innerHTML += '<p>Мирные жители победили!</p>';
|
1434 |
+
gameState.isRunning = false;
|
1435 |
}
|
1436 |
+
}
|
1437 |
+
}
|
1438 |
+
}
|
1439 |
+
|
1440 |
+
//Карточная игра "Дурак"
|
1441 |
+
function initDurak(gameId, gameInfo, gameContent){
|
1442 |
+
if (isAdmin) {
|
1443 |
+
const startButton = document.createElement('button');
|
1444 |
+
startButton.textContent = 'Раздать карты';
|
1445 |
+
startButton.classList.add('game-button');
|
1446 |
+
startButton.onclick = () => {
|
1447 |
+
const { deck, hands } = dealDurakCards({{ rooms[token]['users']|tojson }});
|
1448 |
+
socket.emit('set_game_state', { token, game_id: gameId, state: { deck, hands, table: [], turn: 0, attacker: null, defender: null, trumpSuit: deck.length > 0 ? deck[deck.length-1].suit : null, isGameEnd: false, winner: null} });
|
1449 |
+
};
|
1450 |
+
gameContent.appendChild(startButton);
|
1451 |
+
}
|
1452 |
+
|
1453 |
+
const playerHandContainer = document.createElement('div');
|
1454 |
+
playerHandContainer.id = 'player-hand';
|
1455 |
+
playerHandContainer.classList.add('card-container');
|
1456 |
+
gameContent.appendChild(playerHandContainer);
|
1457 |
|
1458 |
+
const tableContainer = document.createElement('div');
|
1459 |
+
tableContainer.id = 'durak-table';
|
1460 |
+
gameContent.appendChild(tableContainer);
|
1461 |
+
|
1462 |
+
|
1463 |
+
const buttonsDiv = document.createElement('div');
|
1464 |
+
buttonsDiv.id = 'durak-buttons';
|
1465 |
+
gameContent.appendChild(buttonsDiv);
|
1466 |
+
|
1467 |
+
const takeButton = document.createElement('button');
|
1468 |
+
takeButton.textContent = 'Взять';
|
1469 |
+
takeButton.id = 'take-button';
|
1470 |
+
takeButton.classList.add('game-button');
|
1471 |
+
buttonsDiv.appendChild(takeButton);
|
1472 |
+
|
1473 |
+
|
1474 |
+
const doneButton = document.createElement('button');
|
1475 |
+
doneButton.textContent = 'Готово/Пас';
|
1476 |
+
doneButton.id = 'done-button';
|
1477 |
+
doneButton.classList.add('game-button');
|
1478 |
+
buttonsDiv.appendChild(doneButton);
|
1479 |
+
|
1480 |
+
const infoDiv = document.createElement('div');
|
1481 |
+
infoDiv.id = 'durak-info';
|
1482 |
+
gameContent.appendChild(infoDiv);
|
1483 |
+
|
1484 |
+
takeButton.onclick = () => {
|
1485 |
+
socket.emit('game_action', { token, game_id: gameId, action: 'take', user: username });
|
1486 |
+
};
|
1487 |
+
doneButton.onclick = () => {
|
1488 |
+
socket.emit('game_action', { token, game_id: gameId, action: 'done', user: username });
|
1489 |
+
}
|
1490 |
+
}
|
1491 |
+
|
1492 |
+
function dealDurakCards(players) {
|
1493 |
+
const suits = ['Hearts', 'Diamonds', 'Clubs', 'Spades'];
|
1494 |
+
const ranks = ['6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
|
1495 |
+
let deck = [];
|
1496 |
+
|
1497 |
+
// Создаем колоду
|
1498 |
+
for (const suit of suits) {
|
1499 |
+
for (const rank of ranks) {
|
1500 |
+
deck.push({ suit, rank });
|
1501 |
}
|
1502 |
+
}
|
1503 |
+
|
1504 |
+
// Перемешиваем колоду
|
1505 |
+
for (let i = deck.length - 1; i > 0; i--) {
|
1506 |
+
const j = Math.floor(Math.random() * (i + 1));
|
1507 |
+
[deck[i], deck[j]] = [deck[j], deck[i]];
|
1508 |
+
}
|
1509 |
+
|
1510 |
+
// Раздаем карты
|
1511 |
+
const hands = {};
|
1512 |
+
players.forEach(player => hands[player] = []);
|
1513 |
+
let cardIndex = 0;
|
1514 |
+
while (cardIndex < 6 * players.length && cardIndex < deck.length) {
|
1515 |
+
players.forEach(player => {
|
1516 |
+
if (cardIndex < deck.length) {
|
1517 |
+
hands[player].push(deck[cardIndex]);
|
1518 |
+
cardIndex++;
|
1519 |
+
}
|
1520 |
+
});
|
1521 |
+
}
|
1522 |
+
deck = deck.slice(cardIndex); // Оставшиеся карты - колода.
|
1523 |
+
return { deck, hands };
|
1524 |
+
}
|
1525 |
+
|
1526 |
+
function renderCard(card, isHidden) {
|
1527 |
+
const cardElement = document.createElement('div');
|
1528 |
+
cardElement.classList.add('card');
|
1529 |
+
if (isHidden) {
|
1530 |
+
cardElement.classList.add('back'); // Рубашка
|
1531 |
+
} else {
|
1532 |
+
cardElement.textContent = `${card.rank}${getSuitSymbol(card.suit)}`;
|
1533 |
+
cardElement.dataset.suit = card.suit;
|
1534 |
+
cardElement.dataset.rank = card.rank;
|
1535 |
+
}
|
1536 |
+
|
1537 |
+
return cardElement;
|
1538 |
+
}
|
1539 |
+
function getSuitSymbol(suit) {
|
1540 |
+
switch (suit) {
|
1541 |
+
case 'Hearts': return '♥';
|
1542 |
+
case 'Diamonds': return '♦';
|
1543 |
+
case 'Clubs': return '♣';
|
1544 |
+
case 'Spades': return '♠';
|
1545 |
+
default: return '';
|
1546 |
+
}
|
1547 |
}
|
1548 |
|
1549 |
+
function canBeat(attackingCard, defendingCard, trumpSuit){
|
1550 |
+
if(attackingCard.suit === defendingCard.suit){
|
1551 |
+
return compareRanks(defendingCard.rank, attackingCard.rank) > 0;
|
1552 |
+
}else{
|
1553 |
+
return defendingCard.suit === trumpSuit;
|
1554 |
+
}
|
1555 |
+
}
|
1556 |
+
|
1557 |
+
function compareRanks(rank1, rank2){
|
1558 |
+
const ranks = ['6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'];
|
1559 |
+
return ranks.indexOf(rank1) - ranks.indexOf(rank2);
|
1560 |
+
}
|
1561 |
+
|
1562 |
+
function updateDurakState(gameId, gameState){
|
1563 |
+
const playerHandContainer = document.getElementById('player-hand');
|
1564 |
+
const tableContainer = document.getElementById('durak-table');
|
1565 |
+
const infoDiv = document.getElementById('durak-info');
|
1566 |
+
|
1567 |
+
if (!playerHandContainer || !tableContainer || !infoDiv) return;
|
1568 |
+
playerHandContainer.innerHTML = ''; // Очистка
|
1569 |
+
tableContainer.innerHTML = '';
|
1570 |
+
infoDiv.innerHTML = '';
|
1571 |
+
|
1572 |
+
const trumpCardEl = document.createElement('div');
|
1573 |
+
if(gameState.trumpSuit){
|
1574 |
+
infoDiv.innerHTML = `Козырь: ${getSuitSymbol(gameState.trumpSuit)}`;
|
1575 |
+
|
1576 |
+
trumpCardEl.classList.add('card');
|
1577 |
+
|
1578 |
+
if(gameState.deck.length > 0){
|
1579 |
+
trumpCardEl.textContent = `${gameState.deck[gameState.deck.length -1].rank} ${getSuitSymbol(gameState.deck[gameState.deck.length -1].suit)}`;
|
1580 |
+
infoDiv.appendChild(trumpCardEl)
|
1581 |
+
}
|
1582 |
+
|
1583 |
+
}
|
1584 |
+
|
1585 |
+
const myHand = gameState.hands[username] || [];
|
1586 |
+
myHand.forEach(card => {
|
1587 |
+
const cardElement = renderCard(card, false);
|
1588 |
+
cardElement.addEventListener('click', () => {
|
1589 |
+
|
1590 |
+
const selectedCards = playerHandContainer.querySelectorAll('.selected');
|
1591 |
+
|
1592 |
+
if (cardElement.classList.contains('selected')) {
|
1593 |
+
cardElement.classList.remove('selected');
|
1594 |
+
} else {
|
1595 |
+
// Логика выбора карты для хода/защиты
|
1596 |
+
if(gameState.attacker === username){ //Если игрок атакующий
|
1597 |
+
if(gameState.table.length < 6){
|
1598 |
+
//Выбираем только одну карту
|
1599 |
+
selectedCards.forEach(c => c.classList.remove('selected'));
|
1600 |
+
cardElement.classList.add('selected');
|
1601 |
+
}
|
1602 |
+
}
|
1603 |
+
else if(gameState.defender === username){
|
1604 |
+
// Можно выбрать только одну карту для защиты
|
1605 |
+
selectedCards.forEach(c => c.classList.remove('selected'));
|
1606 |
+
cardElement.classList.add('selected');
|
1607 |
+
}
|
1608 |
+
|
1609 |
+
//Выделяем карту
|
1610 |
+
//cardElement.classList.add('selected');
|
1611 |
+
}
|
1612 |
+
//Определяем, можем ли мы сделать ход выбранной картой
|
1613 |
+
const selected = playerHandContainer.querySelector('.selected'); //Одна карта
|
1614 |
+
if(selected){
|
1615 |
+
const selectedCard = {suit: selected.dataset.suit, rank: selected.dataset.rank};
|
1616 |
+
socket.emit('game_action', {token, game_id: gameId, action: 'move', card: selectedCard, user: username});
|
1617 |
+
}
|
1618 |
+
|
1619 |
+
});
|
1620 |
+
playerHandContainer.appendChild(cardElement);
|
1621 |
+
});
|
1622 |
+
|
1623 |
+
const takeButton = document.getElementById('take-button');
|
1624 |
+
const doneButton = document.getElementById('done-button');
|
1625 |
+
|
1626 |
+
// Обновляем кнопки
|
1627 |
+
takeButton.style.display = 'none';
|
1628 |
+
doneButton.style.display = 'none';
|
1629 |
+
|
1630 |
+
// Если текущий игрок - защищающийся, и он еще не взял карты, показываем кнопку
|
1631 |
+
if (gameState.defender === username && gameState.turn === 1) { //turn === 1 - Защищающийся
|
1632 |
+
takeButton.style.display = 'inline-block';
|
1633 |
+
}
|
1634 |
+
// Если текущий игрок - атакующий, и он еще не закончил ход, показываем "Готово"
|
1635 |
+
if(gameState.attacker === username && gameState.turn === 0){
|
1636 |
+
doneButton.style.display = 'inline-block';
|
1637 |
+
}
|
1638 |
+
|
1639 |
+
// Отображаем карты на столе
|
1640 |
+
gameState.table.forEach(pair => {
|
1641 |
+
const pairDiv = document.createElement('div');
|
1642 |
+
pairDiv.classList.add('card-pair');
|
1643 |
+
|
1644 |
+
const attackingCardEl = renderCard(pair.attackingCard, false);
|
1645 |
+
pairDiv.appendChild(attackingCardEl);
|
1646 |
+
|
1647 |
+
if (pair.defendingCard) {
|
1648 |
+
const defendingCardEl = renderCard(pair.defendingCard, false);
|
1649 |
+
pairDiv.appendChild(defendingCardEl);
|
1650 |
+
}
|
1651 |
+
tableContainer.appendChild(pairDiv);
|
1652 |
+
});
|
1653 |
+
|
1654 |
+
if(gameState.isGameEnd){
|
1655 |
+
infoDiv.innerHTML += `<p>Игра окончена, победитель - ${gameState.winner} </p>`
|
1656 |
+
}
|
1657 |
+
}
|
1658 |
+
|
1659 |
+
socket.on('update_game_state', (data) => {
|
1660 |
+
const gameId = data.game_id;
|
1661 |
+
const gameState = data.state;
|
1662 |
+
|
1663 |
+
if (gameId === 'crocodile'){
|
1664 |
+
updateCrocodileState(gameId, gameState);
|
1665 |
+
}else if(gameId === 'alias'){
|
1666 |
+
updateAliasState(gameId, gameState);
|
1667 |
+
}else if(gameId === 'mafia'){
|
1668 |
+
updateMafiaState(gameId, gameState)
|
1669 |
+
}else if(gameId === 'durak'){
|
1670 |
+
updateDurakState(gameId, gameState);
|
1671 |
+
}
|
1672 |
+
|
1673 |
+
});
|
1674 |
+
|
1675 |
+
socket.on('timer_tick', (data) => {
|
1676 |
+
const gameId = data.game_id;
|
1677 |
+
if(gameId === 'crocodile' && document.getElementById('crocodile-timer')){
|
1678 |
+
document.getElementById('crocodile-timer').textContent = `Время: ${data.time}`;
|
1679 |
+
}else if(gameId === 'alias' && document.getElementById('alias-timer')){
|
1680 |
+
document.getElementById('alias-timer').textContent = `Время: ${data.time}`;
|
1681 |
+
}
|
1682 |
+
});
|
1683 |
+
|
1684 |
+
socket.on('game_action_result', (data) => {
|
1685 |
+
const gameId = data.game_id;
|
1686 |
+
const action = data.action;
|
1687 |
+
//console.log("LOG:", gameId, action)
|
1688 |
+
if(gameId === 'durak' && action === 'move'){ // Для дурака убрал, так как все через update
|
1689 |
+
}
|
1690 |
+
});
|
1691 |
+
|
1692 |
function leaveRoom() {
|
1693 |
socket.emit('leave', { token, username });
|
1694 |
if (localStream) {
|
|
|
1709 |
def handle_join(data):
|
1710 |
token = data['token']
|
1711 |
username = data['username']
|
1712 |
+
print( f"User {username} joining room {token}")
|
1713 |
if token in rooms and len(rooms[token]['users']) < rooms[token]['max_users']:
|
1714 |
join_room(token)
|
1715 |
if username not in rooms[token]['users']:
|
|
|
1747 |
if rooms[token].get('current_game'): # Если игра в процессе
|
1748 |
game_id = rooms[token]['current_game']
|
1749 |
if games_data[game_id]['state'].get(token):
|
1750 |
+
if game_id == 'crocodile':
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1751 |
if games_data[game_id]['state'][token].get('presenter') == username:
|
1752 |
# Сброс игры, если уходит ведущий
|
1753 |
del games_data[game_id]['state'][token]
|
|
|
1757 |
#Удаление догадок
|
1758 |
if games_data[game_id]['state'][token].get('guesses'):
|
1759 |
games_data[game_id]['state'][token]['guesses'] = [guess for guess in games_data[game_id]['state'][token]['guesses'] if guess['user'] != username]
|
1760 |
+
|
1761 |
+
elif game_id == 'alias':
|
1762 |
+
if games_data[game_id]['state'][token].get('presenter') == username:
|
1763 |
+
del games_data[game_id]['state'][token]
|
1764 |
+
emit('update_game_state', {'game_id': game_id, 'state': {}}, room=token)
|
1765 |
+
save_json(GAMES_DB, games_data)
|
1766 |
+
return
|
1767 |
+
if games_data[game_id]['state'][token].get('guesses'):
|
1768 |
+
games_data[game_id]['state'][token]['guesses'] = [guess for guess in games_data[game_id]['state'][token]['guesses'] if guess['user'] != username]
|
1769 |
+
|
1770 |
+
elif game_id == 'mafia':
|
1771 |
+
if games_data[game_id]['state'][token].get('roles') and username in games_data[game_id]['state'][token]['roles']:
|
1772 |
+
del games_data[game_id]['state'][token]['roles'][username]
|
1773 |
+
|
1774 |
+
if games_data[game_id]['state'][token].get('votes'):
|
1775 |
+
#Удаляем голос, если он был
|
1776 |
+
if username in games_data[game_id]['state'][token]['votes']:
|
1777 |
+
del games_data[game_id]['state'][token]['votes'][username]
|
1778 |
+
|
1779 |
+
elif game_id == 'durak':
|
1780 |
+
if games_data[game_id]['state'][token].get('hands') and username in games_data[game_id]['state'][token]['hands']:
|
1781 |
+
del games_data[game_id]['state'][token]['hands'][username]
|
1782 |
+
|
1783 |
+
#Если ушел атакующий или защищающийся
|
1784 |
+
if games_data[game_id]['state'][token].get('attacker') == username:
|
1785 |
+
games_data[game_id]['state'][token]['attacker'] = None;
|
1786 |
+
|
1787 |
+
if games_data[game_id]['state'][token].get('defender') == username:
|
1788 |
+
games_data[game_id]['state'][token]['defender'] = None;
|
1789 |
save_json(GAMES_DB, games_data); # Сохраняем изменения в любом случае
|
1790 |
emit('update_game_state', {'game_id': game_id, 'state': games_data[game_id]['state'][token]}, room=token);
|
1791 |
|
|
|
1821 |
token = data['token']
|
1822 |
game_id = data['game_id']
|
1823 |
state = data['state']
|
1824 |
+
#print("SET STATE", data)
|
1825 |
|
1826 |
if token in rooms:
|
1827 |
if rooms[token]['admin'] == session.get('username'):
|
|
|
1831 |
save_json(GAMES_DB, games_data)
|
1832 |
emit('update_game_state', {'game_id': game_id, 'state': state}, room=token)
|
1833 |
|
1834 |
+
if (game_id == 'crocodile' or game_id == 'alias') and state.get('isRunning'): # Запуск таймера
|
1835 |
start_timer(token, game_id)
|
1836 |
|
1837 |
@socketio.on('game_action')
|
|
|
1839 |
token = data['token']
|
1840 |
game_id = data['game_id']
|
1841 |
action = data['action']
|
1842 |
+
value = data.get('value') # Могут быть и другие данные (не только value)
|
1843 |
user = data['user']
|
1844 |
+
card = data.get('card') # Для карт
|
1845 |
|
1846 |
if token in rooms and game_id == rooms[token]['current_game']:
|
1847 |
current_state = games_data[game_id]['state'].get(token, {})
|
1848 |
|
1849 |
+
if game_id == 'crocodile' or game_id == 'alias':
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1850 |
if action == 'guess' and current_state.get('isRunning'):
|
1851 |
result = "Не угадано"
|
1852 |
if value.lower() == current_state.get('word', '').lower():
|
|
|
1860 |
save_json(GAMES_DB, games_data)
|
1861 |
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1862 |
|
1863 |
+
elif game_id == 'mafia':
|
1864 |
+
if action == 'vote' and current_state.get('phase') == 'day' and current_state.get('isRunning'):
|
1865 |
+
if 'votes' not in current_state:
|
1866 |
+
current_state['votes'] = {}
|
1867 |
|
1868 |
+
current_state['votes'][user] = value # Сохраняем голос
|
|
|
|
|
1869 |
|
1870 |
+
#Если все проголосовали
|
1871 |
+
if len(current_state['votes']) == len(rooms[token]['users']):
|
1872 |
+
#Меняем фазу на ночь
|
1873 |
+
current_state['phase'] = 'night'
|
|
|
|
|
|
|
|
|
1874 |
|
1875 |
games_data[game_id]['state'][token] = current_state
|
1876 |
+
save_json(GAMES_DB, games_data);
|
1877 |
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1878 |
|
1879 |
+
elif game_id == 'durak':
|
1880 |
+
if action == 'move':
|
1881 |
+
if current_state.get('attacker') == user:
|
1882 |
+
# Проверяем, можно ли походить этой картой
|
1883 |
+
if len(current_state['table']) == 0 or any(c['rank'] == card['rank'] for pair in current_state['table'] for c in [pair.get('attackingCard'), pair.get('defendingCard')] if c):
|
1884 |
+
current_state['table'].append({'attackingCard': card, 'defendingCard': None})
|
1885 |
+
current_state['hands'][user].remove(card)
|
1886 |
+
#Передаем ход защищающемуся
|
1887 |
+
current_state['turn'] = 1;
|
1888 |
+
current_state['defender'] = get_next_player(token, current_state['attacker']);
|
1889 |
+
|
1890 |
+
games_data[game_id]['state'][token] = current_state
|
1891 |
+
save_json(GAMES_DB, games_data)
|
1892 |
+
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1893 |
+
|
1894 |
+
elif current_state.get('defender') == user:
|
1895 |
+
|
1896 |
+
if len(current_state['table']) > 0 :
|
1897 |
+
last_pair = current_state['table'][-1]
|
1898 |
+
if last_pair.get('defendingCard') is None:
|
1899 |
+
attacking_card = last_pair['attackingCard']
|
1900 |
+
#Можем ли побить
|
1901 |
+
if canBeat(attacking_card, card, current_state['trumpSuit']):
|
1902 |
+
last_pair['defendingCard'] = card
|
1903 |
+
current_state['hands'][user].remove(card)
|
1904 |
+
current_state['turn'] = 0 #Ход переходит атакующему
|
1905 |
+
#Меняем атакующего и защищающегося, если нужно
|
1906 |
+
current_state['attacker'] = get_next_player(token, current_state['defender']);
|
1907 |
+
current_state['defender'] = get_next_player(token, current_state['attacker']);
|
1908 |
+
|
1909 |
+
games_data[game_id]['state'][token] = current_state
|
1910 |
+
save_json(GAMES_DB, games_data)
|
1911 |
+
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1912 |
+
|
1913 |
+
elif action == 'take':
|
1914 |
+
if current_state.get('defender') == user:
|
1915 |
+
# Защищающийся берет карты со стола
|
1916 |
+
taken_cards = []
|
1917 |
+
for pair in current_state['table']:
|
1918 |
+
taken_cards.append(pair['attackingCard'])
|
1919 |
+
if pair.get('defendingCard'):
|
1920 |
+
taken_cards.append(pair['defendingCard'])
|
1921 |
+
current_state['hands'][user].extend(taken_cards)
|
1922 |
+
current_state['table'] = [] # Очищаем стол
|
1923 |
+
#Добираем карты
|
1924 |
+
current_state = refill_hands(current_state, token);
|
1925 |
+
|
1926 |
+
# Ход переходит к следующему игроку после защищавшегося.
|
1927 |
+
current_state['attacker'] = get_next_player(token, current_state['defender']);
|
1928 |
+
current_state['defender'] = get_next_player(token, current_state['attacker']);
|
1929 |
+
current_state['turn'] = 0; # Ходит атакующий
|
1930 |
+
|
1931 |
+
games_data[game_id]['state'][token] = current_state
|
1932 |
+
save_json(GAMES_DB, games_data)
|
1933 |
+
emit('update_game_state', {'game_id': game_id, 'state': current_state}, room=token)
|
1934 |
+
|
1935 |
+
elif action == 'done':
|
1936 |
+
if current_state.get('attacker') == user:
|
1937 |
+
current_state['table'] = []
|
1938 |
+
current_state = refill_hands(current_state, token); #Раздача
|
1939 |
+
|
1940 |
+
#Определение следующего атакующего и защищающегося
|
1941 |
+
current_state['attacker'] = get_next_player(token, current_state['attacker']);
|
1942 |
+
current_state['defender'] = get_next_player(token, current_state['attacker']);
|
1943 |
+
current_state['turn'] = 0; # Ходит атакующий
|
1944 |
+
|
1945 |
+
games_data[game_id]['state'][token] = current_state;
|
1946 |
+
save_json(GAMES_DB, games_data);
|
1947 |
+
emit('update_game_state', {'game_id': game_id, 'state':current_state}, room=token);
|
1948 |
+
|
1949 |
+
|
1950 |
+
def get_next_player(token, current_player):
|
1951 |
+
"""Определяет следующего игрока в комнате."""
|
1952 |
+
if token not in rooms:
|
1953 |
+
return None
|
1954 |
+
|
1955 |
+
users = rooms[token]['users']
|
1956 |
+
if not users:
|
1957 |
+
return None
|
1958 |
+
|
1959 |
+
current_index = users.index(current_player)
|
1960 |
+
next_index = (current_index + 1) % len(users) # Циклический переход
|
1961 |
+
return users[next_index]
|
1962 |
+
|
1963 |
+
def refill_hands(game_state, token):
|
1964 |
+
"""Раздает карты игрокам до 6, если в колоде еще есть карты."""
|
1965 |
+
players = rooms[token]['users']
|
1966 |
+
for player in players:
|
1967 |
+
while len(game_state['hands'].get(player, [])) < 6 and len(game_state['deck']) > 0:
|
1968 |
+
game_state['hands'][player].append(game_state['deck'].pop())
|
1969 |
+
|
1970 |
+
#Проверка, не закончилась ли игра
|
1971 |
+
if len(game_state['deck']) == 0:
|
1972 |
+
players_without_cards = [player for player in players if len(game_state['hands'].get(player,[])) == 0]
|
1973 |
+
if len(players_without_cards) > 0:
|
1974 |
+
game_state['isGameEnd'] = True
|
1975 |
+
game_state['winner'] = players_without_cards[0] # Первый, кто избавился от карт
|
1976 |
+
|
1977 |
+
return game_state;
|
1978 |
|
1979 |
def start_timer(token, game_id):
|
1980 |
+
if game_id != 'crocodile' and game_id != 'alias': # Таймер пока только для крокодила
|
1981 |
return
|
1982 |
|
1983 |
def timer_loop():
|