470 lines
22 KiB
Python
470 lines
22 KiB
Python
# src/cogs/packs.py
|
||
import random
|
||
import discord
|
||
from discord.ext import commands
|
||
from typing import Optional, Dict, List, Tuple
|
||
|
||
from .. import db
|
||
|
||
# ---- Tunables (override via constants.py if you want) ----
|
||
try:
|
||
from ..utils.constants import PACK_MIN_BET, PACK_SIZE, PACK_RARITY_WEIGHTS
|
||
except Exception:
|
||
PACK_MIN_BET = 50
|
||
PACK_SIZE = 5
|
||
# weights per rarity (sum doesn't need to be 1; we normalize)
|
||
PACK_RARITY_WEIGHTS = {
|
||
"Common": 600,
|
||
"Uncommon": 250,
|
||
"Rare": 100,
|
||
"Epic": 40,
|
||
"Legendary": 9,
|
||
"Mythic": 1,
|
||
}
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Catalog: id, emoji, display name, rarity, multiplier (× bet)
|
||
# Kept your original 25 items & IDs; expanded to 100 total.
|
||
# -----------------------------------------------------------------------------
|
||
CATALOG: List[Dict] = [
|
||
# ---------------------- Common (40) ----------------------
|
||
{"id":"cherry","emoji":"🍒","name":"Cherries","rarity":"Common","mult":0.10},
|
||
{"id":"coin","emoji":"🪙","name":"Coin","rarity":"Common","mult":0.12},
|
||
{"id":"mug","emoji":"☕","name":"Mug","rarity":"Common","mult":0.15},
|
||
{"id":"leaf","emoji":"🌿","name":"Leaf","rarity":"Common","mult":0.18},
|
||
{"id":"sock","emoji":"🧦","name":"Sock","rarity":"Common","mult":0.20},
|
||
{"id":"teddy","emoji":"🧸","name":"Teddy","rarity":"Common","mult":0.22},
|
||
{"id":"ice","emoji":"🧊","name":"Ice","rarity":"Common","mult":0.24},
|
||
{"id":"clover","emoji":"🍀","name":"Clover","rarity":"Common","mult":0.25},
|
||
|
||
{"id":"apple","emoji":"🍎","name":"Apple","rarity":"Common","mult":0.12},
|
||
{"id":"banana","emoji":"🍌","name":"Banana","rarity":"Common","mult":0.14},
|
||
{"id":"pear","emoji":"🍐","name":"Pear","rarity":"Common","mult":0.13},
|
||
{"id":"peach","emoji":"🍑","name":"Peach","rarity":"Common","mult":0.16},
|
||
{"id":"strawberry","emoji":"🍓","name":"Strawberry","rarity":"Common","mult":0.18},
|
||
{"id":"grapes","emoji":"🍇","name":"Grapes","rarity":"Common","mult":0.20},
|
||
{"id":"carrot","emoji":"🥕","name":"Carrot","rarity":"Common","mult":0.12},
|
||
{"id":"corn","emoji":"🌽","name":"Corn","rarity":"Common","mult":0.15},
|
||
{"id":"bread","emoji":"🍞","name":"Bread","rarity":"Common","mult":0.14},
|
||
{"id":"cheese","emoji":"🧀","name":"Cheese","rarity":"Common","mult":0.18},
|
||
{"id":"egg","emoji":"🥚","name":"Egg","rarity":"Common","mult":0.16},
|
||
{"id":"milk","emoji":"🥛","name":"Milk","rarity":"Common","mult":0.12},
|
||
{"id":"mushroom","emoji":"🍄","name":"Mushroom","rarity":"Common","mult":0.20},
|
||
{"id":"seedling","emoji":"🌱","name":"Seedling","rarity":"Common","mult":0.16},
|
||
{"id":"flower","emoji":"🌼","name":"Flower","rarity":"Common","mult":0.16},
|
||
{"id":"maple","emoji":"🍁","name":"Maple Leaf","rarity":"Common","mult":0.18},
|
||
{"id":"sun","emoji":"☀️","name":"Sun","rarity":"Common","mult":0.24},
|
||
{"id":"moon","emoji":"🌙","name":"Moon","rarity":"Common","mult":0.22},
|
||
{"id":"cloud","emoji":"☁️","name":"Cloud","rarity":"Common","mult":0.18},
|
||
{"id":"snowflake","emoji":"❄️","name":"Snowflake","rarity":"Common","mult":0.20},
|
||
{"id":"umbrella","emoji":"☂️","name":"Umbrella","rarity":"Common","mult":0.18},
|
||
{"id":"balloon","emoji":"🎈","name":"Balloon","rarity":"Common","mult":0.22},
|
||
{"id":"pencil","emoji":"✏️","name":"Pencil","rarity":"Common","mult":0.14},
|
||
{"id":"book","emoji":"📘","name":"Book","rarity":"Common","mult":0.16},
|
||
{"id":"paperclip","emoji":"🖇️","name":"Paperclip","rarity":"Common","mult":0.14},
|
||
{"id":"scissors","emoji":"✂️","name":"Scissors","rarity":"Common","mult":0.16},
|
||
{"id":"bulb","emoji":"💡","name":"Light Bulb","rarity":"Common","mult":0.24},
|
||
{"id":"battery","emoji":"🔋","name":"Battery","rarity":"Common","mult":0.22},
|
||
{"id":"wrench","emoji":"🔧","name":"Wrench","rarity":"Common","mult":0.20},
|
||
{"id":"hammer","emoji":"🔨","name":"Hammer","rarity":"Common","mult":0.20},
|
||
{"id":"camera","emoji":"📷","name":"Camera","rarity":"Common","mult":0.24},
|
||
{"id":"gamepad","emoji":"🎮","name":"Gamepad","rarity":"Common","mult":0.24},
|
||
|
||
# -------------------- Uncommon (25) ---------------------
|
||
{"id":"donut","emoji":"🍩","name":"Donut","rarity":"Uncommon","mult":0.35},
|
||
{"id":"pizza","emoji":"🍕","name":"Pizza","rarity":"Uncommon","mult":0.40},
|
||
{"id":"soccer","emoji":"⚽","name":"Soccer Ball","rarity":"Uncommon","mult":0.45},
|
||
{"id":"headset","emoji":"🎧","name":"Headset","rarity":"Uncommon","mult":0.50},
|
||
{"id":"magnet","emoji":"🧲","name":"Magnet","rarity":"Uncommon","mult":0.55},
|
||
{"id":"cat","emoji":"🐱","name":"Cat","rarity":"Uncommon","mult":0.60},
|
||
|
||
{"id":"basketball","emoji":"🏀","name":"Basketball","rarity":"Uncommon","mult":0.45},
|
||
{"id":"baseball","emoji":"⚾","name":"Baseball","rarity":"Uncommon","mult":0.42},
|
||
{"id":"guitar","emoji":"🎸","name":"Guitar","rarity":"Uncommon","mult":0.55},
|
||
{"id":"violin","emoji":"🎻","name":"Violin","rarity":"Uncommon","mult":0.58},
|
||
{"id":"joystick","emoji":"🕹️","name":"Joystick","rarity":"Uncommon","mult":0.50},
|
||
{"id":"keyboard","emoji":"⌨️","name":"Keyboard","rarity":"Uncommon","mult":0.48},
|
||
{"id":"laptop","emoji":"💻","name":"Laptop","rarity":"Uncommon","mult":0.60},
|
||
{"id":"robot","emoji":"🤖","name":"Robot","rarity":"Uncommon","mult":0.62},
|
||
{"id":"dog","emoji":"🐶","name":"Dog","rarity":"Uncommon","mult":0.55},
|
||
{"id":"fox","emoji":"🦊","name":"Fox","rarity":"Uncommon","mult":0.58},
|
||
{"id":"penguin","emoji":"🐧","name":"Penguin","rarity":"Uncommon","mult":0.60},
|
||
{"id":"koala","emoji":"🐨","name":"Koala","rarity":"Uncommon","mult":0.60},
|
||
{"id":"panda","emoji":"🐼","name":"Panda","rarity":"Uncommon","mult":0.60},
|
||
{"id":"owl","emoji":"🦉","name":"Owl","rarity":"Uncommon","mult":0.65},
|
||
{"id":"butterfly","emoji":"🦋","name":"Butterfly","rarity":"Uncommon","mult":0.65},
|
||
{"id":"car","emoji":"🚗","name":"Car","rarity":"Uncommon","mult":0.55},
|
||
{"id":"train","emoji":"🚆","name":"Train","rarity":"Uncommon","mult":0.55},
|
||
{"id":"sailboat","emoji":"⛵","name":"Sailboat","rarity":"Uncommon","mult":0.58},
|
||
{"id":"airplane","emoji":"✈️","name":"Airplane","rarity":"Uncommon","mult":0.62},
|
||
|
||
# ---------------------- Rare (18) -----------------------
|
||
{"id":"melon","emoji":"🍉","name":"Watermelon","rarity":"Rare","mult":0.85},
|
||
{"id":"tiger","emoji":"🐯","name":"Tiger","rarity":"Rare","mult":1.00},
|
||
{"id":"ufo","emoji":"🛸","name":"UFO","rarity":"Rare","mult":1.10},
|
||
{"id":"unicorn","emoji":"🦄","name":"Unicorn","rarity":"Rare","mult":1.20},
|
||
|
||
{"id":"elephant","emoji":"🐘","name":"Elephant","rarity":"Rare","mult":0.95},
|
||
{"id":"lion","emoji":"🦁","name":"Lion","rarity":"Rare","mult":1.05},
|
||
{"id":"wolf","emoji":"🐺","name":"Wolf","rarity":"Rare","mult":1.05},
|
||
{"id":"dolphin","emoji":"🐬","name":"Dolphin","rarity":"Rare","mult":0.90},
|
||
{"id":"whale","emoji":"🐳","name":"Whale","rarity":"Rare","mult":0.90},
|
||
{"id":"alien","emoji":"👽","name":"Alien","rarity":"Rare","mult":1.10},
|
||
{"id":"ghost","emoji":"👻","name":"Ghost","rarity":"Rare","mult":1.00},
|
||
{"id":"crystalball","emoji":"🔮","name":"Crystal Ball","rarity":"Rare","mult":1.10},
|
||
{"id":"satellite","emoji":"🛰️","name":"Satellite","rarity":"Rare","mult":1.15},
|
||
{"id":"comet","emoji":"☄️","name":"Comet","rarity":"Rare","mult":1.20},
|
||
{"id":"ninja","emoji":"🥷","name":"Ninja","rarity":"Rare","mult":1.10},
|
||
{"id":"mountain","emoji":"⛰️","name":"Mountain","rarity":"Rare","mult":0.95},
|
||
{"id":"volcano","emoji":"🌋","name":"Volcano","rarity":"Rare","mult":1.00},
|
||
{"id":"ring_rare","emoji":"💍","name":"Ring","rarity":"Rare","mult":1.25},
|
||
|
||
# ---------------------- Epic (10) -----------------------
|
||
{"id":"dragon","emoji":"🐉","name":"Dragon","rarity":"Epic","mult":2.0},
|
||
{"id":"lab","emoji":"🧪","name":"Lab Flask","rarity":"Epic","mult":2.5},
|
||
{"id":"rocket","emoji":"🚀","name":"Rocket","rarity":"Epic","mult":3.0},
|
||
|
||
{"id":"wizard","emoji":"🧙","name":"Wizard","rarity":"Epic","mult":2.2},
|
||
{"id":"wand","emoji":"🪄","name":"Magic Wand","rarity":"Epic","mult":2.6},
|
||
{"id":"dragonface","emoji":"🐲","name":"Dragon Face","rarity":"Epic","mult":2.8},
|
||
{"id":"castle","emoji":"🏰","name":"Castle","rarity":"Epic","mult":2.4},
|
||
{"id":"shield","emoji":"🛡️","name":"Shield","rarity":"Epic","mult":2.3},
|
||
{"id":"swords","emoji":"⚔️","name":"Crossed Swords","rarity":"Epic","mult":2.7},
|
||
{"id":"trophy_epic","emoji":"🏆","name":"Trophy","rarity":"Epic","mult":3.2},
|
||
|
||
# ------------------- Legendary (5) ----------------------
|
||
{"id":"crown","emoji":"👑","name":"Crown","rarity":"Legendary","mult":6.0},
|
||
{"id":"eagle","emoji":"🦅","name":"Eagle","rarity":"Legendary","mult":7.5},
|
||
|
||
{"id":"medal","emoji":"🥇","name":"Gold Medal","rarity":"Legendary","mult":9.0},
|
||
{"id":"moneybag","emoji":"💰","name":"Money Bag","rarity":"Legendary","mult":8.5},
|
||
{"id":"ring_legend","emoji":"💍","name":"Royal Ring","rarity":"Legendary","mult":10.0},
|
||
|
||
# --------------------- Mythic (2) -----------------------
|
||
{"id":"dino","emoji":"🦖","name":"Dino","rarity":"Mythic","mult":25.0},
|
||
{"id":"diamond","emoji":"💎","name":"Diamond","rarity":"Mythic","mult":50.0},
|
||
]
|
||
|
||
# Build rarity index
|
||
RARITY_TO_ITEMS: Dict[str, List[Dict]] = {}
|
||
for it in CATALOG:
|
||
RARITY_TO_ITEMS.setdefault(it["rarity"], []).append(it)
|
||
|
||
def _norm_weights(weights: Dict[str, float]) -> Dict[str, float]:
|
||
s = float(sum(weights.values()))
|
||
return {k: (v / s if s > 0 else 0.0) for k, v in weights.items()}
|
||
|
||
NORM_RARITY = _norm_weights(PACK_RARITY_WEIGHTS)
|
||
|
||
def _weighted_choice(items: List[Tuple[float, Dict]]) -> Dict:
|
||
"""items: list of (weight, item) where weight >= 0"""
|
||
total = sum(w for w, _ in items)
|
||
r = random.random() * total
|
||
upto = 0.0
|
||
for w, it in items:
|
||
upto += w
|
||
if r <= upto:
|
||
return it
|
||
return items[-1][1]
|
||
|
||
def pick_one() -> Dict:
|
||
# pick rarity
|
||
rar_roll = _weighted_choice([(p, {"rarity": r}) for r, p in NORM_RARITY.items()])["rarity"]
|
||
pool = RARITY_TO_ITEMS.get(rar_roll, CATALOG)
|
||
# equal within rarity
|
||
return random.choice(pool)
|
||
|
||
def money(n: int) -> str:
|
||
return f"${n:,}"
|
||
|
||
# ---------- Pagination helpers ----------
|
||
RARITY_RANK = {"Mythic": 0, "Legendary": 1, "Epic": 2, "Rare": 3, "Uncommon": 4, "Common": 5}
|
||
SORTED_CATALOG = sorted(
|
||
CATALOG,
|
||
key=lambda it: (RARITY_RANK.get(it["rarity"], 99), -float(it["mult"]), it["name"])
|
||
)
|
||
# Attach rank (1 = rarest)
|
||
for i, it in enumerate(SORTED_CATALOG, 1):
|
||
it["_rank"] = i
|
||
|
||
PAGE_SIZE = 20 # 100 items -> 5 pages
|
||
|
||
def _collection_page_lines(user_id: int, page: int) -> Tuple[str, int, int, int]:
|
||
"""
|
||
Returns (text, start_idx, end_idx, total_pages) for the given page (0-based).
|
||
"""
|
||
coll = db.get_packs_collection(user_id)
|
||
total = len(SORTED_CATALOG)
|
||
pages = max(1, (total + PAGE_SIZE - 1) // PAGE_SIZE)
|
||
page = max(0, min(page, pages - 1))
|
||
start = page * PAGE_SIZE
|
||
end = min(total, start + PAGE_SIZE)
|
||
|
||
lines: List[str] = []
|
||
for it in SORTED_CATALOG[start:end]:
|
||
owned = coll.get(it["id"], 0) > 0
|
||
idx = it["_rank"]
|
||
if owned:
|
||
lines.append(f"**#{idx:02d}** {it['emoji']} **{it['name']}** · *{it['rarity']}*")
|
||
else:
|
||
lines.append(f"**#{idx:02d}** ?")
|
||
text = "\n".join(lines)
|
||
return text, start, end, pages
|
||
|
||
def _collection_embed(user_id: int, page: int) -> discord.Embed:
|
||
coll = db.get_packs_collection(user_id)
|
||
have = sum(1 for it in SORTED_CATALOG if coll.get(it["id"], 0) > 0)
|
||
total = len(SORTED_CATALOG)
|
||
text, start, end, pages = _collection_page_lines(user_id, page)
|
||
e = discord.Embed(
|
||
title="🗂️ Packs Collection",
|
||
description=f"**Progress:** {have}/{total} collected\n"
|
||
f"Rarest is **#01**, Common end is **#{total:02d}**.\n\n{text}",
|
||
color=discord.Color.dark_gold()
|
||
)
|
||
e.set_footer(text=f"Page {page+1}/{pages} • Showing #{start+1:02d}–#{end:02d}")
|
||
return e
|
||
|
||
# ---------- UI: Set Bet modal ----------
|
||
class SetBetModal(discord.ui.Modal, title="Set Packs Bet"):
|
||
def __init__(self, view: "PacksView"):
|
||
super().__init__()
|
||
self.view = view
|
||
self.amount = discord.ui.TextInput(
|
||
label=f"Amount (min {PACK_MIN_BET})",
|
||
placeholder=str(self.view.bet),
|
||
required=True, min_length=1, max_length=10,
|
||
)
|
||
self.add_item(self.amount)
|
||
|
||
async def on_submit(self, itx: discord.Interaction):
|
||
if itx.user.id != self.view.user_id:
|
||
return await itx.response.send_message("This isn’t your Packs panel.", ephemeral=True)
|
||
if self.view.busy:
|
||
return await itx.response.send_message("Please wait a moment.", ephemeral=True)
|
||
raw = str(self.amount.value)
|
||
try:
|
||
amt = int("".join(ch for ch in raw if ch.isdigit()))
|
||
except Exception:
|
||
return await itx.response.send_message("Enter a valid number.", ephemeral=True)
|
||
self.view.bet = max(PACK_MIN_BET, amt)
|
||
await itx.response.defer(ephemeral=True)
|
||
if self.view.message:
|
||
await self.view.message.edit(embed=self.view.render(), view=self.view)
|
||
|
||
# ---------- UI: Collection View ----------
|
||
class PacksCollectionView(discord.ui.View):
|
||
def __init__(self, user_id: int, *, start_page: int = 0, timeout: int = 180):
|
||
super().__init__(timeout=timeout)
|
||
self.user_id = user_id
|
||
self.page = start_page
|
||
self.pages = max(1, (len(SORTED_CATALOG) + PAGE_SIZE - 1) // PAGE_SIZE)
|
||
self.message: Optional[discord.Message] = None
|
||
|
||
async def on_timeout(self):
|
||
for c in self.children:
|
||
c.disabled = True
|
||
try:
|
||
if self.message:
|
||
await self.message.edit(view=self)
|
||
except:
|
||
pass
|
||
|
||
async def interaction_check(self, interaction: discord.Interaction) -> bool:
|
||
if interaction.user.id != self.user_id:
|
||
await interaction.response.send_message("This collection belongs to someone else.", ephemeral=True)
|
||
return False
|
||
return True
|
||
|
||
def _sync(self):
|
||
# enable/disable nav buttons based on current page
|
||
for c in self.children:
|
||
if isinstance(c, discord.ui.Button):
|
||
if c.custom_id == "first": c.disabled = (self.page <= 0)
|
||
if c.custom_id == "prev": c.disabled = (self.page <= 0)
|
||
if c.custom_id == "next": c.disabled = (self.page >= self.pages - 1)
|
||
if c.custom_id == "last": c.disabled = (self.page >= self.pages - 1)
|
||
|
||
def embed(self) -> discord.Embed:
|
||
self._sync()
|
||
return _collection_embed(self.user_id, self.page)
|
||
|
||
@discord.ui.button(label="⏮️", style=discord.ButtonStyle.secondary, custom_id="first")
|
||
async def first(self, itx: discord.Interaction, _):
|
||
self.page = 0
|
||
await itx.response.edit_message(embed=self.embed(), view=self)
|
||
|
||
@discord.ui.button(label="◀️", style=discord.ButtonStyle.secondary, custom_id="prev")
|
||
async def prev(self, itx: discord.Interaction, _):
|
||
if self.page > 0:
|
||
self.page -= 1
|
||
await itx.response.edit_message(embed=self.embed(), view=self)
|
||
|
||
@discord.ui.button(label="▶️", style=discord.ButtonStyle.secondary, custom_id="next")
|
||
async def next(self, itx: discord.Interaction, _):
|
||
if self.page < self.pages - 1:
|
||
self.page += 1
|
||
await itx.response.edit_message(embed=self.embed(), view=self)
|
||
|
||
@discord.ui.button(label="⏭️", style=discord.ButtonStyle.secondary, custom_id="last")
|
||
async def last(self, itx: discord.Interaction, _):
|
||
self.page = self.pages - 1
|
||
await itx.response.edit_message(embed=self.embed(), view=self)
|
||
|
||
@discord.ui.button(label="Close", style=discord.ButtonStyle.danger)
|
||
async def close(self, itx: discord.Interaction, _):
|
||
try:
|
||
await itx.message.delete()
|
||
except:
|
||
try:
|
||
for c in self.children: c.disabled = True
|
||
await itx.response.edit_message(view=self)
|
||
except:
|
||
pass
|
||
|
||
# ---------- Packs game UI ----------
|
||
class PacksView(discord.ui.View):
|
||
"""
|
||
Row 0: Set Bet · ×2 · ½ · Open Pack
|
||
Row 1: Open Again · Collection
|
||
"""
|
||
def __init__(self, user_id: int, initial_bet: int = 100, *, timeout: int = 180):
|
||
super().__init__(timeout=timeout)
|
||
self.user_id = user_id
|
||
self.bet = max(PACK_MIN_BET, initial_bet)
|
||
self.last_pack: Optional[List[Dict]] = None
|
||
self.last_return: int = 0
|
||
self.last_net: int = 0
|
||
self.busy = False
|
||
self.message: Optional[discord.Message] = None
|
||
|
||
async def on_timeout(self):
|
||
for c in self.children: c.disabled = True
|
||
try:
|
||
if self.message: await self.message.edit(view=self)
|
||
except: pass
|
||
|
||
# ---- render
|
||
def render(self) -> discord.Embed:
|
||
color = discord.Color.blurple()
|
||
if self.last_pack is not None:
|
||
color = discord.Color.green() if self.last_net > 0 else (discord.Color.red() if self.last_net < 0 else discord.Color.orange())
|
||
e = discord.Embed(title="📦 Packs", color=color)
|
||
|
||
cash, _ = db.get_wallet(self.user_id)
|
||
desc = [f"**Bet:** {money(self.bet)} • **Balance:** {money(cash)}"]
|
||
if self.last_pack is None:
|
||
desc += ["Open a pack of 5 items. Each item pays its multiplier × your bet. Collect them all!"]
|
||
e.description = "\n".join(desc)
|
||
|
||
if self.last_pack is not None:
|
||
lines = []
|
||
for it in self.last_pack:
|
||
tag = "NEW" if it.get("_new") else "DUP"
|
||
lines.append(f"{it['emoji']} **{it['name']}** · *{it['rarity']}* · ×{it['mult']:.2f} `{tag}`")
|
||
e.add_field(name="Last Pack", value="\n".join(lines), inline=False)
|
||
e.add_field(
|
||
name="Result",
|
||
value=f"Return: **{money(self.last_return)}** • Net: **{'+' if self.last_net>0 else ''}{money(self.last_net)}**",
|
||
inline=False
|
||
)
|
||
return e
|
||
|
||
# ---- actions
|
||
async def _open_pack(self, itx: discord.Interaction):
|
||
if self.busy: return
|
||
self.busy = True
|
||
|
||
cash, _ = db.get_wallet(self.user_id)
|
||
if self.bet > cash:
|
||
self.busy = False
|
||
return await itx.response.send_message("Not enough cash for that bet.", ephemeral=True)
|
||
|
||
# debit
|
||
db.add_cash(self.user_id, -self.bet)
|
||
|
||
# current collection to mark NEW/DUP
|
||
coll = db.get_packs_collection(self.user_id) # dict card_id -> qty
|
||
|
||
pulled: List[Dict] = []
|
||
total_mult = 0.0
|
||
for _ in range(PACK_SIZE):
|
||
it = pick_one().copy()
|
||
it["_new"] = (coll.get(it["id"], 0) == 0)
|
||
pulled.append(it)
|
||
total_mult += it["mult"]
|
||
# persist collection increment
|
||
db.add_pack_card(self.user_id, it["id"], 1)
|
||
coll[it["id"]] = coll.get(it["id"], 0) + 1
|
||
|
||
returned = int(self.bet * total_mult)
|
||
if returned > 0:
|
||
db.add_cash(self.user_id, returned)
|
||
|
||
db.record_packs_open(self.user_id, bet=self.bet, return_amount=returned, won=(returned > self.bet))
|
||
|
||
self.last_pack = pulled
|
||
self.last_return = returned
|
||
self.last_net = returned - self.bet
|
||
|
||
await itx.response.edit_message(embed=self.render(), view=self)
|
||
self.busy = False
|
||
|
||
# ----- Buttons
|
||
@discord.ui.button(label="Set Bet", style=discord.ButtonStyle.secondary, row=0)
|
||
async def set_bet(self, itx: discord.Interaction, _):
|
||
if itx.user.id != self.user_id: return await itx.response.send_message("Not your panel.", ephemeral=True)
|
||
await itx.response.send_modal(SetBetModal(self))
|
||
|
||
@discord.ui.button(label="×2", style=discord.ButtonStyle.secondary, row=0)
|
||
async def x2(self, itx: discord.Interaction, _):
|
||
if itx.user.id != self.user_id: return await itx.response.send_message("Not your panel.", ephemeral=True)
|
||
self.bet = max(PACK_MIN_BET, self.bet * 2)
|
||
await itx.response.edit_message(embed=self.render(), view=self)
|
||
|
||
@discord.ui.button(label="½", style=discord.ButtonStyle.secondary, row=0)
|
||
async def half(self, itx: discord.Interaction, _):
|
||
if itx.user.id != self.user_id: return await itx.response.send_message("Not your panel.", ephemeral=True)
|
||
self.bet = max(PACK_MIN_BET, self.bet // 2)
|
||
await itx.response.edit_message(embed=self.render(), view=self)
|
||
|
||
@discord.ui.button(label="Open Pack", style=discord.ButtonStyle.success, emoji="🎁", row=0)
|
||
async def open_pack(self, itx: discord.Interaction, _):
|
||
if itx.user.id != self.user_id: return await itx.response.send_message("Not your panel.", ephemeral=True)
|
||
await self._open_pack(itx)
|
||
|
||
@discord.ui.button(label="Open Again", style=discord.ButtonStyle.primary, emoji="🔁", row=1)
|
||
async def open_again(self, itx: discord.Interaction, _):
|
||
if itx.user.id != self.user_id: return await itx.response.send_message("Not your panel.", ephemeral=True)
|
||
await self._open_pack(itx)
|
||
|
||
@discord.ui.button(label="Collection", style=discord.ButtonStyle.secondary, emoji="🗂️", row=1)
|
||
async def collection(self, itx: discord.Interaction, _):
|
||
if itx.user.id != self.user_id:
|
||
return await itx.response.send_message("This isn’t your Packs panel.", ephemeral=True)
|
||
view = PacksCollectionView(self.user_id, start_page=0, timeout=180)
|
||
await itx.response.send_message(embed=view.embed(), view=view, ephemeral=True)
|
||
|
||
# ---------- Cog ----------
|
||
class PacksCog(commands.Cog):
|
||
def __init__(self, bot): self.bot = bot
|
||
|
||
@commands.command(name="packs")
|
||
async def packs(self, ctx: commands.Context, bet: int = None):
|
||
uid = ctx.author.id
|
||
view = PacksView(uid, initial_bet=bet or PACK_MIN_BET, timeout=180)
|
||
msg = await ctx.send(embed=view.render(), view=view)
|
||
view.message = msg
|
||
|
||
@commands.command(name="collection", aliases=["packdex","packsdex"])
|
||
async def collection(self, ctx: commands.Context):
|
||
uid = ctx.author.id
|
||
view = PacksCollectionView(uid, start_page=0, timeout=180)
|
||
msg = await ctx.send(embed=view.embed(), view=view)
|
||
view.message = msg
|
||
|
||
async def setup(bot):
|
||
# make sure DB tables exist for packs
|
||
db.ensure_packs_tables()
|
||
await bot.add_cog(PacksCog(bot))
|