Treat ground as yet another element

This commit is contained in:
Idrees Hassan
2025-10-23 21:25:43 -04:00
parent bed3d37940
commit 5298b7801b
3 changed files with 177 additions and 174 deletions

101
birb.js
View File

@@ -685,6 +685,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
let targetY = 0; let targetY = 0;
/** @type {HTMLElement|null} */ /** @type {HTMLElement|null} */
let focusedElement = null; let focusedElement = null;
let focusedBounds = { left: 0, right: 0, top: 0 };
let lastActionTimestamp = Date.now(); let lastActionTimestamp = Date.now();
/** @type {number[]} */ /** @type {number[]} */
let petStack = []; let petStack = [];
@@ -1026,12 +1027,20 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
// Won't be restored on fullscreen exit // Won't be restored on fullscreen exit
} }
if (currentState === States.IDLE) { if (currentState === States.IDLE && !frozen && !isMenuOpen()) {
if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART && !isMenuOpen()) { if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART) {
hop(); hop();
} else if (focusedElement !== null && Math.random() < 1 / (60 * 20) && Date.now() - lastActionTimestamp > AFK_TIME && !isMenuOpen()) { } else if (Date.now() - lastActionTimestamp > AFK_TIME) {
// Idle for a while, do something
if (focusedElement === null) {
// Fly to an element
focusOnElement(); focusOnElement();
lastActionTimestamp = Date.now(); lastActionTimestamp = Date.now();
} else if (Math.random() < 1 / (60 * 20)) {
// Fly to another element if idle for a longer while
focusOnElement();
lastActionTimestamp = Date.now();
}
} }
} else if (currentState === States.HOP) { } else if (currentState === States.HOP) {
if (updateParabolicPath(HOP_SPEED)) { if (updateParabolicPath(HOP_SPEED)) {
@@ -1055,14 +1064,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
return; return;
} }
updateFocusedElementBounds();
// Update the bird's position // Update the bird's position
if (currentState === States.IDLE) { if (currentState === States.IDLE) {
if (focusedElement !== null) { if (focusedElement && !isWithinHorizontalBounds()) {
birdY = getFocusedElementY() - 0.5;
if (!isWithinHorizontalBounds()) {
focusOnGround(); focusOnGround();
} }
} birdY = getFocusedY();
} else if (currentState === States.FLYING) { } else if (currentState === States.FLYING) {
// Fly to target location (even if in the air) // Fly to target location (even if in the air)
if (updateParabolicPath(FLY_SPEED)) { if (updateParabolicPath(FLY_SPEED)) {
@@ -1070,22 +1079,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
} }
if (focusedElement === null) {
if (currentState === States.IDLE && Date.now() - lastActionTimestamp > AFK_TIME && !isMenuOpen()) {
// Fly to an element if the user is AFK
focusOnElement();
lastActionTimestamp = Date.now();
}
} else if (focusedElement !== null) {
const oldTargetY = targetY; const oldTargetY = targetY;
targetY = getFocusedElementY(); targetY = getFocusedY();
// Adjust startY to account for scrolling of the focused element // Adjust startY to account for scrolling
startY += targetY - oldTargetY; startY += targetY - oldTargetY;
if (targetY < 0 || targetY > window.innerHeight) { if (targetY < 0 || targetY > window.innerHeight) {
// Fly to ground if the focused element moves out of bounds // Fly to ground if the focused element moves out of bounds
focusOnGround(); focusOnGround();
} }
}
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) {
@@ -1660,34 +1661,29 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
function getFocusedElementRandomX() { function getFocusedElementRandomX() {
if (focusedElement === null) { return Math.random() * (focusedBounds.right - focusedBounds.left) + focusedBounds.left;
return Math.random() * window.innerWidth;
}
const rect = focusedElement.getBoundingClientRect();
return Math.random() * (rect.right - rect.left) + rect.left;
} }
function isWithinHorizontalBounds() { function isWithinHorizontalBounds() {
if (focusedElement === null) { return birdX >= focusedBounds.left && birdX <= focusedBounds.right;
return true;
}
const rect = focusedElement.getBoundingClientRect();
return birdX >= rect.left && birdX <= rect.right;
} }
function getFocusedElementY() { // function getFocusedElementY() {
if (focusedElement === null) { // if (focusedElement === null) {
return 0; // return 0;
} // }
const rect = focusedElement.getBoundingClientRect(); // const rect = focusedElement.getBoundingClientRect();
return window.innerHeight - rect.top; // return window.innerHeight - rect.top;
// }
function getFocusedY() {
return window.innerHeight - focusedBounds.top;
} }
function focusOnGround() { function focusOnGround() {
if (focusedElement === null) { console.log("Focusing on ground");
return;
}
focusedElement = null; focusedElement = null;
focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight };
flyTo(Math.random() * window.innerWidth, 0); flyTo(Math.random() * window.innerWidth, 0);
} }
@@ -1709,7 +1705,23 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
const randomElement = largeElements[Math.floor(Math.random() * largeElements.length)]; const randomElement = largeElements[Math.floor(Math.random() * largeElements.length)];
focusedElement = randomElement; focusedElement = randomElement;
flyTo(getFocusedElementRandomX(), getFocusedElementY()); log("Focusing on element: ", focusedElement);
updateFocusedElementBounds();
flyTo(getFocusedElementRandomX(), getFocusedY());
}
function updateFocusedElementBounds() {
if (focusedElement === null) {
// Update ground location to bottom of window
focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight };
return;
}
const rect = focusedElement.getBoundingClientRect();
focusedBounds = {
left: rect.left,
right: rect.right,
top: rect.top
};
} }
function getCanvasWidth() { function getCanvasWidth() {
@@ -1725,25 +1737,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
return; return;
} }
if (currentState === States.IDLE) { if (currentState === States.IDLE) {
// Determine bounds for hopping
let minX = 0;
let maxX = window.innerWidth;
let y = 0;
if (focusedElement !== null) {
// Hop on the element
const rect = focusedElement.getBoundingClientRect();
minX = rect.left;
maxX = rect.right;
y = window.innerHeight - rect.top;
}
setState(States.HOP); setState(States.HOP);
setAnimation(Animations.FLYING); setAnimation(Animations.FLYING);
if ((Math.random() < 0.5 && birdX - HOP_DISTANCE > minX) || birdX + HOP_DISTANCE > maxX) { if ((Math.random() < 0.5 && birdX - HOP_DISTANCE > focusedBounds.left) || birdX + HOP_DISTANCE > focusedBounds.right) {
targetX = birdX - HOP_DISTANCE; targetX = birdX - HOP_DISTANCE;
} else { } else {
targetX = birdX + HOP_DISTANCE; targetX = birdX + HOP_DISTANCE;
} }
targetY = y; targetY = getFocusedY();
} }
} }

