diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7929358 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +# EditorConfig is awesome: https://editorconfig.org + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true diff --git a/.env b/.env new file mode 100644 index 0000000..8ec22da --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +NODE_ENV=development +SESSION_KEY=THIS_IS_MY_TEMP_KEY \ No newline at end of file diff --git a/.gitignore b/.gitignore index d8d6855..5e6f416 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ TODO.md # old directories kept locally stash/ -trash/ \ No newline at end of file +trash/ diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c689dfb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "[json5]": { + "editor.insertSpaces": true, + "editor.tabSize": 4 + } +} diff --git a/package-lock.json b/package-lock.json index d8a62fc..989d54d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "iew-site", - "version": "0.14.0", + "version": "0.14.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "iew-site", - "version": "0.14.0", + "version": "0.14.2", "license": "MIT", "devDependencies": { "web-weevr": "git+ssh://git@git.itsericwoodward.com:eric/web-weevr.git" diff --git a/package.json b/package.json index fa4aa77..94fd632 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iew-site", - "version": "0.14.1", + "version": "0.14.2", "description": "", "main": "index.js", "scripts": {}, diff --git a/site.config.json5 b/site.config.json5 index 9f9dc37..981ed4c 100644 --- a/site.config.json5 +++ b/site.config.json5 @@ -1,60 +1,59 @@ { - site: { - title: "It's Eric Woodward (dotcom)", - author: { - name: "Eric Woodward", - email: "hey@itsericwoodward.com", // not used - photo: "/images/eric-8bit.gif", - site: "https://itsericwoodward.com", - geo: { - position: "35.4, -80.5", - placename: "Concord", - region: "US-NC", - }, - }, + site: { + title: "It's Eric Woodward (dotcom)", + author: { + name: "Eric Woodward", + email: "hey@itsericwoodward.com", // not used + photo: "/images/eric-8bit.gif", + site: "https://itsericwoodward.com", + geo: { + position: "35.4, -80.5", + placename: "Concord", + region: "US-NC", + }, + }, - base_uri: "", - // csp: "default-src 'self' data: https://v8.js-dos.com 'unsafe-inline'; img-src 'self' https://*; media-src 'self' https://* data:; script-src 'self' https://v8.js-dos.com 'wasm-eval' 'unsafe-eval' 'unsafe-inline'; style-src 'self' https://v8.js-dos.com 'unsafe-inline'; worker-src 'self' blob:;", - csp: "default-src 'self' data: ; img-src 'self' https://*; media-src 'self' https://* data:;", - robots: "index,follow", - language: "en-us", - copyright: "Copyright 2014-2025 Eric Woodward, licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.", - basePath: "", - uri: "https://www.itsericwoodward.com", - comment_insert: "\n\ - ___________________________.\n\ - |;;| |;;||\n\ - |[]|---------------------|[]||\n\ - |;;| |;;||\n\ - |;;| |;;||\n\ - |;;| ItsEricWoodward.com |;;||\n\ - |;;| |;;||\n\ - |;;| |;;||\n\ - |;;| |;;||\n\ - |;;|_____________________|;;||\n\ - |;;;;;;;;;;;;;;;;;;;;;;;;;;;||\n\ - |;;;;;;_______________ ;;;;;||\n\ - |;;;;;| ___ |;;;;;||\n\ - |;;;;;| |;;;| |;;;;;||\n\ - |;;;;;| |;;;| |;;;;;||\n\ - |;;;;;| |;;;| |;;;;;||\n\ - |;;;;;| |;;;| |;;;;;||\n\ - |;;;;;| |___| |;;;;;||\n\ - \\_____|_______________|_____||\n\ - ~~~~~^^^^^^^^^^^^^^^^^~~~~~~\n\ - ", - }, - build: { - journalsPerPage: 5, - srcPath: "src", - outputPath: "out", - publishPath: "public", - }, - serve: { - authTypeUI: "basic", - handleStatic: true, - port: 4997, - shortCodeLink: "/q/", - static404: "./public/errors/404.html", - }, + base_uri: "", + csp: "default-src 'self' data: ; img-src 'self' https://*; media-src 'self' https://* data:; worker-src 'self' blob:;", + robots: "index,follow", + language: "en-us", + copyright: "Copyright 2014-2026 Eric Woodward, licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.", + basePath: "", + uri: "https://www.itsericwoodward.com", + comment_insert: "\n\ + ___________________________.\n\ + |;;| |;;||\n\ + |[]|---------------------|[]||\n\ + |;;| |;;||\n\ + |;;| |;;||\n\ + |;;| ItsEricWoodward.com |;;||\n\ + |;;| |;;||\n\ + |;;| |;;||\n\ + |;;| |;;||\n\ + |;;|_____________________|;;||\n\ + |;;;;;;;;;;;;;;;;;;;;;;;;;;;||\n\ + |;;;;;;_______________ ;;;;;||\n\ + |;;;;;| ___ |;;;;;||\n\ + |;;;;;| |;;;| |;;;;;||\n\ + |;;;;;| |;;;| |;;;;;||\n\ + |;;;;;| |;;;| |;;;;;||\n\ + |;;;;;| |;;;| |;;;;;||\n\ + |;;;;;| |___| |;;;;;||\n\ + \\_____|_______________|_____||\n\ + ~~~~~^^^^^^^^^^^^^^^^^~~~~~~\n\ + ", + }, + build: { + journalsPerPage: 5, + srcPath: "src", + outputPath: "out", + publishPath: "public", + }, + serve: { + authTypeUI: "basic", + handleStatic: true, + port: 4997, + shortCodeLink: "/q/", + static404: "./public/errors/404.html", + }, } diff --git a/src/assets/_root/webtoys/bbe/scripts/bbe.js b/src/assets/_root/webtoys/bbe/scripts/bbe.js index 31867a7..e765418 100644 --- a/src/assets/_root/webtoys/bbe/scripts/bbe.js +++ b/src/assets/_root/webtoys/bbe/scripts/bbe.js @@ -1,536 +1,545 @@ +// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0 +/**************************************************************************** + * Byte Beasties Escape! + * + * Copyright 2023-2026 Eric Woodward + * Source released under CC0 Public Domain License v1.0 + * https://www.planarvagabond.com/licenses/cc0/ + * http://creativecommons.org/publicdomain/zero/1.0/ + ****************************************************************************/ import { createLightning, getMousePos } from "./lightning.js"; const startGame = (canvas) => { - const ctx = canvas.getContext("2d"), - BEASTIE_IMAGES = [ - "blinker_shadow_red.png", - "creeper_shadow_red.png", - "grabber_shadow_red.png", - "scanner_shadow_red.png", - "bobbler_shadow_yellow.png", - "gawker_shadow_yellow.png", - "reacher_shadow_yellow.png", - "shuffler_shadow_yellow.png", - "puffer_shadow_green.png", - "seer_shadow_green.png", - "spooker_shadow_green.png", - "thinker_shadow_green.png", - "chomper_shadow_blue.png", - "diver_shadow_blue.png", - "snapper_shadow_blue.png", - "walker_shadow_blue.png", - ], - GATE = { - HEIGHT: 20, - MARGIN: 25, - MAX_SPACE: 140, - MIN_SPACE: 60, - START: canvas.height - 200, - WIDTH: 65, - }, - game = { - interval: 1000 / 90, // ms/f = 1000 ms/s / 90 f/s - winScore: 15000, - winScores: { - easy: 15000, - medium: 30000, - hard: 50000, - endless: 0, - }, - blockerFrequency: 0.3, - blockerFrequencies: { - easy: 0.3, - medium: 0.4, - hard: 0.5, - endless: 0.3, - }, - hasBlockers: true, - isOver: false, - isPaused: false, - showRestartModal: false, - maxLead: 400, - maxSpeed: -8, + const ctx = canvas.getContext("2d"), + BEASTIE_IMAGES = [ + "blinker_shadow_red.png", + "creeper_shadow_red.png", + "grabber_shadow_red.png", + "scanner_shadow_red.png", + "bobbler_shadow_yellow.png", + "gawker_shadow_yellow.png", + "reacher_shadow_yellow.png", + "shuffler_shadow_yellow.png", + "puffer_shadow_green.png", + "seer_shadow_green.png", + "spooker_shadow_green.png", + "thinker_shadow_green.png", + "chomper_shadow_blue.png", + "diver_shadow_blue.png", + "snapper_shadow_blue.png", + "walker_shadow_blue.png", + ], + GATE = { + HEIGHT: 20, + MARGIN: 25, + MAX_SPACE: 140, + MIN_SPACE: 60, + START: canvas.height - 200, + WIDTH: 65, + }, + game = { + interval: 1000 / 90, // ms/f = 1000 ms/s / 90 f/s + winScore: 15000, + winScores: { + easy: 15000, + medium: 30000, + hard: 50000, + endless: 0, + }, + blockerFrequency: 0.3, + blockerFrequencies: { + easy: 0.3, + medium: 0.4, + hard: 0.5, + endless: 0.3, + }, + hasBlockers: true, + isOver: false, + isPaused: false, + showRestartModal: false, + maxLead: 400, + maxSpeed: -8, - speed: -1, - speedBoost: { - duration: 1, - multiplier: 2, - }, - }, - drag = 0.3, - beastie = { - width: 48, - height: 48, - x: canvas.width / 2 - 20, - y: GATE.START + 100, + speed: -1, + speedBoost: { + duration: 1, + multiplier: 2, + }, + }, + drag = 0.3, + beastie = { + width: 48, + height: 48, + x: canvas.width / 2 - 20, + y: GATE.START + 100, - // velocity - dx: 0, - dy: 0, + // velocity + dx: 0, + dy: 0, - // for tracking speed boosts - boostEnd: 0, + // for tracking speed boosts + boostEnd: 0, - score: 0, - image: null, - }, - player = { - dx: 0, - dy: 0, - }, - enemy = { - width: canvas.width, - height: canvas.height, - x: 0, - y: canvas.height - 50, - delaySeconds: 5, - speed: -2, - }, - random = (min, max) => Math.random() * (max - min) + min, - images = BEASTIE_IMAGES.reduce((acc, name) => { - const tempImg = new Image(); - tempImg.src = `/webtoys/bbe/beasties/${name}`; - acc[name] = tempImg; - return acc; - }, {}), - img = new Image(), - hasCollision = (gate) => - // Collision check: AAB (Axis-Aligned Bounding Box) - beastie.x < gate.x + GATE.WIDTH && - beastie.x > gate.x - beastie.width && - beastie.y < gate.y + GATE.HEIGHT && - beastie.y > gate.y - beastie.height, - settingsBeastie = document.getElementById("settingsBeastie"); + score: 0, + image: null, + }, + player = { + dx: 0, + dy: 0, + }, + enemy = { + width: canvas.width, + height: canvas.height, + x: 0, + y: canvas.height - 50, + delaySeconds: 5, + speed: -2, + }, + random = (min, max) => Math.random() * (max - min) + min, + images = BEASTIE_IMAGES.reduce((acc, name) => { + const tempImg = new Image(); + tempImg.src = `/webtoys/bbe/beasties/${name}`; + acc[name] = tempImg; + return acc; + }, {}), + img = new Image(), + hasCollision = (gate) => + // Collision check: AAB (Axis-Aligned Bounding Box) + beastie.x < gate.x + GATE.WIDTH && + beastie.x > gate.x - beastie.width && + beastie.y < gate.y + GATE.HEIGHT && + beastie.y > gate.y - beastie.height, + settingsBeastie = document.getElementById("settingsBeastie"); - let // vertical space between gates - minPlatformSpace = GATE.MIN_SPACE, - maxPlatformSpace = GATE.MAX_SPACE, - gates = [ - { - x: canvas.width / 2 - GATE.WIDTH / 2, - y: GATE.START, - touched: false, - blocker: false, - }, - ], - y = GATE.START, - keydown = false, - runtime = 0, - lastTimestamp; + let // vertical space between gates + minPlatformSpace = GATE.MIN_SPACE, + maxPlatformSpace = GATE.MAX_SPACE, + gates = [ + { + x: canvas.width / 2 - GATE.WIDTH / 2, + y: GATE.START, + touched: false, + blocker: false, + }, + ], + y = GATE.START, + keydown = false, + runtime = 0, + lastTimestamp; - img.onload = () => { - ctx.drawImage(img, beastie.x, beastie.y); - }; - // img.src = "/webtoys/bbe/beasties/blinker_shadow_red.png"; + img.onload = () => { + ctx.drawImage(img, beastie.x, beastie.y); + }; + // img.src = "/webtoys/bbe/beasties/blinker_shadow_red.png"; - Object.keys(images).forEach((key, i) => { - const input = document.createElement("input"), - label = document.createElement("label"), - bImage = images[key]; - label.className = "beastieImageInputWrapper"; - input.name = "beastie"; - input.type = "radio"; - // TODO: Add Cookie support - input.checked = i === 0; - input.value = key; - label.appendChild(input); - bImage.className = "beastieImage"; - label.appendChild(bImage); - settingsBeastie.appendChild(label); - }); + Object.keys(images).forEach((key, i) => { + const input = document.createElement("input"), + label = document.createElement("label"), + bImage = images[key]; + label.className = "beastieImageInputWrapper"; + input.name = "beastie"; + input.type = "radio"; + // TODO: Add Cookie support + input.checked = i === 0; + input.value = key; + label.appendChild(input); + bImage.className = "beastieImage"; + label.appendChild(bImage); + settingsBeastie.appendChild(label); + }); - const renderLightning = () => { - var color = "hsla(60, 100%, 50%, .7)"; + const renderLightning = () => { + var color = "hsla(60, 100%, 50%, .7)"; - ctx.save(); - ctx.globalCompositeOperation = "lighter"; + ctx.save(); + ctx.globalCompositeOperation = "lighter"; - ctx.strokeStyle = color; - ctx.shadowColor = color; + ctx.strokeStyle = color; + ctx.shadowColor = color; - ctx.fillStyle = color; - ctx.fillRect(enemy.x, enemy.y, enemy.width, canvas.height); - ctx.fillStyle = "hsla(0, 0%, 10%, 0.2)"; - ctx.shadowBlur = 0; - ctx.globalCompositeOperation = "source-over"; - ctx.fillRect(enemy.x, enemy.y, enemy.width, canvas.height); - ctx.globalCompositeOperation = "lighter"; - ctx.shadowBlur = 15; + ctx.fillStyle = color; + ctx.fillRect(enemy.x, enemy.y, enemy.width, canvas.height); + ctx.fillStyle = "hsla(0, 0%, 10%, 0.2)"; + ctx.shadowBlur = 0; + ctx.globalCompositeOperation = "source-over"; + ctx.fillRect(enemy.x, enemy.y, enemy.width, canvas.height); + ctx.globalCompositeOperation = "lighter"; + ctx.shadowBlur = 15; - const lightning = createLightning(canvas, enemy); - ctx.beginPath(); - for (let i = 0; i < lightning.length; i++) { - ctx.lineTo(lightning[i].x, Math.max(lightning[i].y, enemy.y)); - } - ctx.stroke(); - // requestAnimationFrame(renderLightning); - ctx.restore(); - }; + const lightning = createLightning(canvas, enemy); + ctx.beginPath(); + for (let i = 0; i < lightning.length; i++) { + ctx.lineTo(lightning[i].x, Math.max(lightning[i].y, enemy.y)); + } + ctx.stroke(); + // requestAnimationFrame(renderLightning); + ctx.restore(); + }; - const restartGame = () => { - const whichBeastie = document.querySelector( - 'input[name="beastie"]:checked' - ).value, - settingsMode = document.getElementById("settingsMode"); + const restartGame = () => { + const whichBeastie = document.querySelector( + 'input[name="beastie"]:checked' + ).value, + settingsMode = document.getElementById("settingsMode"); - if (settingsMode) { - const { value } = settingsMode; - game.winScore = game.winScores[value]; - game.blockerFrequency = game.blockerFrequencies[value]; - } + if (settingsMode) { + const { value } = settingsMode; + game.winScore = game.winScores[value]; + game.blockerFrequency = game.blockerFrequencies[value]; + } - img.src = `/webtoys/bbe/beasties/${whichBeastie}`; + img.src = `/webtoys/bbe/beasties/${whichBeastie}`; - game.isPaused = false; - game.isOver = false; - game.showRestartModal = false; - y = GATE.START; - minPlatformSpace = GATE.MIN_SPACE; - maxPlatformSpace = GATE.MAX_SPACE; - // all the gates - starts in the bottom middle - gates = [ - { - x: canvas.width / 2 - GATE.WIDTH / 2, - y: GATE.START, - touched: false, - blocker: false, - }, - ]; - beastie.dx = 0; - beastie.dy = 0; - beastie.boostEnd = 0; - beastie.score = 0; - beastie.x = canvas.width / 2 - 20; - beastie.y = canvas.height - 50; + game.isPaused = false; + game.isOver = false; + game.showRestartModal = false; + y = GATE.START; + minPlatformSpace = GATE.MIN_SPACE; + maxPlatformSpace = GATE.MAX_SPACE; + // all the gates - starts in the bottom middle + gates = [ + { + x: canvas.width / 2 - GATE.WIDTH / 2, + y: GATE.START, + touched: false, + blocker: false, + }, + ]; + beastie.dx = 0; + beastie.dy = 0; + beastie.boostEnd = 0; + beastie.score = 0; + beastie.x = canvas.width / 2 - 20; + beastie.y = canvas.height - 50; - player.dx = 0; - player.dy = 0; + player.dx = 0; + player.dy = 0; - enemy.y = canvas.height - 50; + enemy.y = canvas.height - 50; - runtime = 0; + runtime = 0; - while (y > 0) { - // the next gate can be placed above the previous one with a space - // somewhere between the min and max space - y -= GATE.HEIGHT + random(minPlatformSpace, maxPlatformSpace); + while (y > 0) { + // the next gate can be placed above the previous one with a space + // somewhere between the min and max space + y -= GATE.HEIGHT + random(minPlatformSpace, maxPlatformSpace); - gates.push({ - x: random( - GATE.MARGIN, - canvas.width - GATE.MARGIN - GATE.WIDTH - ), - y, - touched: false, - }); - } - }, - // game loop - loop = (timestamp) => { - requestAnimationFrame(loop); + gates.push({ + x: random( + GATE.MARGIN, + canvas.width - GATE.MARGIN - GATE.WIDTH + ), + y, + touched: false, + }); + } + }, + // game loop + loop = (timestamp) => { + requestAnimationFrame(loop); - // limit FPS - if (timestamp - lastTimestamp <= game.interval) return; + // limit FPS + if (timestamp - lastTimestamp <= game.interval) return; - if (!(game.isOver || game.isPaused)) - runtime += timestamp - lastTimestamp; - lastTimestamp = timestamp; + if (!(game.isOver || game.isPaused)) + runtime += timestamp - lastTimestamp; + lastTimestamp = timestamp; - if (game.isOver) { - ctx.save(); - ctx.fillStyle = "yellow"; - ctx.fillRect( - canvas.width / 4, - canvas.height / 4, - enemy.width / 2, - canvas.height / 2 - ); - ctx.fillStyle = "black"; - ctx.font = "32px sans"; - ctx.fillText( - beastie.y <= 0 ? "You Win!" : "Game Over", - canvas.width / 3, - canvas.height / 3, - canvas.width / 3 - ); - ctx.fillText( - `Score: + if (game.isOver) { + ctx.save(); + ctx.fillStyle = "yellow"; + ctx.fillRect( + canvas.width / 4, + canvas.height / 4, + enemy.width / 2, + canvas.height / 2 + ); + ctx.fillStyle = "black"; + ctx.font = "32px sans"; + ctx.fillText( + beastie.y <= 0 ? "You Win!" : "Game Over", + canvas.width / 3, + canvas.height / 3, + canvas.width / 3 + ); + ctx.fillText( + `Score: ${beastie.score}`, - canvas.width / 3, - (canvas.height / 3) * 2, - canvas.width / 3 - ); - ctx.restore(); - return; - } + canvas.width / 3, + (canvas.height / 3) * 2, + canvas.width / 3 + ); + ctx.restore(); + return; + } - if (game.showRestartModal) { - ctx.save(); - ctx.fillStyle = "yellow"; - ctx.fillRect( - canvas.width / 4, - canvas.height / 4, - enemy.width / 2, - canvas.height / 2 - ); - ctx.fillStyle = "black"; - ctx.font = "48px sans"; - ctx.fillText( - `Restart? (Y / N)`, - canvas.width / 3, - canvas.height / 3, - canvas.width / 3 - ); - ctx.restore(); - return; - } + if (game.showRestartModal) { + ctx.save(); + ctx.fillStyle = "yellow"; + ctx.fillRect( + canvas.width / 4, + canvas.height / 4, + enemy.width / 2, + canvas.height / 2 + ); + ctx.fillStyle = "black"; + ctx.font = "48px sans"; + ctx.fillText( + `Restart? (Y / N)`, + canvas.width / 3, + canvas.height / 3, + canvas.width / 3 + ); + ctx.restore(); + return; + } - if (game.isPaused) { - ctx.save(); - ctx.fillStyle = "yellow"; - ctx.fillRect( - canvas.width / 4, - canvas.height / 3, - canvas.width / 2, - canvas.height / 3 - ); - ctx.fillStyle = "black"; - ctx.font = "40px sans"; - ctx.fillText(`Paused`, canvas.width / 3 - 5, canvas.height / 2); - ctx.restore(); - return; - } - ctx.clearRect(0, 0, canvas.width, canvas.height); + if (game.isPaused) { + ctx.save(); + ctx.fillStyle = "yellow"; + ctx.fillRect( + canvas.width / 4, + canvas.height / 3, + canvas.width / 2, + canvas.height / 3 + ); + ctx.fillStyle = "black"; + ctx.font = "40px sans"; + ctx.fillText(`Paused`, canvas.width / 3 - 5, canvas.height / 2); + ctx.restore(); + return; + } + ctx.clearRect(0, 0, canvas.width, canvas.height); - // if beastie reaches the middle of the screen, move the gates down - // instead of beastie up to make it look like beastie is going up - if ( - beastie.y < canvas.height - canvas.height / 3 && - beastie.dy < 0 && - (beastie.score <= game.winScore || !game.winScore) - ) { - gates = gates.map(({ y, ...otherProps }) => ({ - ...otherProps, - y: y - beastie.dy, - })); + // if beastie reaches the middle of the screen, move the gates down + // instead of beastie up to make it look like beastie is going up + if ( + beastie.y < canvas.height - canvas.height / 3 && + beastie.dy < 0 && + (beastie.score <= game.winScore || !game.winScore) + ) { + gates = gates.map(({ y, ...otherProps }) => ({ + ...otherProps, + y: y - beastie.dy, + })); - // add more gates to the top of the screen as beastie moves up - while (gates[gates.length - 1].y > 0) { - gates.push({ - x: random(25, canvas.width - 25 - GATE.WIDTH), - y: - gates[gates.length - 1].y - - (GATE.HEIGHT + - random(minPlatformSpace, maxPlatformSpace)), - touched: false, - blocker: - game.hasBlockers && - gates.filter(({ blocker }) => blocker).length / - gates.length < - game.blockerFrequency && - Math.random() <= game.blockerFrequency - ? true - : false, - }); + // add more gates to the top of the screen as beastie moves up + while (gates[gates.length - 1].y > 0) { + gates.push({ + x: random(25, canvas.width - 25 - GATE.WIDTH), + y: + gates[gates.length - 1].y - + (GATE.HEIGHT + + random(minPlatformSpace, maxPlatformSpace)), + touched: false, + blocker: + game.hasBlockers && + gates.filter(({ blocker }) => blocker).length / + gates.length < + game.blockerFrequency && + Math.random() <= game.blockerFrequency + ? true + : false, + }); - // add a bit to the min/max gate space as the player goes up - minPlatformSpace += 0.5; - maxPlatformSpace += 0.5; + // add a bit to the min/max gate space as the player goes up + minPlatformSpace += 0.5; + maxPlatformSpace += 0.5; - // cap max space - maxPlatformSpace = Math.min( - maxPlatformSpace, - canvas.height / 2 - ); - } - // move enemy backwards - enemy.y -= beastie.dy; - } else { - if (beastie.y <= 0) beastie.dy = -10; - beastie.y += beastie.dy; - } + // cap max space + maxPlatformSpace = Math.min( + maxPlatformSpace, + canvas.height / 2 + ); + } + // move enemy backwards + enemy.y -= beastie.dy; + } else { + if (beastie.y <= 0) beastie.dy = -10; + beastie.y += beastie.dy; + } - // apply drag when key not pressed - if (!keydown) { - if (player.dx < 0) { - beastie.dx += drag; + // apply drag when key not pressed + if (!keydown) { + if (player.dx < 0) { + beastie.dx += drag; - // don't let dx go above 0 - if (beastie.dx > 0) { - beastie.dx = 0; - player.dx = 0; - } - } else if (player.dx > 0) { - beastie.dx -= drag; + // don't let dx go above 0 + if (beastie.dx > 0) { + beastie.dx = 0; + player.dx = 0; + } + } else if (player.dx > 0) { + beastie.dx -= drag; - if (beastie.dx < 0) { - beastie.dx = 0; - player.dx = 0; - } - } + if (beastie.dx < 0) { + beastie.dx = 0; + player.dx = 0; + } + } - if ( - player.dy === 0 || - (player.dy < 0 && Date.now() > beastie.boostEnd) - ) { - beastie.dy += drag; + if ( + player.dy === 0 || + (player.dy < 0 && Date.now() > beastie.boostEnd) + ) { + beastie.dy += drag; - // don't let dx go above 0 - if (beastie.dy > game.speed) { - beastie.dy = game.speed; - player.dy = game.speed; - } - } else if (player.dy > 0) { - beastie.dy -= drag; + // don't let dx go above 0 + if (beastie.dy > game.speed) { + beastie.dy = game.speed; + player.dy = game.speed; + } + } else if (player.dy > 0) { + beastie.dy -= drag; - if (beastie.dy < 0) { - beastie.dy = 0; - player.dy = 0; - } - } - } + if (beastie.dy < 0) { + beastie.dy = 0; + player.dy = 0; + } + } + } - beastie.x += beastie.dx; + beastie.x += beastie.dx; - // make beastie wrap the screen - if (beastie.x + beastie.width < 0) { - beastie.x = canvas.width; - } else if (beastie.x > canvas.width) { - beastie.x = -beastie.width; - } + // make beastie wrap the screen + if (beastie.x + beastie.width < 0) { + beastie.x = canvas.width; + } else if (beastie.x > canvas.width) { + beastie.x = -beastie.width; + } - ctx.fillStyle = "green"; - gates = gates.map((gate) => { - // draw gates - ctx.save(); - if (gate.blocker) ctx.fillStyle = "red"; - else ctx.fillStyle = gate.touched ? "lime" : "green"; - ctx.fillRect(gate.x, gate.y, GATE.WIDTH, GATE.HEIGHT); - if (gate.blocker) { - ctx.save(); - ctx.lineWidth = 4; - ctx.strokeStyle = "black"; - ctx.beginPath(); - ctx.moveTo(gate.x, gate.y); - ctx.lineTo(gate.x + GATE.WIDTH, gate.y + GATE.HEIGHT); - ctx.moveTo(gate.x + GATE.WIDTH, gate.y); - ctx.lineTo(gate.x, gate.y + GATE.HEIGHT); - ctx.stroke(); - ctx.restore(); - } + ctx.fillStyle = "green"; + gates = gates.map((gate) => { + // draw gates + ctx.save(); + if (gate.blocker) ctx.fillStyle = "red"; + else ctx.fillStyle = gate.touched ? "lime" : "green"; + ctx.fillRect(gate.x, gate.y, GATE.WIDTH, GATE.HEIGHT); + if (gate.blocker) { + ctx.save(); + ctx.lineWidth = 4; + ctx.strokeStyle = "black"; + ctx.beginPath(); + ctx.moveTo(gate.x, gate.y); + ctx.lineTo(gate.x + GATE.WIDTH, gate.y + GATE.HEIGHT); + ctx.moveTo(gate.x + GATE.WIDTH, gate.y); + ctx.lineTo(gate.x, gate.y + GATE.HEIGHT); + ctx.stroke(); + ctx.restore(); + } - // make beastie stop if it collides with a blocking gate - if (gate.blocker && hasCollision(gate)) { - beastie.dy = game.speed * 0.5; - gate.touched = true; - } + // make beastie stop if it collides with a blocking gate + if (gate.blocker && hasCollision(gate)) { + beastie.dy = game.speed * 0.5; + gate.touched = true; + } - // make beastie jump if it collides with a gate from above - if ( - // beastie is falling - // beastie.dy > 0 && - // beastie was previous above the gate - // prevDoodleY + doodle.height <= gate.y && - // doodle collides with gate + // make beastie jump if it collides with a gate from above + if ( + // beastie is falling + // beastie.dy > 0 && + // beastie was previous above the gate + // prevDoodleY + doodle.height <= gate.y && + // doodle collides with gate - !gate.blocker && - // !gate.touched && - hasCollision(gate) - ) { - // reset beastie position so it's on top of the gate - // beastie.y = gate.y - beastie.height; - if (!gate.touched) { - beastie.dy = Math.max( - game.maxSpeed, - game.speedBoost.multiplier * - Math.min(beastie.dy, game.speed) - ); - beastie.score += 100; - } - beastie.boostEnd = - Date.now() + game.speedBoost.duration * 1000; - gate.touched = true; - } + !gate.blocker && + // !gate.touched && + hasCollision(gate) + ) { + // reset beastie position so it's on top of the gate + // beastie.y = gate.y - beastie.height; + if (!gate.touched) { + beastie.dy = Math.max( + game.maxSpeed, + game.speedBoost.multiplier * + Math.min(beastie.dy, game.speed) + ); + beastie.score += 100; + } + beastie.boostEnd = + Date.now() + game.speedBoost.duration * 1000; + gate.touched = true; + } - return gate; - }); + return gate; + }); - // check on enemy - if (!runtime) runtime = 0; + // check on enemy + if (!runtime) runtime = 0; - if (runtime > enemy.delaySeconds * 1000) - enemy.y = enemy.y + enemy.speed; - if (enemy.y < 1) { - enemy.y = 0; - game.isOver = true; - } else if (enemy.y > beastie.y + game.maxLead) - enemy.y = beastie.y + game.maxLead; + if (runtime > enemy.delaySeconds * 1000) + enemy.y = enemy.y + enemy.speed; + if (enemy.y < 1) { + enemy.y = 0; + game.isOver = true; + } else if (enemy.y > beastie.y + game.maxLead) + enemy.y = beastie.y + game.maxLead; - // draw beastie - ctx.drawImage( - img, - beastie.x, - beastie.y, - beastie.width, - beastie.height - ); + // draw beastie + ctx.drawImage( + img, + beastie.x, + beastie.y, + beastie.width, + beastie.height + ); - // +100 points for each offscreen blocker that wasn't touched - beastie.score += - 100 * - gates.filter( - (gate) => - gate.y > canvas.height && gate.blocker && !gate.touched - ).length; - // remove any offscreen gates - gates = gates.filter((gate) => gate.y <= canvas.height); + // +100 points for each offscreen blocker that wasn't touched + beastie.score += + 100 * + gates.filter( + (gate) => + gate.y > canvas.height && gate.blocker && !gate.touched + ).length; + // remove any offscreen gates + gates = gates.filter((gate) => gate.y <= canvas.height); - beastie.score += Math.floor(Math.abs(beastie.dy)); - ctx.fillStyle = "white"; - ctx.font = "12px sans"; - ctx.fillText(`Score: ${beastie.score}`, 10, 20); + beastie.score += Math.floor(Math.abs(beastie.dy)); + ctx.fillStyle = "white"; + ctx.font = "12px sans"; + ctx.fillText(`Score: ${beastie.score}`, 10, 20); - renderLightning(); - }; + renderLightning(); + }; - // listen to keyboard events to move beastie - document.addEventListener("keydown", (e) => { - if (keydown) return; + // listen to keyboard events to move beastie + document.addEventListener("keydown", (e) => { + if (keydown) return; - if (e.code === "ArrowLeft") { - keydown = true; - player.dx = -1; - beastie.dx = -3; - } else if (e.code === "ArrowRight") { - keydown = true; - player.dx = 1; - beastie.dx = 3; - } else if (e.code === "KeyP") { - keydown = true; - game.isPaused = !game.isPaused; - } else if (e.code === "KeyR") { - keydown = true; - if (game.isOver) { - restartGame(); - return; - } - game.isPaused = true; - game.showRestartModal = true; - } else if (e.code === "KeyS") { - keydown = true; - game.isPaused = true; - document.getElementById("settings").classList.add("showSettings"); - } else if (e.code === "KeyY") { - keydown = true; - if (game.showRestartModal) restartGame(); - } else if (e.code === "KeyN") { - keydown = true; - if (game.showRestartModal) { - game.showRestartModal = false; - game.isPaused = false; - } + if (e.code === "ArrowLeft") { + keydown = true; + player.dx = -1; + beastie.dx = -3; + } else if (e.code === "ArrowRight") { + keydown = true; + player.dx = 1; + beastie.dx = 3; + } else if (e.code === "KeyP") { + keydown = true; + game.isPaused = !game.isPaused; + } else if (e.code === "KeyR") { + keydown = true; + if (game.isOver) { + restartGame(); + return; + } + game.isPaused = true; + game.showRestartModal = true; + } else if (e.code === "KeyS") { + keydown = true; + game.isPaused = true; + document.getElementById("settings").classList.add("showSettings"); + } else if (e.code === "KeyY") { + keydown = true; + if (game.showRestartModal) restartGame(); + } else if (e.code === "KeyN") { + keydown = true; + if (game.showRestartModal) { + game.showRestartModal = false; + game.isPaused = false; + } - /* + /* } else if (e.code === "ArrowDown") { keydown = true; player.dy = 1; @@ -540,117 +549,118 @@ const startGame = (canvas) => { player.dy = -1; beastie.dy = -3; */ - } - }); + } + }); - document.addEventListener("keyup", () => { - keydown = false; - }); + document.addEventListener("keyup", () => { + keydown = false; + }); - canvas.addEventListener( - "mousedown", - (e) => { - keydown = true; - const result = getMousePos(canvas, e); - if (result.x < canvas.width / 2) { - player.dx = -1; - beastie.dx = -3; - } else { - player.dx = 1; - beastie.dx = 3; - } - }, - false - ); + canvas.addEventListener( + "mousedown", + (e) => { + keydown = true; + const result = getMousePos(canvas, e); + if (result.x < canvas.width / 2) { + player.dx = -1; + beastie.dx = -3; + } else { + player.dx = 1; + beastie.dx = 3; + } + }, + false + ); - canvas.addEventListener( - "mousemove", - (e) => { - if (!keydown) return; - const result = getMousePos(canvas, e); - if (result.x < beastie.x) { - player.dx = -1; - beastie.dx = -3; - } else { - player.dx = 1; - beastie.dx = 3; - } - }, - false - ); + canvas.addEventListener( + "mousemove", + (e) => { + if (!keydown) return; + const result = getMousePos(canvas, e); + if (result.x < beastie.x) { + player.dx = -1; + beastie.dx = -3; + } else { + player.dx = 1; + beastie.dx = 3; + } + }, + false + ); - canvas.addEventListener( - "mouseup", - () => { - keydown = false; - }, - false - ); + canvas.addEventListener( + "mouseup", + () => { + keydown = false; + }, + false + ); - canvas.addEventListener( - "touchstart", - (e) => { - mousePos = getTouchPos(canvas, e); - const touch = e.touches[0], - mouseEvent = new MouseEvent("mousedown", { - clientX: touch.clientX, - clientY: touch.clientY, - }); - canvas.dispatchEvent(mouseEvent); - }, - false - ); - canvas.addEventListener( - "touchend", - () => { - var mouseEvent = new MouseEvent("mouseup", {}); - canvas.dispatchEvent(mouseEvent); - }, - false - ); - canvas.addEventListener( - "touchmove", - (e) => { - const touch = e.touches[0]; - keydown = true; - if (touch.clientX < beastie.x) { - player.dx = -1; - beastie.dx = -3; - } else { - player.dx = 1; - beastie.dx = 3; - } - canvas.dispatchEvent(mouseEvent); - }, - false - ); - document.getElementById("cancelSettings").addEventListener("click", (e) => { - // close modal - document.getElementById("settings").classList.remove("showSettings"); - // leave game paused - }); - document.getElementById("applySettings").addEventListener("click", (e) => { - // close modal - document.getElementById("settings").classList.remove("showSettings"); - // restart game - restartGame(); - }); - document - .getElementById("toggleSettings") - ?.addEventListener("click", (e) => { - // pause game - game.isPaused = true; + canvas.addEventListener( + "touchstart", + (e) => { + mousePos = getTouchPos(canvas, e); + const touch = e.touches[0], + mouseEvent = new MouseEvent("mousedown", { + clientX: touch.clientX, + clientY: touch.clientY, + }); + canvas.dispatchEvent(mouseEvent); + }, + false + ); + canvas.addEventListener( + "touchend", + () => { + var mouseEvent = new MouseEvent("mouseup", {}); + canvas.dispatchEvent(mouseEvent); + }, + false + ); + canvas.addEventListener( + "touchmove", + (e) => { + const touch = e.touches[0]; + keydown = true; + if (touch.clientX < beastie.x) { + player.dx = -1; + beastie.dx = -3; + } else { + player.dx = 1; + beastie.dx = 3; + } + canvas.dispatchEvent(mouseEvent); + }, + false + ); + document.getElementById("cancelSettings").addEventListener("click", (e) => { + // close modal + document.getElementById("settings").classList.remove("showSettings"); + // leave game paused + }); + document.getElementById("applySettings").addEventListener("click", (e) => { + // close modal + document.getElementById("settings").classList.remove("showSettings"); + // restart game + restartGame(); + }); + document + .getElementById("toggleSettings") + ?.addEventListener("click", (e) => { + // pause game + game.isPaused = true; - // show settings - document - .getElementById("settings") - .classList.toggle("showSettings"); - }); + // show settings + document + .getElementById("settings") + .classList.toggle("showSettings"); + }); - // start the game - restartGame(); + // start the game + restartGame(); - requestAnimationFrame(loop); + requestAnimationFrame(loop); }; export { startGame }; +// @license-end diff --git a/src/assets/_root/webtoys/bbe/scripts/lightning.js b/src/assets/_root/webtoys/bbe/scripts/lightning.js index 60093a0..864eb73 100644 --- a/src/assets/_root/webtoys/bbe/scripts/lightning.js +++ b/src/assets/_root/webtoys/bbe/scripts/lightning.js @@ -1,45 +1,55 @@ +// @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0 +/**************************************************************************** + * Byte Beasties Escape! + * + * Copyright 2023-2026 Eric Woodward + * Source released under CC0 Public Domain License v1.0 + * https://www.planarvagabond.com/licenses/cc0/ + * http://creativecommons.org/publicdomain/zero/1.0/ + ****************************************************************************/ export const createLightning = (canvas, enemy) => { - const { y: size } = enemy; - var center = { x: canvas.width / 2, y: canvas.height }; - var minSegmentHeight = 5; - var roughness = 2; - var maxDifference = size / 5; + const { y: size } = enemy; + var center = { x: canvas.width / 2, y: canvas.height }; + var minSegmentHeight = 5; + var roughness = 2; + var maxDifference = size / 5; - let lightning = []; - let segmentHeight = size / 3; - lightning.push({ - x: center.x, - y: center.y + 200, - }); - lightning.push({ - x: Math.random() * (canvas.width - 100) + 50, - y: Math.abs((Math.random() - 0.9) * 100), - }); - let currDiff = maxDifference; - while (segmentHeight > minSegmentHeight) { - const newSegments = []; - for (var i = 0; i < lightning.length - 1; i++) { - const start = lightning[i], - end = lightning[i + 1], - midX = (start.x + end.x) / 2, - newX = midX + (Math.random() * 2 - 1) * currDiff; - newSegments.push(start, { x: newX, y: (start.y + end.y) / 2 }); - } + let lightning = []; + let segmentHeight = size / 3; + lightning.push({ + x: center.x, + y: center.y + 200, + }); + lightning.push({ + x: Math.random() * (canvas.width - 100) + 50, + y: Math.abs((Math.random() - 0.9) * 100), + }); + let currDiff = maxDifference; + while (segmentHeight > minSegmentHeight) { + const newSegments = []; + for (var i = 0; i < lightning.length - 1; i++) { + const start = lightning[i], + end = lightning[i + 1], + midX = (start.x + end.x) / 2, + newX = midX + (Math.random() * 2 - 1) * currDiff; + newSegments.push(start, { x: newX, y: (start.y + end.y) / 2 }); + } - newSegments.push(lightning.pop()); - lightning = newSegments; + newSegments.push(lightning.pop()); + lightning = newSegments; - currDiff /= roughness; - segmentHeight /= 2; - } - return lightning; + currDiff /= roughness; + segmentHeight /= 2; + } + return lightning; }; // Get the position of the mouse relative to the canvas export const getMousePos = (canvasDom, mouseEvent) => { - var rect = canvasDom.getBoundingClientRect(); - return { - x: mouseEvent.clientX - rect.left, - y: mouseEvent.clientY - rect.top, - }; + var rect = canvasDom.getBoundingClientRect(); + return { + x: mouseEvent.clientX - rect.left, + y: mouseEvent.clientY - rect.top, + }; }; +// @license-end diff --git a/src/assets/_root/webtoys/bbe/scripts/main.js b/src/assets/_root/webtoys/bbe/scripts/main.js index b4d5469..422679f 100644 --- a/src/assets/_root/webtoys/bbe/scripts/main.js +++ b/src/assets/_root/webtoys/bbe/scripts/main.js @@ -1,8 +1,8 @@ // @license magnet:?xt=urn:btih:90dc5c0be029de84e523b9b3922520e79e0e6f08&dn=cc0.txt CC0-1.0 /**************************************************************************** - * Planar Vagabond's Guide to the Multiverse (planarvagabond.com) + * Byte Beasties Escape! * - * Copyright 2023-2024 Eric Woodward + * Copyright 2023-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.planarvagabond.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ @@ -11,22 +11,22 @@ import { startGame } from "./bbe.js"; export default (() => { - // we load this library as a module to guarantee baseline ES6 functionality + // we load this library as a module to guarantee baseline ES6 functionality - // Indicate JS is loaded - document.documentElement.className = - document.documentElement.className.replace("no-js", "js"); + // Indicate JS is loaded + document.documentElement.className = + document.documentElement.className.replace("no-js", "js"); - setTimeout(() => { - const canvas = document.createElement("canvas"), - contentDiv = document.getElementById("game"); + setTimeout(() => { + const canvas = document.createElement("canvas"), + contentDiv = document.getElementById("game"); - canvas.setAttribute("width", "375"); - canvas.setAttribute("height", "667"); - canvas.setAttribute("id", "game"); - contentDiv.appendChild(canvas); + canvas.setAttribute("width", "375"); + canvas.setAttribute("height", "667"); + canvas.setAttribute("id", "game"); + contentDiv.appendChild(canvas); - startGame(canvas); - }, 1); + startGame(canvas); + }, 1); })(); // @license-end diff --git a/src/assets/_root/webtoys/bps/index.html b/src/assets/_root/webtoys/bps/index.html index 3b3051f..e525f8c 100644 --- a/src/assets/_root/webtoys/bps/index.html +++ b/src/assets/_root/webtoys/bps/index.html @@ -1,150 +1,168 @@ - - - - - - BPS (Bill Paxton Soundboard) - - - -
+ + + + + BPS (Bill Paxton Soundboard) + + + +
- - + src="scripts/main.js" + type="module" + > - + -

