Files
dodep-simulator/achievements.py
T

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