1419 lines
51 KiB
Python
1419 lines
51 KiB
Python
import os
|
||
import json
|
||
from datetime import datetime, timedelta
|
||
from flask import Flask, render_template, request, jsonify, session, redirect, url_for, flash
|
||
from flask_sqlalchemy import SQLAlchemy
|
||
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
||
from flask_socketio import SocketIO, emit, join_room, leave_room, send
|
||
from config import config
|
||
import random
|
||
import logging
|
||
from werkzeug.security import generate_password_hash, check_password_hash
|
||
|
||
# Настройка логирования
|
||
logging.basicConfig(level=logging.DEBUG)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Инициализация Flask
|
||
app = Flask(__name__)
|
||
app.config.from_object(config)
|
||
|
||
# Инициализация расширений
|
||
db = SQLAlchemy(app)
|
||
login_manager = LoginManager(app)
|
||
login_manager.login_view = 'login'
|
||
socketio = SocketIO(app, cors_allowed_origins="*")
|
||
|
||
|
||
# --- МОДЕЛИ БАЗЫ ДАННЫХ ---
|
||
|
||
class GameRoom(db.Model):
|
||
__tablename__ = 'game_rooms'
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
name = db.Column(db.String(100), nullable=False)
|
||
code = db.Column(db.String(10), unique=True, nullable=False)
|
||
creator_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
status = db.Column(db.String(20), default='waiting') # waiting, playing, finished
|
||
current_month = db.Column(db.Integer, default=1)
|
||
total_months = db.Column(db.Integer, default=12)
|
||
start_capital = db.Column(db.Integer, default=100000)
|
||
settings = db.Column(db.Text, default='{}') # JSON с настройками
|
||
|
||
# Связи - убираем lazy='dynamic' для players
|
||
players = db.relationship('GamePlayer', backref='room', lazy='select') # Изменено
|
||
game_state = db.relationship('GameState', backref='room', uselist=False)
|
||
|
||
|
||
class GamePlayer(db.Model):
|
||
__tablename__ = 'game_players'
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
|
||
room_id = db.Column(db.Integer, db.ForeignKey('game_rooms.id'))
|
||
joined_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
is_ready = db.Column(db.Boolean, default=False)
|
||
is_admin = db.Column(db.Boolean, default=False)
|
||
|
||
# Игровые данные
|
||
capital = db.Column(db.Float, default=100000)
|
||
ability = db.Column(db.String(50))
|
||
assets = db.Column(db.Text, default='[]') # JSON с активами
|
||
position = db.Column(db.Integer, default=0) # Позиция в рейтинге
|
||
|
||
|
||
class User(UserMixin, db.Model):
|
||
__tablename__ = 'users'
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
username = db.Column(db.String(64), unique=True, nullable=False)
|
||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||
password_hash = db.Column(db.String(256))
|
||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
# Статистика
|
||
total_games = db.Column(db.Integer, default=0)
|
||
games_won = db.Column(db.Integer, default=0)
|
||
total_earnings = db.Column(db.Float, default=0)
|
||
is_active = db.Column(db.Boolean, default=True)
|
||
|
||
# Связи
|
||
game_players = db.relationship('GamePlayer', backref='user', lazy='dynamic')
|
||
rooms_created = db.relationship('GameRoom', backref='creator', lazy='dynamic')
|
||
|
||
def set_password(self, password):
|
||
"""Установка хеша пароля"""
|
||
self.password_hash = generate_password_hash(password)
|
||
|
||
def check_password(self, password):
|
||
"""Проверка пароля"""
|
||
if not self.password_hash:
|
||
return False
|
||
return check_password_hash(self.password_hash, password)
|
||
|
||
def __repr__(self):
|
||
return f'<User {self.username}>'
|
||
|
||
|
||
class GameState(db.Model):
|
||
__tablename__ = 'game_states'
|
||
id = db.Column(db.Integer, primary_key=True)
|
||
room_id = db.Column(db.Integer, db.ForeignKey('game_rooms.id'), unique=True)
|
||
phase = db.Column(db.String(20), default='action') # action, market, event, results
|
||
phase_end = db.Column(db.DateTime)
|
||
market_data = db.Column(db.Text, default='{}') # JSON с данными рынка
|
||
events = db.Column(db.Text, default='[]') # JSON с событиями
|
||
history = db.Column(db.Text, default='[]') # JSON с историей
|
||
updated_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||
|
||
|
||
# --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ---
|
||
|
||
def generate_room_code():
|
||
"""Генерация уникального кода комнаты"""
|
||
import string
|
||
characters = string.ascii_uppercase + string.digits
|
||
return ''.join(random.choice(characters) for _ in range(6))
|
||
|
||
|
||
def calculate_market_changes(room_id):
|
||
"""Расчет изменений на рынке (по формулам из концепции)"""
|
||
room = GameRoom.query.get(room_id)
|
||
players = GamePlayer.query.filter_by(room_id=room_id).all()
|
||
|
||
# Пример расчета (упрощенный)
|
||
market_data = json.loads(room.game_state.market_data) if room.game_state else {}
|
||
|
||
# Активы и их базовые цены
|
||
assets = {
|
||
'stock_gazprom': {'price': 1000, 'volatility': 0.2},
|
||
'real_estate': {'price': 3000000, 'volatility': 0.1},
|
||
'bitcoin': {'price': 1850000, 'volatility': 0.3},
|
||
'oil': {'price': 5000, 'volatility': 0.25}
|
||
}
|
||
|
||
# Считаем спрос по каждому активу
|
||
for asset_id, asset_data in assets.items():
|
||
demand = 0
|
||
for player in players:
|
||
player_assets = json.loads(player.assets)
|
||
for player_asset in player_assets:
|
||
if player_asset.get('id') == asset_id:
|
||
demand += player_asset.get('quantity', 0)
|
||
|
||
# Формула влияния спроса
|
||
total_players = len(players)
|
||
if total_players > 0:
|
||
price_change = asset_data['volatility'] * (demand / (total_players * 10))
|
||
new_price = asset_data['price'] * (1 + price_change)
|
||
|
||
# Эффект перегрева
|
||
if new_price > asset_data['price'] * (1 + 2 * asset_data['volatility']):
|
||
new_price *= random.uniform(0.7, 0.9)
|
||
|
||
assets[asset_id]['price'] = new_price
|
||
|
||
# Случайные события
|
||
events = [
|
||
('boom_oil', 'Бум нефти', {'oil': 1.3}, 'positive'),
|
||
('cyber_attack', 'Кибератака', {'bitcoin': 0.5}, 'negative'),
|
||
('elections', 'Выборы президента', {'all': 0.95}, 'neutral'),
|
||
('sanctions', 'Санкции', {'stock_gazprom': 0.65, 'oil': 0.8}, 'negative')
|
||
]
|
||
|
||
event_name, event_description, event_effects, event_type = random.choice(events)
|
||
|
||
# Применяем эффекты события
|
||
for asset_id, multiplier in event_effects.items():
|
||
if asset_id == 'all':
|
||
for key in assets.keys():
|
||
assets[key]['price'] *= multiplier
|
||
elif asset_id in assets:
|
||
assets[asset_id]['price'] *= multiplier
|
||
|
||
return {
|
||
'assets': assets,
|
||
'event': {
|
||
'name': event_name,
|
||
'description': event_description,
|
||
'type': event_type,
|
||
'effects': event_effects
|
||
},
|
||
'timestamp': datetime.utcnow().isoformat()
|
||
}
|
||
|
||
|
||
# --- FLASK-LOGIN ---
|
||
|
||
@login_manager.user_loader
|
||
def load_user(user_id):
|
||
return User.query.get(int(user_id))
|
||
|
||
|
||
# --- РОУТЫ ---
|
||
|
||
# Главная страница теперь будет index.html с описанием
|
||
@app.route('/')
|
||
def index():
|
||
return render_template('index.html')
|
||
|
||
# Старый маршрут index теперь перенаправляет на главную
|
||
@app.route('/home')
|
||
def home():
|
||
return redirect(url_for('index'))
|
||
|
||
|
||
@app.route('/login', methods=['GET', 'POST'])
|
||
def login():
|
||
if current_user.is_authenticated:
|
||
return redirect(url_for('rooms'))
|
||
|
||
if request.method == 'POST':
|
||
username = request.form.get('username', '').strip()
|
||
password = request.form.get('password', '')
|
||
remember = request.form.get('remember', False)
|
||
|
||
user = User.query.filter_by(username=username).first()
|
||
|
||
if user and user.check_password(password):
|
||
if not user.is_active:
|
||
flash('Аккаунт заблокирован', 'error')
|
||
else:
|
||
login_user(user, remember=remember)
|
||
user.last_seen = datetime.utcnow()
|
||
db.session.commit()
|
||
flash(f'Добро пожаловать, {username}!', 'success')
|
||
|
||
# Редирект на следующую страницу или rooms
|
||
next_page = request.args.get('next')
|
||
return redirect(next_page or url_for('rooms'))
|
||
else:
|
||
flash('Неверное имя пользователя или пароль', 'error')
|
||
|
||
return render_template('login.html')
|
||
|
||
|
||
@app.route('/quick_login/<username>')
|
||
def quick_login(username):
|
||
"""Быстрый вход для тестирования (только для разработки!)"""
|
||
user = User.query.filter_by(username=username).first()
|
||
if user:
|
||
login_user(user)
|
||
flash(f'Быстрый вход как {username}', 'info')
|
||
return redirect(url_for('rooms'))
|
||
|
||
# Если пользователя нет, создаем его
|
||
user = User(
|
||
username=username,
|
||
email=f'{username}@test.com',
|
||
is_active=True
|
||
)
|
||
user.set_password('test123')
|
||
db.session.add(user)
|
||
db.session.commit()
|
||
|
||
login_user(user)
|
||
flash(f'Создан и вошли как {username}', 'success')
|
||
return redirect(url_for('rooms'))
|
||
|
||
|
||
@app.route('/register', methods=['GET', 'POST'])
|
||
def register():
|
||
if current_user.is_authenticated:
|
||
return redirect(url_for('rooms'))
|
||
|
||
if request.method == 'POST':
|
||
username = request.form.get('username', '').strip()
|
||
email = request.form.get('email', '').strip()
|
||
password = request.form.get('password', '')
|
||
password2 = request.form.get('password2', '')
|
||
|
||
# Простая валидация
|
||
errors = []
|
||
|
||
if not username:
|
||
errors.append('Введите имя пользователя')
|
||
elif len(username) < 3:
|
||
errors.append('Имя пользователя должно быть не менее 3 символов')
|
||
elif User.query.filter_by(username=username).first():
|
||
errors.append('Имя пользователя уже занято')
|
||
|
||
if not email:
|
||
errors.append('Введите email')
|
||
elif '@' not in email:
|
||
errors.append('Введите корректный email')
|
||
elif User.query.filter_by(email=email).first():
|
||
errors.append('Email уже используется')
|
||
|
||
if not password:
|
||
errors.append('Введите пароль')
|
||
elif len(password) < 4:
|
||
errors.append('Пароль должен быть не менее 4 символов')
|
||
elif password != password2:
|
||
errors.append('Пароли не совпадают')
|
||
|
||
if errors:
|
||
for error in errors:
|
||
flash(error, 'error')
|
||
return render_template('register.html')
|
||
|
||
try:
|
||
# Создаем пользователя
|
||
user = User(
|
||
username=username,
|
||
email=email
|
||
)
|
||
user.set_password(password)
|
||
|
||
db.session.add(user)
|
||
db.session.commit()
|
||
|
||
login_user(user, remember=True)
|
||
flash('Регистрация успешна! Добро пожаловать!', 'success')
|
||
return redirect(url_for('rooms'))
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
flash(f'Ошибка при регистрации: {str(e)}', 'error')
|
||
|
||
return render_template('register.html')
|
||
|
||
|
||
@app.route('/logout')
|
||
@login_required
|
||
def logout():
|
||
logout_user()
|
||
return redirect(url_for('index'))
|
||
|
||
|
||
@app.route('/rooms')
|
||
@login_required
|
||
def rooms():
|
||
"""Страница списка комнат"""
|
||
try:
|
||
# Получаем все активные комнаты
|
||
all_rooms = GameRoom.query.filter(
|
||
GameRoom.status.in_(['waiting', 'playing'])
|
||
).order_by(GameRoom.created_at.desc()).all()
|
||
|
||
# Для каждой комнаты получаем количество игроков
|
||
rooms_with_counts = []
|
||
for room in all_rooms:
|
||
# Получаем количество игроков
|
||
player_count = GamePlayer.query.filter_by(room_id=room.id).count()
|
||
|
||
# Получаем создателя
|
||
creator = User.query.get(room.creator_id) if room.creator_id else None
|
||
|
||
# Добавляем комнату с дополнительной информацией
|
||
rooms_with_counts.append({
|
||
'id': room.id,
|
||
'name': room.name,
|
||
'code': room.code,
|
||
'status': room.status,
|
||
'creator': creator,
|
||
'creator_id': room.creator_id,
|
||
'current_month': room.current_month,
|
||
'total_months': room.total_months,
|
||
'start_capital': room.start_capital,
|
||
'settings': room.settings,
|
||
'player_count': player_count,
|
||
'players': [] # Пустой список, так как не загружаем всех игроков
|
||
})
|
||
|
||
# Комнаты текущего пользователя
|
||
user_rooms = []
|
||
user_room_ids = GamePlayer.query.filter_by(
|
||
user_id=current_user.id
|
||
).with_entities(GamePlayer.room_id).all()
|
||
|
||
user_room_ids = [rid for (rid,) in user_room_ids]
|
||
|
||
for room_data in rooms_with_counts:
|
||
if room_data['id'] in user_room_ids:
|
||
user_rooms.append(room_data)
|
||
|
||
print(f"Found {len(rooms_with_counts)} rooms, user in {len(user_rooms)} rooms")
|
||
|
||
return render_template('rooms.html',
|
||
rooms=rooms_with_counts,
|
||
user_rooms=user_rooms,
|
||
config=app.config)
|
||
|
||
except Exception as e:
|
||
print(f"Error in rooms route: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
db.session.rollback()
|
||
flash(f'Ошибка при загрузке комнат: {str(e)}', 'error')
|
||
return render_template('rooms.html',
|
||
rooms=[],
|
||
user_rooms=[],
|
||
config=app.config)
|
||
|
||
|
||
@app.route('/room/create', methods=['POST'])
|
||
@login_required
|
||
def create_room():
|
||
"""Создание новой комнаты"""
|
||
try:
|
||
# Получаем данные из формы
|
||
room_name = request.form.get('name', 'Новая комната').strip()
|
||
total_months = int(request.form.get('total_months', 12))
|
||
start_capital = int(request.form.get('start_capital', 100000))
|
||
allow_loans = request.form.get('allow_loans') == 'on'
|
||
allow_black_market = request.form.get('allow_black_market') == 'on'
|
||
private_room = request.form.get('private_room') == 'on'
|
||
|
||
# Валидация
|
||
if not room_name:
|
||
return jsonify({'error': 'Введите название комнаты'}), 400
|
||
|
||
# Генерируем уникальный код комнаты
|
||
import string
|
||
import random
|
||
|
||
def generate_room_code():
|
||
chars = string.ascii_uppercase + string.digits
|
||
return ''.join(random.choice(chars) for _ in range(6))
|
||
|
||
room_code = generate_room_code()
|
||
|
||
# Создаем комнату
|
||
room = GameRoom(
|
||
name=room_name,
|
||
code=room_code,
|
||
creator_id=current_user.id,
|
||
total_months=total_months,
|
||
start_capital=start_capital,
|
||
settings=json.dumps({
|
||
'allow_loans': allow_loans,
|
||
'allow_black_market': allow_black_market,
|
||
'private_room': private_room
|
||
})
|
||
)
|
||
|
||
db.session.add(room)
|
||
db.session.commit()
|
||
|
||
# Добавляем создателя как игрока-администратора
|
||
player = GamePlayer(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True,
|
||
is_ready=True,
|
||
capital=start_capital,
|
||
ability=random.choice([
|
||
'crisis_investor', 'lobbyist', 'predictor',
|
||
'golden_pillow', 'shadow_accountant', 'credit_magnate'
|
||
])
|
||
)
|
||
|
||
db.session.add(player)
|
||
db.session.commit()
|
||
|
||
# Создаем начальное состояние игры
|
||
game_state = GameState(
|
||
room_id=room.id,
|
||
market_data=json.dumps({
|
||
'initialized': True,
|
||
'assets': {
|
||
'stock_gazprom': {'price': 1000, 'volatility': 0.2},
|
||
'real_estate': {'price': 3000000, 'volatility': 0.1},
|
||
'bitcoin': {'price': 1850000, 'volatility': 0.3},
|
||
'oil': {'price': 5000, 'volatility': 0.25}
|
||
},
|
||
'last_update': datetime.utcnow().isoformat()
|
||
})
|
||
)
|
||
|
||
db.session.add(game_state)
|
||
db.session.commit()
|
||
|
||
# Отправляем успешный ответ
|
||
return jsonify({
|
||
'success': True,
|
||
'room_code': room.code,
|
||
'redirect': url_for('lobby', room_code=room.code)
|
||
})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
print(f"Error creating room: {str(e)}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
return jsonify({'error': f'Ошибка при создании комнаты: {str(e)}'}), 500
|
||
|
||
|
||
@app.route('/room/<room_code>')
|
||
@login_required
|
||
def lobby(room_code):
|
||
"""Лобби комнаты"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Получаем всех игроков комнаты
|
||
players = GamePlayer.query.filter_by(room_id=room.id).all()
|
||
|
||
# Получаем информацию о пользователях для игроков
|
||
players_with_users = []
|
||
for player in players:
|
||
user = User.query.get(player.user_id)
|
||
player.user = user # Добавляем объект пользователя к игроку
|
||
players_with_users.append(player)
|
||
|
||
# Проверяем, есть ли текущий пользователь в комнате
|
||
current_player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id
|
||
).first()
|
||
|
||
# Если игрока нет и комната в ожидании, добавляем его
|
||
if not current_player and room.status == 'waiting':
|
||
# Проверяем, есть ли место в комнате
|
||
if len(players) >= app.config['MAX_PLAYERS_PER_ROOM']:
|
||
flash('Комната заполнена', 'error')
|
||
return redirect(url_for('rooms'))
|
||
|
||
# Случайная способность
|
||
import random
|
||
abilities = [
|
||
'crisis_investor', 'lobbyist', 'predictor',
|
||
'golden_pillow', 'shadow_accountant', 'credit_magnate',
|
||
'bear_raid', 'fake_news', 'dividend_king',
|
||
'raider_capture', 'mafia_connections', 'economic_advisor',
|
||
'currency_speculator'
|
||
]
|
||
|
||
current_player = GamePlayer(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=False,
|
||
is_ready=False,
|
||
capital=room.start_capital,
|
||
ability=random.choice(abilities)
|
||
)
|
||
|
||
db.session.add(current_player)
|
||
db.session.commit()
|
||
|
||
players_with_users.append(current_player)
|
||
flash(f'Вы присоединились к комнате "{room.name}"', 'success')
|
||
|
||
elif not current_player:
|
||
flash('Вы не можете присоединиться к этой комнате', 'error')
|
||
return redirect(url_for('rooms'))
|
||
|
||
# Получаем создателя комнаты
|
||
creator = User.query.get(room.creator_id) if room.creator_id else None
|
||
|
||
return render_template('lobby.html',
|
||
room=room,
|
||
players=players_with_users,
|
||
current_player=current_player,
|
||
creator=creator,
|
||
config=app.config)
|
||
|
||
|
||
@app.route('/room/<room_code>/start', methods=['POST'])
|
||
@login_required
|
||
def start_room_game(room_code):
|
||
"""Начало игры в комнате"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем права администратора
|
||
player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True
|
||
).first()
|
||
|
||
if not player:
|
||
return jsonify({'error': 'Только администратор может начать игру'}), 403
|
||
|
||
if room.status != 'waiting':
|
||
return jsonify({'error': 'Игра уже начата или завершена'}), 400
|
||
|
||
# Проверяем минимальное количество игроков
|
||
player_count = GamePlayer.query.filter_by(room_id=room.id).count()
|
||
if player_count < 2:
|
||
return jsonify({'error': 'Нужно минимум 2 игрока для начала игры'}), 400
|
||
|
||
# Меняем статус комнаты
|
||
room.status = 'playing'
|
||
db.session.commit()
|
||
|
||
# Отправляем событие через WebSocket
|
||
socketio.emit('game_started', {
|
||
'room': room.code,
|
||
'message': 'Игра началась!'
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
|
||
@app.route('/room/<room_code>/ready', methods=['POST'])
|
||
@login_required
|
||
def toggle_player_ready(room_code):
|
||
"""Изменение статуса готовности игрока"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id
|
||
).first_or_404()
|
||
|
||
data = request.get_json()
|
||
is_ready = data.get('ready', not player.is_ready)
|
||
|
||
player.is_ready = is_ready
|
||
db.session.commit()
|
||
|
||
# Отправляем событие через WebSocket
|
||
socketio.emit('player_ready_changed', {
|
||
'user_id': current_user.id,
|
||
'username': current_user.username,
|
||
'is_ready': is_ready
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True, 'is_ready': is_ready})
|
||
|
||
|
||
@app.route('/api/room/<room_code>/status')
|
||
@login_required
|
||
def get_room_status(room_code):
|
||
"""Получение статуса комнаты"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
player_count = GamePlayer.query.filter_by(room_id=room.id).count()
|
||
ready_count = GamePlayer.query.filter_by(room_id=room.id, is_ready=True).count()
|
||
|
||
return jsonify({
|
||
'status': room.status,
|
||
'player_count': player_count,
|
||
'ready_count': ready_count,
|
||
'current_month': room.current_month
|
||
})
|
||
|
||
# @app.route('/game/<room_code>')
|
||
# @login_required
|
||
# def game(room_code):
|
||
# room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
#
|
||
# # Проверяем, что игрок в комнате и игра идет
|
||
# player = GamePlayer.query.filter_by(
|
||
# user_id=current_user.id,
|
||
# room_id=room.id
|
||
# ).first_or_404()
|
||
#
|
||
# if room.status != 'playing':
|
||
# return redirect(url_for('lobby', room_code=room_code))
|
||
#
|
||
# # Получаем данные игры
|
||
# game_state = room.game_state
|
||
# market_data = json.loads(game_state.market_data) if game_state else {}
|
||
#
|
||
# # Получаем всех игроков для лидерборда
|
||
# players = GamePlayer.query.filter_by(room_id=room.id).order_by(GamePlayer.capital.desc()).all()
|
||
#
|
||
# return render_template('game.html',
|
||
# room=room,
|
||
# player=player,
|
||
# players=players,
|
||
# game_state=game_state,
|
||
# market_data=market_data)
|
||
|
||
@app.route('/room/<room_code>/update', methods=['POST'])
|
||
@login_required
|
||
def update_room_settings(room_code):
|
||
"""Обновление настроек комнаты"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем права администратора
|
||
player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True
|
||
).first()
|
||
|
||
if not player:
|
||
return jsonify({'error': 'Только администратор может изменять настройки'}), 403
|
||
|
||
if room.status != 'waiting':
|
||
return jsonify({'error': 'Нельзя изменять настройки во время игры'}), 400
|
||
|
||
try:
|
||
room.name = request.form.get('name', room.name)
|
||
room.total_months = int(request.form.get('total_months', room.total_months))
|
||
room.start_capital = int(request.form.get('start_capital', room.start_capital))
|
||
|
||
# Обновляем настройки
|
||
settings = json.loads(room.settings) if room.settings else {}
|
||
settings['allow_loans'] = request.form.get('allow_loans') == 'on'
|
||
settings['allow_black_market'] = request.form.get('allow_black_market') == 'on'
|
||
room.settings = json.dumps(settings)
|
||
|
||
db.session.commit()
|
||
|
||
socketio.emit('room_updated', {
|
||
'room': room.code,
|
||
'name': room.name
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
return jsonify({'error': f'Ошибка при обновлении настроек: {str(e)}'}), 500
|
||
|
||
|
||
@app.route('/room/<room_code>/kick/<int:user_id>', methods=['POST'])
|
||
@login_required
|
||
def kick_player(room_code, user_id):
|
||
"""Выгнать игрока из комнаты"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем права администратора
|
||
admin_player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True
|
||
).first()
|
||
|
||
if not admin_player:
|
||
return jsonify({'error': 'Только администратор может выгонять игроков'}), 403
|
||
|
||
# Нельзя выгнать себя или другого администратора
|
||
if user_id == current_user.id:
|
||
return jsonify({'error': 'Нельзя выгнать самого себя'}), 400
|
||
|
||
player_to_kick = GamePlayer.query.filter_by(
|
||
user_id=user_id,
|
||
room_id=room.id
|
||
).first()
|
||
|
||
if not player_to_kick:
|
||
return jsonify({'error': 'Игрок не найден в комнате'}), 404
|
||
|
||
if player_to_kick.is_admin:
|
||
return jsonify({'error': 'Нельзя выгнать другого администратора'}), 400
|
||
|
||
# Удаляем игрока
|
||
db.session.delete(player_to_kick)
|
||
db.session.commit()
|
||
|
||
socketio.emit('player_kicked', {
|
||
'user_id': user_id,
|
||
'username': User.query.get(user_id).username if User.query.get(user_id) else 'Игрок'
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
|
||
@app.route('/room/<room_code>/kick_all', methods=['POST'])
|
||
@login_required
|
||
def kick_all_players(room_code):
|
||
"""Выгнать всех игроков из комнаты"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем права администратора
|
||
admin_player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True
|
||
).first()
|
||
|
||
if not admin_player:
|
||
return jsonify({'error': 'Только администратор может выгонять игроков'}), 403
|
||
|
||
# Удаляем всех игроков кроме администратора
|
||
GamePlayer.query.filter_by(room_id=room.id).filter(
|
||
GamePlayer.user_id != current_user.id
|
||
).delete(synchronize_session=False)
|
||
|
||
db.session.commit()
|
||
|
||
socketio.emit('all_players_kicked', {
|
||
'room': room.code
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
|
||
@app.route('/room/<room_code>/reset', methods=['POST'])
|
||
@login_required
|
||
def reset_room(room_code):
|
||
"""Сбросить комнату к начальному состоянию"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем права администратора
|
||
admin_player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True
|
||
).first()
|
||
|
||
if not admin_player:
|
||
return jsonify({'error': 'Только администратор может сбрасывать комнату'}), 403
|
||
|
||
# Сбрасываем статус комнаты
|
||
room.status = 'waiting'
|
||
room.current_month = 1
|
||
|
||
# Сбрасываем всех игроков
|
||
GamePlayer.query.filter_by(room_id=room.id).update({
|
||
'is_ready': False,
|
||
'capital': room.start_capital,
|
||
'assets': '[]',
|
||
'position': 0
|
||
})
|
||
|
||
# Сбрасываем состояние игры
|
||
game_state = GameState.query.filter_by(room_id=room.id).first()
|
||
if game_state:
|
||
game_state.phase = 'action'
|
||
game_state.phase_end = None
|
||
game_state.market_data = json.dumps({
|
||
'initialized': True,
|
||
'assets': {
|
||
'stock_gazprom': {'price': 1000, 'volatility': 0.2},
|
||
'real_estate': {'price': 3000000, 'volatility': 0.1},
|
||
'bitcoin': {'price': 1850000, 'volatility': 0.3},
|
||
'oil': {'price': 5000, 'volatility': 0.25}
|
||
},
|
||
'last_update': datetime.utcnow().isoformat()
|
||
})
|
||
game_state.events = '[]'
|
||
game_state.history = '[]'
|
||
|
||
db.session.commit()
|
||
|
||
socketio.emit('room_reset', {
|
||
'room': room.code
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True})
|
||
|
||
|
||
@app.route('/api/room/<room_code>/players')
|
||
@login_required
|
||
def get_room_players(room_code):
|
||
"""Получение списка игроков комнаты без перезагрузки страницы"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем, состоит ли пользователь в комнате
|
||
current_player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id
|
||
).first()
|
||
|
||
if not current_player:
|
||
return jsonify({'error': 'Вы не в этой комнате'}), 403
|
||
|
||
# Получаем всех игроков
|
||
players = GamePlayer.query.filter_by(room_id=room.id).all()
|
||
|
||
players_data = []
|
||
for player in players:
|
||
user = User.query.get(player.user_id)
|
||
players_data.append({
|
||
'user_id': player.user_id,
|
||
'username': user.username if user else 'Игрок',
|
||
'is_admin': player.is_admin,
|
||
'is_ready': player.is_ready,
|
||
'capital': player.capital,
|
||
'ability_name': get_ability_name(player.ability),
|
||
'ability_description': get_ability_description(player.ability)
|
||
})
|
||
|
||
return jsonify({
|
||
'success': True,
|
||
'players': players_data,
|
||
'room_status': room.status
|
||
})
|
||
|
||
|
||
@app.route('/room/<room_code>/delete', methods=['POST'])
|
||
@login_required
|
||
def delete_room(room_code):
|
||
"""Удалить комнату"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем права администратора
|
||
admin_player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id,
|
||
is_admin=True
|
||
).first()
|
||
|
||
if not admin_player:
|
||
return jsonify({'error': 'Только администратор может удалить комнату'}), 403
|
||
|
||
# Удаляем комнату и все связанные данные
|
||
GamePlayer.query.filter_by(room_id=room.id).delete()
|
||
GameState.query.filter_by(room_id=room.id).delete()
|
||
db.session.delete(room)
|
||
db.session.commit()
|
||
|
||
return jsonify({'success': True})
|
||
|
||
|
||
@app.route('/api/game/<room_code>/assets')
|
||
@login_required
|
||
def get_assets(room_code):
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
game_state = room.game_state
|
||
|
||
if not game_state:
|
||
return jsonify({'error': 'Game state not found'}), 404
|
||
|
||
market_data = json.loads(game_state.market_data)
|
||
return jsonify(market_data)
|
||
|
||
|
||
@app.route('/api/game/<room_code>/action', methods=['POST'])
|
||
@login_required
|
||
def perform_action(room_code):
|
||
"""Выполнение действия игрока"""
|
||
room = GameRoom.query.filter_by(code=room_code).first_or_404()
|
||
|
||
# Проверяем, что сейчас фаза действий
|
||
if room.game_state.phase != 'action':
|
||
return jsonify({'error': 'Not action phase'}), 400
|
||
|
||
data = request.json
|
||
action_type = data.get('type')
|
||
|
||
player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id
|
||
).first_or_404()
|
||
|
||
# Обработка разных типов действий
|
||
if action_type == 'buy_asset':
|
||
asset_id = data.get('asset_id')
|
||
quantity = data.get('quantity', 1)
|
||
|
||
# Здесь должна быть логика покупки актива
|
||
# ...
|
||
|
||
# Обновляем капитал игрока
|
||
player.capital -= data.get('cost', 0)
|
||
|
||
# Сохраняем активы
|
||
assets = json.loads(player.assets)
|
||
assets.append({
|
||
'id': asset_id,
|
||
'quantity': quantity,
|
||
'purchase_price': data.get('price'),
|
||
'timestamp': datetime.utcnow().isoformat()
|
||
})
|
||
player.assets = json.dumps(assets)
|
||
|
||
db.session.commit()
|
||
|
||
# Отправляем обновление через WebSocket
|
||
socketio.emit('player_action', {
|
||
'player_id': current_user.id,
|
||
'player_name': current_user.username,
|
||
'action': action_type,
|
||
'asset_id': asset_id,
|
||
'quantity': quantity
|
||
}, room=room.code)
|
||
|
||
return jsonify({'success': True, 'new_capital': player.capital})
|
||
|
||
return jsonify({'error': 'Unknown action type'}), 400
|
||
|
||
@app.template_filter('from_json')
|
||
def from_json_filter(value):
|
||
"""Преобразует JSON строку в объект"""
|
||
if isinstance(value, str):
|
||
return json.loads(value)
|
||
return value
|
||
|
||
@app.template_filter('format_currency')
|
||
def format_currency_filter(value):
|
||
"""Форматирует число как валюту"""
|
||
if value is None:
|
||
return "0 ₽"
|
||
return f"{value:,.0f} ₽".replace(",", " ")
|
||
|
||
|
||
# --- WEBSOCKET ОБРАБОТЧИКИ ---
|
||
|
||
@socketio.on('connect')
|
||
def handle_connect():
|
||
if current_user.is_authenticated:
|
||
logger.info(f'User {current_user.username} connected')
|
||
emit('connected', {'user_id': current_user.id, 'username': current_user.username})
|
||
|
||
|
||
@socketio.on('join_room')
|
||
def handle_join_room(data):
|
||
room_code = data.get('room')
|
||
if room_code and current_user.is_authenticated:
|
||
join_room(room_code)
|
||
logger.info(f'User {current_user.username} joined room {room_code}')
|
||
|
||
# Проверяем, было ли уже уведомление о входе
|
||
session_key = f'has_notified_join_{room_code}'
|
||
if not session.get(session_key):
|
||
# Отправляем уведомление другим игрокам
|
||
emit('player_joined', {
|
||
'user_id': current_user.id,
|
||
'username': current_user.username,
|
||
'timestamp': datetime.utcnow().isoformat(),
|
||
'is_reconnect': False
|
||
}, room=room_code, include_self=False)
|
||
|
||
# Отмечаем в сессии, что уведомление отправлено
|
||
session[session_key] = True
|
||
else:
|
||
# Это реконнект, отправляем тихое обновление
|
||
emit('player_reconnected', {
|
||
'user_id': current_user.id,
|
||
'username': current_user.username
|
||
}, room=room_code, include_self=False)
|
||
|
||
|
||
@socketio.on('leave_room')
|
||
def handle_leave_room(data):
|
||
room_code = data.get('room')
|
||
if room_code:
|
||
leave_room(room_code)
|
||
logger.info(f'User {current_user.username} left room {room_code}')
|
||
|
||
emit('player_left', {
|
||
'user_id': current_user.id,
|
||
'username': current_user.username
|
||
}, room=room_code)
|
||
|
||
|
||
@socketio.on('player_ready')
|
||
def handle_player_ready(data):
|
||
room_code = data.get('room')
|
||
is_ready = data.get('ready', True)
|
||
|
||
room = GameRoom.query.filter_by(code=room_code).first()
|
||
if room:
|
||
player = GamePlayer.query.filter_by(
|
||
user_id=current_user.id,
|
||
room_id=room.id
|
||
).first()
|
||
|
||
if player:
|
||
player.is_ready = is_ready
|
||
db.session.commit()
|
||
|
||
emit('player_ready_changed', {
|
||
'user_id': current_user.id,
|
||
'username': current_user.username,
|
||
'is_ready': is_ready
|
||
}, room=room_code)
|
||
|
||
|
||
@socketio.on('chat_message')
|
||
def handle_chat_message(data):
|
||
room_code = data.get('room')
|
||
message = data.get('message', '').strip()
|
||
|
||
if message and room_code:
|
||
emit('chat_message', {
|
||
'user_id': current_user.id,
|
||
'username': current_user.username,
|
||
'message': message,
|
||
'timestamp': datetime.utcnow().isoformat()
|
||
}, room=room_code)
|
||
|
||
|
||
@socketio.on('join_global_room')
|
||
def handle_join_global_room():
|
||
"""Присоединение к глобальной комнате для обновлений"""
|
||
join_room('global_updates')
|
||
print(f'User {current_user.username if current_user else "Guest"} joined global room')
|
||
|
||
|
||
@socketio.on('user_online')
|
||
def handle_user_online(data):
|
||
"""Обработка статуса онлайн пользователя"""
|
||
if current_user.is_authenticated:
|
||
current_user.last_seen = datetime.utcnow()
|
||
db.session.commit()
|
||
print(f'User {current_user.username} is online')
|
||
|
||
|
||
# Добавим обработчик для обновления комнат
|
||
@socketio.on('get_rooms')
|
||
def handle_get_rooms():
|
||
"""Отправка списка комнат"""
|
||
rooms = GameRoom.query.filter(GameRoom.status != 'finished').all()
|
||
|
||
rooms_data = []
|
||
for room in rooms:
|
||
players_count = GamePlayer.query.filter_by(room_id=room.id).count()
|
||
rooms_data.append({
|
||
'id': room.id,
|
||
'name': room.name,
|
||
'code': room.code,
|
||
'status': room.status,
|
||
'players': players_count,
|
||
'max_players': app.config['MAX_PLAYERS_PER_ROOM'],
|
||
'month': room.current_month,
|
||
'total_months': room.total_months
|
||
})
|
||
|
||
emit('rooms_list', {'rooms': rooms_data}, room=request.sid)
|
||
|
||
# --- КОМАНДЫ УПРАВЛЕНИЯ ---
|
||
|
||
@app.cli.command('init-db')
|
||
def init_db_command():
|
||
"""Инициализация базы данных"""
|
||
db.create_all()
|
||
print('База данных инициализирована.')
|
||
|
||
|
||
@app.cli.command('create-test-data')
|
||
def create_test_data():
|
||
"""Создание тестовых данных"""
|
||
from werkzeug.security import generate_password_hash
|
||
|
||
# Создаем тестового пользователя
|
||
test_user = User(
|
||
username='test',
|
||
email='test@example.com',
|
||
password_hash=generate_password_hash('test123')
|
||
)
|
||
db.session.add(test_user)
|
||
db.session.commit()
|
||
|
||
print('Тестовые данные созданы.')
|
||
|
||
|
||
@app.cli.command('create-test-users')
|
||
def create_test_users():
|
||
"""Создание тестовых пользователей"""
|
||
test_users = [
|
||
{'username': 'Игрок1', 'email': 'player1@test.com', 'password': '123456'},
|
||
{'username': 'Игрок2', 'email': 'player2@test.com', 'password': '123456'},
|
||
{'username': 'Инвестор', 'email': 'investor@test.com', 'password': '123456'},
|
||
{'username': 'Трейдер', 'email': 'trader@test.com', 'password': '123456'},
|
||
{'username': 'Банкир', 'email': 'banker@test.com', 'password': '123456'},
|
||
{'username': 'admin', 'email': 'admin@test.com', 'password': 'admin123'},
|
||
]
|
||
|
||
created_count = 0
|
||
for user_data in test_users:
|
||
# Проверяем, существует ли пользователь
|
||
existing_user = User.query.filter_by(username=user_data['username']).first()
|
||
if not existing_user:
|
||
user = User(
|
||
username=user_data['username'],
|
||
email=user_data['email'],
|
||
is_active=True
|
||
)
|
||
user.set_password(user_data['password'])
|
||
db.session.add(user)
|
||
created_count += 1
|
||
print(f'✓ Создан пользователь: {user_data["username"]}')
|
||
else:
|
||
print(f'⏭ Пользователь уже существует: {user_data["username"]}')
|
||
|
||
try:
|
||
db.session.commit()
|
||
print(f'\n✅ Создано {created_count} тестовых пользователей!')
|
||
print('\nТестовые учетные данные:')
|
||
print('------------------------')
|
||
for user_data in test_users:
|
||
print(f'Логин: {user_data["username"]}, Пароль: {user_data["password"]}')
|
||
except Exception as e:
|
||
db.session.rollback()
|
||
print(f'❌ Ошибка при создании пользователей: {str(e)}')
|
||
|
||
|
||
@app.cli.command('create-demo-rooms')
|
||
def create_demo_rooms():
|
||
"""Создание демонстрационных комнат"""
|
||
import random
|
||
|
||
demo_rooms = [
|
||
{
|
||
'name': '🤑 Быстрая игра для новичков',
|
||
'total_months': 6,
|
||
'start_capital': 50000,
|
||
'allow_loans': False,
|
||
'allow_black_market': False
|
||
},
|
||
{
|
||
'name': '📈 Турнир профессионалов',
|
||
'total_months': 12,
|
||
'start_capital': 200000,
|
||
'allow_loans': True,
|
||
'allow_black_market': True
|
||
},
|
||
{
|
||
'name': '👥 Игра с друзьями',
|
||
'total_months': 12,
|
||
'start_capital': 100000,
|
||
'allow_loans': True,
|
||
'allow_black_market': False
|
||
},
|
||
{
|
||
'name': '⚡ Экспресс-торги',
|
||
'total_months': 3,
|
||
'start_capital': 75000,
|
||
'allow_loans': False,
|
||
'allow_black_market': True
|
||
},
|
||
]
|
||
|
||
# Получаем случайного пользователя для создания комнат
|
||
users = User.query.all()
|
||
if not users:
|
||
print('❌ Нет пользователей. Сначала создайте тестовых пользователей.')
|
||
return
|
||
|
||
created_count = 0
|
||
|
||
for room_data in demo_rooms:
|
||
creator = random.choice(users)
|
||
|
||
# Генерируем уникальный код
|
||
import string
|
||
chars = string.ascii_uppercase + string.digits
|
||
room_code = ''.join(random.choice(chars) for _ in range(6))
|
||
|
||
# Проверяем, существует ли комната с таким кодом
|
||
while GameRoom.query.filter_by(code=room_code).first():
|
||
room_code = ''.join(random.choice(chars) for _ in range(6))
|
||
|
||
# Создаем комнату
|
||
room = GameRoom(
|
||
name=room_data['name'],
|
||
code=room_code,
|
||
creator_id=creator.id,
|
||
total_months=room_data['total_months'],
|
||
start_capital=room_data['start_capital'],
|
||
settings=json.dumps({
|
||
'allow_loans': room_data['allow_loans'],
|
||
'allow_black_market': room_data['allow_black_market'],
|
||
'private_room': False
|
||
})
|
||
)
|
||
|
||
db.session.add(room)
|
||
db.session.commit()
|
||
|
||
# Добавляем создателя в комнату
|
||
player = GamePlayer(
|
||
user_id=creator.id,
|
||
room_id=room.id,
|
||
is_admin=True,
|
||
is_ready=True,
|
||
capital=room_data['start_capital'],
|
||
ability=random.choice([
|
||
'crisis_investor', 'lobbyist', 'predictor',
|
||
'golden_pillow', 'shadow_accountant', 'credit_magnate'
|
||
])
|
||
)
|
||
|
||
db.session.add(player)
|
||
|
||
# Создаем начальное состояние игры
|
||
game_state = GameState(
|
||
room_id=room.id,
|
||
market_data=json.dumps({
|
||
'initialized': True,
|
||
'assets': {
|
||
'stock_gazprom': {'price': 1000, 'volatility': 0.2},
|
||
'real_estate': {'price': 3000000, 'volatility': 0.1},
|
||
'bitcoin': {'price': 1850000, 'volatility': 0.3},
|
||
'oil': {'price': 5000, 'volatility': 0.25}
|
||
},
|
||
'last_update': datetime.utcnow().isoformat()
|
||
})
|
||
)
|
||
|
||
db.session.add(game_state)
|
||
db.session.commit()
|
||
|
||
# Добавляем еще 2-3 случайных игроков в комнату
|
||
other_users = [u for u in users if u.id != creator.id]
|
||
if other_users:
|
||
for _ in range(random.randint(2, 3)):
|
||
if other_users:
|
||
random_user = random.choice(other_users)
|
||
other_users.remove(random_user)
|
||
|
||
player = GamePlayer(
|
||
user_id=random_user.id,
|
||
room_id=room.id,
|
||
is_admin=False,
|
||
is_ready=random.choice([True, False]),
|
||
capital=room_data['start_capital'],
|
||
ability=random.choice([
|
||
'crisis_investor', 'lobbyist', 'predictor',
|
||
'golden_pillow', 'shadow_accountant', 'credit_magnate'
|
||
])
|
||
)
|
||
|
||
db.session.add(player)
|
||
|
||
db.session.commit()
|
||
created_count += 1
|
||
print(f'✓ Создана демо-комната: {room_data["name"]} (код: {room_code})')
|
||
|
||
print(f'\n✅ Создано {created_count} демонстрационных комнат!')
|
||
|
||
|
||
@app.cli.command('reset-db')
|
||
def reset_db():
|
||
"""Полный сброс базы данных"""
|
||
confirmation = input('⚠️ Вы уверены, что хотите сбросить всю базу данных? (yes/no): ')
|
||
if confirmation.lower() != 'yes':
|
||
print('❌ Отменено')
|
||
return
|
||
|
||
# Удаляем все таблицы
|
||
db.drop_all()
|
||
print('🗑️ Таблицы удалены')
|
||
|
||
# Создаем заново
|
||
db.create_all()
|
||
print('✅ Таблицы созданы заново')
|
||
|
||
# Создаем тестовых пользователей
|
||
print('\n👥 Создание тестовых пользователей...')
|
||
ctx = app.test_request_context()
|
||
with ctx:
|
||
create_test_users.callback()
|
||
|
||
# Создаем демо-комнаты
|
||
print('\n🏢 Создание демонстрационных комнат...')
|
||
with ctx:
|
||
create_demo_rooms.callback()
|
||
|
||
print('\n🎉 База данных успешно сброшена и заполнена тестовыми данными!')
|
||
|
||
|
||
# --- ОБРАБОТЧИКИ ОШИБОК ---
|
||
from datetime import datetime
|
||
|
||
@app.errorhandler(404)
|
||
def not_found_error(error):
|
||
return render_template('404.html'), 404
|
||
|
||
@app.errorhandler(500)
|
||
def internal_error(error):
|
||
db.session.rollback()
|
||
return render_template('500.html', now=datetime.now()), 500
|
||
|
||
@app.errorhandler(403)
|
||
def forbidden_error(error):
|
||
return render_template('403.html'), 403
|
||
|
||
# Создадим и 403 ошибку для полноты
|
||
@app.route('/403')
|
||
def forbidden_page():
|
||
return render_template('403.html'), 403
|
||
|
||
# Дополнительные функции
|
||
|
||
def get_ability_name(ability_code):
|
||
"""Получить название способности по коду"""
|
||
abilities = {
|
||
'crisis_investor': 'Кризисный инвестор',
|
||
'lobbyist': 'Лоббист',
|
||
'predictor': 'Предсказатель',
|
||
'golden_pillow': 'Золотая подушка',
|
||
'shadow_accountant': 'Теневая бухгалтерия',
|
||
'credit_magnate': 'Кредитный магнат',
|
||
'bear_raid': 'Медвежий набег',
|
||
'fake_news': 'Фейковые новости',
|
||
'dividend_king': 'Король дивидендов',
|
||
'raider_capture': 'Рейдерский захват',
|
||
'mafia_connections': 'Мафиозные связи',
|
||
'economic_advisor': 'Экономический советник',
|
||
'currency_speculator': 'Валютный спекулянт'
|
||
}
|
||
return abilities.get(ability_code, 'Неизвестная способность')
|
||
|
||
def get_ability_description(ability_code):
|
||
"""Получить описание способности по коду"""
|
||
descriptions = {
|
||
'crisis_investor': '+20% к доходу при падении рынка',
|
||
'lobbyist': 'Может временно менять правила налогообложения',
|
||
'predictor': 'Видит следующее случайное событие',
|
||
'golden_pillow': 'Может защитить 20% капитала от кризисов',
|
||
'shadow_accountant': 'Раз в игру уменьшить налоги на 50%',
|
||
'credit_magnate': 'Может давать кредиты другим игрокам',
|
||
'bear_raid': 'Вызвать искусственное падение цены актива на 15%',
|
||
'fake_news': 'Подменить случайное событие на выгодное',
|
||
'dividend_king': 'Получать +10% дохода от всех акций',
|
||
'raider_capture': 'Попытаться отобрать 5% капитала у лидера',
|
||
'mafia_connections': 'Отменить одно негативное событие',
|
||
'economic_advisor': 'Раз в 3 месяца изменить налоговую ставку',
|
||
'currency_speculator': 'Отвязать рубль от нефти на 1 месяц'
|
||
}
|
||
return descriptions.get(ability_code, 'Описание отсутствует')
|
||
|
||
# Добавим функции в контекст шаблонов
|
||
@app.context_processor
|
||
def utility_processor():
|
||
return dict(
|
||
get_ability_name=get_ability_name,
|
||
get_ability_description=get_ability_description
|
||
)
|
||
|
||
# --- ТОЧКА ВХОДА ---
|
||
|
||
if __name__ == '__main__':
|
||
# Создаем таблицы если их нет
|
||
with app.app_context():
|
||
db.create_all()
|
||
|
||
# Запускаем сервер
|
||
socketio.run(app,
|
||
host='0.0.0.0',
|
||
port=5000,
|
||
debug=True,
|
||
allow_unsafe_werkzeug=True) |