- Download ZIP - Download TAR -

+

+ Download ZIP + Download TAR +

-
+
-

What is This?

+

What is This?

-

- This is the BPS (Bill Paxton Soundboard), my entry into the UI Developers Guild Coding Challenge for - February 2024 at the company I work for. -

+

+ This is the BPS (Bill Paxton Soundboard), my entry into the UI + Developers Guild Coding Challenge for February 2024 at the company I + work for. +

-

- If you want to know a bit more about how (and why) it was made, be sure to check - out the blog post I wrote about it when it was released - on 2024-02-10. -

+

+ If you want to know a bit more about how (and why) it was made, be + sure to check out + the blog post I wrote about it + when it was released on 2024-02-10. +

-

The Rules

+

The Rules

-

- This particular challenge had 3 simple rules (copied verbatim below): -

+

+ This particular challenge had 3 simple rules (copied verbatim + below): +

-
    -
  1. Play some kind of music / sound
  2. -
  3. Be viewable
  4. -
  5. Don't work over 4hrs!!!!
  6. -
+
    +
  1. Play some kind of music / sound
  2. +
  3. Be viewable
  4. +
  5. Don't work over 4hrs!!!!
  6. +
-

- The BPS satisfies all 3 of the rules: it's an HTML5 / CSS / JS application that creates - a series of virtual audio tags, and then loads a WAV or MP3 into each one before - inserting them into the document. It then renders a series of clickable image-buttons - (each one being an img tag, surrounded by a figure tag, and - augmented by the text of a figcaption tag) and attaches a play() - function to the click handlers for those figures. Plus, I wrote it all in under 4 hours: -

