let rows = [ [ 0, 0, 0, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5 ], [ 6, 0, 5, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4 ], [ 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 4, 4 ], [ 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 5, 6, 0, 0, 5, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4 ], [ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 5, 4, 6, 0, 0, 0, 5, 4, 4, 4, 4 ] ]; const rowsBackup = JSON.parse(JSON.stringify(rows)); const cloudW = 110; const density = 0.0333; const maxDroplets = 60; let raining = false; const times = []; const rainSpeed = 7; const rainIncrease = 0.025; const spreadIncrement = 0.01; // how fast the water spreads const spreadCutoff = 0.01; // determines when the water should consider itself "equalized" let reset = false; let fps; $(".reset").on("click", function (e) { reset = true; }); $(".debug").on("click", function () { console.log(rows); }); $(document).on("mousemove", function (e) { $(".cloud").css("left", e.pageX); }); $(document).on("mousedown", function () { raining = true; }); $(document).on("mouseup", function () { raining = false; }); function mainLoop() { if (reset) { console.log("resetting flood..."); rows = JSON.parse(JSON.stringify(rowsBackup)); $(".raindrop").remove(); reset = false; } else { if (raining && $(".raindrop").length < maxDroplets) { addDrop(); } moveDrops(); calcDrops(); spreadWater(); } calcFPS(); drawRows(); requestAnimationFrame(mainLoop); } requestAnimationFrame(mainLoop); function moveDrops() { const screenH = $(window).outerHeight(); const screenW = $(window).outerWidth(); $(".raindrop").each(function () { const currY = $(this).offset().top; const newY = currY + rainSpeed; const dropCol = parseInt($(this).data("col")); const colHeight = getColHeight(dropCol); if (newY > screenH - colHeight * (screenW * density)) { let emptyRow = -1; $.each(rows.slice().reverse(), function (index, row) { if (row[dropCol] < 1) { emptyRow = rows.length - 1 - index; return false; } }); if (emptyRow > -1) { rows[emptyRow][dropCol] = Math.min( 1, rows[emptyRow][dropCol] + rainIncrease ); } $(this).remove(); } else { $(this).css("transform", "translateY(" + newY + "px)"); } }); } function addDrop() { const xRand = Math.random() * cloudW; const cloudLeft = $(".cloud").offset().left; const rainPos = cloudLeft + xRand; const winW = $(window).outerWidth(); const colSector = Math.floor((rainPos / winW / 3.33) * 100); // console.log("adding raindrop to column " + colSector); $("body").append('
'); $(".raindrop--new") .removeClass("raindrop--new") .css("left", rainPos) .data("col", colSector); } function calcFPS() { const now = performance.now(); while (times.length > 0 && times[0] <= now - 1000) { times.shift(); } times.push(now); fps = times.length; $(".fps").text("FPS: " + fps); } function calcDrops() { $(".rain-count").text("Droplets: " + $(".raindrop").length); } function drawRows() { const grid = $(".ground-grid"); grid.empty(); $.each(rows, function (index, row) { $.each(row, function (index2, col) { let removeSpillage = false; let newBlock = $( '
' ); if (heavyWater) { newBlock.addClass("heavyWater"); } } grid.append(newBlock); rows[index][index2] = removeSpillage ? rows[index][index2] - 2 : rows[index][index2]; }); }); } function spreadWater() { // remove spillage animations $(".spillage").removeClass("spillage"); // go through each water tile and spread water $(".ground-grid__block--water").each(function () { const row = $(this).data("row"); const col = $(this).data("col"); const level = $(this).find("span").data("level") / 100; let addBelow = false; // always check block below first const belowBlockVal = row != rows.length - 1 ? rows[row + 1][col] : null; if (belowBlockVal !== null && belowBlockVal < 1) { rows[row + 1][col] += spreadIncrement; if (rows[row + 1][col] > 1) { rows[row + 1][col] = 1; } rows[row][col] -= spreadIncrement; addBelow = true; } // check block to the left const leftBlockVal = col != 0 ? rows[row][col - 1] : null; if ( leftBlockVal !== null && leftBlockVal < 1 && leftBlockVal < level && Math.abs(leftBlockVal - level) > spreadCutoff && !addBelow ) { rows[row][col] -= spreadIncrement; rows[row][col - 1] += spreadIncrement; if (rows[row][col - 1] > 1) { rows[row][col - 1] = 1; } // if not falling down, add spillage below if (belowBlockVal !== null && belowBlockVal != 4 && belowBlockVal != 1) { rows[row + 1][col] = belowBlockVal == 5 ? 7 : 8; } } // check block to the right const rightBlockVal = col != 29 ? rows[row][col + 1] : null; if ( rightBlockVal !== null && rightBlockVal < 1 && rightBlockVal < level && Math.abs(rightBlockVal - level) > spreadCutoff && !addBelow ) { rows[row][col] -= spreadIncrement; rows[row][col + 1] += spreadIncrement; if (rows[row][col + 1] > 1) { rows[row][col + 1] = 1; } // if not falling down, add spillage below if (belowBlockVal !== null && belowBlockVal != 4 && belowBlockVal != 1) { rows[row + 1][col] = belowBlockVal == 5 ? 7 : 8; } } // if tile is essentially empty, drain it if (level < spreadIncrement) { rows[row][col] = 0; } }); } function getColHeight(col) { let height = 0; $.each(rows.slice().reverse(), function (index, row) { const rowCol = row[col]; if (rowCol == 0) { return false; } else if (rowCol <= 1) { height += rowCol; } else { height += 1; } }); return height; }