brighter rarity colors, fix comments, verify pool accuracy

This commit is contained in:
esef committed 2026-07-11 18:27:57 +03:00
1 parent ed95c7ac30
commit e61b0f1d88
3 files changed
+37 -26

No files matched your search

+21 -14
View File
@@ -128,13 +128,16 @@ class StudentDB:
return self.by_name.get(name.lower())
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
- PickupGacha / LimitedGacha: standard pool + rate-up students
- FesGacha: standard pool + all limiteds + rate-ups
Returns {1: [...], 2: [...], 3: [...]}.
Pass banner=None for regular recruitment.
"""
pool: dict[int, list[dict]] = {
r: list(self._std_pool[r]) for r in (1, 2, 3)
@@ -165,10 +168,12 @@ class StudentDB:
return pool
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
real game's 0.7% per rate-up out of 3% total). For 1-2★, uniform.
For 3★: rate-up characters get 3x higher weight than standard ones,
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:
return random.choice(self.students)
@@ -205,16 +210,17 @@ db = StudentDB()
# --- Banner types & rates ---------------------------------------------------
# In-game rates per banner type
# Format: { gachaType: (rate_3star, rate_2star, rate_1star) }
# Fes banners double the 3-star rate to 6%
# Rate tables matching real Blue Archive gacha:
# Regular: 3★ = 3.0%, 2★ = 18.5%, 1★ = 78.5%
# 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]] = {
"FesGacha": (0.06, 0.185, 0.755),
}
DEFAULT_RATES = (0.03, 0.185, 0.785)
# The 10th pull on a 10-pull: guarantee at least 2-star
# (97% 2-star, 3% 3-star — the 1-star rate is redirected into 2-star)
# 10th pull on a 10-pull guarantees 2★ minimum:
# 3★ = 3.0%, 2★ = 97.0%, 1★ = 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:
"""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
if close_session:
+3 -3
View File
@@ -101,7 +101,7 @@ class BlueArchiveGacha(commands.Cog):
@commands.group(name="gacha", aliases=["banners", "g"], invoke_without_command=True)
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:
await asyncio.wait_for(self._cache_ready.wait(), timeout=15.0)
except asyncio.TimeoutError:
@@ -193,9 +193,9 @@ class BlueArchiveGacha(commands.Cog):
@commands.command(name="pull")
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)
"""
mode = mode.strip().lower()
+13 -9
View File
@@ -14,7 +14,8 @@ from .data import db
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
CARD_W, CARD_H = 280, 390
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_H = CARD_H - PORTRAIT_TOP - NAME_STRIP_H - 4
# Rarity colours
# Rarity frame and star colours
RARITY_COLORS = {
3: (210, 150, 250), # purple (bright)
2: (255, 225, 80), # gold (bright)
1: (130, 200, 255), # blue (bright)
3: (240, 190, 255), # bright purple
2: (255, 250, 140), # bright gold
1: (170, 225, 255), # bright sky blue
}
RARITY_BG = {
3: (30, 18, 42),
2: (42, 34, 18),
1: (18, 26, 42),
3: (38, 22, 50),
2: (50, 42, 22),
1: (22, 30, 50),
}
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]]:
"""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
points = []
for i in range(10):