"""Vanity shop: cosmetic badges/titles - a pure status sink for the wealthy. No gameplay effect whatsoever. Buying burns the coins (they are NOT credited to the house, so they leave circulation for good) and unlocks a badge the player can equip; the equipped badge shows on /profile. Prices scale steeply on purpose - this is where the richest players dump coins for bragging rights, draining the top end of the economy where inflation hurts most. A single entry point, do_vanity_select, handles buy / equip / unequip so the whole feature fits one slash command. """ from __future__ import annotations from typing import TypedDict from ..pb_client import DatabaseError from .store import _commit, _locked_by, _txn, get_user __all__ = [ "Vanity", "VANITY", "NONE_ID", "vanity_badge", "do_vanity_select", ] class Vanity(TypedDict): name: str # short shop name emoji: str # the badge shown next to the player title: str # the flavour title shown on /profile cost: int # Ordered cheapest -> priciest: a LAN/gaming status ladder from casual couch # gamer to LAN legend. The top rungs are the deliberate whale sink. VANITY: dict[str, Vanity] = { "couch": {"name": "Sohvapadi", "emoji": "🎮", "title": "Sohvasõdur", "cost": 5_000}, "cables": {"name": "Võrgukaabel", "emoji": "🔌", "title": "Kaablihaldur", "cost": 8_000}, "discord": {"name": "Peakomplekt", "emoji": "🎧", "title": "Discordi Admin", "cost": 12_000}, "aimbot": {"name": "Kahtlane Hiir", "emoji": "🖱️", "title": "Aimbot Kahtlusalune", "cost": 18_000}, "sniper": {"name": "360Hz Monitor", "emoji": "🎯", "title": "Snaipripüss", "cost": 25_000}, "champ": {"name": "Meistrikarikas", "emoji": "🏆", "title": "Turniirivõitja", "cost": 40_000}, "legend": {"name": "Mängurijaam", "emoji": "🖥️", "title": "LAN Legend", "cost": 75_000}, } # Sentinel choice value that unequips the active badge. NONE_ID = "none" def vanity_badge(user) -> tuple[str, str] | None: """Return (emoji, title) for the user's equipped badge, or None.""" vid = user.get("vanity_active") vanity = VANITY.get(vid) if vid else None return (vanity["emoji"], vanity["title"]) if vanity else None @_locked_by(0) async def do_vanity_select(user_id: int, vanity_id: str) -> dict: """Buy (if unowned), equip (if owned), or unequip (NONE_ID) a badge.""" try: user = await get_user(user_id) except DatabaseError: return {"ok": False, "reason": "db_error"} if user.get("eco_banned"): return {"ok": False, "reason": "banned"} if vanity_id == NONE_ID: user["vanity_active"] = None await _commit(user_id, user) _txn("VANITY_UNEQUIP", user=user_id) return {"ok": True, "action": "unequipped"} if vanity_id not in VANITY: return {"ok": False, "reason": "not_found"} owned = list(user.get("vanity_owned") or []) vanity = VANITY[vanity_id] # Already owned -> just equip it (free). if vanity_id in owned: user["vanity_active"] = vanity_id await _commit(user_id, user) _txn("VANITY_EQUIP", user=user_id, item=vanity_id) return {"ok": True, "action": "equipped", "vanity": vanity} # Not owned -> purchase (burns the coins) and auto-equip. if user["balance"] < vanity["cost"]: return {"ok": False, "reason": "insufficient", "need": vanity["cost"] - user["balance"]} user["balance"] -= vanity["cost"] # burned: not credited to the house owned.append(vanity_id) user["vanity_owned"] = owned user["vanity_active"] = vanity_id await _commit(user_id, user) _txn("VANITY_BUY", user=user_id, item=vanity_id, cost=f"-{vanity['cost']}", bal=user["balance"]) return {"ok": True, "action": "bought", "vanity": vanity, "balance": user["balance"]}