+

+ The BPS satisfies all 3 of the rules: it's an HTML5 / CSS / JS + application that creates a series of virtual + audio tags, and then loads a WAV or MP3 into each one + before inserting them into the document. It then renders a series of + clickable image-buttons (each one being an img tag, + surrounded by a figure tag, and augmented by the text + of a figcaption tag) and attaches a + play() function to the click handlers for those + figures. Plus, I wrote it all in under 4 hours: +

- - -

How to Install Locally

- -
    -
  1. - Download either the ZIP'd - or TAR-balled version. -
  2. -
  3. - Decompress it: -
  4. -
  5. - Change to the new directory: cd bps -
  6. -
  7. - Start an HTTP server: npx http-server -
  8. -
  9. - Point your web browser to - http://127.0.0.1:8080/. -
  10. -
-

Copyright

+

How to Install Locally

- +
    +
  1. + Download either the ZIP'd or + TAR-balled version. +
  2. +
  3. + Decompress it: + +
  4. +
  5. Change to the new directory: cd bps
  6. +
  7. Start an HTTP server: npx http-server
  8. +
  9. + Point your web browser to + http://127.0.0.1:8080/. +
  10. +
-

- The images and sound clips used for the Bill Paxton Soundboard are copyright their - respective owners, and used under the - fair use provision - of the Constitution of the United - States of America. -

