brighter rarity colors, fix comments, verify pool accuracy
This commit is contained in:
1 parent
ed95c7ac30
commit
e61b0f1d88
3 files changed
+37
-26
No files matched your search
+21
-14
@@ -128,13 +128,16 @@ class StudentDB:
|
|||||||
return self.by_name.get(name.lower())
|
return self.by_name.get(name.lower())
|
||||||
|
|
||||||
def build_pool(self, banner: Optional[dict]) -> dict[int, list[dict]]:
|
def build_pool(self, banner: Optional[dict]) -> dict[int, list[dict]]:
|
||||||
"""Build the effective character pool for a banner.
|
"""Build the pullable character pool for a specific banner.
|
||||||
|
|
||||||
Returns {1: [...], 2: [...], 3: [...]} with characters pullable on this banner.
|
Pool rules match the real game:
|
||||||
|
- Regular: standard characters only (IsLimited == 0)
|
||||||
|
- Pickup: standard pool + rate-up students
|
||||||
|
- Limited: standard pool + limited rate-up (only that banner)
|
||||||
|
- Fes: standard pool + ALL limiteds (IsLimited == 1) + rate-ups
|
||||||
|
|
||||||
- Regular Recruitment (banner=None): standard pool only
|
Returns {1: [...], 2: [...], 3: [...]}.
|
||||||
- PickupGacha / LimitedGacha: standard pool + rate-up students
|
Pass banner=None for regular recruitment.
|
||||||
- FesGacha: standard pool + all limiteds + rate-ups
|
|
||||||
"""
|
"""
|
||||||
pool: dict[int, list[dict]] = {
|
pool: dict[int, list[dict]] = {
|
||||||
r: list(self._std_pool[r]) for r in (1, 2, 3)
|
r: list(self._std_pool[r]) for r in (1, 2, 3)
|
||||||
@@ -165,10 +168,12 @@ class StudentDB:
|
|||||||
return pool
|
return pool
|
||||||
|
|
||||||
def weighted_pick(self, pool: list[dict], rateup_names: list[str], rarity: int) -> dict:
|
def weighted_pick(self, pool: list[dict], rateup_names: list[str], rarity: int) -> dict:
|
||||||
"""Pick a student from a pool with rate-up weighting.
|
"""Pick a random student from a rarity pool with rate-up weighting.
|
||||||
|
|
||||||
For 3★ rarity, rate-up students each get ~23% weight (matching the
|
For 3★: rate-up characters get 3x higher weight than standard ones,
|
||||||
real game's 0.7% per rate-up out of 3% total). For 1-2★, uniform.
|
producing roughly the same rate-up frequency as the real game's 0.7%
|
||||||
|
per rate-up out of the 3% total.
|
||||||
|
For 1-2★: uniform random (rate-ups don't affect lower rarities).
|
||||||
"""
|
"""
|
||||||
if not pool:
|
if not pool:
|
||||||
return random.choice(self.students)
|
return random.choice(self.students)
|
||||||
@@ -205,16 +210,17 @@ db = StudentDB()
|
|||||||
|
|
||||||
# --- Banner types & rates ---------------------------------------------------
|
# --- Banner types & rates ---------------------------------------------------
|
||||||
|
|
||||||
# In-game rates per banner type
|
# Rate tables matching real Blue Archive gacha:
|
||||||
# Format: { gachaType: (rate_3star, rate_2star, rate_1star) }
|
# Regular: 3★ = 3.0%, 2★ = 18.5%, 1★ = 78.5%
|
||||||
# Fes banners double the 3-star rate to 6%
|
# Fes: 3★ = 6.0%, 2★ = 18.5%, 1★ = 75.5%
|
||||||
|
# 10th pull: 3★ = 3.0%, 2★ = 97.0%, 1★ = 0% (2★+ guarantee)
|
||||||
BANNER_RATES: dict[str, tuple[float, float, float]] = {
|
BANNER_RATES: dict[str, tuple[float, float, float]] = {
|
||||||
"FesGacha": (0.06, 0.185, 0.755),
|
"FesGacha": (0.06, 0.185, 0.755),
|
||||||
}
|
}
|
||||||
DEFAULT_RATES = (0.03, 0.185, 0.785)
|
DEFAULT_RATES = (0.03, 0.185, 0.785)
|
||||||
|
|
||||||
# The 10th pull on a 10-pull: guarantee at least 2-star
|
# 10th pull on a 10-pull guarantees 2★ minimum:
|
||||||
# (97% 2-star, 3% 3-star — the 1-star rate is redirected into 2-star)
|
# 3★ = 3.0%, 2★ = 97.0%, 1★ = 0%
|
||||||
PULL10_RATES = (0.03, 0.97, 0.0)
|
PULL10_RATES = (0.03, 0.97, 0.0)
|
||||||
|
|
||||||
|
|
||||||
@@ -239,7 +245,8 @@ def get_rates_for_banner(banner: dict) -> tuple[float, float, float]:
|
|||||||
async def fetch_banners(session: Optional[aiohttp.ClientSession] = None) -> dict:
|
async def fetch_banners(session: Optional[aiohttp.ClientSession] = None) -> dict:
|
||||||
"""Fetch live banner data from the BlueArchiveAPI.
|
"""Fetch live banner data from the BlueArchiveAPI.
|
||||||
|
|
||||||
Returns the parsed JSON: {"current": [...], "upcoming": [...], "ended": [...]}
|
Returns {"current": [...], "upcoming": [...], "ended": [...]}.
|
||||||
|
Each banner has: id, gachaType, rateups (list of names), startedAt, endedAt.
|
||||||
"""
|
"""
|
||||||
close_session = session is None
|
close_session = session is None
|
||||||
if close_session:
|
if close_session:
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ class BlueArchiveGacha(commands.Cog):
|
|||||||
|
|
||||||
@commands.group(name="gacha", aliases=["banners", "g"], invoke_without_command=True)
|
@commands.group(name="gacha", aliases=["banners", "g"], invoke_without_command=True)
|
||||||
async def gacha(self, ctx: commands.Context) -> None:
|
async def gacha(self, ctx: commands.Context) -> None:
|
||||||
"""Show live recruitment banners. Use subcommands: pick, info."""
|
"""Show currently active recruitment banners from the live API."""
|
||||||
try:
|
try:
|
||||||
await asyncio.wait_for(self._cache_ready.wait(), timeout=15.0)
|
await asyncio.wait_for(self._cache_ready.wait(), timeout=15.0)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
@@ -193,9 +193,9 @@ class BlueArchiveGacha(commands.Cog):
|
|||||||
|
|
||||||
@commands.command(name="pull")
|
@commands.command(name="pull")
|
||||||
async def pull_gacha(self, ctx: commands.Context, *, mode: str = "10") -> None:
|
async def pull_gacha(self, ctx: commands.Context, *, mode: str = "10") -> None:
|
||||||
"""Perform a gacha pull. Default is 10-pull.
|
"""Pull on your active recruitment banner.
|
||||||
|
|
||||||
Usage: !pull (10-pull)
|
Usage: !pull (10-pull with guaranteed 2★+ on 10th)
|
||||||
!pull single (1-pull)
|
!pull single (1-pull)
|
||||||
"""
|
"""
|
||||||
mode = mode.strip().lower()
|
mode = mode.strip().lower()
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ from .data import db
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# ── Layout constants (1920×1080 canvas) ────────────────────────────────────
|
# Layout: 1920x1080 canvas, 5x2 card grid, 10px rounded corners
|
||||||
|
# Each card: star banner → portrait → name strip
|
||||||
CANVAS_W, CANVAS_H = 1920, 1080
|
CANVAS_W, CANVAS_H = 1920, 1080
|
||||||
CARD_W, CARD_H = 280, 390
|
CARD_W, CARD_H = 280, 390
|
||||||
CARD_RADIUS = 10 # corner radius
|
CARD_RADIUS = 10 # corner radius
|
||||||
@@ -32,16 +33,16 @@ NAME_STRIP_H = 42 # name/school strip at bottom
|
|||||||
PORTRAIT_TOP = STAR_BANNER_H + 4 # portrait starts below star banner
|
PORTRAIT_TOP = STAR_BANNER_H + 4 # portrait starts below star banner
|
||||||
PORTRAIT_H = CARD_H - PORTRAIT_TOP - NAME_STRIP_H - 4
|
PORTRAIT_H = CARD_H - PORTRAIT_TOP - NAME_STRIP_H - 4
|
||||||
|
|
||||||
# Rarity colours
|
# Rarity frame and star colours
|
||||||
RARITY_COLORS = {
|
RARITY_COLORS = {
|
||||||
3: (210, 150, 250), # purple (bright)
|
3: (240, 190, 255), # bright purple
|
||||||
2: (255, 225, 80), # gold (bright)
|
2: (255, 250, 140), # bright gold
|
||||||
1: (130, 200, 255), # blue (bright)
|
1: (170, 225, 255), # bright sky blue
|
||||||
}
|
}
|
||||||
RARITY_BG = {
|
RARITY_BG = {
|
||||||
3: (30, 18, 42),
|
3: (38, 22, 50),
|
||||||
2: (42, 34, 18),
|
2: (50, 42, 22),
|
||||||
1: (18, 26, 42),
|
1: (22, 30, 50),
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSETS_DIR = Path(__file__).parent / "assets"
|
ASSETS_DIR = Path(__file__).parent / "assets"
|
||||||
@@ -126,7 +127,10 @@ def _rounded_rect(
|
|||||||
|
|
||||||
|
|
||||||
def _star_polygon(cx: int, cy: int, outer_r: int, inner_r: int) -> list[tuple[int, int]]:
|
def _star_polygon(cx: int, cy: int, outer_r: int, inner_r: int) -> list[tuple[int, int]]:
|
||||||
"""Return vertices for a 5-pointed star centered at (cx, cy)."""
|
"""Generate vertex list for a 5-pointed star centered at (cx, cy).
|
||||||
|
|
||||||
|
Uses 10 alternating outer/inner radius points, rotated by pi/5 each step.
|
||||||
|
"""
|
||||||
import math
|
import math
|
||||||
points = []
|
points = []
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
|
|||||||
Reference in new issue
Block a user