add hilo, mines, and packs
This commit is contained in:
469
src/cogs/packs.py
Normal file
469
src/cogs/packs.py
Normal file
@@ -0,0 +1,469 @@
|
||||
# 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))
|
||||
Reference in New Issue
Block a user