A game I made just recently, please give it some love because I am new to the platform and this took 8 hours to code.
let grid, w = 3.5, cols, rows, currentElement = 1, brushSize = 4;
let menuHeight = 120, sideWidth = 60, ignoreInput = false, lastMouseX, lastMouseY;
let timeScale = 1.0, isPaused = false, frameSubCount = 0, xIndices = [];
let lightningTimer = 0;
let gameVersion = "v0.0.64";
function setup() {
let canvas = createCanvas(windowWidth, windowHeight);
canvas.style('display', 'block');
refreshGrid(null, true);
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
refreshGrid(grid);
}
function refreshGrid(oldGrid = null, isFirstTime = false) {
cols = floor(width / w); rows = floor(height / w);
let newGrid = make2DArray(cols, rows);
if (oldGrid) {
for (let i = 0; i < min(cols, oldGrid.length); i++) {
for (let r = 0; r < min(rows, oldGrid.length); r++) {
let tI = min(i, cols-1), tR = min(r, rows-1);
newGrid[tI][tR] = oldGrid[i][r];
}
}
} else if (isFirstTime) {
for (let i = 0; i < cols; i++) {
let floorDepth = floor(rows - 12 + sin(i * 0.1) * 4);
for (let r = floorDepth; r < rows; r++) {
newGrid[i][r] = { type: random() > 0.7 ? 6 : 1, v: random(-10, 10), life: -1, temp: 0 };
}
}
for (let k = 0; k < 400; k++) {
let rx = floor(random(cols)), ry = floor(random(rows * 0.4, rows - 15));
newGrid[rx][ry] = { type: random() > 0.4 ? 2 : 8, v: random(-10, 10), life: -1, temp: 0 };
}
}
grid = newGrid; xIndices = Array.from({length: cols}, (_, i) => i);
}
function mousePressed() {
// Fullscreen Button Check (Bottom Right Icon Area)
if (mouseX > width - 50 && mouseY > height - 65 && mouseY < height - 25) {
fullscreen(!fullscreen());
ignoreInput = true;
return;
}
if (mouseX < sideWidth && mouseY < menuHeight) { currentElement = (mouseY < menuHeight/2) ? 21 : 23; ignoreInput = true; }
else if (mouseX > width - sideWidth && mouseY < menuHeight) { currentElement = (mouseY < menuHeight/2) ? 20 : 24; ignoreInput = true; }
else if (mouseY < menuHeight) {
let sw = (width - (sideWidth * 2)) / 7, sh = menuHeight / 3;
let col = floor((mouseX - sideWidth) / sw), row = floor(mouseY / sh);
let idxMap = [
[1, 2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[15, 16, 17, 18, 19, 26, 27]
];
if (idxMap[row] && idxMap[row][col]) currentElement = idxMap[row][col];
ignoreInput = true;
} else ignoreInput = false;
lastMouseX = mouseX; lastMouseY = mouseY;
}
function draw() {
background(245);
if (lightningTimer > 0) lightningTimer--;
if (mouseIsPressed && !ignoreInput && mouseY > menuHeight) {
let steps = max(1, ceil(dist(lastMouseX, lastMouseY, mouseX, mouseY) / (w / 2)));
for (let s = 0; s <= steps; s++) {
let t = s / steps;
let iX = lerp(lastMouseX, mouseX, t), iY = lerp(lastMouseY, mouseY, t);
let mC = floor(iX / w), mR = floor(iY / w);
if (currentElement === 27) {
if (lightningTimer <= 0) { createBranchingLightning(mC, mR, mouseX - pmouseX, mouseY - pmouseY, dist(mouseX, mouseY, pmouseX, pmouseY)); lightningTimer = 25; }
} else {
for (let i = -brushSize; i <= brushSize; i++) {
for (let j = -brushSize; j <= brushSize; j++) {
let c = mC+i, r = mR+j;
if (c >= 0 && c < cols && r >= 0 && r < rows && dist(i,j,0,0) <= brushSize) applyTool(c, r);
}
}
}
}
}
lastMouseX = mouseX; lastMouseY = mouseY;
if (!isPaused) {
frameSubCount += timeScale;
while (frameSubCount >= 1.0) { updatePhysics(); frameSubCount -= 1.0; }
}
renderGrid();
renderUI();
renderOverlays();
}
function applyTool(c, r) {
if (currentElement === 18) grid[c][r] = { type: 0 };
else if (grid[c][r].type === 0 || currentElement === 20 || currentElement === 24) {
if (currentElement === 20) grid[c][r].temp += 40;
else {
let life = (currentElement === 3 || currentElement === 22) ? random(40, 100) : -1;
grid[c][r] = { type: currentElement, v: random(-20, 20), life: life, temp: 0 };
}
}
}
function updatePhysics() {
let nextGrid = make2DArray(cols, rows);
let roofY = floor(menuHeight / w) + 2;
// PASS 1: Indestructibles
for (let r = 0; r < rows; r++) {
for (let i = 0; i < cols; i++) {
let t = grid[i][r].type;
if (t === 19 || t === 12 || t === 17) nextGrid[i][r] = grid[i][r];
}
}
// PASS 2: Dynamics
shuffle(xIndices, true);
for (let r = rows - 1; r >= 0; r--) {
for (let x = 0; x < cols; x++) {
let i = xIndices[x]; let cell = grid[i][r];
if (cell.type === 0 || nextGrid[i][r].type !== 0) {
if (cell.type === 19) {
for(let ox=-5; ox<=5; ox++) for(let oy=-5; oy<=5; oy++) {
let ni=i+ox, nr=r+oy;
if(ni>=0 && ni<cols && nr>=0 && nr<rows && grid[ni][nr].type > 0 && grid[ni][nr].type !== 19 && grid[ni][nr].type !== 12) {
if(abs(ox)<=1 && abs(oy)<=1) grid[ni][nr] = {type:0};
else if(random(1)<0.2) {
let mi=ox>0?-1:1, mr=oy>0?-1:1;
if (ni+mi >= 0 && ni+mi < cols && nr+mr >= 0 && nr+mr < rows && grid[ni+mi][nr+mr].type === 0) {
let mvd=grid[ni][nr]; grid[ni][nr]={type:0}; grid[ni+mi][nr+mr]=mvd;
}
}
}
}
}
continue;
}
if (cell.type === 27) { cell.life--; if(cell.life > 0) nextGrid[i][r] = cell; continue; }
if (cell.life > 0) { cell.life--; if (cell.life <= 0) { if(cell.type === 3) nextGrid[i][r] = {type:22, v:0, life:60}; continue; } }
let typeB = (r < rows-1) ? grid[i][r+1].type : -1;
let dir = random() < 0.5 ? 1 : -1;
let sideType = (i+dir >= 0 && i+dir < cols) ? grid[i+dir][r].type : -1;
if (cell.type === 3 || cell.type === 7 || cell.type === 22 || cell.type === 9) {
if (r > roofY && (r>0 && grid[i][r-1].type === 0)) nextGrid[i][r-1] = cell;
else if (r > roofY && sideType === 0) nextGrid[i+dir][r] = cell;
else nextGrid[i][r] = cell;
} else {
if (typeB === 0 || typeB === 7) nextGrid[i][r+1] = cell;
else if (sideType === 0) {
let tr = (cell.type === 2 || cell.type === 10) ? r : r+1;
if (tr < rows && i+dir >= 0 && i+dir < cols && grid[i+dir][tr].type === 0) nextGrid[i+dir][tr] = cell;
else nextGrid[i][r] = cell;
}
else nextGrid[i][r] = cell;
if (cell.type === 10 && typeB === 2) nextGrid[i][r+1] = {type: 12, v:0, life:-1};
}
}
}
grid = nextGrid;
}
function renderGrid() {
noStroke();
for (let i = 0; i < cols; i++) {
for (let r = 0; r < rows; r++) {
let c = grid[i][r]; if (c.type === 0) continue;
if (c.type === 1) fill(210, 180, 100);
else if (c.type === 2) fill(50, 150, 255);
else if (c.type === 3) fill(255, random(100,200), 0);
else if (c.type === 26) fill(120+c.v, 160+c.v, 160+c.v);
else if (c.type === 19) {
let n = noise(i*0.05, r*0.05); fill(lerp(20,60,n), 0, lerp(40,120,n)); rect(i*w, r*w, w, w);
if(noise(i*1.5, r*1.5) > 0.92) { fill(255); rect(i*w, r*w, 1, 1); } continue;
}
else if (c.type === 27) { if (c.v === 100) fill(220, 240, 255); else fill(0, 80, 255); }
else if (c.type === 17) fill(160, 230, 255, 200);
else if (c.type === 15) fill(255, 215, 0);
else if (c.type === 12) fill(80);
else if (c.type === 8) fill(150, 50, 250);
else if (c.type === 4) fill(34, 139, 34);
else fill(100);
rect(i * w, r * w, w, w);
}
}
}
function renderUI() {
let sw = (width - sideWidth * 2) / 7, sh = menuHeight / 3;
let lbls = [
["Sand", "Water", "Fire", "Plant", "Wood", "Soil", "Gas"],
["Seed", "Rocket", "Lava", "Acid", "Wall", "Steel", "Copper"],
["Gold", "Tungsten", "Diamond", "Erase", "Void", "Glass", "Lightning"]
];
let vals = [
[1, 2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[15, 16, 17, 18, 19, 26, 27]
];
for(let row=0; row<3; row++) {
for(let col=0; col<7; col++) {
let isSpecial = (row === 2); let x = sideWidth + col * sw, y = row * sh;
fill(isSpecial ? color(180, 140, 20) : (currentElement === vals[row][col] ? 100 : 50));
stroke(0); rect(x, y, sw, sh);
if(isSpecial) {
let progress = (frameCount % 120) / 100;
if(progress < 1.2) {
let sx = x + (progress * (sw + 40)) - 20;
fill(255, 255, 200, 150); noStroke(); quad(sx, y, sx + 8, y, sx - 2, y + sh, sx - 10, y + sh);
}
}
let txt = lbls[row][col]; drawingContext.strokeStyle = 'black'; drawingContext.lineWidth = 2.5;
fill(255); textAlign(CENTER, CENTER); textSize(10);
drawingContext.strokeText(txt, x + sw/2, y + sh/2); drawingContext.fillText(txt, x + sw/2, y + sh/2);
}
}
drawSidebar(0, "COOL", "FREEZE", color(50, 100, 200), 21, 23);
drawSidebar(width - sideWidth, "HEAT", "VAPOR", color(200, 50, 50), 20, 24);
}
function renderOverlays() {
// Version History (Bottom Right)
fill(50, 180); noStroke(); rect(width - 70, height - 25, 65, 20, 5);
fill(255); textAlign(CENTER, CENTER); textSize(11);
text(gameVersion, width - 37, height - 15);
// Fullscreen Icon Button (Corner Brackets Box)
fill(80, 180); stroke(255); strokeWeight(1.5);
let bx = width - 50, by = height - 65, bs = 35;
rect(bx, by, bs, bs, 6);
// Draw the bracket corners
strokeWeight(2);
let p = 8; // padding
line(bx+p, by+p, bx+p+6, by+p); line(bx+p, by+p, bx+p, by+p+6); // Top left
line(bx+bs-p, by+p, bx+bs-p-6, by+p); line(bx+bs-p, by+p, bx+bs-p, by+p+6); // Top right
line(bx+p, by+bs-p, bx+p+6, by+bs-p); line(bx+p, by+bs-p, bx+p, by+bs-p-6); // Bottom left
line(bx+bs-p, by+bs-p, bx+bs-p-6, by+bs-p); line(bx+bs-p, by+bs-p, bx+bs-p, by+bs-p-6); // Bottom right
}
function createBranchingLightning(x, y, dx, dy, moveVel) {
let cx = x, cy = y, angle = atan2(dy, dx);
for (let s = 0; s < 300; s++) {
let icx = floor(cx), icy = floor(cy);
if (icx < 1 || icx >= cols-1 || icy < floor(menuHeight/w)+2 || icy >= rows-1) break;
grid[icx][icy] = { type: 27, v: 100, life: 12 };
cx += cos(angle) * 2 + random(-2.5, 2.5); cy += sin(angle) * 2 + random(-2.5, 2.5);
if (icx >= 0 && icy >= 0 && grid[icx][icy].type > 0 && grid[icx][icy].type !== 27) break;
}
}
function drawSidebar(x, t1, t2, col, v1, v2) {
fill(col); stroke(0); rect(x, 0, sideWidth, menuHeight);
line(x, menuHeight/2, x+sideWidth, menuHeight/2);
noStroke(); fill(255); textAlign(CENTER, CENTER); textSize(10);
fill(currentElement === v1 ? 255 : 200); text(t1, x + sideWidth/2, menuHeight/4);
fill(currentElement === v2 ? 255 : 200); text(t2, x + sideWidth/2, 3*menuHeight/4);
}
function make2DArray(c, r) {
let arr = new Array(c);
for (let i = 0; i < c; i++) {
arr[i] = new Array(r);
for (let j = 0; j < r; j++) arr[i][j] = { type: 0, v: 0, life: -1, temp: 0 };
}
return arr;
}