418 lines
14 KiB
Python
418 lines
14 KiB
Python
from typing import Optional
|
|
from sqlalchemy.orm import Session
|
|
from database import Achievement, UserAchievement, ActivityFeed
|
|
from datetime import datetime
|
|
import json
|
|
|
|
# ─── Определения достижений ─────────────────────────────────────────────────
|
|
|
|
ACHIEVEMENT_DEFS = [
|
|
{
|
|
"name": "first_case",
|
|
"title": "Первый шаг",
|
|
"description": "Открой свой первый кейс",
|
|
"icon": "📦",
|
|
"category": "cases",
|
|
"requirement_type": "open_cases",
|
|
"requirement_value": 1,
|
|
"reward_amount": 100,
|
|
"sort_order": 1,
|
|
},
|
|
{
|
|
"name": "case_collector_10",
|
|
"title": "Коллекционер",
|
|
"description": "Открой 10 кейсов",
|
|
"icon": "📦",
|
|
"category": "cases",
|
|
"requirement_type": "open_cases",
|
|
"requirement_value": 10,
|
|
"reward_amount": 250,
|
|
"sort_order": 2,
|
|
},
|
|
{
|
|
"name": "case_collector_100",
|
|
"title": "Заядлый коллекционер",
|
|
"description": "Открой 100 кейсов",
|
|
"icon": "📦",
|
|
"category": "cases",
|
|
"requirement_type": "open_cases",
|
|
"requirement_value": 100,
|
|
"reward_amount": 1000,
|
|
"sort_order": 3,
|
|
},
|
|
{
|
|
"name": "case_collector_1000",
|
|
"title": "Одержимый",
|
|
"description": "Открой 1000 кейсов",
|
|
"icon": "💎",
|
|
"category": "cases",
|
|
"requirement_type": "open_cases",
|
|
"requirement_value": 1000,
|
|
"reward_amount": 5000,
|
|
"sort_order": 4,
|
|
},
|
|
{
|
|
"name": "first_contract",
|
|
"title": "Обменщик",
|
|
"description": "Выполни первый контракт обмена",
|
|
"icon": "🔄",
|
|
"category": "contracts",
|
|
"requirement_type": "contracts",
|
|
"requirement_value": 1,
|
|
"reward_amount": 200,
|
|
"sort_order": 5,
|
|
},
|
|
{
|
|
"name": "contract_master",
|
|
"title": "Мастер контрактов",
|
|
"description": "Выполни 50 контрактов обмена",
|
|
"icon": "🔄",
|
|
"category": "contracts",
|
|
"requirement_type": "contracts",
|
|
"requirement_value": 50,
|
|
"reward_amount": 2000,
|
|
"sort_order": 6,
|
|
},
|
|
{
|
|
"name": "first_upgrade",
|
|
"title": "Рисковый",
|
|
"description": "Попробуй апгрейд в первый раз",
|
|
"icon": "🆙",
|
|
"category": "upgrade",
|
|
"requirement_type": "upgrade_attempts",
|
|
"requirement_value": 1,
|
|
"reward_amount": 100,
|
|
"sort_order": 7,
|
|
},
|
|
{
|
|
"name": "upgrade_winner",
|
|
"title": "Везунчик",
|
|
"description": "Выиграй 10 апгрейдов",
|
|
"icon": "🍀",
|
|
"category": "upgrade",
|
|
"requirement_type": "upgrade_wins",
|
|
"requirement_value": 10,
|
|
"reward_amount": 1500,
|
|
"sort_order": 8,
|
|
},
|
|
{
|
|
"name": "rare_finder",
|
|
"title": "Охотник за редким",
|
|
"description": "Выбей предмет редкости Covert или Rare Special",
|
|
"icon": "🔴",
|
|
"category": "cases",
|
|
"requirement_type": "rare_item",
|
|
"requirement_value": 1,
|
|
"reward_amount": 500,
|
|
"sort_order": 9,
|
|
},
|
|
{
|
|
"name": "knife_collector",
|
|
"title": "Ножевод",
|
|
"description": "Выбей нож или перчатки",
|
|
"icon": "🔪",
|
|
"category": "cases",
|
|
"requirement_type": "knife",
|
|
"requirement_value": 1,
|
|
"reward_amount": 2000,
|
|
"sort_order": 10,
|
|
},
|
|
{
|
|
"name": "big_spender",
|
|
"title": "Транжира",
|
|
"description": "Потрать 10000 ₽ на кейсы",
|
|
"icon": "💰",
|
|
"category": "general",
|
|
"requirement_type": "total_spent",
|
|
"requirement_value": 10000,
|
|
"reward_amount": 1000,
|
|
"sort_order": 11,
|
|
},
|
|
{
|
|
"name": "whale",
|
|
"title": "Кит",
|
|
"description": "Потрать 100000 ₽ на кейсы",
|
|
"icon": "🐋",
|
|
"category": "general",
|
|
"requirement_type": "total_spent",
|
|
"requirement_value": 100000,
|
|
"reward_amount": 10000,
|
|
"sort_order": 12,
|
|
},
|
|
{
|
|
"name": "slot_winner",
|
|
"title": "Джекпот",
|
|
"description": "Собери 3 одинаковых символа в слот-машине",
|
|
"icon": "🎰",
|
|
"category": "cases",
|
|
"requirement_type": "slot_match",
|
|
"requirement_value": 1,
|
|
"reward_amount": 1000,
|
|
"sort_order": 13,
|
|
},
|
|
{
|
|
"name": "rich_100k",
|
|
"title": "Богач",
|
|
"description": "Накопи 100000 ₽ на балансе",
|
|
"icon": "🤑",
|
|
"category": "general",
|
|
"requirement_type": "balance_100k",
|
|
"requirement_value": 1,
|
|
"reward_amount": 5000,
|
|
"sort_order": 14,
|
|
},
|
|
{
|
|
"name": "slots_10",
|
|
"title": "Слото-фанат",
|
|
"description": "Сыграй 10 раундов в слот-машине",
|
|
"icon": "🎰",
|
|
"category": "cases",
|
|
"requirement_type": "slot_plays",
|
|
"requirement_value": 10,
|
|
"reward_amount": 500,
|
|
"sort_order": 15,
|
|
},
|
|
{
|
|
"name": "crash_first",
|
|
"title": "Крэш-тестер",
|
|
"description": "Сыграй в Crash в первый раз",
|
|
"icon": "💥",
|
|
"category": "crash",
|
|
"requirement_type": "crash_plays",
|
|
"requirement_value": 1,
|
|
"reward_amount": 100,
|
|
"sort_order": 16,
|
|
},
|
|
{
|
|
"name": "crash_pro",
|
|
"title": "Крэш-профи",
|
|
"description": "Сыграй 50 раундов в Crash",
|
|
"icon": "💥",
|
|
"category": "crash",
|
|
"requirement_type": "crash_plays",
|
|
"requirement_value": 50,
|
|
"reward_amount": 2000,
|
|
"sort_order": 17,
|
|
},
|
|
{
|
|
"name": "crash_5x",
|
|
"title": "Смельчак",
|
|
"description": "Выведи деньги на множителе x5 или выше в Crash",
|
|
"icon": "🚀",
|
|
"category": "crash",
|
|
"requirement_type": "crash_cashout_5x",
|
|
"requirement_value": 1,
|
|
"reward_amount": 1500,
|
|
"sort_order": 18,
|
|
},
|
|
{
|
|
"name": "inventory_50",
|
|
"title": "Собиратель",
|
|
"description": "Собери 50 предметов в инвентаре",
|
|
"icon": "🎒",
|
|
"category": "general",
|
|
"requirement_type": "inventory_count",
|
|
"requirement_value": 50,
|
|
"reward_amount": 500,
|
|
"sort_order": 19,
|
|
},
|
|
]
|
|
|
|
|
|
def seed_achievements(db: Session):
|
|
"""Создаёт достижения в БД если их ещё нет"""
|
|
existing = db.query(Achievement).count()
|
|
if existing > 0:
|
|
return
|
|
|
|
for defn in ACHIEVEMENT_DEFS:
|
|
ach = Achievement(**defn)
|
|
db.add(ach)
|
|
db.commit()
|
|
|
|
|
|
def get_or_create_user_achievement(db: Session, user_id: int, achievement_id: int) -> UserAchievement:
|
|
ua = db.query(UserAchievement).filter(
|
|
UserAchievement.user_id == user_id,
|
|
UserAchievement.achievement_id == achievement_id
|
|
).first()
|
|
if not ua:
|
|
ua = UserAchievement(user_id=user_id, achievement_id=achievement_id)
|
|
db.add(ua)
|
|
db.commit()
|
|
db.refresh(ua)
|
|
return ua
|
|
|
|
|
|
def check_achievement(db: Session, user_id: int, achievement_name: str, current_value: int) -> Optional[dict]:
|
|
"""Проверяет и разблокирует достижение. Возвращает dict с инфой если разблокировано."""
|
|
ach = db.query(Achievement).filter(Achievement.name == achievement_name).first()
|
|
if not ach:
|
|
return None
|
|
|
|
ua = get_or_create_user_achievement(db, user_id, ach.id)
|
|
if ua.unlocked_at:
|
|
return None
|
|
|
|
ua.progress = min(current_value, ach.requirement_value)
|
|
|
|
if current_value >= ach.requirement_value:
|
|
ua.unlocked_at = datetime.utcnow()
|
|
# Награда
|
|
from database import User
|
|
user = db.query(User).filter(User.id == user_id).first()
|
|
if user:
|
|
user.balance += ach.reward_amount
|
|
db.commit()
|
|
|
|
# Запись в ленту активности
|
|
activity = ActivityFeed(
|
|
user_id=user_id,
|
|
username=user.username if user else "unknown",
|
|
activity_type="achievement",
|
|
message=f"🏆 Получено достижение: {ach.title}",
|
|
data_json=json.dumps({
|
|
"achievement_name": ach.name,
|
|
"achievement_title": ach.title,
|
|
"achievement_icon": ach.icon,
|
|
})
|
|
)
|
|
db.add(activity)
|
|
db.commit()
|
|
|
|
return {
|
|
"name": ach.name,
|
|
"title": ach.title,
|
|
"icon": ach.icon,
|
|
"reward": ach.reward_amount
|
|
}
|
|
|
|
db.commit()
|
|
return None
|
|
|
|
|
|
def check_open_cases_achievements(db: Session, user_id: int):
|
|
from database import CaseOpening
|
|
count = db.query(CaseOpening).filter(CaseOpening.user_id == user_id).count()
|
|
|
|
results = []
|
|
for ach_name in ["first_case", "case_collector_10", "case_collector_100", "case_collector_1000"]:
|
|
r = check_achievement(db, user_id, ach_name, count)
|
|
if r:
|
|
results.append(r)
|
|
return results
|
|
|
|
|
|
def check_contracts_achievements(db: Session, user_id: int):
|
|
from database import Contract
|
|
count = db.query(Contract).filter(Contract.user_id == user_id).count()
|
|
|
|
results = []
|
|
for ach_name in ["first_contract", "contract_master"]:
|
|
r = check_achievement(db, user_id, ach_name, count)
|
|
if r:
|
|
results.append(r)
|
|
return results
|
|
|
|
|
|
def check_upgrade_achievements(db: Session, user_id: int):
|
|
from database import Upgrade
|
|
attempts = db.query(Upgrade).filter(Upgrade.user_id == user_id).count()
|
|
wins = db.query(Upgrade).filter(Upgrade.user_id == user_id, Upgrade.success == True).count()
|
|
|
|
results = []
|
|
r1 = check_achievement(db, user_id, "first_upgrade", attempts)
|
|
if r1: results.append(r1)
|
|
r2 = check_achievement(db, user_id, "upgrade_winner", wins)
|
|
if r2: results.append(r2)
|
|
return results
|
|
|
|
|
|
def check_rare_item_achievement(db: Session, user_id: int, rarity: str):
|
|
results = []
|
|
if rarity in ("Covert", "Extraordinary", "Rare Special Item"):
|
|
r = check_achievement(db, user_id, "rare_finder", 1)
|
|
if r: results.append(r)
|
|
return results
|
|
|
|
|
|
def check_knife_achievement(db: Session, user_id: int, item_data: dict):
|
|
from backend import is_knife, is_gloves
|
|
results = []
|
|
if is_knife(item_data) or is_gloves(item_data):
|
|
r = check_achievement(db, user_id, "knife_collector", 1)
|
|
if r: results.append(r)
|
|
return results
|
|
|
|
|
|
def check_spent_achievements(db: Session, user_id: int, total_spent: float):
|
|
results = []
|
|
r1 = check_achievement(db, user_id, "big_spender", int(total_spent))
|
|
if r1: results.append(r1)
|
|
r2 = check_achievement(db, user_id, "whale", int(total_spent))
|
|
if r2: results.append(r2)
|
|
return results
|
|
|
|
|
|
def check_balance_achievement(db: Session, user_id: int):
|
|
from database import User
|
|
user = db.query(User).filter(User.id == user_id).first()
|
|
if user and user.balance >= 100000:
|
|
r = check_achievement(db, user_id, "rich_100k", 1)
|
|
if r: return [r]
|
|
return []
|
|
|
|
|
|
def check_slot_match_achievement(db: Session, user_id: int):
|
|
results = []
|
|
r = check_achievement(db, user_id, "slot_winner", 1)
|
|
if r: results.append(r)
|
|
return results
|
|
|
|
|
|
def check_slot_plays_achievements(db: Session, user_id: int):
|
|
from database import CaseOpening
|
|
slot_count = db.query(CaseOpening).filter(
|
|
CaseOpening.user_id == user_id,
|
|
CaseOpening.case_name.ilike("%slot%")
|
|
).count()
|
|
results = []
|
|
r = check_achievement(db, user_id, "slots_10", slot_count)
|
|
if r: results.append(r)
|
|
return results
|
|
|
|
|
|
def check_inventory_achievement(db: Session, user_id: int):
|
|
from database import InventoryItem
|
|
count = db.query(InventoryItem).filter(InventoryItem.user_id == user_id).count()
|
|
results = []
|
|
r = check_achievement(db, user_id, "inventory_50", count)
|
|
if r: results.append(r)
|
|
return results
|
|
|
|
|
|
def get_user_achievements(db: Session, user_id: int) -> list:
|
|
"""Возвращает все достижения пользователя с прогрессом"""
|
|
all_ach = db.query(Achievement).order_by(Achievement.sort_order).all()
|
|
result = []
|
|
for ach in all_ach:
|
|
ua = db.query(UserAchievement).filter(
|
|
UserAchievement.user_id == user_id,
|
|
UserAchievement.achievement_id == ach.id
|
|
).first()
|
|
result.append({
|
|
"id": ach.id,
|
|
"name": ach.name,
|
|
"title": ach.title,
|
|
"description": ach.description,
|
|
"icon": ach.icon,
|
|
"category": ach.category,
|
|
"requirement_type": ach.requirement_type,
|
|
"requirement_value": ach.requirement_value,
|
|
"reward_amount": ach.reward_amount,
|
|
"progress": ua.progress if ua else 0,
|
|
"unlocked": ua.unlocked_at is not None if ua else False,
|
|
"unlocked_at": ua.unlocked_at.isoformat() if ua and ua.unlocked_at else None,
|
|
})
|
|
return result
|