+

Copyright

-

- All other content on this page (including scripts and text content) is released under a - Creative Commons CC0 1.0 Universal - license. -

+ -

Share and enjoy!

+

+ The images and sound clips used for the Bill Paxton Soundboard are + copyright their respective owners, and used under the + fair use provision + of the + Constitution of the United States of America. +

-

- More of Eric's Web Stuff -

+

+ All other content on this page (including scripts and text content) + is released under a + Creative Commons CC0 1.0 Universal + license. +

- - \ No newline at end of file +

Share and enjoy!

+ +

+ More of Eric's Web Stuff +

+ + diff --git a/src/assets/_root/webtoys/bps/scripts/main.js b/src/assets/_root/webtoys/bps/scripts/main.js index c58c0d9..facfac7 100644 --- a/src/assets/_root/webtoys/bps/scripts/main.js +++ b/src/assets/_root/webtoys/bps/scripts/main.js @@ -2,7 +2,7 @@ /**************************************************************************** * BPS (Bill Paxton Soundboard) * - * Copyright 2024 Eric Woodward + * Copyright 2024-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.itsericwoodward.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ @@ -12,127 +12,127 @@ import { load, play } from "./player.js"; import rootdir from "./rootdir.js"; export default (() => { - // adapted from: https://cheatcode.co/tutorials/how-to-build-a-soundboard-with-javascript + // adapted from: https://cheatcode.co/tutorials/how-to-build-a-soundboard-with-javascript - const sounds = [ - { - name: "true_lies_navel_lint", - fmt: "wav", - image: "aliens_17_days.png", - text: "Navel Lint", - }, - { - name: "true_lies_pathetic", - fmt: "mp3", - image: "aliens_17_days.png", - text: "It's Pathetic!", - }, + const sounds = [ + { + name: "true_lies_navel_lint", + fmt: "wav", + image: "aliens_17_days.png", + text: "Navel Lint", + }, + { + name: "true_lies_pathetic", + fmt: "mp3", + image: "aliens_17_days.png", + text: "It's Pathetic!", + }, - { - name: "aliens_game_over", - fmt: "wav", - image: "aliens_17_days.png", - text: "Game Over!", - }, - { - name: "aliens_current_affairs", - fmt: "wav", - image: "aliens_17_days.png", - text: "Current Affairs", - }, - { - name: "aliens_17_days", - fmt: "wav", - image: "aliens_17_days.png", - text: "17 Days?", - }, - { - name: "aliens_express_elevator", - fmt: "wav", - image: "aliens_17_days.png", - text: "Express Elevator", - }, + { + name: "aliens_game_over", + fmt: "wav", + image: "aliens_17_days.png", + text: "Game Over!", + }, + { + name: "aliens_current_affairs", + fmt: "wav", + image: "aliens_17_days.png", + text: "Current Affairs", + }, + { + name: "aliens_17_days", + fmt: "wav", + image: "aliens_17_days.png", + text: "17 Days?", + }, + { + name: "aliens_express_elevator", + fmt: "wav", + image: "aliens_17_days.png", + text: "Express Elevator", + }, - { - name: "weird_science_booze", - fmt: "mp3", - image: "aliens_17_days.png", - text: "Boozehounds Return", - }, - { - name: "weird_science_dead_meat", - fmt: "mp3", - image: "aliens_17_days.png", - text: "Dead Meat", - }, - { - name: "weird_science_sandwich", - fmt: "mp3", - image: "aliens_17_days.png", - text: "Pork Sandwich", - }, - { - name: "weird_science_stewwed", - fmt: "mp3", - image: "aliens_17_days.png", - text: "You're Stewwed", - }, - { - name: "weird_science_turd_brain", - fmt: "mp3", - image: "aliens_17_days.png", - text: "Turd Brain", - }, - ]; + { + name: "weird_science_booze", + fmt: "mp3", + image: "aliens_17_days.png", + text: "Boozehounds Return", + }, + { + name: "weird_science_dead_meat", + fmt: "mp3", + image: "aliens_17_days.png", + text: "Dead Meat", + }, + { + name: "weird_science_sandwich", + fmt: "mp3", + image: "aliens_17_days.png", + text: "Pork Sandwich", + }, + { + name: "weird_science_stewwed", + fmt: "mp3", + image: "aliens_17_days.png", + text: "You're Stewwed", + }, + { + name: "weird_science_turd_brain", + fmt: "mp3", + image: "aliens_17_days.png", + text: "Turd Brain", + }, + ]; - sounds.forEach(({ name, fmt }) => { - load(name, `${rootdir}/sounds/${name}.${fmt}`); - }); + sounds.forEach(({ name, fmt }) => { + load(name, `${rootdir}/sounds/${name}.${fmt}`); + }); - // adapted from https://stackoverflow.com/questions/12813573/position-icons-into-circle + // adapted from https://stackoverflow.com/questions/12813573/position-icons-into-circle - let m = sounds.length; /* how many are ON the circle */ - let tan = Math.tan(Math.PI / m); /* tangent of half the base angle */ + let m = sounds.length; /* how many are ON the circle */ + let tan = Math.tan(Math.PI / m); /* tangent of half the base angle */ - const build = () => { - const figureButtons = sounds.map(({ name, text }, idx) => { - const caption = document.createElement("figcaption"), - figure = document.createElement("figure"), - img = document.createElement("img"); - caption.textContent = text; - img.alt = text; - img.src = `${rootdir}/images/${name}.png`; - figure.className = "figureButton"; - figure.onclick = () => play(name); - figure.style = `--i: ${idx}`; - figure.append(...[img, caption]); - return figure; - }); + const build = () => { + const figureButtons = sounds.map(({ name, text }, idx) => { + const caption = document.createElement("figcaption"), + figure = document.createElement("figure"), + img = document.createElement("img"); + caption.textContent = text; + img.alt = text; + img.src = `${rootdir}/images/${name}.png`; + figure.className = "figureButton"; + figure.onclick = () => play(name); + figure.style = `--i: ${idx}`; + figure.append(...[img, caption]); + return figure; + }); - const randomFigure = document.createElement("figure"), - randomCaption = document.createElement("figcaption"), - randomImg = document.createElement("img"); - randomCaption.textContent = "Random"; - randomImg.alt = "Random"; - randomImg.src = `${rootdir}/images/random.png`; - randomFigure.className = "figureButton"; - randomFigure.onclick = () => - play(sounds[~~(sounds.length * Math.random())].name); - randomFigure.append(...[randomImg, randomCaption]); + const randomFigure = document.createElement("figure"), + randomCaption = document.createElement("figcaption"), + randomImg = document.createElement("img"); + randomCaption.textContent = "Random"; + randomImg.alt = "Random"; + randomImg.src = `${rootdir}/images/random.png`; + randomFigure.className = "figureButton"; + randomFigure.onclick = () => + play(sounds[~~(sounds.length * Math.random())].name); + randomFigure.append(...[randomImg, randomCaption]); - const div = document.createElement("div"); - div.className = "circleWrapper"; - div.style = `--m: ${m}; --tan: ${+tan.toFixed(2)}`; - div.append(...[randomFigure, ...figureButtons]); - document.getElementById("output").replaceWith(div); - }; + const div = document.createElement("div"); + div.className = "circleWrapper"; + div.style = `--m: ${m}; --tan: ${+tan.toFixed(2)}`; + div.append(...[randomFigure, ...figureButtons]); + document.getElementById("output").replaceWith(div); + }; - document.addEventListener("DOMContentLoaded", () => { - setTimeout(() => { - build(); - }, 1); - }); + document.addEventListener("DOMContentLoaded", () => { + setTimeout(() => { + build(); + }, 1); + }); - return { play, build }; + return { play, build }; })(); // @license-end diff --git a/src/assets/_root/webtoys/bps/scripts/player.js b/src/assets/_root/webtoys/bps/scripts/player.js index 305d31a..cd2b497 100644 --- a/src/assets/_root/webtoys/bps/scripts/player.js +++ b/src/assets/_root/webtoys/bps/scripts/player.js @@ -2,7 +2,7 @@ /**************************************************************************** * BPS (Bill Paxton Soundboard) * - * Copyright 2024 Eric Woodward + * Copyright 2024-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.itsericwoodward.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ @@ -13,25 +13,25 @@ let sounds = []; const injectPlayerIntoPage = (name, path) => { - const player = document.createElement("audio"); - player.id = name; - player.src = path; - player.volume = 0.5; - player.type = "audio/mpeg"; - document.body.appendChild(player); - }, - load = (name, path) => { - sounds = [...sounds, { name, path }]; - injectPlayerIntoPage(name, path); - }, - play = (name) => { - const player = document.getElementById(name); - if (player) { - player.pause(); - player.currentTime = 0; - player.play(); - } - }; + const player = document.createElement("audio"); + player.id = name; + player.src = path; + player.volume = 0.5; + player.type = "audio/mpeg"; + document.body.appendChild(player); + }, + load = (name, path) => { + sounds = [...sounds, { name, path }]; + injectPlayerIntoPage(name, path); + }, + play = (name) => { + const player = document.getElementById(name); + if (player) { + player.pause(); + player.currentTime = 0; + player.play(); + } + }; export { load, play }; // @license-end diff --git a/src/assets/_root/webtoys/bps/styles/styles.css b/src/assets/_root/webtoys/bps/styles/styles.css index 0264d93..d8c04d9 100644 --- a/src/assets/_root/webtoys/bps/styles/styles.css +++ b/src/assets/_root/webtoys/bps/styles/styles.css @@ -2,7 +2,7 @@ /**************************************************************************** * BPS (Bill Paxton Soundboard) * - * Copyright 2024 Eric Woodward + * Copyright 2024-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.itsericwoodward.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/src/assets/fragments/license/mit.md b/src/assets/fragments/license/mit.md index 2b75054..78e4102 100644 --- a/src/assets/fragments/license/mit.md +++ b/src/assets/fragments/license/mit.md @@ -1,4 +1,4 @@ -Copyright © 2023-2024 [Eric Woodward](https://www.itsericwoodward.com/) +Copyright © 2023-2026 [Eric Woodward](https://www.itsericwoodward.com/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/src/assets/images/eric-8bit.png b/src/assets/images/eric-8bit.png new file mode 100644 index 0000000..162a7b3 Binary files /dev/null and b/src/assets/images/eric-8bit.png differ diff --git a/src/assets/scripts/backgroundScroller.js b/src/assets/scripts/backgroundScroller.js index fe28e19..fec873f 100644 --- a/src/assets/scripts/backgroundScroller.js +++ b/src/assets/scripts/backgroundScroller.js @@ -2,7 +2,7 @@ /**************************************************************************** * It's Eric Woodward's Site * - * Copyright 2014-2025 Eric Woodward + * Copyright 2014-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.itsericwoodward.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ @@ -43,29 +43,21 @@ export default (actionBox) => { actionBox.classList.add("js-actionBox"); const actionBoxTitle = document.createElement("summary"); - actionBoxTitle.innerHTML = - "

