#!/usr/bin/env python3 """Gera, para o build de 20,000 Light-Years Into Space, os arquivos que trocam material de procedencia duvidosa: tres texturas (as originais vinham do site Accustudio, "livre de direitos" so pelos termos do site) e o som do terremoto (a original dizia "dominio publico", sem fonte verificavel). Tudo e desenhado/sintetizado aqui, sem nenhum arquivo de terceiros, e fica sob a mesma GPL-2 do jogo. Mesma saida a cada execucao (sementes fixas). python3 -I gen-assets.py Texturas: PNG gravados com os nomes antigos (006metal_jpg, greenrust_jpg, rivets_jpg); o pygame le pelo conteudo. Precisa do Pillow (artifacts/tools/venv). """ import math import os import random import struct import sys import wave from PIL import Image, ImageDraw data_dir, wav_path = sys.argv[1:] def lattice(rng, n): return [[rng.random() for _ in range(n)] for _ in range(n)] def smooth(t): return t * t * (3 - 2 * t) def value_noise(w, h, cells, rng): """Ruido de valor periodico (emenda sem costura) de w x h pixels com 'cells' celulas por lado.""" g = lattice(rng, cells) out = [[0.0] * w for _ in range(h)] for y in range(h): fy = y * cells / h y0 = int(fy) ty = smooth(fy - y0) y1 = (y0 + 1) % cells for x in range(w): fx = x * cells / w x0 = int(fx) tx = smooth(fx - x0) x1 = (x0 + 1) % cells a = g[y0][x0] + (g[y0][x1] - g[y0][x0]) * tx b = g[y1][x0] + (g[y1][x1] - g[y1][x0]) * tx out[y][x] = a + (b - a) * ty return out def fractal(w, h, seed, octaves, base=4): rng = random.Random(seed) total = [[0.0] * w for _ in range(h)] amp, norm = 1.0, 0.0 for o in range(octaves): n = value_noise(w, h, base * (2 ** o), rng) for y in range(h): row, nrow = total[y], n[y] for x in range(w): row[x] += nrow[x] * amp norm += amp amp *= 0.55 return [[v / norm for v in row] for row in total] def mix(c0, c1, t): return tuple(int(c0[i] + (c1[i] - c0[i]) * t) for i in range(3)) def texture(name, w, h, seed, dark, light, octaves=5, base=4): f = fractal(w, h, seed, octaves, base) img = Image.new("RGB", (w, h)) px = img.load() for y in range(h): for x in range(w): t = min(1.0, max(0.0, (f[y][x] - 0.25) * 2.0)) px[x, y] = mix(dark, light, t) img.save(os.path.join(data_dir, name), format="PNG", optimize=False, compress_level=9) # rusty plate: dark brown to orange texture("006metal_jpg", 512, 512, 1101, (38, 16, 10), (150, 66, 28), octaves=8) # green-grey patina texture("greenrust_jpg", 256, 166, 2202, (18, 34, 28), (74, 96, 70), octaves=5, base=3) # diamond plate: little raised bars, alternating direction, on blue-grey steel S = 4 w, h, cell = 100, 76, 10 big = Image.new("RGB", (w * S, h * S), (96, 118, 152)) d = ImageDraw.Draw(big) rows, cols = h // cell, w // cell for r in range(rows + 1): for c in range(cols + 1): cx, cy = c * cell + cell // 2, r * cell + cell // 2 sign = 1 if (r + c) % 2 == 0 else -1 dx, dy = 3.6 * sign, 3.6 pts = [((cx - dx) * S, (cy - dy) * S), ((cx + dx) * S, (cy + dy) * S)] d.line([(pts[0][0] + S, pts[0][1] + S), (pts[1][0] + S, pts[1][1] + S)], fill=(60, 76, 104), width=int(2.6 * S)) d.line(pts, fill=(152, 172, 204), width=int(2.6 * S)) big.resize((w, h), Image.LANCZOS).save(os.path.join(data_dir, "rivets_jpg"), format="PNG", compress_level=9) # earthquake rumble: low-passed noise with a slow swell, 2.4 s, stereo 16 bit 44.1 kHz, looping without a click rate, secs = 44100, 2.4 n = int(rate * secs) rng = random.Random(3303) raw = [rng.uniform(-1, 1) for _ in range(n)] lp1 = lp2 = 0.0 filt = [] for i in range(n): lp1 += 0.018 * (raw[i] - lp1) # ~120 Hz lp2 += 0.10 * (raw[i] - lp2) # ~700 Hz filt.append(lp1 * 6.0 + lp2 * 0.9) fade = int(rate * 0.25) for i in range(fade): # cross-fade the end into the start, so the loop is seamless t = i / fade j = n - fade + i filt[j] = filt[j] * (1 - t) + filt[i] * t filt = filt[:n - fade] peak = max(abs(v) for v in filt) frames = bytearray() m = len(filt) for i, v in enumerate(filt): swell = 0.75 + 0.25 * math.sin(2 * math.pi * i / m * 3) s = int(32767 * 0.8 * swell * v / peak) frames += struct.pack("