Files
Flora/ui/relics_overview.py

178 lines
7.7 KiB
Python

import pygame
from engine.plants import RARITY_COLORS
from engine.relics import RELIC_POOL
BG = (24, 30, 24)
TEXT = (230, 235, 230)
MUTED = (150, 155, 150)
HINT = (255, 220, 120)
PANEL = (30, 36, 30)
PANEL_BORDER = (150, 190, 150)
CARD = (36, 44, 36)
CARD_OWNED = (46, 54, 42)
GOLD = (245, 200, 90)
OWNED = (110, 220, 255) # bright cyan: distinct from the legendary gold border
TIER_ORDER = ("common", "rare", "epic", "legendary")
TIER_LABELS = {"common": "Common", "rare": "Rare", "epic": "Epic", "legendary": "Legendary"}
def _wrap(text, font, width):
words = text.split()
lines = []
cur = ""
for w in words:
if font.size(cur + " " + w)[0] > width and cur:
lines.append(cur)
cur = w
else:
cur = (cur + " " + w).strip()
if cur:
lines.append(cur)
return lines
class RelicsOverview:
"""Full-screen overlay listing every blessing, grouped by rarity, with the
ones collected this run highlighted. Click a relic to inspect it up close.
Host screens draw this onto their own surface (no pygame display ownership),
so it can open mid-battle without tearing down the window."""
def __init__(self, screen, bold, small, tiny, pool=None, owned_keys=()):
self.screen = screen
self.bold = bold
self.small = small
self.tiny = tiny
self.pool = list(pool if pool is not None else RELIC_POOL)
self.owned = set(owned_keys)
self.scroll = 0
self.detail = None
w, h = screen.get_size()
pw = min(760, w - 60)
ph = min(500, h - 140)
self.panel = pygame.Rect((w - pw) // 2, (h - ph) // 2, pw, ph)
pad = 24
self.cards_top = self.panel.y + 78
self.inner = pygame.Rect(self.panel.x + pad, self.cards_top,
self.panel.w - 2 * pad,
self.panel.bottom - 20 - self.cards_top)
self.close_rect = pygame.Rect(self.panel.right - 74, self.panel.y + 14, 58, 28)
rank = {t: i for i, t in enumerate(TIER_ORDER)}
self.pool.sort(key=lambda r: (rank.get(r.rarity, 9), r.name.lower()))
cols = 3
gap = 14
cw = (self.inner.w - gap * (cols - 1)) // cols
ch = 118
self.cards = []
x, y = self.inner.x, self.inner.y
for i, r in enumerate(self.pool):
self.cards.append((r, pygame.Rect(x, y, cw, ch)))
if (i + 1) % cols == 0:
x = self.inner.x
y += ch + gap
else:
x += cw + gap
self.content_h = y - self.inner.y if self.cards else 0
self.scroll_max = max(0, self.content_h - self.inner.h)
def handle_wheel(self, y):
if self.detail is not None:
return
self.scroll = max(0, min(self.scroll_max, self.scroll - y * 40))
def handle_click(self, pos):
if self.detail is not None:
self.detail = None
return None
if self.close_rect.collidepoint(pos):
return "close"
if not self.inner.collidepoint(pos):
return "close" if not self.panel.collidepoint(pos) else None
for r, rect in self.cards:
draw_rect = rect.move(0, -self.scroll)
if draw_rect.collidepoint(pos) and draw_rect.colliderect(self.inner):
self.detail = r
return None
return None
def _draw_card(self, r, rect):
owned = r.key in self.owned
bg = CARD_OWNED if owned else CARD
border = OWNED if owned else RARITY_COLORS.get(r.rarity, (150, 150, 150))
pygame.draw.rect(self.screen, bg, rect, border_radius=8)
pygame.draw.rect(self.screen, border, rect, 3 if owned else 1, border_radius=8)
name = self.small.render(r.name, True, RARITY_COLORS.get(r.rarity, TEXT))
self.screen.blit(name, (rect.x + 10, rect.y + 8))
if owned:
tag = self.tiny.render("OWNED", True, OWNED)
self.screen.blit(tag, (rect.right - tag.get_width() - 8, rect.y + 10))
y = rect.y + 34
for ln in _wrap(r.desc, self.tiny, rect.w - 20)[:3]:
self.screen.blit(self.tiny.render(ln, True, MUTED), (rect.x + 10, y))
y += 16
tip = self.tiny.render(f"{TIER_LABELS.get(r.rarity, r.rarity)}" +
(" · click to inspect" if owned else " · not owned"),
True, OWNED if owned else (110, 120, 110))
self.screen.blit(tip, (rect.x + 10, rect.bottom - 18))
def _draw_detail(self):
r = self.detail
if r is None:
return
shade = pygame.Surface(self.screen.get_size(), pygame.SRCALPHA)
shade.fill((0, 0, 0, 130))
self.screen.blit(shade, (0, 0))
w, h = 440, 230
panel = pygame.Rect((self.screen.get_width() - w) // 2,
(self.screen.get_height() - h) // 2, w, h)
pygame.draw.rect(self.screen, (36, 44, 36), panel, border_radius=10)
pygame.draw.rect(self.screen, RARITY_COLORS.get(r.rarity, PANEL_BORDER),
panel, 2, border_radius=10)
name = self.bold.render(r.name, True, RARITY_COLORS.get(r.rarity, TEXT))
self.screen.blit(name, name.get_rect(midtop=(panel.centerx, panel.y + 22)))
rar = self.small.render(TIER_LABELS.get(r.rarity, r.rarity).capitalize(), True, MUTED)
self.screen.blit(rar, rar.get_rect(midtop=(panel.centerx, panel.y + 54)))
y = panel.y + 92
for ln in _wrap(r.desc, self.small, panel.w - 48)[:5]:
line = self.small.render(ln, True, TEXT)
self.screen.blit(line, line.get_rect(midtop=(panel.centerx, y)))
y += 26
owned = r.key in self.owned
st = self.tiny.render("OWNED this run" if owned else "Not owned yet", True,
OWNED if owned else MUTED)
self.screen.blit(st, st.get_rect(midtop=(panel.centerx, y + 4)))
close = self.tiny.render("Click anywhere to close", True, MUTED)
self.screen.blit(close, close.get_rect(center=(panel.centerx, panel.bottom - 20)))
def draw(self, mouse):
overlay = pygame.Surface(self.screen.get_size(), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 180))
self.screen.blit(overlay, (0, 0))
pygame.draw.rect(self.screen, PANEL, self.panel, border_radius=12)
pygame.draw.rect(self.screen, PANEL_BORDER, self.panel, 2, border_radius=12)
title = self.bold.render("All blessings", True, HINT)
self.screen.blit(title, title.get_rect(midtop=(self.panel.centerx, self.panel.y + 14)))
sub = self.tiny.render(
f"{len(self.owned)} owned this run · cyan border = collected · click a card to inspect",
True, MUTED)
self.screen.blit(sub, sub.get_rect(midtop=(self.panel.centerx, self.panel.y + 44)))
pygame.draw.rect(self.screen, (90, 110, 90), self.close_rect, border_radius=6)
pygame.draw.rect(self.screen, PANEL_BORDER, self.close_rect, 1, border_radius=6)
c = self.small.render("Close", True, TEXT)
self.screen.blit(c, c.get_rect(center=self.close_rect.center))
old = self.screen.get_clip()
self.screen.set_clip(self.inner)
for r, rect in self.cards:
draw_rect = rect.move(0, -self.scroll)
if not draw_rect.colliderect(self.inner):
continue
self._draw_card(r, draw_rect)
self.screen.set_clip(old)
if self.scroll_max > 0:
hint = ("▲" if self.scroll > 0 else "") + ("▼" if self.scroll < self.scroll_max else "")
if hint:
ht = self.tiny.render(hint, True, MUTED)
self.screen.blit(ht, ht.get_rect(midbottom=(self.inner.centerx, self.inner.bottom)))
if self.detail is not None:
self._draw_detail()