Settings

"; + actionBoxTitle.innerHTML = "

Settings

"; actionBox.append(actionBoxTitle); // add toggle event document .getElementById("scrollingToggle") .addEventListener("click", function (e) { - const theList = - document.getElementById("gridContainer").classList; + const theList = document.getElementById("gridContainer").classList; theList.toggle("js-isAnimated"); - Cookies.set( - "scrollToggle", - !!theList.contains("js-isAnimated") - ); + Cookies.set("scrollToggle", !!theList.contains("js-isAnimated")); }); // add class if active at startup if (hasScrollToggle) - document - .getElementById("gridContainer") - .classList.add("js-isAnimated"); - + document.getElementById("gridContainer").classList.add("js-isAnimated"); }; // @license-end diff --git a/src/assets/scripts/scripts.js b/src/assets/scripts/scripts.js index 4ef2b7c..54ee301 100644 --- a/src/assets/scripts/scripts.js +++ b/src/assets/scripts/scripts.js @@ -2,76 +2,76 @@ /**************************************************************************** * It's Eric Woodward's Site * - * Copyright 2014-2025 Eric Woodward + * Copyright 2014-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.itsericwoodward.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ ****************************************************************************/ -import backgroundScroller from './backgroundScroller.js'; -import themeSwitcher from './themeSwitcher.js'; +import backgroundScroller from "./backgroundScroller.js"; +import themeSwitcher from "./themeSwitcher.js"; export default (() => { - // we load this library via "module" to guarantee baseline ES6 functionality + // we load this library via "module" to guarantee baseline ES6 functionality - // check for loaded libraries - if (!window.Cookies) return; + // check for loaded libraries + if (!window.Cookies) return; - if (window.dayjs) dayjs.extend(window.dayjs_plugin_relativeTime); + if (window.dayjs) dayjs.extend(window.dayjs_plugin_relativeTime); - // Indicate JS is loaded - document.documentElement.className = - document.documentElement.className.replace("no-js", "js"); + // Indicate JS is loaded + document.documentElement.className = + document.documentElement.className.replace("no-js", "js"); - document.addEventListener("DOMContentLoaded", () => { - setTimeout(() => { - if (!window.Cookies) return; + document.addEventListener("DOMContentLoaded", () => { + setTimeout(() => { + if (!window.Cookies) return; - // Lazy-Load Media - if (typeof loadMedia === "function") { - loadMedia(".js-lazyLoader", null, true); - } + // Lazy-Load Media + if (typeof loadMedia === "function") { + loadMedia(".js-lazyLoader", null, true); + } - // Add relative dates via dayjs - if (window.dayjs) { - const times = document.getElementsByTagName("time"); + // Add relative dates via dayjs + if (window.dayjs) { + const times = document.getElementsByTagName("time"); - [].forEach.call(times, function (the_time) { - let pub_time = the_time.getAttribute("datetime"); - if (the_time.className.indexOf("js-noRelativeTime") === -1) - the_time.innerHTML += - ' (' + - dayjs(pub_time).from(dayjs()) + - ")"; - the_time.classList.add("isDone"); - }); - } + [].forEach.call(times, function (the_time) { + let pub_time = the_time.getAttribute("datetime"); + if (the_time.className.indexOf("js-noRelativeTime") === -1) + the_time.innerHTML += + ' (' + + dayjs(pub_time).from(dayjs()) + + ")"; + the_time.classList.add("isDone"); + }); + } - const actionBoxDiv = document.querySelector("#bio .actionBox"); + const actionBoxDiv = document.querySelector("#bio .actionBox"); - const actionBox = document.createElement("details"); + const actionBox = document.createElement("details"); - actionBoxDiv.append(actionBox); + actionBoxDiv.append(actionBox); - actionBox.setAttribute("open", "true"); + actionBox.setAttribute("open", "true"); - themeSwitcher(actionBox); + themeSwitcher(actionBox); - backgroundScroller(actionBox); + backgroundScroller(actionBox); - if (document.documentElement.className.indexOf("is404") > -1) { - document.getElementById("searchQuery").value = - window.location.pathname - .replace(/\\.html?$/, "") - .replace(/\//g, " "); - } + if (document.documentElement.className.indexOf("is404") > -1) { + document.getElementById("searchQuery").value = + window.location.pathname + .replace(/\\.html?$/, "") + .replace(/\//g, " "); + } - document - .getElementById("searchForm") - .addEventListener("submit", function (e) { - document.getElementById("searchQuery").value += - " site:" + window.location.hostname; - }); - }, 1); - }); + document + .getElementById("searchForm") + .addEventListener("submit", function (e) { + document.getElementById("searchQuery").value += + " site:" + window.location.hostname; + }); + }, 1); + }); })(); // @license-end diff --git a/src/assets/scripts/themeSwitcher.js b/src/assets/scripts/themeSwitcher.js index f59d84e..50a13f7 100644 --- a/src/assets/scripts/themeSwitcher.js +++ b/src/assets/scripts/themeSwitcher.js @@ -2,7 +2,7 @@ /**************************************************************************** * It's Eric Woodward's Site * - * Copyright 2014-2025 Eric Woodward + * Copyright 2014-2026 Eric Woodward * Source released under CC0 Public Domain License v1.0 * https://www.itsericwoodward.com/licenses/cc0/ * http://creativecommons.org/publicdomain/zero/1.0/ @@ -54,9 +54,7 @@ export default (actionBox) => { themeSwitch.classList.add("js-themeSwitch", "themeSwitch"); if (currentTheme) - document - .getElementsByTagName("body")[0] - .classList.add(currentTheme); + document.getElementsByTagName("body")[0].classList.add(currentTheme); actionBox.append(themeSwitch); @@ -72,5 +70,5 @@ export default (actionBox) => { .join(" "); Cookies.set("currentTheme", e.target.value); }); -} +}; // @license-end diff --git a/src/layouts/partials/bio.ejs b/src/layouts/partials/bio.ejs index 2a6441a..392c48c 100644 --- a/src/layouts/partials/bio.ejs +++ b/src/layouts/partials/bio.ejs @@ -41,7 +41,10 @@