101
dist/birb.js vendored
View File

@@ -1024,6 +1024,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
let targetY = 0; let targetY = 0;
/** @type {HTMLElement|null} */ /** @type {HTMLElement|null} */
let focusedElement = null; let focusedElement = null;
let focusedBounds = { left: 0, right: 0, top: 0 };
let lastActionTimestamp = Date.now(); let lastActionTimestamp = Date.now();
/** @type {number[]} */ /** @type {number[]} */
let petStack = []; let petStack = [];
@@ -1365,12 +1366,20 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
// Won't be restored on fullscreen exit // Won't be restored on fullscreen exit
} }
if (currentState === States.IDLE) { if (currentState === States.IDLE && !frozen && !isMenuOpen()) {
if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART && !isMenuOpen()) { if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART) {
hop(); hop();
} else if (focusedElement !== null && Math.random() < 1 / (60 * 20) && Date.now() - lastActionTimestamp > AFK_TIME && !isMenuOpen()) { } else if (Date.now() - lastActionTimestamp > AFK_TIME) {
// Idle for a while, do something
if (focusedElement === null) {
// Fly to an element
focusOnElement(); focusOnElement();
lastActionTimestamp = Date.now(); lastActionTimestamp = Date.now();
} else if (Math.random() < 1 / (60 * 20)) {
// Fly to another element if idle for a longer while
focusOnElement();
lastActionTimestamp = Date.now();
}
} }
} else if (currentState === States.HOP) { } else if (currentState === States.HOP) {
if (updateParabolicPath(HOP_SPEED)) { if (updateParabolicPath(HOP_SPEED)) {
@@ -1394,14 +1403,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
return; return;
} }
updateFocusedElementBounds();
// Update the bird's position // Update the bird's position
if (currentState === States.IDLE) { if (currentState === States.IDLE) {
if (focusedElement !== null) { if (focusedElement && !isWithinHorizontalBounds()) {
birdY = getFocusedElementY() - 0.5;
if (!isWithinHorizontalBounds()) {
focusOnGround(); focusOnGround();
} }
} birdY = getFocusedY();
} else if (currentState === States.FLYING) { } else if (currentState === States.FLYING) {
// Fly to target location (even if in the air) // Fly to target location (even if in the air)
if (updateParabolicPath(FLY_SPEED)) { if (updateParabolicPath(FLY_SPEED)) {
@@ -1409,22 +1418,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
} }
if (focusedElement === null) {
if (currentState === States.IDLE && Date.now() - lastActionTimestamp > AFK_TIME && !isMenuOpen()) {
// Fly to an element if the user is AFK
focusOnElement();
lastActionTimestamp = Date.now();
}
} else if (focusedElement !== null) {
const oldTargetY = targetY; const oldTargetY = targetY;
targetY = getFocusedElementY(); targetY = getFocusedY();
// Adjust startY to account for scrolling of the focused element // Adjust startY to account for scrolling
startY += targetY - oldTargetY; startY += targetY - oldTargetY;
if (targetY < 0 || targetY > window.innerHeight) { if (targetY < 0 || targetY > window.innerHeight) {
// Fly to ground if the focused element moves out of bounds // Fly to ground if the focused element moves out of bounds
focusOnGround(); focusOnGround();
} }
}
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) {
@@ -1999,34 +2000,29 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
function getFocusedElementRandomX() { function getFocusedElementRandomX() {
if (focusedElement === null) { return Math.random() * (focusedBounds.right - focusedBounds.left) + focusedBounds.left;
return Math.random() * window.innerWidth;
}
const rect = focusedElement.getBoundingClientRect();
return Math.random() * (rect.right - rect.left) + rect.left;
} }
function isWithinHorizontalBounds() { function isWithinHorizontalBounds() {
if (focusedElement === null) { return birdX >= focusedBounds.left && birdX <= focusedBounds.right;
return true;
}
const rect = focusedElement.getBoundingClientRect();
return birdX >= rect.left && birdX <= rect.right;
} }
function getFocusedElementY() { // function getFocusedElementY() {
if (focusedElement === null) { // if (focusedElement === null) {
return 0; // return 0;
} // }
const rect = focusedElement.getBoundingClientRect(); // const rect = focusedElement.getBoundingClientRect();
return window.innerHeight - rect.top; // return window.innerHeight - rect.top;
// }
function getFocusedY() {
return window.innerHeight - focusedBounds.top;
} }
function focusOnGround() { function focusOnGround() {
if (focusedElement === null) { console.log("Focusing on ground");
return;
}
focusedElement = null; focusedElement = null;
focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight };
flyTo(Math.random() * window.innerWidth, 0); flyTo(Math.random() * window.innerWidth, 0);
} }
@@ -2048,7 +2044,23 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
const randomElement = largeElements[Math.floor(Math.random() * largeElements.length)]; const randomElement = largeElements[Math.floor(Math.random() * largeElements.length)];
focusedElement = randomElement; focusedElement = randomElement;
flyTo(getFocusedElementRandomX(), getFocusedElementY()); log("Focusing on element: ", focusedElement);
updateFocusedElementBounds();
flyTo(getFocusedElementRandomX(), getFocusedY());
}
function updateFocusedElementBounds() {
if (focusedElement === null) {
// Update ground location to bottom of window
focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight };
return;
}
const rect = focusedElement.getBoundingClientRect();
focusedBounds = {
left: rect.left,
right: rect.right,
top: rect.top
};
} }
function getCanvasWidth() { function getCanvasWidth() {
@@ -2064,25 +2076,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
return; return;
} }
if (currentState === States.IDLE) { if (currentState === States.IDLE) {
// Determine bounds for hopping
let minX = 0;
let maxX = window.innerWidth;
let y = 0;
if (focusedElement !== null) {
// Hop on the element
const rect = focusedElement.getBoundingClientRect();
minX = rect.left;
maxX = rect.right;
y = window.innerHeight - rect.top;
}
setState(States.HOP); setState(States.HOP);
setAnimation(Animations.FLYING); setAnimation(Animations.FLYING);
if ((Math.random() < 0.5 && birdX - HOP_DISTANCE > minX) || birdX + HOP_DISTANCE > maxX) { if ((Math.random() < 0.5 && birdX - HOP_DISTANCE > focusedBounds.left) || birdX + HOP_DISTANCE > focusedBounds.right) {
targetX = birdX - HOP_DISTANCE; targetX = birdX - HOP_DISTANCE;
} else { } else {
targetX = birdX + HOP_DISTANCE; targetX = birdX + HOP_DISTANCE;
} }
targetY = y; targetY = getFocusedY();
} }
} }

