Изменения:
1. Ошибка "WHERE game_sessions.user_id IS NULL" + игра получает user_id из бд после создания пользователя, авторизации 2. Провиль пользователя + отображается никнейм 3. Уровни + добавлена рамка с 100 уровнями + добавлена система смены цвета кнопки уровня в зависимости от состояния (пройден/не пройден) 4. Окно победы + добавлено отображение никнейма
This commit is contained in:
parent
9f5d36d1a1
commit
5d34162267
13 changed files with 5512 additions and 234 deletions
BIN
database/DogAcademy.db
Normal file
BIN
database/DogAcademy.db
Normal file
Binary file not shown.
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
from database.db_session import get_session
|
from database.db_session import get_session
|
||||||
|
|
@ -6,36 +7,50 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
|
|
||||||
def get_user_by_id(user_id):
|
def get_user_by_id(user_id):
|
||||||
"""Получение данных пользователя по ID."""
|
"""Получение данных пользователя по ID с предварительной загрузкой связанных данных."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
user = session.query(Users).filter_by(user_id=user_id).first()
|
user = (
|
||||||
|
session.query(Users)
|
||||||
|
.options(joinedload(Users.game_sessions)) # Предзагрузка связанных игровых сессий
|
||||||
|
.filter_by(user_id=user_id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
return user
|
return user
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
print(f"Ошибка при получении пользователя: {e}")
|
logging.error(f"Ошибка при получении пользователя: {e}")
|
||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
def create_user(login, password, username):
|
def create_user(login, password, username):
|
||||||
"""Создание нового пользователя в базе данных."""
|
"""Регистрация нового пользователя."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
|
|
||||||
# Проверяем, есть ли уже пользователь с таким логином
|
# Проверка, есть ли уже пользователь с таким логином
|
||||||
if session.query(Auth).filter_by(login=login).first():
|
if session.query(Auth).filter_by(login=login).first():
|
||||||
return False, "Логин уже используется."
|
return False, "Логин уже используется."
|
||||||
|
|
||||||
# Создаем новую запись в таблице Auth
|
# Создаём новую запись в таблице Auth
|
||||||
new_auth = Auth(login=login, password=password)
|
new_auth = Auth(login=login, password=password)
|
||||||
session.add(new_auth)
|
session.add(new_auth)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
session.commit() # Сохраняем изменения в таблице Auth
|
session.commit() # Сохраняем изменения в таблице Auth
|
||||||
|
|
||||||
# Создаем новую запись в таблице Users, связывая с только что добавленным Auth
|
# Создаём новую запись в таблице Users, связываем с только что добавленным Auth
|
||||||
new_user = Users(user_id=new_auth.user_id, username=username)
|
new_user = Users(user_id=new_auth.user_id, username=username)
|
||||||
session.add(new_user)
|
session.add(new_user)
|
||||||
session.commit() # Сохраняем изменения в таблице Users
|
session.commit() # Сохраняем изменения в таблице Users
|
||||||
|
|
||||||
|
# Создаём новый игровой процесс для этого пользователя
|
||||||
|
new_game_session = GameSession(user_id=new_user.user_id, level=1) # Устанавливаем уровень по умолчанию
|
||||||
|
session.add(new_game_session)
|
||||||
|
session.commit() # Сохраняем данные в GameSession
|
||||||
|
|
||||||
|
print(f"Пользователь {username} успешно добавлен!")
|
||||||
|
return True, "Регистрация успешна."
|
||||||
|
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
session.rollback() # Откат изменений при ошибке
|
session.rollback() # Откат изменений при ошибке
|
||||||
print(f"Ошибка при создании пользователя: {e}")
|
print(f"Ошибка при создании пользователя: {e}")
|
||||||
|
|
@ -43,28 +58,18 @@ def create_user(login, password, username):
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return True, "Регистрация успешна."
|
|
||||||
|
|
||||||
|
|
||||||
def check_user(login, password=None):
|
def check_user(login, password=None):
|
||||||
"""Проверка существования пользователя по логину и паролю (если передан)."""
|
|
||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
print(f"Проверяем пользователя с логином: {login}")
|
|
||||||
# Фильтрация только по логину
|
|
||||||
query = session.query(Auth).filter_by(login=login)
|
query = session.query(Auth).filter_by(login=login)
|
||||||
|
|
||||||
# Если передан пароль, фильтруем и по паролю
|
|
||||||
if password:
|
if password:
|
||||||
query = query.filter_by(password=password)
|
query = query.filter_by(password=password)
|
||||||
|
|
||||||
user = query.first()
|
user = query.first()
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
print(f"Пользователь найден: {user.user_id}")
|
return user.user_id
|
||||||
return user.user_id # Возвращаем user_id пользователя
|
|
||||||
else:
|
else:
|
||||||
print("Пользователь не найден.")
|
|
||||||
return None
|
return None
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
print(f"Ошибка при проверке пользователя: {e}")
|
print(f"Ошибка при проверке пользователя: {e}")
|
||||||
|
|
@ -76,6 +81,8 @@ def save_progress(user_id, level, score, duration, health, hunger, sleepiness):
|
||||||
"""Сохранение игрового прогресса в базу данных."""
|
"""Сохранение игрового прогресса в базу данных."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
|
if not user_id:
|
||||||
|
raise ValueError("user_id не указан!")
|
||||||
session_data = GameSession(
|
session_data = GameSession(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
level=level,
|
level=level,
|
||||||
|
|
@ -89,8 +96,10 @@ def save_progress(user_id, level, score, duration, health, hunger, sleepiness):
|
||||||
session.add(session_data)
|
session.add(session_data)
|
||||||
session.commit()
|
session.commit()
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
print(f"Ошибка при сохранении прогресса: {e}")
|
logging.error(f"Ошибка при сохранении прогресса: {e}")
|
||||||
session.rollback()
|
session.rollback()
|
||||||
|
except ValueError as e:
|
||||||
|
logging.error(e)
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
|
@ -100,7 +109,7 @@ def get_user_progress(user_id):
|
||||||
try:
|
try:
|
||||||
return session.query(GameSession).filter_by(user_id=user_id).all()
|
return session.query(GameSession).filter_by(user_id=user_id).all()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Ошибка при получении прогресса пользователя: {e}")
|
logging.error(f"Ошибка при получении прогресса пользователя: {e}")
|
||||||
return []
|
return []
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
from database.db_session import get_session
|
from database.db_session import get_session
|
||||||
from database.models import Dogs
|
from database.models import Dogs
|
||||||
|
|
||||||
|
|
@ -47,21 +51,44 @@ DOG_CHARACTERS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
def populate_dogs():
|
def populate_dogs():
|
||||||
|
"""
|
||||||
|
Заполнение таблицы Dogs предустановленными данными.
|
||||||
|
"""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
|
logging.info("Начинается заполнение таблицы Dogs.")
|
||||||
for breed, data in DOG_CHARACTERS.items():
|
for breed, data in DOG_CHARACTERS.items():
|
||||||
dog = Dogs(
|
existing_dog = session.query(Dogs).filter_by(breed=breed).first()
|
||||||
breed=breed,
|
if not existing_dog:
|
||||||
characteristics=data['characteristics'],
|
dog = Dogs(
|
||||||
behavior=data['behavior'],
|
breed=breed,
|
||||||
care_info=data['care_info'],
|
characteristics=data['characteristics'],
|
||||||
admin_comments=data['admin_comments']
|
behavior=data['behavior'],
|
||||||
)
|
care_info=data['care_info'],
|
||||||
session.add(dog)
|
admin_comments=data['admin_comments']
|
||||||
|
)
|
||||||
|
session.add(dog)
|
||||||
session.commit()
|
session.commit()
|
||||||
print("Таблица Dogs успешно заполнена.")
|
logging.info("Таблица Dogs успешно заполнена.")
|
||||||
except Exception as e:
|
except SQLAlchemyError as e:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
print(f"Ошибка при заполнении Dogs: {e}")
|
logging.error(f"Ошибка при заполнении Dogs: {e}")
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_dogs():
|
||||||
|
"""
|
||||||
|
Получение списка всех пород собак из базы данных.
|
||||||
|
|
||||||
|
:return: Список объектов Dogs.
|
||||||
|
"""
|
||||||
|
session = get_session()
|
||||||
|
try:
|
||||||
|
dogs = session.query(Dogs).all()
|
||||||
|
return dogs
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
logging.error(f"Ошибка при получении списка собак: {e}")
|
||||||
|
return []
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,48 @@
|
||||||
|
import logging
|
||||||
|
from sqlalchemy import func
|
||||||
from database.db_events import get_user_progress
|
from database.db_events import get_user_progress
|
||||||
from database.db_session import get_session
|
from database.db_session import get_session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from database.models import GameSession
|
from database.models import GameSession
|
||||||
|
|
||||||
|
|
||||||
def save_game_session(user_id, level, score, duration, health, hunger, sleepiness):
|
def save_game_session(user_id, level, score, steps, duration=0, health=100, hunger=0, sleepiness=0):
|
||||||
"""Сохранение игрового процесса в таблицу GameSessions."""
|
"""Сохранение игрового прогресса."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
try:
|
try:
|
||||||
# Создаем новый объект GameSession
|
session.add(GameSession(
|
||||||
game_session = GameSession(
|
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
level=level,
|
level=level,
|
||||||
score=score,
|
score=score,
|
||||||
|
steps=steps,
|
||||||
duration=duration,
|
duration=duration,
|
||||||
health=health,
|
health=health,
|
||||||
hunger=hunger,
|
hunger=hunger,
|
||||||
sleepiness=sleepiness,
|
sleepiness=sleepiness
|
||||||
)
|
))
|
||||||
session.add(game_session)
|
session.commit()
|
||||||
session.commit() # Сохраняем данные в таблице
|
logging.info(f"Сессия сохранена: user_id={user_id}, level={level}, score={score}")
|
||||||
print(f"Игровой процесс для пользователя {user_id} на уровне {level} успешно сохранен.")
|
except Exception as e:
|
||||||
except SQLAlchemyError as e:
|
|
||||||
session.rollback()
|
session.rollback()
|
||||||
print(f"Ошибка при сохранении игрового процесса: {e}")
|
logging.error(f"Ошибка при сохранении игровой сессии: {e}")
|
||||||
finally:
|
raise
|
||||||
session.close()
|
|
||||||
|
|
||||||
def print_user_progress(user_id):
|
def print_user_progress(user_id):
|
||||||
"""Печать прогресса пользователя из таблицы GameSessions."""
|
"""
|
||||||
|
Печать прогресса пользователя из таблицы GameSessions.
|
||||||
|
|
||||||
|
:param user_id: ID пользователя
|
||||||
|
"""
|
||||||
|
if not user_id:
|
||||||
|
logging.error("user_id отсутствует. Невозможно получить прогресс.")
|
||||||
|
return
|
||||||
|
|
||||||
progress = get_user_progress(user_id)
|
progress = get_user_progress(user_id)
|
||||||
|
if not progress:
|
||||||
|
print(f"У пользователя с ID {user_id} нет сохраненного прогресса.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Прогресс пользователя (user_id={user_id}):")
|
||||||
for session in progress:
|
for session in progress:
|
||||||
print(f"Уровень: {session.level}, Очки: {session.score}, Время: {session.duration} секунд")
|
print(f"- Уровень: {session.level}, Очки: {session.score}, Время: {session.duration} сек")
|
||||||
|
|
|
||||||
5116
logs/logfile.log
5116
logs/logfile.log
File diff suppressed because it is too large
Load diff
56
src/auth.py
56
src/auth.py
|
|
@ -1,3 +1,5 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from database.db_session import get_session
|
from database.db_session import get_session
|
||||||
from database.models import Auth, Users, GameSession
|
from database.models import Auth, Users, GameSession
|
||||||
|
|
@ -6,50 +8,42 @@ from database.models import Auth, Users, GameSession
|
||||||
def register_user(login, password, username):
|
def register_user(login, password, username):
|
||||||
"""Регистрация нового пользователя."""
|
"""Регистрация нового пользователя."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
|
|
||||||
# Проверяем, есть ли уже пользователь с таким логином
|
|
||||||
if session.query(Auth).filter_by(login=login).first():
|
|
||||||
return False, "Логин уже используется."
|
|
||||||
|
|
||||||
# Создаем новую запись в таблице Auth
|
|
||||||
new_auth = Auth(login=login, password=password)
|
|
||||||
session.add(new_auth)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
session.commit() # Сохраняем изменения в таблице Auth
|
if session.query(Auth).filter_by(login=login).first():
|
||||||
|
return False, "Логин уже используется."
|
||||||
|
|
||||||
# Создаем новую запись в таблице Users, связывая с только что добавленным Auth
|
# Создаем новую запись в Auth
|
||||||
# Используем new_auth.user_id для связи
|
new_auth = Auth(login=login, password=password)
|
||||||
|
session.add(new_auth)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
# Создаем запись в Users
|
||||||
new_user = Users(user_id=new_auth.user_id, username=username)
|
new_user = Users(user_id=new_auth.user_id, username=username)
|
||||||
session.add(new_user)
|
session.add(new_user)
|
||||||
session.commit() # Сохраняем изменения в таблице Users
|
|
||||||
|
|
||||||
# Создаем новый игровой процесс в GameSession для этого пользователя
|
# Создаем запись в GameSession
|
||||||
new_game_session = GameSession(user_id=new_user.user_id, level=1) # Устанавливаем уровень по умолчанию
|
new_game_session = GameSession(user_id=new_user.user_id, level=1)
|
||||||
session.add(new_game_session)
|
session.add(new_game_session)
|
||||||
session.commit() # Сохраняем данные в GameSession
|
session.commit()
|
||||||
|
|
||||||
print(f"Пользователь {username} успешно добавлен!")
|
return True, "Регистрация успешна."
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
session.rollback() # Откат изменений при ошибке
|
session.rollback()
|
||||||
print(f"Ошибка при создании пользователя: {e}")
|
logging.error(f"Ошибка при регистрации: {e}")
|
||||||
return False, "Произошла ошибка при регистрации."
|
return False, "Произошла ошибка при регистрации."
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return True, "Регистрация успешна."
|
|
||||||
|
|
||||||
|
|
||||||
def login_user(login, password):
|
def login_user(login, password):
|
||||||
"""Авторизация пользователя."""
|
"""Авторизация пользователя."""
|
||||||
session = get_session()
|
session = get_session()
|
||||||
|
try:
|
||||||
# Проверяем, существует ли пользователь с таким логином и паролем
|
auth = session.query(Auth).filter_by(login=login, password=password).first()
|
||||||
user_auth = session.query(Auth).filter_by(login=login, password=password).first()
|
if auth:
|
||||||
|
return True, auth.user_id
|
||||||
if user_auth:
|
return False, "Неверный логин или пароль."
|
||||||
# Возвращаем успешный вход и ID пользователя из таблицы Users
|
except SQLAlchemyError as e:
|
||||||
user = session.query(Users).filter_by(user_id=user_auth.user_id).first()
|
return False, f"Ошибка авторизации: {e}"
|
||||||
return True, user.user_id
|
finally:
|
||||||
|
session.close()
|
||||||
return False, "Неверный логин или пароль."
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox
|
||||||
from config import BACKGROUND_COLOR, PRIMARY_COLOR, BUTTON_COLOR, BUTTON_TEXT_COLOR, FONT, BIG_FONT, ADMIN_LOGIN, ADMIN_PASSWORD
|
from config import BACKGROUND_COLOR, PRIMARY_COLOR, BUTTON_COLOR, BUTTON_TEXT_COLOR, FONT, BIG_FONT, ADMIN_LOGIN, ADMIN_PASSWORD
|
||||||
|
from src.auth import login_user
|
||||||
from src.ui.admin_ui import AdminApp # Импорт интерфейса администратора
|
from src.ui.admin_ui import AdminApp # Импорт интерфейса администратора
|
||||||
from database.db_events import create_user, check_user
|
from database.db_events import create_user, check_user
|
||||||
from src.ui.user_ui.main_menu import UserApp
|
from src.ui.user_ui.main_menu import UserApp
|
||||||
|
|
||||||
class DogAcademyApp:
|
class DogAcademyApp:
|
||||||
def __init__(self, root):
|
def __init__(self, root, user_id=None):
|
||||||
|
"""Инициализация приложения."""
|
||||||
self.root = root
|
self.root = root
|
||||||
|
self.user_id = user_id
|
||||||
self.root.title("Dog Academy Game")
|
self.root.title("Dog Academy Game")
|
||||||
self.root.geometry("1920x1080")
|
self.root.geometry("1920x1080")
|
||||||
self.root.configure(bg=BACKGROUND_COLOR)
|
self.root.configure(bg=BACKGROUND_COLOR)
|
||||||
|
|
@ -20,12 +24,11 @@ class DogAcademyApp:
|
||||||
self.current_frame.destroy()
|
self.current_frame.destroy()
|
||||||
|
|
||||||
def show_main_menu(self):
|
def show_main_menu(self):
|
||||||
"""Показать главное меню с названием игры и кнопками."""
|
"""Показать главное меню."""
|
||||||
self.clear_frame()
|
self.clear_frame()
|
||||||
self.current_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
self.current_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
||||||
self.current_frame.pack(expand=True)
|
self.current_frame.pack(expand=True)
|
||||||
|
|
||||||
# Название игры
|
|
||||||
title = tk.Label(
|
title = tk.Label(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Dog Academy Game",
|
text="Dog Academy Game",
|
||||||
|
|
@ -35,7 +38,6 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
title.pack(pady=50)
|
title.pack(pady=50)
|
||||||
|
|
||||||
# Кнопка "Войти"
|
|
||||||
login_button = tk.Button(
|
login_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Войти",
|
text="Войти",
|
||||||
|
|
@ -46,7 +48,6 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
login_button.pack(pady=20)
|
login_button.pack(pady=20)
|
||||||
|
|
||||||
# Кнопка "Зарегистрироваться"
|
|
||||||
register_button = tk.Button(
|
register_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Зарегистрироваться",
|
text="Зарегистрироваться",
|
||||||
|
|
@ -63,7 +64,6 @@ class DogAcademyApp:
|
||||||
self.current_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
self.current_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
||||||
self.current_frame.pack(expand=True)
|
self.current_frame.pack(expand=True)
|
||||||
|
|
||||||
# Заголовок
|
|
||||||
title = tk.Label(
|
title = tk.Label(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Авторизация",
|
text="Авторизация",
|
||||||
|
|
@ -73,19 +73,16 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
title.pack(pady=50)
|
title.pack(pady=50)
|
||||||
|
|
||||||
# Логин
|
|
||||||
login_label = tk.Label(self.current_frame, text="Логин:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
login_label = tk.Label(self.current_frame, text="Логин:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
||||||
login_label.pack()
|
login_label.pack()
|
||||||
self.login_entry = tk.Entry(self.current_frame, font=FONT)
|
self.login_entry = tk.Entry(self.current_frame, font=FONT)
|
||||||
self.login_entry.pack(pady=10)
|
self.login_entry.pack(pady=10)
|
||||||
|
|
||||||
# Пароль
|
|
||||||
password_label = tk.Label(self.current_frame, text="Пароль:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
password_label = tk.Label(self.current_frame, text="Пароль:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
||||||
password_label.pack()
|
password_label.pack()
|
||||||
self.password_entry = tk.Entry(self.current_frame, show="*", font=FONT)
|
self.password_entry = tk.Entry(self.current_frame, show="*", font=FONT)
|
||||||
self.password_entry.pack(pady=10)
|
self.password_entry.pack(pady=10)
|
||||||
|
|
||||||
# Кнопка "Показать пароль"
|
|
||||||
show_password_button = tk.Button(
|
show_password_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Показать пароль",
|
text="Показать пароль",
|
||||||
|
|
@ -96,7 +93,6 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
show_password_button.pack(pady=10)
|
show_password_button.pack(pady=10)
|
||||||
|
|
||||||
# Кнопка "Войти"
|
|
||||||
login_button = tk.Button(
|
login_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Войти",
|
text="Войти",
|
||||||
|
|
@ -107,7 +103,6 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
login_button.pack(pady=20)
|
login_button.pack(pady=20)
|
||||||
|
|
||||||
# Кнопка "Вернуться на главную"
|
|
||||||
back_button = tk.Button(
|
back_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Вернуться на главную",
|
text="Вернуться на главную",
|
||||||
|
|
@ -132,17 +127,21 @@ class DogAcademyApp:
|
||||||
|
|
||||||
if login == ADMIN_LOGIN and password == ADMIN_PASSWORD:
|
if login == ADMIN_LOGIN and password == ADMIN_PASSWORD:
|
||||||
messagebox.showinfo("Успех", "Вы успешно авторизованы как администратор!")
|
messagebox.showinfo("Успех", "Вы успешно авторизованы как администратор!")
|
||||||
self.show_admin_panel() # Переходим к админ-панели
|
self.user_id = None # Администратору не нужен user_id
|
||||||
elif check_user(login, password):
|
self.show_admin_panel()
|
||||||
messagebox.showinfo("Успех", "Вы успешно авторизованы!")
|
|
||||||
self.show_user_dashboard() # Переходим к панели пользователя
|
|
||||||
else:
|
else:
|
||||||
messagebox.showerror("Ошибка", "Неверные данные. Попробуйте снова.")
|
success, user_id = login_user(login, password)
|
||||||
|
if success:
|
||||||
|
messagebox.showinfo("Успех", "Вы успешно авторизованы!")
|
||||||
|
self.user_id = user_id # Сохраняем user_id
|
||||||
|
self.show_user_dashboard()
|
||||||
|
else:
|
||||||
|
messagebox.showerror("Ошибка", "Неверный логин или пароль.")
|
||||||
|
|
||||||
def show_admin_panel(self):
|
def show_admin_panel(self):
|
||||||
"""Отображение интерфейса администратора."""
|
"""Отображение интерфейса администратора."""
|
||||||
self.clear_frame()
|
self.clear_frame()
|
||||||
AdminApp(self.root) # Создаем экземпляр админ-панели
|
AdminApp(self.root)
|
||||||
|
|
||||||
def show_registration_screen(self):
|
def show_registration_screen(self):
|
||||||
"""Показать экран регистрации."""
|
"""Показать экран регистрации."""
|
||||||
|
|
@ -150,7 +149,6 @@ class DogAcademyApp:
|
||||||
self.current_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
self.current_frame = tk.Frame(self.root, bg=BACKGROUND_COLOR)
|
||||||
self.current_frame.pack(expand=True)
|
self.current_frame.pack(expand=True)
|
||||||
|
|
||||||
# Заголовок
|
|
||||||
title = tk.Label(
|
title = tk.Label(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Регистрация",
|
text="Регистрация",
|
||||||
|
|
@ -160,36 +158,21 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
title.pack(pady=50)
|
title.pack(pady=50)
|
||||||
|
|
||||||
# Логин
|
|
||||||
login_label = tk.Label(self.current_frame, text="Логин:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
login_label = tk.Label(self.current_frame, text="Логин:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
||||||
login_label.pack()
|
login_label.pack()
|
||||||
self.reg_login_entry = tk.Entry(self.current_frame, font=FONT)
|
self.reg_login_entry = tk.Entry(self.current_frame, font=FONT)
|
||||||
self.reg_login_entry.pack(pady=10)
|
self.reg_login_entry.pack(pady=10)
|
||||||
|
|
||||||
# Пароль
|
|
||||||
password_label = tk.Label(self.current_frame, text="Пароль:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
password_label = tk.Label(self.current_frame, text="Пароль:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
||||||
password_label.pack()
|
password_label.pack()
|
||||||
self.reg_password_entry = tk.Entry(self.current_frame, show="*", font=FONT)
|
self.reg_password_entry = tk.Entry(self.current_frame, show="*", font=FONT)
|
||||||
self.reg_password_entry.pack(pady=10)
|
self.reg_password_entry.pack(pady=10)
|
||||||
|
|
||||||
# Кнопка "Показать пароль"
|
|
||||||
show_password_button = tk.Button(
|
|
||||||
self.current_frame,
|
|
||||||
text="Показать пароль",
|
|
||||||
bg=BUTTON_COLOR,
|
|
||||||
fg=BUTTON_TEXT_COLOR,
|
|
||||||
font=FONT,
|
|
||||||
command=self.toggle_registration_password,
|
|
||||||
)
|
|
||||||
show_password_button.pack(pady=10)
|
|
||||||
|
|
||||||
# Никнейм
|
|
||||||
username_label = tk.Label(self.current_frame, text="Никнейм:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
username_label = tk.Label(self.current_frame, text="Никнейм:", bg=BACKGROUND_COLOR, fg=PRIMARY_COLOR, font=FONT)
|
||||||
username_label.pack()
|
username_label.pack()
|
||||||
self.username_entry = tk.Entry(self.current_frame, font=FONT)
|
self.username_entry = tk.Entry(self.current_frame, font=FONT)
|
||||||
self.username_entry.pack(pady=10)
|
self.username_entry.pack(pady=10)
|
||||||
|
|
||||||
# Кнопка "Зарегистрироваться"
|
|
||||||
register_button = tk.Button(
|
register_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Зарегистрироваться",
|
text="Зарегистрироваться",
|
||||||
|
|
@ -200,7 +183,6 @@ class DogAcademyApp:
|
||||||
)
|
)
|
||||||
register_button.pack(pady=20)
|
register_button.pack(pady=20)
|
||||||
|
|
||||||
# Кнопка "Вернуться на главную"
|
|
||||||
back_button = tk.Button(
|
back_button = tk.Button(
|
||||||
self.current_frame,
|
self.current_frame,
|
||||||
text="Вернуться на главную",
|
text="Вернуться на главную",
|
||||||
|
|
@ -222,19 +204,19 @@ class DogAcademyApp:
|
||||||
"""Регистрация нового пользователя."""
|
"""Регистрация нового пользователя."""
|
||||||
login = self.reg_login_entry.get()
|
login = self.reg_login_entry.get()
|
||||||
password = self.reg_password_entry.get()
|
password = self.reg_password_entry.get()
|
||||||
username = self.username_entry.get() # Получаем имя пользователя
|
username = self.username_entry.get()
|
||||||
|
|
||||||
if login and password and username:
|
if login and password and username:
|
||||||
success, message = create_user(login, password, username) # Передаем имя пользователя
|
success, message = create_user(login, password, username)
|
||||||
if success:
|
if success:
|
||||||
messagebox.showinfo("Успех", message)
|
messagebox.showinfo("Успех", message)
|
||||||
self.show_login_screen()
|
self.show_login_screen()
|
||||||
else:
|
else:
|
||||||
messagebox.showerror("Ошибка", message)
|
messagebox.showerror("Ошибка", message)
|
||||||
else:
|
else:
|
||||||
messagebox.showerror("Ошибка", "Пожалуйста, заполните все поля.")
|
messagebox.showerror("Ошибка", "Заполните все поля.")
|
||||||
|
|
||||||
def show_user_dashboard(self):
|
def show_user_dashboard(self):
|
||||||
|
"""Переход к пользовательскому интерфейсу."""
|
||||||
self.clear_frame()
|
self.clear_frame()
|
||||||
"""Перейти к главному меню пользователя после авторизации."""
|
UserApp(self.root, self.user_id)
|
||||||
UserApp(self.root)
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from tkinter import messagebox
|
||||||
|
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
import random
|
import random
|
||||||
import logging
|
import logging
|
||||||
|
from database.db_events import get_user_by_id, get_user_progress, save_progress
|
||||||
from database.db_events import get_user_by_id
|
|
||||||
from database.info.GameSessions_table import save_game_session
|
from database.info.GameSessions_table import save_game_session
|
||||||
from src.user_functions.game_logs import setup_logging
|
from src.user_functions.game_logs import setup_logging
|
||||||
from config import DOG_CHARACTERS, DONE, BONE, BACKGROUND_GAME
|
from config import DOG_CHARACTERS, DONE, BONE, BACKGROUND_GAME
|
||||||
|
|
@ -12,14 +13,22 @@ from src.utils import clear_frame
|
||||||
# Настройка логирования
|
# Настройка логирования
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
|
user = get_user_by_id(user_id=1)
|
||||||
|
if user:
|
||||||
|
print(f"Данные пользователя: {user}")
|
||||||
|
else:
|
||||||
|
print("Пользователь не найден")
|
||||||
|
|
||||||
class GameUI:
|
class GameUI:
|
||||||
def __init__(self, root, user_id, return_to_main_menu_callback):
|
def __init__(self, root, user_id, return_to_main_menu_callback):
|
||||||
|
if not user_id:
|
||||||
|
raise ValueError("user_id отсутствует при инициализации GameUI!")
|
||||||
|
|
||||||
self.root = root
|
self.root = root
|
||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
self.return_to_main_menu_callback = return_to_main_menu_callback
|
self.return_to_main_menu_callback = return_to_main_menu_callback
|
||||||
self.selected_dog = None
|
self.selected_dog = None
|
||||||
self.current_level = 1
|
self.current_level = 1
|
||||||
self.max_unlocked_level = 1
|
|
||||||
self.completed_levels = set()
|
self.completed_levels = set()
|
||||||
|
|
||||||
self.total_bones = 0
|
self.total_bones = 0
|
||||||
|
|
@ -28,6 +37,11 @@ class GameUI:
|
||||||
self.bones_positions = []
|
self.bones_positions = []
|
||||||
self.max_bones_per_level = 10
|
self.max_bones_per_level = 10
|
||||||
self.steps_taken = 0
|
self.steps_taken = 0
|
||||||
|
self.user_data = get_user_by_id(self.user_id)
|
||||||
|
|
||||||
|
# Получаем прогресс пользователя
|
||||||
|
self.user_progress = get_user_progress(user_id)
|
||||||
|
self.max_unlocked_level = max([session.level for session in self.user_progress]) if self.user_progress else 1
|
||||||
|
|
||||||
# Изображения
|
# Изображения
|
||||||
self.done_image = ImageTk.PhotoImage(Image.open(DONE).resize((50, 50), Image.Resampling.LANCZOS))
|
self.done_image = ImageTk.PhotoImage(Image.open(DONE).resize((50, 50), Image.Resampling.LANCZOS))
|
||||||
|
|
@ -45,7 +59,7 @@ class GameUI:
|
||||||
# Флаги
|
# Флаги
|
||||||
self.is_pause_menu_open = False
|
self.is_pause_menu_open = False
|
||||||
self.is_victory_screen_open = False
|
self.is_victory_screen_open = False
|
||||||
self.is_game_active = False # Для контроля состояния игры
|
self.is_game_active = False
|
||||||
|
|
||||||
# Привязка клавиш
|
# Привязка клавиш
|
||||||
self.root.bind("<KeyPress-w>", self.move_up)
|
self.root.bind("<KeyPress-w>", self.move_up)
|
||||||
|
|
@ -57,6 +71,15 @@ class GameUI:
|
||||||
# Отображение начального экрана
|
# Отображение начального экрана
|
||||||
self.show_dog_selection()
|
self.show_dog_selection()
|
||||||
|
|
||||||
|
# Инициализация интерфейса
|
||||||
|
self.create_main_menu_button()
|
||||||
|
|
||||||
|
if self.user_data:
|
||||||
|
self.max_unlocked_level = self.user_data.level or 1
|
||||||
|
self.total_bones = sum([session.score for session in get_user_progress(self.user_id)])
|
||||||
|
else:
|
||||||
|
logging.warning("Данные пользователя не найдены")
|
||||||
|
|
||||||
def create_background(self):
|
def create_background(self):
|
||||||
"""Создаёт фон для игры."""
|
"""Создаёт фон для игры."""
|
||||||
try:
|
try:
|
||||||
|
|
@ -122,62 +145,99 @@ class GameUI:
|
||||||
clear_frame(self.root)
|
clear_frame(self.root)
|
||||||
self.create_background()
|
self.create_background()
|
||||||
|
|
||||||
tk.Label(
|
tk.Label(self.root, text="Выберите уровень", font=("Comic Sans MS", 24), bg="#E5E5E5").pack(pady=20)
|
||||||
self.root, text="Выберите уровень", font=("Comic Sans MS", 24), bg="#E5E5E5"
|
|
||||||
).pack(pady=20)
|
|
||||||
|
|
||||||
level_frame = tk.Frame(self.root, bg="#E5E5E5")
|
level_frame = tk.Frame(self.root, bg="#E5E5E5")
|
||||||
level_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
|
level_frame.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
|
||||||
|
|
||||||
for level in range(1, 6):
|
progress = get_user_progress(self.user_id)
|
||||||
|
completed_levels = {session.level for session in progress if session.score > 0}
|
||||||
|
self.max_unlocked_level = max(completed_levels) if completed_levels else 1
|
||||||
|
|
||||||
|
for level in range(1, 101):
|
||||||
|
color = (
|
||||||
|
"#4CAF50" if level in completed_levels else
|
||||||
|
"#FFEB3B" if level <= self.max_unlocked_level else
|
||||||
|
"#A9A9A9"
|
||||||
|
)
|
||||||
|
state = tk.NORMAL if level <= self.max_unlocked_level else tk.DISABLED
|
||||||
|
|
||||||
button = tk.Button(
|
button = tk.Button(
|
||||||
level_frame,
|
level_frame,
|
||||||
text=f"Уровень {level}",
|
text=f"Уровень {level}",
|
||||||
state=tk.NORMAL if level <= self.max_unlocked_level else tk.DISABLED,
|
bg=color,
|
||||||
font=("Comic Sans MS", 20),
|
state=state,
|
||||||
bg="#4CAF50" if level <= self.max_unlocked_level else "#A9A9A9",
|
font=("Comic Sans MS", 14),
|
||||||
width=15,
|
command=lambda l=level: self.handle_level_selection(l)
|
||||||
height=2,
|
|
||||||
command=lambda l=level: self.start_level(l),
|
|
||||||
)
|
)
|
||||||
button.pack(pady=10)
|
button.grid(row=(level - 1) // 10, column=(level - 1) % 10, padx=5, pady=5)
|
||||||
|
|
||||||
tk.Button(
|
tk.Button(
|
||||||
self.root,
|
self.root,
|
||||||
text="Вернуться",
|
text="Вернуться",
|
||||||
font=("Comic Sans MS", 16),
|
font=("Comic Sans MS", 16),
|
||||||
bg="lightgreen",
|
bg="lightgreen",
|
||||||
command=self.show_dog_selection,
|
command=self.show_dog_selection
|
||||||
).place(relx=0.5, rely=0.9, anchor=tk.CENTER)
|
).place(relx=0.5, rely=0.9, anchor=tk.CENTER)
|
||||||
|
|
||||||
|
def handle_level_selection(self, level):
|
||||||
|
"""Обработка выбора уровня."""
|
||||||
|
if level in self.completed_levels:
|
||||||
|
if messagebox.askyesno("Повторить уровень", f"Вы уже прошли уровень {level}. Хотите пройти его заново?"):
|
||||||
|
self.current_level = level
|
||||||
|
self.total_bones = 0
|
||||||
|
self.steps_taken = 0
|
||||||
|
self.start_game()
|
||||||
|
elif level <= self.max_unlocked_level:
|
||||||
|
self.current_level = level
|
||||||
|
self.countdown()
|
||||||
|
else:
|
||||||
|
messagebox.showinfo("Недоступно", "Пройдите предыдущие уровни, чтобы разблокировать этот.")
|
||||||
|
|
||||||
def start_level(self, level):
|
def start_level(self, level):
|
||||||
"""Начало выбранного уровня."""
|
"""Запуск уровня."""
|
||||||
self.current_level = level
|
if level in self.completed_levels:
|
||||||
self.countdown()
|
if messagebox.askyesno("Повторить уровень", f"Вы уже прошли уровень {level}. Хотите пройти его заново?"):
|
||||||
|
self.current_level = level
|
||||||
|
self.total_bones = 0
|
||||||
|
self.steps_taken = 0
|
||||||
|
self.start_game() # Запускаем уровень заново
|
||||||
|
return
|
||||||
|
elif level <= self.max_unlocked_level:
|
||||||
|
self.current_level = level
|
||||||
|
self.countdown() # Запуск обратного отсчёта перед началом игры
|
||||||
|
else:
|
||||||
|
messagebox.showinfo("Недоступно", "Этот уровень заблокирован.")
|
||||||
|
|
||||||
def countdown(self):
|
def countdown(self):
|
||||||
"""Обратный отсчёт перед началом уровня."""
|
"""Обратный отсчёт перед началом уровня с анимацией."""
|
||||||
clear_frame(self.root)
|
clear_frame(self.root) # Очищаем экран
|
||||||
countdown_label = tk.Label(
|
countdown_label = tk.Label(
|
||||||
self.root, text="", font=("Comic Sans MS", 30), bg="#E5E5E5"
|
self.root, text="Готовьтесь!", font=("Comic Sans MS", 40), bg="#E5E5E5"
|
||||||
)
|
)
|
||||||
countdown_label.pack(expand=True)
|
countdown_label.pack(expand=True)
|
||||||
|
|
||||||
for i in range(3, 0, -1):
|
def update_countdown(counter):
|
||||||
countdown_label.config(text=f"{i}...")
|
if counter > 0:
|
||||||
self.root.update()
|
countdown_label.config(text=f"{counter}", fg=random.choice(["red", "green", "blue"]))
|
||||||
self.root.after(1000)
|
self.root.update()
|
||||||
|
self.root.after(1000, update_countdown, counter - 1)
|
||||||
|
else:
|
||||||
|
countdown_label.config(text="Вперёд!", fg="orange")
|
||||||
|
self.root.update()
|
||||||
|
self.root.after(1000, self.start_game)
|
||||||
|
|
||||||
self.start_game()
|
update_countdown(3) # Старт обратного отсчёта с 3 секунд
|
||||||
|
|
||||||
def start_game(self):
|
def start_game(self):
|
||||||
"""Запуск игрового процесса."""
|
"""Запуск игрового процесса."""
|
||||||
clear_frame(self.root)
|
logging.info(f"Игра начата на уровне {self.current_level}")
|
||||||
|
clear_frame(self.root) # Очищаем экран
|
||||||
self.map_canvas = tk.Canvas(self.root, width=1920, height=1080, bg="#E5E5E5")
|
self.map_canvas = tk.Canvas(self.root, width=1920, height=1080, bg="#E5E5E5")
|
||||||
self.map_canvas.pack()
|
self.map_canvas.pack()
|
||||||
|
|
||||||
self.draw_grid()
|
self.draw_grid()
|
||||||
self.bones_positions = self.generate_bones()
|
self.bones_positions = self.generate_bones() # Генерация новых косточек
|
||||||
|
|
||||||
# Прямоугольник и изображение косточек (создаются один раз)
|
# Прямоугольник и изображение косточек (создаются один раз)
|
||||||
self.rect_x1, self.rect_y1 = 1600, 0
|
self.rect_x1, self.rect_y1 = 1600, 0
|
||||||
|
|
@ -189,11 +249,6 @@ class GameUI:
|
||||||
self.bones_label = tk.Label(self.root, text=f"{self.total_bones}", font=("Comic Sans MS", 16), bg="#CCCCCC")
|
self.bones_label = tk.Label(self.root, text=f"{self.total_bones}", font=("Comic Sans MS", 16), bg="#CCCCCC")
|
||||||
self.bones_label.place(x=1700, y=30)
|
self.bones_label.place(x=1700, y=30)
|
||||||
|
|
||||||
# В методе update_map (не удаляем прямоугольник):
|
|
||||||
self.map_canvas.delete("all") # Удаляем только динамичные объекты карты (косточки, собаку)
|
|
||||||
self.draw_grid()
|
|
||||||
self.collect_bones()
|
|
||||||
|
|
||||||
self.update_map() # Начальное обновление карты
|
self.update_map() # Начальное обновление карты
|
||||||
|
|
||||||
def draw_grid(self):
|
def draw_grid(self):
|
||||||
|
|
@ -204,11 +259,15 @@ class GameUI:
|
||||||
self.map_canvas.create_line(0, y, 1920, y, fill="lightgray")
|
self.map_canvas.create_line(0, y, 1920, y, fill="lightgray")
|
||||||
|
|
||||||
def generate_bones(self):
|
def generate_bones(self):
|
||||||
"""Генерация косточек на карте."""
|
"""Генерация косточек на карте с увеличением их количества по геометрической прогрессии."""
|
||||||
return [
|
# Количество косточек увеличивается по геометрической прогрессии
|
||||||
|
bones_count = min(5, 10 * (2 ** (self.current_level - 1))) # Ограничение в 5 косточек на уровне
|
||||||
|
bones = [
|
||||||
(random.randint(0, self.cols - 1), random.randint(0, self.rows - 1))
|
(random.randint(0, self.cols - 1), random.randint(0, self.rows - 1))
|
||||||
for _ in range(2)
|
for _ in range(bones_count)
|
||||||
]
|
]
|
||||||
|
logging.info(f"Генерация косточек: {bones}") # Логируем координаты косточек
|
||||||
|
return bones
|
||||||
|
|
||||||
def collect_bones(self):
|
def collect_bones(self):
|
||||||
"""Проверка и сбор косточек."""
|
"""Проверка и сбор косточек."""
|
||||||
|
|
@ -216,10 +275,19 @@ class GameUI:
|
||||||
if self.dog_position == [bone[0], bone[1]]:
|
if self.dog_position == [bone[0], bone[1]]:
|
||||||
self.bones_positions.remove(bone)
|
self.bones_positions.remove(bone)
|
||||||
self.total_bones += 1
|
self.total_bones += 1
|
||||||
|
|
||||||
|
# Сохранение прогресса
|
||||||
|
save_progress(self.user_id, self.current_level, self.total_bones, self.steps_taken, 100, 0, 0)
|
||||||
|
|
||||||
self.bones_label.config(text=f"{self.total_bones}")
|
self.bones_label.config(text=f"{self.total_bones}")
|
||||||
|
|
||||||
|
# Условие для победы
|
||||||
|
target_bones = 10 * (2 ** (self.current_level - 1)) # Модифицируем целевое количество косточек
|
||||||
|
if self.total_bones >= target_bones and not self.is_victory_screen_open:
|
||||||
|
self.show_victory_screen() # Показываем экран победы
|
||||||
|
|
||||||
if self.steps_taken % 10 == 0 and len(self.bones_positions) < self.max_bones_per_level:
|
if self.steps_taken % 10 == 0 and len(self.bones_positions) < self.max_bones_per_level:
|
||||||
self.bones_positions.extend(self.generate_bones())
|
self.bones_positions.extend(self.generate_bones()) # Генерация новых косточек, если нужно
|
||||||
|
|
||||||
def move_up(self, event):
|
def move_up(self, event):
|
||||||
"""Движение вверх."""
|
"""Движение вверх."""
|
||||||
|
|
@ -299,6 +367,7 @@ class GameUI:
|
||||||
if self.is_pause_menu_open:
|
if self.is_pause_menu_open:
|
||||||
self.pause_window.destroy() # Закрываем окно паузы
|
self.pause_window.destroy() # Закрываем окно паузы
|
||||||
self.is_pause_menu_open = False # Сбрасываем флаг
|
self.is_pause_menu_open = False # Сбрасываем флаг
|
||||||
|
self.is_game_active = True # Возвращаем игру в активное состояние
|
||||||
|
|
||||||
def save_and_exit(self):
|
def save_and_exit(self):
|
||||||
"""Сохранение данных и выход в главное меню."""
|
"""Сохранение данных и выход в главное меню."""
|
||||||
|
|
@ -313,8 +382,8 @@ class GameUI:
|
||||||
if self.is_victory_screen_open: # Отключаем обновления, если окно победы открыто
|
if self.is_victory_screen_open: # Отключаем обновления, если окно победы открыто
|
||||||
return
|
return
|
||||||
|
|
||||||
self.map_canvas.delete("all")
|
self.map_canvas.delete("all") # Удаляем старые объекты карты
|
||||||
self.draw_grid()
|
self.draw_grid() # Перерисовываем сетку
|
||||||
|
|
||||||
# Отображение косточек
|
# Отображение косточек
|
||||||
for x, y in self.bones_positions:
|
for x, y in self.bones_positions:
|
||||||
|
|
@ -325,14 +394,17 @@ class GameUI:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Отображение собаки
|
# Отображение собаки
|
||||||
dog_image = Image.open(DOG_CHARACTERS[self.selected_dog]["image"]).resize((self.grid_size, self.grid_size),
|
if self.selected_dog:
|
||||||
Image.Resampling.LANCZOS)
|
dog_image = Image.open(DOG_CHARACTERS[self.selected_dog]["image"]).resize(
|
||||||
self.dog_photo = ImageTk.PhotoImage(dog_image)
|
(self.grid_size, self.grid_size),
|
||||||
self.map_canvas.create_image(
|
Image.Resampling.LANCZOS
|
||||||
self.dog_position[0] * self.grid_size + self.grid_size // 2,
|
)
|
||||||
self.dog_position[1] * self.grid_size + self.grid_size // 2,
|
self.dog_photo = ImageTk.PhotoImage(dog_image)
|
||||||
image=self.dog_photo
|
self.map_canvas.create_image(
|
||||||
)
|
self.dog_position[0] * self.grid_size + self.grid_size // 2,
|
||||||
|
self.dog_position[1] * self.grid_size + self.grid_size // 2,
|
||||||
|
image=self.dog_photo
|
||||||
|
)
|
||||||
|
|
||||||
# Проверка сбора косточек
|
# Проверка сбора косточек
|
||||||
self.collect_bones()
|
self.collect_bones()
|
||||||
|
|
@ -344,17 +416,17 @@ class GameUI:
|
||||||
|
|
||||||
def show_victory_screen(self):
|
def show_victory_screen(self):
|
||||||
"""Экран победы."""
|
"""Экран победы."""
|
||||||
if self.is_victory_screen_open: # Проверяем, чтобы не было второго окна
|
if self.is_victory_screen_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.is_victory_screen_open = True # Устанавливаем флаг
|
self.is_victory_screen_open = True
|
||||||
self.is_game_active = False # Останавливаем движение
|
self.is_game_active = False
|
||||||
|
|
||||||
victory_window = tk.Toplevel(self.root)
|
victory_window = tk.Toplevel(self.root)
|
||||||
victory_window.title("Ура, победа!")
|
victory_window.title("Ура, победа!")
|
||||||
victory_window.geometry("800x600")
|
victory_window.geometry("800x600")
|
||||||
victory_window.configure(bg="#E5E5E5")
|
victory_window.configure(bg="#E5E5E5")
|
||||||
victory_window.grab_set() # Блокируем взаимодействие с основным окном
|
victory_window.grab_set()
|
||||||
|
|
||||||
# Изображение собаки
|
# Изображение собаки
|
||||||
dog_image = Image.open(DOG_CHARACTERS[self.selected_dog]["image"]).resize((200, 200), Image.Resampling.LANCZOS)
|
dog_image = Image.open(DOG_CHARACTERS[self.selected_dog]["image"]).resize((200, 200), Image.Resampling.LANCZOS)
|
||||||
|
|
@ -364,26 +436,26 @@ class GameUI:
|
||||||
dog_label.place(x=50, y=50)
|
dog_label.place(x=50, y=50)
|
||||||
|
|
||||||
# Текст победы
|
# Текст победы
|
||||||
victory_label = tk.Label(
|
victory_label = tk.Label(victory_window, text="Ура, победа!", font=("Comic Sans MS", 24), bg="#E5E5E5")
|
||||||
victory_window, text="Ура, победа!", font=("Comic Sans MS", 24), bg="#E5E5E5"
|
|
||||||
)
|
|
||||||
victory_label.pack(pady=20)
|
victory_label.pack(pady=20)
|
||||||
|
|
||||||
# Характеристики собаки
|
# Характеристики собаки
|
||||||
dog_info = f"Порода: {self.selected_dog}"
|
dog_info = f"Порода: {self.selected_dog}"
|
||||||
info_label = tk.Label(
|
info_label = tk.Label(victory_window, text=dog_info, font=("Comic Sans MS", 16), bg="#E5E5E5")
|
||||||
victory_window, text=dog_info, font=("Comic Sans MS", 16), bg="#E5E5E5"
|
|
||||||
)
|
|
||||||
info_label.place(x=300, y=100)
|
info_label.place(x=300, y=100)
|
||||||
|
|
||||||
# Собрано косточек
|
# Собрано косточек
|
||||||
target_bones = 10 * (2 ** (self.current_level - 1)) # Геометрическая прогрессия для косточек
|
target_bones = 10 * (2 ** (self.current_level - 1)) # Геометрическая прогрессия
|
||||||
collected_info = f"Собрано: {self.total_bones} из {target_bones}"
|
collected_info = f"Собрано: {self.total_bones} из {target_bones}"
|
||||||
score_label = tk.Label(
|
score_label = tk.Label(victory_window, text=collected_info, font=("Comic Sans MS", 16), bg="#E5E5E5")
|
||||||
victory_window, text=collected_info, font=("Comic Sans MS", 16), bg="#E5E5E5"
|
|
||||||
)
|
|
||||||
score_label.place(x=300, y=150)
|
score_label.place(x=300, y=150)
|
||||||
|
|
||||||
|
# Никнейм игрока
|
||||||
|
user_info = get_user_by_id(self.user_id)
|
||||||
|
username_label = tk.Label(victory_window, text=f"Никнейм: {user_info.username}", font=("Comic Sans MS", 16),
|
||||||
|
bg="#E5E5E5")
|
||||||
|
username_label.place(x=300, y=200)
|
||||||
|
|
||||||
# Кнопка перехода на следующий уровень
|
# Кнопка перехода на следующий уровень
|
||||||
next_level_button = tk.Button(
|
next_level_button = tk.Button(
|
||||||
victory_window,
|
victory_window,
|
||||||
|
|
@ -404,29 +476,91 @@ class GameUI:
|
||||||
)
|
)
|
||||||
exit_button.place(relx=0.5, rely=0.8, anchor=tk.CENTER)
|
exit_button.place(relx=0.5, rely=0.8, anchor=tk.CENTER)
|
||||||
|
|
||||||
|
def close_victory_window(self):
|
||||||
|
"""Закрытие окна победы и сброс флага."""
|
||||||
|
self.is_victory_screen_open = False
|
||||||
|
self.is_game_active = True
|
||||||
|
self.return_to_main_menu() # Возвращаем в меню
|
||||||
|
|
||||||
def return_to_main_menu(self):
|
def return_to_main_menu(self):
|
||||||
"""Возврат в главное меню."""
|
"""Возврат в главное меню."""
|
||||||
self.is_victory_screen_open = False # Сбрасываем флаг окна победы
|
if self.is_pause_menu_open:
|
||||||
self.return_to_main_menu_callback() # Вызываем колбэк для возврата
|
self.pause_window.destroy() # Закрываем окно паузы
|
||||||
|
self.is_pause_menu_open = False # Сбрасываем флаг
|
||||||
|
|
||||||
|
clear_frame(self.root) # Очищаем текущий экран
|
||||||
|
self.show_main_menu() # Переходим в главное меню
|
||||||
|
|
||||||
def start_next_level(self):
|
def start_next_level(self):
|
||||||
"""Переход на следующий уровень."""
|
"""Переход на следующий уровень."""
|
||||||
self.save_progress() # Сохраняем прогресс перед переходом на следующий уровень
|
try:
|
||||||
|
# Сохранение текущего прогресса
|
||||||
|
self.save_progress()
|
||||||
|
|
||||||
# Переход на следующий уровень
|
# Переход на следующий уровень
|
||||||
self.current_level += 1
|
self.current_level += 1
|
||||||
self.total_bones = 0 # Сбрасываем счётчик косточек
|
self.max_unlocked_level = max(self.max_unlocked_level, self.current_level)
|
||||||
self.start_level(self.current_level)
|
|
||||||
|
# Сброс состояния игры: собака возвращается в начальную позицию
|
||||||
|
self.total_bones = 0
|
||||||
|
self.steps_taken = 0
|
||||||
|
self.dog_position = [1, 1] # Начальная позиция собаки
|
||||||
|
self.bones_positions = [] # Очищаем текущие косточки
|
||||||
|
|
||||||
|
# Генерация новых косточек с увеличением их количества по геометрической прогрессии
|
||||||
|
self.bones_positions = self.generate_bones()
|
||||||
|
|
||||||
|
# Начинаем новый уровень с обратным отсчётом
|
||||||
|
self.countdown() # Запуск обратного отсчёта
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Ошибка при переходе на следующий уровень: {e}")
|
||||||
|
|
||||||
def save_progress(self):
|
def save_progress(self):
|
||||||
"""Сохранение игрового процесса в таблицу GameSessions."""
|
"""Сохранение игрового процесса в таблицу GameSessions."""
|
||||||
from datetime import datetime
|
if not self.user_id:
|
||||||
|
logging.error("Ошибка: user_id равен None. Запись невозможна.")
|
||||||
|
return
|
||||||
|
|
||||||
# Получаем время начала и окончания уровня
|
try:
|
||||||
duration = self.steps_taken # Время можно рассчитать по шагам или в реальном времени
|
# Рассчитываем длительность уровня и текущий счет
|
||||||
score = self.total_bones # Количество собранных косточек
|
duration = self.steps_taken
|
||||||
|
score = self.total_bones
|
||||||
|
|
||||||
# Сохранение прогресса в базу данных
|
# Логирование
|
||||||
save_game_session(self.user_id, self.current_level, score, duration, 100, 0, 0)
|
logging.info(
|
||||||
|
f"Сохранение сессии: user_id={self.user_id}, level={self.current_level}, score={score}, duration={duration}")
|
||||||
|
|
||||||
|
# Сохранение данных в базу
|
||||||
|
save_game_session(
|
||||||
|
user_id=self.user_id,
|
||||||
|
level=self.current_level,
|
||||||
|
score=self.total_bones,
|
||||||
|
steps=self.steps_taken, # Используйте количество шагов или время
|
||||||
|
duration=self.steps_taken, # Если 'steps' отражают время, используйте их
|
||||||
|
health=100, # Примерное значение
|
||||||
|
hunger=0, # Примерное значение
|
||||||
|
sleepiness=0 # Примерное значение
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info("Прогресс успешно сохранен.")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Ошибка при сохранении прогресса: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
def create_main_menu_button(self):
|
||||||
|
"""Создаём кнопку для возврата в главное меню."""
|
||||||
|
main_menu_button = tk.Button(
|
||||||
|
self.root,
|
||||||
|
text="Главное меню",
|
||||||
|
font=("Comic Sans MS", 16),
|
||||||
|
bg="lightgreen",
|
||||||
|
command=self.show_main_menu, # Вызов нового метода
|
||||||
|
)
|
||||||
|
main_menu_button.pack()
|
||||||
|
|
||||||
|
def show_main_menu(self):
|
||||||
|
"""Переход в главное меню."""
|
||||||
|
self.is_game_active = False # Останавливаем игру, если мы возвращаемся в меню
|
||||||
|
clear_frame(self.root) # Очищаем текущий экран
|
||||||
|
self.return_to_main_menu_callback() # Вызов колбэка для возврата в главное меню
|
||||||
|
|
||||||
# Также можно сохранять дополнительные параметры, если необходимо (например, здоровье, голод, усталость)
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from functools import partial
|
||||||
from tkinter import messagebox, Canvas
|
from tkinter import messagebox, Canvas
|
||||||
from PIL import Image, ImageTk
|
from PIL import Image, ImageTk
|
||||||
import math
|
import math
|
||||||
|
|
@ -31,10 +32,11 @@ PLAY_BUTTON_RADIUS = 100 # Радиус кнопки "Играть"
|
||||||
|
|
||||||
|
|
||||||
class UserApp:
|
class UserApp:
|
||||||
def __init__(self, root, user_id=None):
|
def __init__(self, root, user_id):
|
||||||
|
"""Инициализация пользовательского интерфейса."""
|
||||||
self.root = root
|
self.root = root
|
||||||
self.user_id = user_id
|
self.user_id = user_id
|
||||||
self.root.configure(bg=BACKGROUND_COLOR)
|
self.root.configure(bg="#E5E5E5")
|
||||||
self.root.geometry("1920x1080")
|
self.root.geometry("1920x1080")
|
||||||
self.root.title("Собачья академия")
|
self.root.title("Собачья академия")
|
||||||
self.show_user_dashboard()
|
self.show_user_dashboard()
|
||||||
|
|
@ -151,8 +153,12 @@ class UserApp:
|
||||||
|
|
||||||
def show_profile(self):
|
def show_profile(self):
|
||||||
"""Показать экран профиля пользователя."""
|
"""Показать экран профиля пользователя."""
|
||||||
self.clear_frame()
|
try:
|
||||||
profile_ui(self.root, self.user_id, self) # Передаем сам объект self для доступа к show_user_dashboard
|
self.clear_frame()
|
||||||
|
profile_ui(self.root, self.user_id, self)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Ошибка при отображении профиля: {e}")
|
||||||
|
messagebox.showerror("Ошибка", "Не удалось открыть профиль.")
|
||||||
|
|
||||||
def clear_frame(self):
|
def clear_frame(self):
|
||||||
"""Очистить текущий экран."""
|
"""Очистить текущий экран."""
|
||||||
|
|
@ -160,16 +166,17 @@ class UserApp:
|
||||||
widget.destroy()
|
widget.destroy()
|
||||||
|
|
||||||
def play_game(self):
|
def play_game(self):
|
||||||
"""Переход к игровому интерфейсу."""
|
"""Запуск игры и передача колбэка для возврата в меню."""
|
||||||
print("Запуск игры...")
|
# Передаем метод через partial для корректной передачи self
|
||||||
|
return_to_main_menu = partial(self.return_to_main_menu)
|
||||||
|
GameUI(self.root, self.user_id, return_to_main_menu)
|
||||||
|
|
||||||
def return_to_main_menu():
|
def return_to_main_menu(self):
|
||||||
clear_frame(self.root)
|
"""Возврат в главное меню."""
|
||||||
self.show_user_dashboard() # Возврат в главное меню
|
self.clear_frame() # Очищаем экран перед переходом
|
||||||
|
self.show_user_dashboard() # Показываем главное меню
|
||||||
GameUI(self.root, self.user_id, return_to_main_menu) # Передаём колбэк для возврата
|
|
||||||
|
|
||||||
def exit_app(self):
|
def exit_app(self):
|
||||||
"""Подтверждение выхода из приложения."""
|
"""Подтверждение выхода из приложения."""
|
||||||
if messagebox.askyesno("Выход", "Вы уверены, что хотите выйти?"):
|
if messagebox.askyesno("Выход", "Вы уверены, что хотите выйти?"):
|
||||||
self.root.quit()
|
self.root.quit()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from src.utils import clear_frame
|
from src.utils import clear_frame
|
||||||
from database.db_events import get_user_progress
|
from database.db_events import get_user_progress, get_user_by_id
|
||||||
|
|
||||||
|
|
||||||
def profile_ui(root, user_id, user_app):
|
def profile_ui(root, user_id, user_app):
|
||||||
|
|
@ -10,12 +10,24 @@ def profile_ui(root, user_id, user_app):
|
||||||
frame = tk.Frame(root, bg="#f8e1e1")
|
frame = tk.Frame(root, bg="#f8e1e1")
|
||||||
frame.pack(fill=tk.BOTH, expand=True)
|
frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
tk.Label(frame, text="Профиль", font=("Comic Sans MS", 30), bg="#f8e1e1").pack(pady=20)
|
# Обновляем данные пользователя из базы
|
||||||
|
user = get_user_by_id(user_id)
|
||||||
|
if not user:
|
||||||
|
username = "Неизвестный пользователь"
|
||||||
|
levels_completed = 0
|
||||||
|
bones_collected = 0
|
||||||
|
else:
|
||||||
|
username = user.username
|
||||||
|
progress = get_user_progress(user_id)
|
||||||
|
levels_completed = len({session.level for session in progress if session.score > 0})
|
||||||
|
bones_collected = sum(session.score for session in progress)
|
||||||
|
|
||||||
# Получение прогресса пользователя из базы данных
|
tk.Label(
|
||||||
progress = get_user_progress(user_id)
|
frame,
|
||||||
levels_completed = len(progress) # Считаем количество уровней
|
text=f"Профиль: {username}",
|
||||||
bones_collected = sum([session.score for session in progress]) # Суммируем все собранные косточки
|
font=("Comic Sans MS", 30),
|
||||||
|
bg="#f8e1e1",
|
||||||
|
).pack(pady=20)
|
||||||
|
|
||||||
stats_text = f"Пройдено уровней: {levels_completed}\nСобрано косточек: {bones_collected}"
|
stats_text = f"Пройдено уровней: {levels_completed}\nСобрано косточек: {bones_collected}"
|
||||||
tk.Label(frame, text=stats_text, font=("Comic Sans MS", 20), bg="#f8e1e1").pack(pady=10)
|
tk.Label(frame, text=stats_text, font=("Comic Sans MS", 20), bg="#f8e1e1").pack(pady=10)
|
||||||
|
|
@ -24,7 +36,7 @@ def profile_ui(root, user_id, user_app):
|
||||||
back_button = tk.Button(
|
back_button = tk.Button(
|
||||||
frame,
|
frame,
|
||||||
text="Назад",
|
text="Назад",
|
||||||
command=lambda: [clear_frame(root), user_app.show_user_dashboard()], # Очистить экран и вернуться на главное меню
|
command=lambda: [clear_frame(root), user_app.show_user_dashboard()],
|
||||||
font=("Comic Sans MS", 20)
|
font=("Comic Sans MS", 20)
|
||||||
)
|
)
|
||||||
back_button.pack(pady=20)
|
back_button.pack(pady=20)
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,9 @@
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from src.utils import clear_frame
|
from src.utils import clear_frame
|
||||||
from database.db_events import save_progress
|
from database.db_events import save_progress
|
||||||
|
|
||||||
|
|
||||||
def start_game(root, user_id, dog_id):
|
|
||||||
"""Игровой процесс."""
|
|
||||||
clear_frame(root)
|
|
||||||
|
|
||||||
# Обратный отсчет
|
|
||||||
for i in range(3, 0, -1):
|
|
||||||
clear_frame(root)
|
|
||||||
tk.Label(root, text=f"{i}...", font=("Comic Sans MS", 30)).pack(expand=True)
|
|
||||||
root.update()
|
|
||||||
time.sleep(1) # Пауза между отсчетами
|
|
||||||
|
|
||||||
# Начало уровня
|
|
||||||
print("Начало уровня") # Для отладки
|
|
||||||
|
|
||||||
# Здесь подключается логика работы с картой и вопросами
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def handle_checkpoint(obstacle, current_score, root):
|
def handle_checkpoint(obstacle, current_score, root):
|
||||||
"""
|
"""
|
||||||
Обрабатывает чек-поинт (косточку).
|
Обрабатывает чек-поинт (косточку).
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
def setup_logging():
|
def setup_logging():
|
||||||
"""Настройка логирования в файл."""
|
"""Настройка логирования в файл."""
|
||||||
log_file = "logs/game.log"
|
log_dir = "logs"
|
||||||
if not os.path.exists(log_file):
|
if not os.path.exists(log_dir):
|
||||||
os.makedirs(log_file)
|
os.makedirs(log_dir) # Создание директории, если она не существует
|
||||||
|
|
||||||
|
log_file = os.path.join(log_dir, "game.log")
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
filename=log_file,
|
filename=log_file,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue