mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-25 19:59:38 +00:00
Group sticky note functions together
This commit is contained in:
351
birb.js
351
birb.js
@@ -812,6 +812,178 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return settings().birbMode ? "Birb" : "Bird";
|
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() {
|
function newStickyNote() {
|
||||||
const id = Date.now().toString();
|
const id = Date.now().toString();
|
||||||
const site = window.location.href;
|
const site = window.location.href;
|
||||||
@@ -925,98 +1097,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return true;
|
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() {
|
function drawStickyNotes() {
|
||||||
// Remove all existing sticky notes
|
// Remove all existing sticky notes
|
||||||
const existingNotes = document.querySelectorAll(".birb-sticky-note");
|
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
|
* Create an HTML element with the specified parameters
|
||||||
* @param {string} className
|
* @param {string} className
|
||||||
@@ -1865,6 +1862,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return { ...params, [key]: value };
|
return { ...params, [key]: value };
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run the birb
|
||||||
|
init();
|
||||||
|
draw();
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
error("Error while loading sprite sheets: ", e);
|
error("Error while loading sprite sheets: ", e);
|
||||||
});
|
});
|
||||||
|
|||||||
351
dist/birb.js
vendored
351
dist/birb.js
vendored
@@ -1155,6 +1155,178 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return settings().birbMode ? "Birb" : "Bird";
|
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() {
|
function newStickyNote() {
|
||||||
const id = Date.now().toString();
|
const id = Date.now().toString();
|
||||||
const site = window.location.href;
|
const site = window.location.href;
|
||||||
@@ -1268,98 +1440,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return true;
|
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() {
|
function drawStickyNotes() {
|
||||||
// Remove all existing sticky notes
|
// Remove all existing sticky notes
|
||||||
const existingNotes = document.querySelectorAll(".birb-sticky-note");
|
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
|
* Create an HTML element with the specified parameters
|
||||||
* @param {string} className
|
* @param {string} className
|
||||||
@@ -2208,6 +2205,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return { ...params, [key]: value };
|
return { ...params, [key]: value };
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run the birb
|
||||||
|
init();
|
||||||
|
draw();
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
error("Error while loading sprite sheets: ", e);
|
error("Error while loading sprite sheets: ", e);
|
||||||
});
|
});
|
||||||
|
|||||||
353
dist/birb.user.js
vendored
353
dist/birb.user.js
vendored
@@ -1,7 +1,7 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Pocket Bird
|
// @name Pocket Bird
|
||||||
// @namespace https://idreesinc.com
|
// @namespace https://idreesinc.com
|
||||||
// @version 2025.10.25.117
|
// @version 2025.10.25.124
|
||||||
// @description birb
|
// @description birb
|
||||||
// @author Idrees
|
// @author Idrees
|
||||||
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js
|
// @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";
|
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() {
|
function newStickyNote() {
|
||||||
const id = Date.now().toString();
|
const id = Date.now().toString();
|
||||||
const site = window.location.href;
|
const site = window.location.href;
|
||||||
@@ -1282,98 +1454,6 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return true;
|
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() {
|
function drawStickyNotes() {
|
||||||
// Remove all existing sticky notes
|
// Remove all existing sticky notes
|
||||||
const existingNotes = document.querySelectorAll(".birb-sticky-note");
|
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
|
* Create an HTML element with the specified parameters
|
||||||
* @param {string} className
|
* @param {string} className
|
||||||
@@ -2222,6 +2219,10 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
|
|||||||
return { ...params, [key]: value };
|
return { ...params, [key]: value };
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run the birb
|
||||||
|
init();
|
||||||
|
draw();
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
error("Error while loading sprite sheets: ", e);
|
error("Error while loading sprite sheets: ", e);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Pocket Bird",
|
"name": "Pocket Bird",
|
||||||
"description": "It's a bird, in your browser. What more could you want?",
|
"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",
|
"homepage_url": "https://idreesinc.com",
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user