Files
georgios db49b466c2 Cosmic Candy Pinball: procedural 8-bit pinball game
Native pygame-ce game with fully procedural pixel art and chiptune
audio (no asset files). Pure-logic physics module with flipper angular
momentum, plus headless autoplay/screenshot/probe dev tools.
2026-09-09 16:18:39 +02:00

117 lines
5.0 KiB
Python

"""Tiny hand-rolled 5x5 pixel bitmap font, rendered chunky for an 8-bit look."""
import pygame
GLYPHS = {
"A": [".###.", "#...#", "#####", "#...#", "#...#"],
"B": ["####.", "#...#", "####.", "#...#", "####."],
"C": [".###.", "#....", "#....", "#....", ".###."],
"D": ["####.", "#...#", "#...#", "#...#", "####."],
"E": ["#####", "#....", "####.", "#....", "#####"],
"F": ["#####", "#....", "####.", "#....", "#...."],
"G": [".###.", "#....", "#.###", "#...#", ".###."],
"H": ["#...#", "#...#", "#####", "#...#", "#...#"],
"I": ["#####", "..#..", "..#..", "..#..", "#####"],
"J": ["..###", "...#.", "...#.", "#..#.", ".##.."],
"K": ["#...#", "#..#.", "###..", "#..#.", "#...#"],
"L": ["#....", "#....", "#....", "#....", "#####"],
"M": ["#...#", "##.##", "#.#.#", "#...#", "#...#"],
"N": ["#...#", "##..#", "#.#.#", "#..##", "#...#"],
"O": [".###.", "#...#", "#...#", "#...#", ".###."],
"P": ["####.", "#...#", "####.", "#....", "#...."],
"Q": [".###.", "#...#", "#...#", "#..#.", ".##.#"],
"R": ["####.", "#...#", "####.", "#..#.", "#...#"],
"S": [".###.", "#....", ".###.", "....#", "####."],
"T": ["#####", "..#..", "..#..", "..#..", "..#.."],
"U": ["#...#", "#...#", "#...#", "#...#", ".###."],
"V": ["#...#", "#...#", "#...#", ".#.#.", "..#.."],
"W": ["#...#", "#...#", "#.#.#", "##.##", "#...#"],
"X": ["#...#", ".#.#.", "..#..", ".#.#.", "#...#"],
"Y": ["#...#", ".#.#.", "..#..", "..#..", "..#.."],
"Z": ["#####", "...#.", "..#..", ".#...", "#####"],
"0": [".###.", "#..##", "#.#.#", "##..#", ".###."],
"1": ["..#..", ".##..", "..#..", "..#..", ".###."],
"2": ["####.", "....#", ".###.", "#....", "#####"],
"3": ["####.", "....#", ".###.", "....#", "####."],
"4": ["#...#", "#...#", "#####", "....#", "....#"],
"5": ["#####", "#....", "####.", "....#", "####."],
"6": [".###.", "#....", "####.", "#...#", ".###."],
"7": ["#####", "....#", "...#.", "..#..", ".#..."],
"8": [".###.", "#...#", ".###.", "#...#", ".###."],
"9": [".###.", "#...#", ".####", "....#", ".###."],
" ": [".....", ".....", ".....", ".....", "....."],
".": [".....", ".....", ".....", "..##.", "..##."],
",": [".....", ".....", "..#..", "..#..", ".#..."],
"!": ["..#..", "..#..", "..#..", ".....", "..#.."],
"?": [".###.", "#...#", "..#..", ".....", "..#.."],
"'": ["..#..", "..#..", ".....", ".....", "....."],
'"': [".#.#.", ".#.#.", ".....", ".....", "....."],
":": [".....", "..#..", ".....", "..#..", "....."],
";": [".....", "..#..", ".....", "..#..", ".#..."],
"-": [".....", ".....", "#####", ".....", "....."],
"+": ["..#..", "..#..", "#####", "..#..", "..#.."],
"*": ["#...#", ".#.#.", "..#..", ".#.#.", "#...#"],
"/": ["....#", "...#.", "..#..", ".#...", "#...."],
"=": [".....", "#####", ".....", "#####", "....."],
"<": ["...#.", "..#..", ".#...", "..#..", "...#."],
">": [".#...", "..#..", "...#.", "..#..", ".#..."],
"(": ["...#.", "..#..", ".#...", "..#..", "...#."],
")": [".#...", "..#..", "...#.", "..#..", ".#..."],
"~": [".....", ".#.#.", ".#.#.", ".....", "....."],
"|": ["..#..", "..#..", "..#..", "..#..", "..#.."],
"%": ["##..#", "##.#.", "..#..", ".#.##", "#..##"],
"&": [".##..", "#..#.", ".#...", "#.#.#", ".##.#"],
"@": [".###.", "#.#.#", "#.###", "#....", ".###."],
"#": [".#.#.", "#####", ".#.#.", "#####", ".#.#."],
"^": ["..#..", ".#.#.", "#...#", ".....", "....."],
"_": [".....", ".....", ".....", ".....", "#####"],
}
_text_cache = {}
def text_width(text, scale=1, spacing=1):
if not text:
return 0
return (len(text) * 5 + (len(text) - 1) * spacing) * scale
def render_text(text, color, scale=1, spacing=1, shadow=None):
"""Return a Surface with the pixel text drawn."""
key = (text, color, scale, spacing, shadow)
if key in _text_cache:
return _text_cache[key]
w = text_width(text, scale, spacing)
h = 5 * scale
surf = pygame.Surface((w, h), pygame.SRCALPHA)
if shadow is not None:
_draw_into(surf, text, shadow, scale, spacing, (scale, scale))
_draw_into(surf, text, color, scale, spacing, (0, 0))
_text_cache[key] = surf
return surf
def _draw_into(surf, text, color, scale, spacing, off):
x0 = off[0]
for ch in text.upper():
g = GLYPHS.get(ch, GLYPHS[" "])
for row in range(5):
for col in range(5):
if g[row][col] == "#":
surf.fill(color, (x0 + col * scale, off[1] + row * scale, scale, scale))
x0 += (5 + spacing) * scale
def draw_text(surf, x, y, text, color, scale=1, spacing=1, align="left", shadow=(20, 12, 40, 180)):
"""Draw text. align in {'left','center','right'}. Shadow color or None."""
t = render_text(text, color, scale, spacing, shadow)
if align == "center":
x -= t.get_width() // 2
elif align == "right":
x -= t.get_width()
surf.blit(t, (round(x), round(y)))
return t.get_width(), t.get_height()