diff --git a/birb.js b/birb.js index 795f1cb..72a8564 100644 --- a/birb.js +++ b/birb.js @@ -812,6 +812,178 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return settings().birbMode ? "Birb" : "Bird"; } + function init() { + if (window !== window.top) { + // Skip installation if within an iframe + log("In iframe, skipping Birb script initialization"); + return; + } + log("Sprite sheets loaded successfully, initializing bird..."); + + // Preload font + const MONOCRAFT_SRC = "https://cdn.jsdelivr.net/gh/idreesinc/Monocraft@99b32ab40612ff2533a69d8f14bd8b3d9e604456/dist/Monocraft.otf"; + const fontLink = document.createElement("link"); + fontLink.rel = "stylesheet"; + fontLink.href = `url(${MONOCRAFT_SRC}) format('opentype')`; + document.head.appendChild(fontLink); + + // Add stylesheet font-face + const fontFace = ` + @font-face { + font-family: 'Monocraft'; + src: url(${MONOCRAFT_SRC}) format('opentype'); + font-weight: normal; + font-style: normal; + } + `; + const fontStyle = document.createElement("style"); + fontStyle.innerHTML = fontFace; + document.head.appendChild(fontStyle); + + load(); + + styleElement.innerHTML = STYLESHEET; + document.head.appendChild(styleElement); + + canvas.id = "birb"; + canvas.width = birbFrames.base.getPixels()[0].length * CANVAS_PIXEL_SIZE; + canvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + document.body.appendChild(canvas); + + window.addEventListener("scroll", () => { + lastActionTimestamp = Date.now(); + }); + + onClick(document, (e) => { + lastActionTimestamp = Date.now(); + if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { + removeMenu(); + } + }); + + onClick(canvas, () => { + if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) { + // Currently being pet, don't open menu + return; + } + insertMenu(); + }); + + canvas.addEventListener("mouseover", () => { + lastActionTimestamp = Date.now(); + if (currentState === States.IDLE) { + petStack.push(Date.now()); + if (petStack.length > 10) { + petStack.shift(); + } + const pets = petStack.filter((time) => Date.now() - time < 1000).length; + if (pets >= 3) { + pet(); + // Clear the stack + petStack = []; + } + } + }); + + canvas.addEventListener("touchmove", (e) => { + pet(); + }); + + drawStickyNotes(); + + let lastUrl = (window.location.href ?? "").split("?")[0]; + setInterval(() => { + const currentUrl = (window.location.href ?? "").split("?")[0]; + if (currentUrl !== lastUrl) { + log("URL changed, updating sticky notes"); + lastUrl = currentUrl; + drawStickyNotes(); + } + }, URL_CHECK_INTERVAL); + + setInterval(update, UPDATE_INTERVAL); + } + + function update() { + ticks++; + + // Hide bird if the browser is fullscreen + if (document.fullscreenElement) { + hideBirb(); + // Won't be restored on fullscreen exit + } + + if (currentState === States.IDLE && !frozen && !isMenuOpen()) { + if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) { + hop(); + } else if (Date.now() - lastActionTimestamp > AFK_TIME) { + // Idle for a while, do something + if (focusedElement === null) { + // Fly to an element + focusOnElement(); + lastActionTimestamp = Date.now(); + } else if (Math.random() < FOCUS_SWITCH_CHANCE) { + // Fly to another element if idle for a longer while + focusOnElement(); + lastActionTimestamp = Date.now(); + } + } + } else if (currentState === States.HOP) { + if (updateParabolicPath(HOP_SPEED)) { + setState(States.IDLE); + } + } + + // Double the chance of a feather if recently pet + const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1; + if (visible && Math.random() < FEATHER_CHANCE * petMod) { + lastPetTimestamp = 0; + activateFeather(); + } + updateFeather(); + } + + function draw() { + requestAnimationFrame(draw); + + if (!visible) { + return; + } + + updateFocusedElementBounds(); + + // Update the bird's position + if (currentState === States.IDLE) { + if (focusedElement && !isWithinHorizontalBounds()) { + focusOnGround(); + } + birdY = getFocusedY(); + } else if (currentState === States.FLYING) { + // Fly to target location (even if in the air) + if (updateParabolicPath(FLY_SPEED)) { + setState(States.IDLE); + } + } + + const oldTargetY = targetY; + targetY = getFocusedY(); + // Adjust startY to account for scrolling + startY += targetY - oldTargetY; + if (targetY < 0 || targetY > window.innerHeight) { + // Fly to ground if the focused element moves out of bounds + focusOnGround(); + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { + setAnimation(Animations.STILL); + } + + // Update HTML element position + setX(birdX); + setY(birdY); + } + function newStickyNote() { const id = Date.now().toString(); const site = window.location.href; @@ -925,98 +1097,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return true; } - function init() { - if (window !== window.top) { - // Skip installation if within an iframe - log("In iframe, skipping Birb script initialization"); - return; - } - log("Sprite sheets loaded successfully, initializing bird..."); - - // Preload font - const MONOCRAFT_SRC = "https://cdn.jsdelivr.net/gh/idreesinc/Monocraft@99b32ab40612ff2533a69d8f14bd8b3d9e604456/dist/Monocraft.otf"; - const fontLink = document.createElement("link"); - fontLink.rel = "stylesheet"; - fontLink.href = `url(${MONOCRAFT_SRC}) format('opentype')`; - document.head.appendChild(fontLink); - - // Add stylesheet font-face - const fontFace = ` - @font-face { - font-family: 'Monocraft'; - src: url(${MONOCRAFT_SRC}) format('opentype'); - font-weight: normal; - font-style: normal; - } - `; - const fontStyle = document.createElement("style"); - fontStyle.innerHTML = fontFace; - document.head.appendChild(fontStyle); - - load(); - - styleElement.innerHTML = STYLESHEET; - document.head.appendChild(styleElement); - - canvas.id = "birb"; - canvas.width = birbFrames.base.getPixels()[0].length * CANVAS_PIXEL_SIZE; - canvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; - document.body.appendChild(canvas); - - window.addEventListener("scroll", () => { - lastActionTimestamp = Date.now(); - }); - - onClick(document, (e) => { - lastActionTimestamp = Date.now(); - if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { - removeMenu(); - } - }); - - onClick(canvas, () => { - if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) { - // Currently being pet, don't open menu - return; - } - insertMenu(); - }); - - canvas.addEventListener("mouseover", () => { - lastActionTimestamp = Date.now(); - if (currentState === States.IDLE) { - petStack.push(Date.now()); - if (petStack.length > 10) { - petStack.shift(); - } - const pets = petStack.filter((time) => Date.now() - time < 1000).length; - if (pets >= 3) { - pet(); - // Clear the stack - petStack = []; - } - } - }); - - canvas.addEventListener("touchmove", (e) => { - pet(); - }); - - drawStickyNotes(); - - let lastUrl = (window.location.href ?? "").split("?")[0]; - setInterval(() => { - const currentUrl = (window.location.href ?? "").split("?")[0]; - if (currentUrl !== lastUrl) { - log("URL changed, updating sticky notes"); - lastUrl = currentUrl; - drawStickyNotes(); - } - }, URL_CHECK_INTERVAL); - - setInterval(update, UPDATE_INTERVAL); - } - function drawStickyNotes() { // Remove all existing sticky notes const existingNotes = document.querySelectorAll(".birb-sticky-note"); @@ -1029,89 +1109,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI } } - function update() { - ticks++; - - // Hide bird if the browser is fullscreen - if (document.fullscreenElement) { - hideBirb(); - // Won't be restored on fullscreen exit - } - - if (currentState === States.IDLE && !frozen && !isMenuOpen()) { - if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) { - hop(); - } else if (Date.now() - lastActionTimestamp > AFK_TIME) { - // Idle for a while, do something - if (focusedElement === null) { - // Fly to an element - focusOnElement(); - lastActionTimestamp = Date.now(); - } else if (Math.random() < FOCUS_SWITCH_CHANCE) { - // Fly to another element if idle for a longer while - focusOnElement(); - lastActionTimestamp = Date.now(); - } - } - } else if (currentState === States.HOP) { - if (updateParabolicPath(HOP_SPEED)) { - setState(States.IDLE); - } - } - - // Double the chance of a feather if recently pet - const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1; - if (visible && Math.random() < FEATHER_CHANCE * petMod) { - lastPetTimestamp = 0; - activateFeather(); - } - updateFeather(); - } - - function draw() { - requestAnimationFrame(draw); - - if (!visible) { - return; - } - - updateFocusedElementBounds(); - - // Update the bird's position - if (currentState === States.IDLE) { - if (focusedElement && !isWithinHorizontalBounds()) { - focusOnGround(); - } - birdY = getFocusedY(); - } else if (currentState === States.FLYING) { - // Fly to target location (even if in the air) - if (updateParabolicPath(FLY_SPEED)) { - setState(States.IDLE); - } - } - - const oldTargetY = targetY; - targetY = getFocusedY(); - // Adjust startY to account for scrolling - startY += targetY - oldTargetY; - if (targetY < 0 || targetY > window.innerHeight) { - // Fly to ground if the focused element moves out of bounds - focusOnGround(); - } - - ctx.clearRect(0, 0, canvas.width, canvas.height); - if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { - setAnimation(Animations.STILL); - } - - // Update HTML element position - setX(birdX); - setY(birdY); - } - - init(); - draw(); - /** * Create an HTML element with the specified parameters * @param {string} className @@ -1865,6 +1862,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return { ...params, [key]: value }; }, {}); } + + // Run the birb + init(); + draw(); }).catch((e) => { error("Error while loading sprite sheets: ", e); }); diff --git a/dist/birb.js b/dist/birb.js index d525656..1565bca 100644 --- a/dist/birb.js +++ b/dist/birb.js @@ -1155,6 +1155,178 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return settings().birbMode ? "Birb" : "Bird"; } + function init() { + if (window !== window.top) { + // Skip installation if within an iframe + log("In iframe, skipping Birb script initialization"); + return; + } + log("Sprite sheets loaded successfully, initializing bird..."); + + // Preload font + const MONOCRAFT_SRC = "https://cdn.jsdelivr.net/gh/idreesinc/Monocraft@99b32ab40612ff2533a69d8f14bd8b3d9e604456/dist/Monocraft.otf"; + const fontLink = document.createElement("link"); + fontLink.rel = "stylesheet"; + fontLink.href = `url(${MONOCRAFT_SRC}) format('opentype')`; + document.head.appendChild(fontLink); + + // Add stylesheet font-face + const fontFace = ` + @font-face { + font-family: 'Monocraft'; + src: url(${MONOCRAFT_SRC}) format('opentype'); + font-weight: normal; + font-style: normal; + } + `; + const fontStyle = document.createElement("style"); + fontStyle.innerHTML = fontFace; + document.head.appendChild(fontStyle); + + load(); + + styleElement.innerHTML = STYLESHEET; + document.head.appendChild(styleElement); + + canvas.id = "birb"; + canvas.width = birbFrames.base.getPixels()[0].length * CANVAS_PIXEL_SIZE; + canvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + document.body.appendChild(canvas); + + window.addEventListener("scroll", () => { + lastActionTimestamp = Date.now(); + }); + + onClick(document, (e) => { + lastActionTimestamp = Date.now(); + if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { + removeMenu(); + } + }); + + onClick(canvas, () => { + if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) { + // Currently being pet, don't open menu + return; + } + insertMenu(); + }); + + canvas.addEventListener("mouseover", () => { + lastActionTimestamp = Date.now(); + if (currentState === States.IDLE) { + petStack.push(Date.now()); + if (petStack.length > 10) { + petStack.shift(); + } + const pets = petStack.filter((time) => Date.now() - time < 1000).length; + if (pets >= 3) { + pet(); + // Clear the stack + petStack = []; + } + } + }); + + canvas.addEventListener("touchmove", (e) => { + pet(); + }); + + drawStickyNotes(); + + let lastUrl = (window.location.href ?? "").split("?")[0]; + setInterval(() => { + const currentUrl = (window.location.href ?? "").split("?")[0]; + if (currentUrl !== lastUrl) { + log("URL changed, updating sticky notes"); + lastUrl = currentUrl; + drawStickyNotes(); + } + }, URL_CHECK_INTERVAL); + + setInterval(update, UPDATE_INTERVAL); + } + + function update() { + ticks++; + + // Hide bird if the browser is fullscreen + if (document.fullscreenElement) { + hideBirb(); + // Won't be restored on fullscreen exit + } + + if (currentState === States.IDLE && !frozen && !isMenuOpen()) { + if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) { + hop(); + } else if (Date.now() - lastActionTimestamp > AFK_TIME) { + // Idle for a while, do something + if (focusedElement === null) { + // Fly to an element + focusOnElement(); + lastActionTimestamp = Date.now(); + } else if (Math.random() < FOCUS_SWITCH_CHANCE) { + // Fly to another element if idle for a longer while + focusOnElement(); + lastActionTimestamp = Date.now(); + } + } + } else if (currentState === States.HOP) { + if (updateParabolicPath(HOP_SPEED)) { + setState(States.IDLE); + } + } + + // Double the chance of a feather if recently pet + const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1; + if (visible && Math.random() < FEATHER_CHANCE * petMod) { + lastPetTimestamp = 0; + activateFeather(); + } + updateFeather(); + } + + function draw() { + requestAnimationFrame(draw); + + if (!visible) { + return; + } + + updateFocusedElementBounds(); + + // Update the bird's position + if (currentState === States.IDLE) { + if (focusedElement && !isWithinHorizontalBounds()) { + focusOnGround(); + } + birdY = getFocusedY(); + } else if (currentState === States.FLYING) { + // Fly to target location (even if in the air) + if (updateParabolicPath(FLY_SPEED)) { + setState(States.IDLE); + } + } + + const oldTargetY = targetY; + targetY = getFocusedY(); + // Adjust startY to account for scrolling + startY += targetY - oldTargetY; + if (targetY < 0 || targetY > window.innerHeight) { + // Fly to ground if the focused element moves out of bounds + focusOnGround(); + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { + setAnimation(Animations.STILL); + } + + // Update HTML element position + setX(birdX); + setY(birdY); + } + function newStickyNote() { const id = Date.now().toString(); const site = window.location.href; @@ -1268,98 +1440,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return true; } - function init() { - if (window !== window.top) { - // Skip installation if within an iframe - log("In iframe, skipping Birb script initialization"); - return; - } - log("Sprite sheets loaded successfully, initializing bird..."); - - // Preload font - const MONOCRAFT_SRC = "https://cdn.jsdelivr.net/gh/idreesinc/Monocraft@99b32ab40612ff2533a69d8f14bd8b3d9e604456/dist/Monocraft.otf"; - const fontLink = document.createElement("link"); - fontLink.rel = "stylesheet"; - fontLink.href = `url(${MONOCRAFT_SRC}) format('opentype')`; - document.head.appendChild(fontLink); - - // Add stylesheet font-face - const fontFace = ` - @font-face { - font-family: 'Monocraft'; - src: url(${MONOCRAFT_SRC}) format('opentype'); - font-weight: normal; - font-style: normal; - } - `; - const fontStyle = document.createElement("style"); - fontStyle.innerHTML = fontFace; - document.head.appendChild(fontStyle); - - load(); - - styleElement.innerHTML = STYLESHEET; - document.head.appendChild(styleElement); - - canvas.id = "birb"; - canvas.width = birbFrames.base.getPixels()[0].length * CANVAS_PIXEL_SIZE; - canvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; - document.body.appendChild(canvas); - - window.addEventListener("scroll", () => { - lastActionTimestamp = Date.now(); - }); - - onClick(document, (e) => { - lastActionTimestamp = Date.now(); - if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { - removeMenu(); - } - }); - - onClick(canvas, () => { - if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) { - // Currently being pet, don't open menu - return; - } - insertMenu(); - }); - - canvas.addEventListener("mouseover", () => { - lastActionTimestamp = Date.now(); - if (currentState === States.IDLE) { - petStack.push(Date.now()); - if (petStack.length > 10) { - petStack.shift(); - } - const pets = petStack.filter((time) => Date.now() - time < 1000).length; - if (pets >= 3) { - pet(); - // Clear the stack - petStack = []; - } - } - }); - - canvas.addEventListener("touchmove", (e) => { - pet(); - }); - - drawStickyNotes(); - - let lastUrl = (window.location.href ?? "").split("?")[0]; - setInterval(() => { - const currentUrl = (window.location.href ?? "").split("?")[0]; - if (currentUrl !== lastUrl) { - log("URL changed, updating sticky notes"); - lastUrl = currentUrl; - drawStickyNotes(); - } - }, URL_CHECK_INTERVAL); - - setInterval(update, UPDATE_INTERVAL); - } - function drawStickyNotes() { // Remove all existing sticky notes const existingNotes = document.querySelectorAll(".birb-sticky-note"); @@ -1372,89 +1452,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI } } - function update() { - ticks++; - - // Hide bird if the browser is fullscreen - if (document.fullscreenElement) { - hideBirb(); - // Won't be restored on fullscreen exit - } - - if (currentState === States.IDLE && !frozen && !isMenuOpen()) { - if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) { - hop(); - } else if (Date.now() - lastActionTimestamp > AFK_TIME) { - // Idle for a while, do something - if (focusedElement === null) { - // Fly to an element - focusOnElement(); - lastActionTimestamp = Date.now(); - } else if (Math.random() < FOCUS_SWITCH_CHANCE) { - // Fly to another element if idle for a longer while - focusOnElement(); - lastActionTimestamp = Date.now(); - } - } - } else if (currentState === States.HOP) { - if (updateParabolicPath(HOP_SPEED)) { - setState(States.IDLE); - } - } - - // Double the chance of a feather if recently pet - const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1; - if (visible && Math.random() < FEATHER_CHANCE * petMod) { - lastPetTimestamp = 0; - activateFeather(); - } - updateFeather(); - } - - function draw() { - requestAnimationFrame(draw); - - if (!visible) { - return; - } - - updateFocusedElementBounds(); - - // Update the bird's position - if (currentState === States.IDLE) { - if (focusedElement && !isWithinHorizontalBounds()) { - focusOnGround(); - } - birdY = getFocusedY(); - } else if (currentState === States.FLYING) { - // Fly to target location (even if in the air) - if (updateParabolicPath(FLY_SPEED)) { - setState(States.IDLE); - } - } - - const oldTargetY = targetY; - targetY = getFocusedY(); - // Adjust startY to account for scrolling - startY += targetY - oldTargetY; - if (targetY < 0 || targetY > window.innerHeight) { - // Fly to ground if the focused element moves out of bounds - focusOnGround(); - } - - ctx.clearRect(0, 0, canvas.width, canvas.height); - if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { - setAnimation(Animations.STILL); - } - - // Update HTML element position - setX(birdX); - setY(birdY); - } - - init(); - draw(); - /** * Create an HTML element with the specified parameters * @param {string} className @@ -2208,6 +2205,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return { ...params, [key]: value }; }, {}); } + + // Run the birb + init(); + draw(); }).catch((e) => { error("Error while loading sprite sheets: ", e); }); diff --git a/dist/birb.user.js b/dist/birb.user.js index ab361a5..d450320 100644 --- a/dist/birb.user.js +++ b/dist/birb.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Pocket Bird // @namespace https://idreesinc.com -// @version 2025.10.25.117 +// @version 2025.10.25.124 // @description birb // @author Idrees // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js @@ -1169,6 +1169,178 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return settings().birbMode ? "Birb" : "Bird"; } + function init() { + if (window !== window.top) { + // Skip installation if within an iframe + log("In iframe, skipping Birb script initialization"); + return; + } + log("Sprite sheets loaded successfully, initializing bird..."); + + // Preload font + const MONOCRAFT_SRC = "https://cdn.jsdelivr.net/gh/idreesinc/Monocraft@99b32ab40612ff2533a69d8f14bd8b3d9e604456/dist/Monocraft.otf"; + const fontLink = document.createElement("link"); + fontLink.rel = "stylesheet"; + fontLink.href = `url(${MONOCRAFT_SRC}) format('opentype')`; + document.head.appendChild(fontLink); + + // Add stylesheet font-face + const fontFace = ` + @font-face { + font-family: 'Monocraft'; + src: url(${MONOCRAFT_SRC}) format('opentype'); + font-weight: normal; + font-style: normal; + } + `; + const fontStyle = document.createElement("style"); + fontStyle.innerHTML = fontFace; + document.head.appendChild(fontStyle); + + load(); + + styleElement.innerHTML = STYLESHEET; + document.head.appendChild(styleElement); + + canvas.id = "birb"; + canvas.width = birbFrames.base.getPixels()[0].length * CANVAS_PIXEL_SIZE; + canvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + document.body.appendChild(canvas); + + window.addEventListener("scroll", () => { + lastActionTimestamp = Date.now(); + }); + + onClick(document, (e) => { + lastActionTimestamp = Date.now(); + if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { + removeMenu(); + } + }); + + onClick(canvas, () => { + if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) { + // Currently being pet, don't open menu + return; + } + insertMenu(); + }); + + canvas.addEventListener("mouseover", () => { + lastActionTimestamp = Date.now(); + if (currentState === States.IDLE) { + petStack.push(Date.now()); + if (petStack.length > 10) { + petStack.shift(); + } + const pets = petStack.filter((time) => Date.now() - time < 1000).length; + if (pets >= 3) { + pet(); + // Clear the stack + petStack = []; + } + } + }); + + canvas.addEventListener("touchmove", (e) => { + pet(); + }); + + drawStickyNotes(); + + let lastUrl = (window.location.href ?? "").split("?")[0]; + setInterval(() => { + const currentUrl = (window.location.href ?? "").split("?")[0]; + if (currentUrl !== lastUrl) { + log("URL changed, updating sticky notes"); + lastUrl = currentUrl; + drawStickyNotes(); + } + }, URL_CHECK_INTERVAL); + + setInterval(update, UPDATE_INTERVAL); + } + + function update() { + ticks++; + + // Hide bird if the browser is fullscreen + if (document.fullscreenElement) { + hideBirb(); + // Won't be restored on fullscreen exit + } + + if (currentState === States.IDLE && !frozen && !isMenuOpen()) { + if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) { + hop(); + } else if (Date.now() - lastActionTimestamp > AFK_TIME) { + // Idle for a while, do something + if (focusedElement === null) { + // Fly to an element + focusOnElement(); + lastActionTimestamp = Date.now(); + } else if (Math.random() < FOCUS_SWITCH_CHANCE) { + // Fly to another element if idle for a longer while + focusOnElement(); + lastActionTimestamp = Date.now(); + } + } + } else if (currentState === States.HOP) { + if (updateParabolicPath(HOP_SPEED)) { + setState(States.IDLE); + } + } + + // Double the chance of a feather if recently pet + const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1; + if (visible && Math.random() < FEATHER_CHANCE * petMod) { + lastPetTimestamp = 0; + activateFeather(); + } + updateFeather(); + } + + function draw() { + requestAnimationFrame(draw); + + if (!visible) { + return; + } + + updateFocusedElementBounds(); + + // Update the bird's position + if (currentState === States.IDLE) { + if (focusedElement && !isWithinHorizontalBounds()) { + focusOnGround(); + } + birdY = getFocusedY(); + } else if (currentState === States.FLYING) { + // Fly to target location (even if in the air) + if (updateParabolicPath(FLY_SPEED)) { + setState(States.IDLE); + } + } + + const oldTargetY = targetY; + targetY = getFocusedY(); + // Adjust startY to account for scrolling + startY += targetY - oldTargetY; + if (targetY < 0 || targetY > window.innerHeight) { + // Fly to ground if the focused element moves out of bounds + focusOnGround(); + } + + ctx.clearRect(0, 0, canvas.width, canvas.height); + if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { + setAnimation(Animations.STILL); + } + + // Update HTML element position + setX(birdX); + setY(birdY); + } + function newStickyNote() { const id = Date.now().toString(); const site = window.location.href; @@ -1282,98 +1454,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return true; } - function init() { - if (window !== window.top) { - // Skip installation if within an iframe - log("In iframe, skipping Birb script initialization"); - return; - } - log("Sprite sheets loaded successfully, initializing bird..."); - - // Preload font - const MONOCRAFT_SRC = "https://cdn.jsdelivr.net/gh/idreesinc/Monocraft@99b32ab40612ff2533a69d8f14bd8b3d9e604456/dist/Monocraft.otf"; - const fontLink = document.createElement("link"); - fontLink.rel = "stylesheet"; - fontLink.href = `url(${MONOCRAFT_SRC}) format('opentype')`; - document.head.appendChild(fontLink); - - // Add stylesheet font-face - const fontFace = ` - @font-face { - font-family: 'Monocraft'; - src: url(${MONOCRAFT_SRC}) format('opentype'); - font-weight: normal; - font-style: normal; - } - `; - const fontStyle = document.createElement("style"); - fontStyle.innerHTML = fontFace; - document.head.appendChild(fontStyle); - - load(); - - styleElement.innerHTML = STYLESHEET; - document.head.appendChild(styleElement); - - canvas.id = "birb"; - canvas.width = birbFrames.base.getPixels()[0].length * CANVAS_PIXEL_SIZE; - canvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; - document.body.appendChild(canvas); - - window.addEventListener("scroll", () => { - lastActionTimestamp = Date.now(); - }); - - onClick(document, (e) => { - lastActionTimestamp = Date.now(); - if (e.target instanceof Node && document.querySelector("#" + MENU_EXIT_ID)?.contains(e.target)) { - removeMenu(); - } - }); - - onClick(canvas, () => { - if (currentAnimation === Animations.HEART && (Date.now() - lastPetTimestamp < PET_MENU_COOLDOWN)) { - // Currently being pet, don't open menu - return; - } - insertMenu(); - }); - - canvas.addEventListener("mouseover", () => { - lastActionTimestamp = Date.now(); - if (currentState === States.IDLE) { - petStack.push(Date.now()); - if (petStack.length > 10) { - petStack.shift(); - } - const pets = petStack.filter((time) => Date.now() - time < 1000).length; - if (pets >= 3) { - pet(); - // Clear the stack - petStack = []; - } - } - }); - - canvas.addEventListener("touchmove", (e) => { - pet(); - }); - - drawStickyNotes(); - - let lastUrl = (window.location.href ?? "").split("?")[0]; - setInterval(() => { - const currentUrl = (window.location.href ?? "").split("?")[0]; - if (currentUrl !== lastUrl) { - log("URL changed, updating sticky notes"); - lastUrl = currentUrl; - drawStickyNotes(); - } - }, URL_CHECK_INTERVAL); - - setInterval(update, UPDATE_INTERVAL); - } - function drawStickyNotes() { // Remove all existing sticky notes const existingNotes = document.querySelectorAll(".birb-sticky-note"); @@ -1386,89 +1466,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI } } - function update() { - ticks++; - - // Hide bird if the browser is fullscreen - if (document.fullscreenElement) { - hideBirb(); - // Won't be restored on fullscreen exit - } - - if (currentState === States.IDLE && !frozen && !isMenuOpen()) { - if (Math.random() < HOP_CHANCE && currentAnimation !== Animations.HEART) { - hop(); - } else if (Date.now() - lastActionTimestamp > AFK_TIME) { - // Idle for a while, do something - if (focusedElement === null) { - // Fly to an element - focusOnElement(); - lastActionTimestamp = Date.now(); - } else if (Math.random() < FOCUS_SWITCH_CHANCE) { - // Fly to another element if idle for a longer while - focusOnElement(); - lastActionTimestamp = Date.now(); - } - } - } else if (currentState === States.HOP) { - if (updateParabolicPath(HOP_SPEED)) { - setState(States.IDLE); - } - } - - // Double the chance of a feather if recently pet - const petMod = Date.now() - lastPetTimestamp < PET_BOOST_DURATION ? PET_FEATHER_BOOST : 1; - if (visible && Math.random() < FEATHER_CHANCE * petMod) { - lastPetTimestamp = 0; - activateFeather(); - } - updateFeather(); - } - - function draw() { - requestAnimationFrame(draw); - - if (!visible) { - return; - } - - updateFocusedElementBounds(); - - // Update the bird's position - if (currentState === States.IDLE) { - if (focusedElement && !isWithinHorizontalBounds()) { - focusOnGround(); - } - birdY = getFocusedY(); - } else if (currentState === States.FLYING) { - // Fly to target location (even if in the air) - if (updateParabolicPath(FLY_SPEED)) { - setState(States.IDLE); - } - } - - const oldTargetY = targetY; - targetY = getFocusedY(); - // Adjust startY to account for scrolling - startY += targetY - oldTargetY; - if (targetY < 0 || targetY > window.innerHeight) { - // Fly to ground if the focused element moves out of bounds - focusOnGround(); - } - - ctx.clearRect(0, 0, canvas.width, canvas.height); - if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { - setAnimation(Animations.STILL); - } - - // Update HTML element position - setX(birdX); - setY(birdY); - } - - init(); - draw(); - /** * Create an HTML element with the specified parameters * @param {string} className @@ -2222,6 +2219,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI return { ...params, [key]: value }; }, {}); } + + // Run the birb + init(); + draw(); }).catch((e) => { error("Error while loading sprite sheets: ", e); }); diff --git a/manifest.json b/manifest.json index 37f1cf4..64a6a7f 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "Pocket Bird", "description": "It's a bird, in your browser. What more could you want?", - "version": "2025.10.25.117", + "version": "2025.10.25.124", "homepage_url": "https://idreesinc.com", "content_scripts": [ {