SOURCE / PINNED RELEASE
Made of little things.
GRAV / RIFT
- Release
- 288aa261ce07…
- Author-recorded commit
- c549b5a4e17e…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-8a7361981e8
Archive hash verified: e52a01aff70810b3…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { hullFromPixels, type Vec } from './physics.js';
export interface Sprite { canvas: HTMLCanvasElement; hull: Vec[]; width: number; height: number }
export const PIXEL = 2;
const palettes = [
['#263d40', '#49615f', '#81928a', '#b6c4a7', '#efb663'],
['#402f26', '#835539', '#bd8050', '#e7b76f', '#f4db9a'],
['#273338', '#51656a', '#839c9e', '#cee0d0', '#edaa56'],
];
function make(index: number): Sprite {
const canvas = document.createElement('canvas'); canvas.width = 32; canvas.height = 32;
const c = canvas.getContext('2d', { willReadFrequently: true })!;
const p = palettes[index % 3];
const rect = (x: number, y: number, w: number, h: number, color: string) => { c.fillStyle = color; c.fillRect(x, y, w, h); };
if (index < 6) {
const shapes = [ [[6,11,21,9],[10,8,12,14]], [[9,5,10,23],[6,10,17,9]], [[5,12,24,7],[7,9,7,12]], [[7,7,18,17],[4,13,23,8]], [[9,8,17,17],[6,12,11,9]], [[5,10,22,13],[10,6,12,20]] ];
for (const [x,y,w,h] of shapes[index]) rect(x,y,w,h,p[0]);
rect(9,11,14,10,p[1]); rect(10,10,11,3,p[2]); rect(10,14,9,2,p[3]);
rect(17,18,5,3,p[2]); rect(8,12,2,3,p[4]); rect(22,13,2,2,p[4]);
if (index % 2 === 0) { rect(14,12,3,7,p[0]); rect(10,22,10,2,p[2]); }
else { rect(19,8,3,12,p[0]); rect(11,18,4,4,p[4]); }
} else if (index === 6) {
rect(10,5,12,23,'#342b20'); rect(8,9,16,15,'#4e6760'); rect(11,8,10,16,'#b76734');
rect(12,9,8,14,'#efac50'); rect(13,10,3,11,'#fff1b0'); rect(10,5,12,4,'#9ea99a');
rect(10,24,12,3,'#8c9d91'); rect(8,13,3,5,'#253f3e'); rect(21,13,3,5,'#253f3e');
} else if (index === 7) {
rect(4,9,24,16,'#243a3b'); rect(6,7,19,16,'#617571'); rect(7,8,16,5,'#91a199');
rect(8,15,17,6,'#374c4a'); rect(8,16,14,2,'#d3a555');
for (let x = 9; x < 23; x += 5) rect(x,16,2,2,'#4b3c2b');
rect(7,10,2,2,'#d0d6b5'); rect(23,21,2,2,'#b7c2a4'); rect(12,22,11,4,'#172a2d');
} else if (index === 8) {
rect(7,10,18,13,'#794934'); rect(10,7,12,18,'#bd8a52'); rect(9,11,16,6,'#e3bc79');
rect(3,12,5,3,'#637776'); rect(24,12,6,3,'#637776'); rect(14,12,9,4,'#47281f');
rect(18,12,5,3,'#f1714c'); rect(11,23,3,4,'#f4c96d'); rect(19,23,3,4,'#f4c96d');
} else if (index === 9) {
rect(6,11,21,12,'#515f55'); rect(7,8,17,10,'#9c9977'); rect(9,8,12,3,'#cbc397');
rect(15,5,4,5,'#eabe68'); rect(7,20,20,4,'#242e2a'); rect(6,21,7,7,'#0c1c20');
rect(21,21,7,7,'#0c1c20'); rect(8,23,3,3,'#a1ad91'); rect(23,23,3,3,'#a1ad91');
rect(5,13,5,3,'#f36b51');
} else if (index === 10) {
rect(5,8,22,17,'#152f31'); rect(8,5,16,23,'#657f70'); rect(10,7,12,19,'#a86a38');
rect(12,9,8,15,'#ea9948'); rect(13,10,5,12,'#ffe7a0'); rect(15,11,3,9,'#fffbd0');
for (let y = 9; y < 24; y += 6) { rect(6,y,5,2,'#203b3c'); rect(21,y,5,2,'#203b3c'); }
} else if (index === 11) {
rect(2,11,28,10,'#253b3a'); rect(2,10,28,3,'#a19c78'); rect(2,19,28,3,'#596f62');
rect(4,13,24,6,'#765a39'); rect(6,14,20,3,'#b28c51');
for(const x of [3,26]) { rect(x,12,3,7,'#41544b');rect(x,13,2,2,'#e5c283'); }
} else if (index === 12) {
rect(4,4,24,24,'#243b39');rect(6,6,20,20,'#8b693e');rect(8,8,16,16,'#455e51');
rect(7,7,18,3,'#c3a164');rect(7,22,18,3,'#a08552');
for(let i=0;i<5;i++)rect(8+i*3,10+i*2,4,3,'#ad8a52');
rect(5,5,3,3,'#e4c886');rect(24,24,3,3,'#d2b87b');
} else if (index === 13) {
rect(3,9,26,16,'#283a37');rect(6,6,20,19,'#744d37');rect(5,9,23,11,'#bd8650');
rect(7,7,16,4,'#dbc18a');rect(7,13,18,6,'#5c4736');rect(9,14,13,3,'#edaa64');
rect(4,20,24,5,'#465d50');rect(9,23,14,4,'#213934');
rect(4,11,5,4,'#ed7855');rect(10,12,3,3,'#ffdab0');rect(21,8,3,3,'#e4c77f');
rect(25,13,4,9,'#304a44');rect(26,14,2,4,'#95a991');
} else if (index === 14) {
rect(9,6,14,20,'#253b38');rect(6,10,20,12,'#5a4836');rect(9,8,14,16,'#af5e38');
rect(11,7,10,18,'#efac58');rect(8,12,16,8,'#d67b40');
rect(12,11,8,10,'#fff0b0');rect(14,12,4,7,'#fff8dc');
rect(5,13,4,6,'#8b9b80');rect(23,13,4,6,'#8b9b80');
} else if(index===15) {
rect(2,12,28,8,'#233733');rect(3,13,26,5,'#a88d66');rect(5,13,22,2,'#d3c094');
rect(2,11,5,10,'#496352');rect(25,11,5,10,'#496352');rect(3,13,3,3,'#ebc486');rect(26,13,3,3,'#ebc486');
} else {
rect(8,3,16,26,'#293736');rect(10,5,12,22,'#704f80');rect(12,7,8,18,'#df9ef2');
rect(14,8,4,16,'#fff0ed');rect(5,8,5,4,'#b4c29b');rect(22,20,5,4,'#b4c29b');
}
const alpha = c.getImageData(0,0,32,32).data;
return { canvas, hull: hullFromPixels(alpha,32,32,PIXEL), width:64, height:64 };
}
export function createSprites(): Sprite[] { return Array.from({ length: 17 }, (_, i) => make(i)); }
/** An actual grid used by the inspection UI; hulls use the same per-cell pixels. */
export function createAtlas(sprites: Sprite[]): HTMLCanvasElement {
const atlas = document.createElement('canvas'); atlas.width = 32 * 6; atlas.height = Math.ceil(sprites.length/6)*32;
const c = atlas.getContext('2d')!;
sprites.forEach((s, i) => c.drawImage(s.canvas, (i % 6) * 32, Math.floor(i / 6) * 32));
return atlas;
}
