"""Gera os assets proprios do LCD Racer (FredyGames, CC0): o fundo (moldura, estrada e botoes) e os quatro sons. Substituem os originais do Formula 1 de joyrider3774: o fundo vem de uma foto de um portatil de LCD de origem desconhecida e os sons sao CC BY-NC-ND (MADrigal), que nao podemos usar. Mantem a geometria da janela e da estrada que o codigo do jogo espera (estrada x 204..441, tracejados em x 275..288 e 356..369). Uso: gen-assets.py . Deterministico: sem aleatoriedade fora do gerador congruencial abaixo; precisa do Pillow (artifacts/tools/venv).""" import math import struct import sys import wave from pathlib import Path from PIL import Image, ImageDraw root = Path(sys.argv[1]) RATE = 44100 # ---------- sons: mono, 16 bits, 44,1 kHz ---------- def write_wav(name, samples): path = root / "assets" / "sounds" / name path.parent.mkdir(parents=True, exist_ok=True) with wave.open(str(path), "wb") as w: w.setnchannels(1) w.setsampwidth(2) w.setframerate(RATE) w.writeframes(struct.pack("<%dh" % len(samples), *[max(-32767, min(32767, int(s * 32767))) for s in samples])) def square(freq, dur, duty=0.5, amp=0.35, decay=3.0): n = int(RATE * dur) out = [] for i in range(n): t = i / RATE phase = (t * freq) % 1.0 env = math.exp(-decay * t / dur) * min(1.0, i / 120.0, (n - i) / 120.0) out.append((amp if phase < duty else -amp) * env) return out def notes(seq, duty=0.5, decay=2.0): out = [] for f, d in seq: out += square(f, d, duty, decay=decay) return out write_wav("tick.wav", square(1800, 0.04, 0.5, 0.3, 4.0)) write_wav("start.wav", notes([(262, 0.2), (330, 0.2), (392, 0.2), (523, 0.2), (523, 0.4)], 0.25, 1.5)) write_wav("gameover.wav", notes([(392, 0.3), (330, 0.3), (262, 0.3), (196, 0.6)], 0.5, 1.5)) seed = 12345 crash = [] n = int(RATE * 1.0) phase = 0.0 for i in range(n): seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF noise = (seed / 0x7FFFFFFF) * 2 - 1 t = i / RATE freq = 220 * math.exp(-3.0 * t) + 40 phase += freq / RATE saw = (phase % 1.0) * 2 - 1 env = math.exp(-4.0 * t) * min(1.0, i / 60.0, (n - i) / 400.0) crash.append((0.55 * noise + 0.35 * saw) * env * 0.8) write_wav("crash.wav", crash) # ---------- fundo 640x360 ---------- W, H = 640, 360 im = Image.new("RGBA", (W, H), (46, 50, 60, 255)) d = ImageDraw.Draw(im) d.rectangle([8, 8, W - 9, H - 9], fill=(140, 150, 160, 255), outline=(30, 32, 38, 255), width=3) d.rectangle([138, 28, 506, 338], fill=(12, 12, 14, 255)) # moldura da janela d.rectangle([145, 36, 499, 329], fill=(110, 110, 110, 255)) # laterais da pista d.rectangle([204, 36, 441, 329], fill=(172, 172, 172, 255)) # estrada (cinza do fundo dos carros) d.rectangle([200, 36, 203, 329], fill=(8, 8, 8, 255)) d.rectangle([442, 36, 445, 329], fill=(8, 8, 8, 255)) for x0 in (275, 356): for y0, y1 in ((36, 48), (85, 123), (160, 198), (235, 273), (310, 329)): d.rectangle([x0, y0, x0 + 13, y1], fill=(197, 197, 197, 255)) def button(cx, cy, r): d.ellipse([cx - r - 3, cy - r - 3, cx + r + 3, cy + r + 3], fill=(30, 32, 38, 255)) for k in range(r, 0, -1): # sombreado simples c = 120 + int(100 * (1 - k / r)) d.ellipse([cx - k - 4 + (r - k) // 3, cy - k - 4 + (r - k) // 3, cx + k - 4 + (r - k) // 3, cy + k - 4 + (r - k) // 3], fill=(c, 20, 25, 255)) button(60, 262, 24) button(585, 262, 24) d.polygon([(30, 215), (46, 207), (46, 223)], fill=(10, 10, 12, 255)) # seta esquerda d.polygon([(610, 215), (594, 207), (594, 223)], fill=(10, 10, 12, 255)) # seta direita out = root / "assets" / "graphics" / "background.png" out.parent.mkdir(parents=True, exist_ok=True) im.save(out, optimize=False)