diff --git a/birb.js b/birb.js index 95b97df..061cc41 100644 --- a/birb.js +++ b/birb.js @@ -37,7 +37,8 @@ const HOP_DISTANCE = settings.hopDistance; // Time in milliseconds until the user is considered AFK const AFK_TIME = 1000 * 30; const SPRITE_HEIGHT = 32; -const START_MENU_ID = "birb-start-menu"; +const MENU_ID = "birb-menu"; +const MENU_EXIT_ID = "birb-menu-exit"; const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; @@ -96,6 +97,20 @@ const styles = ` transition-timing-function: ease-in; } + #${MENU_ID} { + transition-duration: 0.2s; + transition-timing-function: ease-out; + } + + #${MENU_EXIT_ID} { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 999999997; + } + @keyframes pop-in { 0% { opacity: 1; transform: scale(0.1); } 100% { opacity: 1; transform: scale(1); } @@ -192,6 +207,8 @@ const styles = ` padding-bottom: 5px; opacity: 0.6; user-select: none; + display: flex; + justify-content: space-between; } .birb-window-list-item:hover { @@ -199,14 +216,18 @@ const styles = ` cursor: pointer; } + .birb-window-list-item-arrow { + display: inline-block; + } + .birb-window-separator { width: 100%; height: 1.5px; background-color: #000000; box-sizing: border-box; - margin-top: 6px; - margin-bottom: 6px; - opacity: 0.45; + margin-top: 4px; + margin-bottom: 4px; + opacity: 0.4; } #${FIELD_GUIDE_ID} { @@ -332,14 +353,6 @@ class Frame { } this.#pixelsByTag[tag] = this.pixels.map(row => row.slice()); } - // Surround non-transparent pixels with border - // for (let y = 0; y < this.pixels.length; y++) { - // for (let x = 0; x < this.pixels[y].length; x++) { - // if (this.pixels[y][x] === TRANSPARENT && this.hasAdjacent(x, y)) { - // this.pixels[y][x] = BORDER; - // } - // } - // } } /** @@ -350,20 +363,6 @@ class Frame { return this.#pixelsByTag[tag] ?? this.#pixelsByTag["default"]; } - // hasAdjacent(x, y) { - // for (let i = -1; i <= 1; i++) { - // for (let j = -1; j <= 1; j++) { - // if (i === 0 && j === 0) { - // continue; - // } - // if (this.#pixels[y + i] && this.#pixels[y + i][x + j] && this.#pixels[y + i][x + j] !== TRANSPARENT && this.#pixels[y + i][x + j] !== BORDER) { - // return true; - // } - // } - // } - // return false - // } - /** * @param {CanvasRenderingContext2D} ctx * @param {number} direction @@ -691,6 +690,47 @@ Promise.all([loadSpritesheetPixels(SPRITE_SHEET_URI), loadSpritesheetPixels(DECO ]), }; + class MenuItem { + /** + * @param {string} text + * @param {() => void} action + * @param {boolean} [removeMenu] + */ + constructor(text, action, removeMenu = true) { + this.text = text; + this.action = action; + this.removeMenu = removeMenu; + } + } + + class Separator extends MenuItem { + constructor() { + super("", () => {}); + } + } + + const menuItems = [ + new MenuItem("Pet Birb", pet), + new MenuItem("Field Guide", insertFieldGuide), + // new MenuItem("Decorations", insertDecoration), + new MenuItem("Programs", () => switchMenuItems(programItems), false), + new Separator(), + new MenuItem("Settings", () => {}), + ]; + + const programItems = [ + new MenuItem("Go Back", () => switchMenuItems(menuItems), false), + new Separator(), + new MenuItem("Pico Dino", () => insertPico8("Pico Dino", "picodino")), + new MenuItem("Tetraminis", () => insertPico8("Tetraminis", "tetraminisdeffect")), + new MenuItem("Woodworm", () => insertPico8("Woodworm", "woodworm")), + new MenuItem("Wobblepaint ", () => insertPico8("Wobblepaint", "wobblepaint")), + new MenuItem("Terra Nova Pinball", () => insertPico8("Terra Nova Pinball", "terra_nova_pinball")), + new MenuItem("Pico and Chill", () => insertPico8("Pico and Chill", "picochill")), + new MenuItem("Celeste 2", () => insertPico8("Celeste 2", "celeste_classic_2")), + new MenuItem("Pool", () => insertPico8("Pool", "mot_pool")), + ]; + const styleElement = document.createElement("style"); const canvas = document.createElement("canvas"); @@ -751,13 +791,13 @@ Promise.all([loadSpritesheetPixels(SPRITE_SHEET_URI), loadSpritesheetPixels(DECO document.addEventListener("click", (e) => { timeOfLastAction = Date.now(); - if (e.target instanceof Node && !canvas.contains(e.target) && !document.querySelector(".birb-window")?.contains(e.target)) { - removeStartMenu(); + if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { + removeMenu(); } }); canvas.addEventListener("click", () => { - insertStartMenu(); + insertMenu(); }); canvas.addEventListener("mouseover", () => { @@ -782,7 +822,7 @@ Promise.all([loadSpritesheetPixels(SPRITE_SHEET_URI), loadSpritesheetPixels(DECO function update() { ticks++; if (currentState === States.IDLE) { - if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART && !isStartMenuOpen()) { + if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART && !isMenuOpen()) { hop(); } } else if (currentState === States.HOP) { @@ -812,7 +852,7 @@ Promise.all([loadSpritesheetPixels(SPRITE_SHEET_URI), loadSpritesheetPixels(DECO } if (focusedElement === null) { - if (Date.now() - timeOfLastAction > AFK_TIME && !isStartMenuOpen()) { + if (Date.now() - timeOfLastAction > AFK_TIME && !isMenuOpen()) { // Fly to an element if the user is AFK focusOnElement(); timeOfLastAction = Date.now(); @@ -1053,6 +1093,19 @@ Promise.all([loadSpritesheetPixels(SPRITE_SHEET_URI), loadSpritesheetPixels(DECO } } + /** + * @param {HTMLElement} closeButton + * @param {() => void} func + */ + function makeClosable(closeButton, func) { + closeButton.addEventListener("click", func); + document.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + func(); + } + }); + } + function removeFieldGuide() { const fieldGuide = document.querySelector("#" + FIELD_GUIDE_ID); if (fieldGuide) { @@ -1060,20 +1113,24 @@ Promise.all([loadSpritesheetPixels(SPRITE_SHEET_URI), loadSpritesheetPixels(DECO } } - insertPico8(); + // insertPico8(); function isSafari() { return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); } - function insertPico8() { + /** + * @param {string} name + * @param {string} pid + */ + function insertPico8(name, pid) { let html = `