101
dist/birb.user.js vendored
View File

@@ -1038,6 +1038,7 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
let targetY = 0; let targetY = 0;
/** @type {HTMLElement|null} */ /** @type {HTMLElement|null} */
let focusedElement = null; let focusedElement = null;
let focusedBounds = { left: 0, right: 0, top: 0 };
let lastActionTimestamp = Date.now(); let lastActionTimestamp = Date.now();
/** @type {number[]} */ /** @type {number[]} */
let petStack = []; let petStack = [];
@@ -1379,12 +1380,20 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
// Won't be restored on fullscreen exit // Won't be restored on fullscreen exit
} }
if (currentState === States.IDLE) { if (currentState === States.IDLE && !frozen && !isMenuOpen()) {
if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART && !isMenuOpen()) { if (Math.random() < 1 / (60 * 3) && currentAnimation !== Animations.HEART) {
hop(); hop();
} else if (focusedElement !== null && Math.random() < 1 / (60 * 20) && Date.now() - lastActionTimestamp > AFK_TIME && !isMenuOpen()) { } else if (Date.now() - lastActionTimestamp > AFK_TIME) {
// Idle for a while, do something
if (focusedElement === null) {
// Fly to an element
focusOnElement(); focusOnElement();
lastActionTimestamp = Date.now(); lastActionTimestamp = Date.now();
} else if (Math.random() < 1 / (60 * 20)) {
// Fly to another element if idle for a longer while
focusOnElement();
lastActionTimestamp = Date.now();
}
} }
} else if (currentState === States.HOP) { } else if (currentState === States.HOP) {
if (updateParabolicPath(HOP_SPEED)) { if (updateParabolicPath(HOP_SPEED)) {
@@ -1408,14 +1417,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
return; return;
} }
updateFocusedElementBounds();
// Update the bird's position // Update the bird's position
if (currentState === States.IDLE) { if (currentState === States.IDLE) {
if (focusedElement !== null) { if (focusedElement && !isWithinHorizontalBounds()) {
birdY = getFocusedElementY() - 0.5;
if (!isWithinHorizontalBounds()) {
focusOnGround(); focusOnGround();
} }
} birdY = getFocusedY();
} else if (currentState === States.FLYING) { } else if (currentState === States.FLYING) {
// Fly to target location (even if in the air) // Fly to target location (even if in the air)
if (updateParabolicPath(FLY_SPEED)) { if (updateParabolicPath(FLY_SPEED)) {
@@ -1423,22 +1432,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
} }
if (focusedElement === null) {
if (currentState === States.IDLE && Date.now() - lastActionTimestamp > AFK_TIME && !isMenuOpen()) {
// Fly to an element if the user is AFK
focusOnElement();
lastActionTimestamp = Date.now();
}
} else if (focusedElement !== null) {
const oldTargetY = targetY; const oldTargetY = targetY;
targetY = getFocusedElementY(); targetY = getFocusedY();
// Adjust startY to account for scrolling of the focused element // Adjust startY to account for scrolling
startY += targetY - oldTargetY; startY += targetY - oldTargetY;
if (targetY < 0 || targetY > window.innerHeight) { if (targetY < 0 || targetY > window.innerHeight) {
// Fly to ground if the focused element moves out of bounds // Fly to ground if the focused element moves out of bounds
focusOnGround(); focusOnGround();
} }
}
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) { if (currentAnimation.draw(ctx, direction, animStart, species[currentSpecies])) {
@@ -2013,34 +2014,29 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
function getFocusedElementRandomX() { function getFocusedElementRandomX() {
if (focusedElement === null) { return Math.random() * (focusedBounds.right - focusedBounds.left) + focusedBounds.left;
return Math.random() * window.innerWidth;
}
const rect = focusedElement.getBoundingClientRect();
return Math.random() * (rect.right - rect.left) + rect.left;
} }
function isWithinHorizontalBounds() { function isWithinHorizontalBounds() {
if (focusedElement === null) { return birdX >= focusedBounds.left && birdX <= focusedBounds.right;
return true;
}
const rect = focusedElement.getBoundingClientRect();
return birdX >= rect.left && birdX <= rect.right;
} }
function getFocusedElementY() { // function getFocusedElementY() {
if (focusedElement === null) { // if (focusedElement === null) {
return 0; // return 0;
} // }
const rect = focusedElement.getBoundingClientRect(); // const rect = focusedElement.getBoundingClientRect();
return window.innerHeight - rect.top; // return window.innerHeight - rect.top;
// }
function getFocusedY() {
return window.innerHeight - focusedBounds.top;
} }
function focusOnGround() { function focusOnGround() {
if (focusedElement === null) { console.log("Focusing on ground");
return;
}
focusedElement = null; focusedElement = null;
focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight };
flyTo(Math.random() * window.innerWidth, 0); flyTo(Math.random() * window.innerWidth, 0);
} }
@@ -2062,7 +2058,23 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
} }
const randomElement = largeElements[Math.floor(Math.random() * largeElements.length)]; const randomElement = largeElements[Math.floor(Math.random() * largeElements.length)];
focusedElement = randomElement; focusedElement = randomElement;
flyTo(getFocusedElementRandomX(), getFocusedElementY()); log("Focusing on element: ", focusedElement);
updateFocusedElementBounds();
flyTo(getFocusedElementRandomX(), getFocusedY());
}
function updateFocusedElementBounds() {
if (focusedElement === null) {
// Update ground location to bottom of window
focusedBounds = { left: 0, right: window.innerWidth, top: window.innerHeight };
return;
}
const rect = focusedElement.getBoundingClientRect();
focusedBounds = {
left: rect.left,
right: rect.right,
top: rect.top
};
} }
function getCanvasWidth() { function getCanvasWidth() {
@@ -2078,25 +2090,14 @@ Promise.all([loadSpriteSheetPixels(SPRITE_SHEET), loadSpriteSheetPixels(DECORATI
return; return;
} }
if (currentState === States.IDLE) { if (currentState === States.IDLE) {
// Determine bounds for hopping
let minX = 0;
let maxX = window.innerWidth;
let y = 0;
if (focusedElement !== null) {
// Hop on the element
const rect = focusedElement.getBoundingClientRect();
minX = rect.left;
maxX = rect.right;
y = window.innerHeight - rect.top;
}
setState(States.HOP); setState(States.HOP);
setAnimation(Animations.FLYING); setAnimation(Animations.FLYING);
if ((Math.random() < 0.5 && birdX - HOP_DISTANCE > minX) || birdX + HOP_DISTANCE > maxX) { if ((Math.random() < 0.5 && birdX - HOP_DISTANCE > focusedBounds.left) || birdX + HOP_DISTANCE > focusedBounds.right) {
targetX = birdX - HOP_DISTANCE; targetX = birdX - HOP_DISTANCE;
} else { } else {
targetX = birdX + HOP_DISTANCE; targetX = birdX + HOP_DISTANCE;
} }
targetY = y; targetY = getFocusedY();
} }
} }