Reduce redundant field guide code

This commit is contained in:
Idrees Hassan
2025-10-26 18:44:37 -04:00
parent d623abee85
commit f422a699f5
4 changed files with 279 additions and 342 deletions

293
dist/birb.js vendored
View File

@@ -188,7 +188,7 @@
}
/** Indicators for parts of the base bird sprite sheet */
const SPRITE = {
const Sprite = {
THEME_HIGHLIGHT: "theme-highlight",
TRANSPARENT: "transparent",
OUTLINE: "outline",
@@ -211,23 +211,23 @@
/** @type {Record<string, string>} */
const SPRITE_SHEET_COLOR_MAP = {
"transparent": SPRITE.TRANSPARENT,
"#ffffff": SPRITE.BORDER,
"#000000": SPRITE.OUTLINE,
"#010a19": SPRITE.BEAK,
"#190301": SPRITE.EYE,
"#af8e75": SPRITE.FOOT,
"#639bff": SPRITE.FACE,
"#99e550": SPRITE.HOOD,
"#d95763": SPRITE.NOSE,
"#f8b143": SPRITE.BELLY,
"#ec8637": SPRITE.UNDERBELLY,
"#578ae6": SPRITE.WING,
"#326ed9": SPRITE.WING_EDGE,
"#c82e2e": SPRITE.HEART,
"#501a1a": SPRITE.HEART_BORDER,
"#ff6b6b": SPRITE.HEART_SHINE,
"#373737": SPRITE.FEATHER_SPINE,
"transparent": Sprite.TRANSPARENT,
"#ffffff": Sprite.BORDER,
"#000000": Sprite.OUTLINE,
"#010a19": Sprite.BEAK,
"#190301": Sprite.EYE,
"#af8e75": Sprite.FOOT,
"#639bff": Sprite.FACE,
"#99e550": Sprite.HOOD,
"#d95763": Sprite.NOSE,
"#f8b143": Sprite.BELLY,
"#ec8637": Sprite.UNDERBELLY,
"#578ae6": Sprite.WING,
"#326ed9": Sprite.WING_EDGE,
"#c82e2e": Sprite.HEART,
"#501a1a": Sprite.HEART_BORDER,
"#ff6b6b": Sprite.HEART_SHINE,
"#373737": Sprite.FEATHER_SPINE,
};
class BirdType {
@@ -241,20 +241,20 @@
this.name = name;
this.description = description;
const defaultColors = {
[SPRITE.TRANSPARENT]: "transparent",
[SPRITE.OUTLINE]: "#000000",
[SPRITE.BORDER]: "#ffffff",
[SPRITE.BEAK]: "#000000",
[SPRITE.EYE]: "#000000",
[SPRITE.HEART]: "#c82e2e",
[SPRITE.HEART_BORDER]: "#501a1a",
[SPRITE.HEART_SHINE]: "#ff6b6b",
[SPRITE.FEATHER_SPINE]: "#373737",
[SPRITE.HOOD]: colors.face,
[SPRITE.NOSE]: colors.face,
[Sprite.TRANSPARENT]: "transparent",
[Sprite.OUTLINE]: "#000000",
[Sprite.BORDER]: "#ffffff",
[Sprite.BEAK]: "#000000",
[Sprite.EYE]: "#000000",
[Sprite.HEART]: "#c82e2e",
[Sprite.HEART_BORDER]: "#501a1a",
[Sprite.HEART_SHINE]: "#ff6b6b",
[Sprite.FEATHER_SPINE]: "#373737",
[Sprite.HOOD]: colors.face,
[Sprite.NOSE]: colors.face,
};
/** @type {Record<string, string>} */
this.colors = { ...defaultColors, ...colors, [SPRITE.THEME_HIGHLIGHT]: colors[SPRITE.THEME_HIGHLIGHT] ?? colors.hood ?? colors.face };
this.colors = { ...defaultColors, ...colors, [Sprite.THEME_HIGHLIGHT]: colors[Sprite.THEME_HIGHLIGHT] ?? colors.hood ?? colors.face };
this.tags = tags;
}
}
@@ -263,127 +263,127 @@
const SPECIES = {
bluebird: new BirdType("Eastern Bluebird",
"Native to North American and very social, though can be timid around people.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#639bff",
[SPRITE.BELLY]: "#f8b143",
[SPRITE.UNDERBELLY]: "#ec8637",
[SPRITE.WING]: "#578ae6",
[SPRITE.WING_EDGE]: "#326ed9",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#639bff",
[Sprite.BELLY]: "#f8b143",
[Sprite.UNDERBELLY]: "#ec8637",
[Sprite.WING]: "#578ae6",
[Sprite.WING_EDGE]: "#326ed9",
}),
shimaEnaga: new BirdType("Shima Enaga",
"Small, fluffy birds found in the snowy regions of Japan, these birds are highly sought after by ornithologists and nature photographers.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#ffffff",
[SPRITE.BELLY]: "#ebe9e8",
[SPRITE.UNDERBELLY]: "#ebd9d0",
[SPRITE.WING]: "#f3d3c1",
[SPRITE.WING_EDGE]: "#2d2d2dff",
[SPRITE.THEME_HIGHLIGHT]: "#d7ac93",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#ffffff",
[Sprite.BELLY]: "#ebe9e8",
[Sprite.UNDERBELLY]: "#ebd9d0",
[Sprite.WING]: "#f3d3c1",
[Sprite.WING_EDGE]: "#2d2d2dff",
[Sprite.THEME_HIGHLIGHT]: "#d7ac93",
}),
tuftedTitmouse: new BirdType("Tufted Titmouse",
"Native to the eastern United States, full of personality, and notably my wife's favorite bird.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#c7cad7",
[SPRITE.BELLY]: "#e4e5eb",
[SPRITE.UNDERBELLY]: "#d7cfcb",
[SPRITE.WING]: "#b1b5c5",
[SPRITE.WING_EDGE]: "#9d9fa9",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#c7cad7",
[Sprite.BELLY]: "#e4e5eb",
[Sprite.UNDERBELLY]: "#d7cfcb",
[Sprite.WING]: "#b1b5c5",
[Sprite.WING_EDGE]: "#9d9fa9",
}, ["tuft"]),
europeanRobin: new BirdType("European Robin",
"Native to western Europe, this is the quintessential robin. Quite friendly, you'll often find them searching for worms.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#ffaf34",
[SPRITE.HOOD]: "#aaa094",
[SPRITE.BELLY]: "#ffaf34",
[SPRITE.UNDERBELLY]: "#babec2",
[SPRITE.WING]: "#aaa094",
[SPRITE.WING_EDGE]: "#888580",
[SPRITE.THEME_HIGHLIGHT]: "#ffaf34",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#ffaf34",
[Sprite.HOOD]: "#aaa094",
[Sprite.BELLY]: "#ffaf34",
[Sprite.UNDERBELLY]: "#babec2",
[Sprite.WING]: "#aaa094",
[Sprite.WING_EDGE]: "#888580",
[Sprite.THEME_HIGHLIGHT]: "#ffaf34",
}),
redCardinal: new BirdType("Red Cardinal",
"Native to the eastern United States, this strikingly red bird is hard to miss.", {
[SPRITE.BEAK]: "#d93619",
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#31353d",
[SPRITE.HOOD]: "#e83a1b",
[SPRITE.BELLY]: "#e83a1b",
[SPRITE.UNDERBELLY]: "#dc3719",
[SPRITE.WING]: "#d23215",
[SPRITE.WING_EDGE]: "#b1321c",
[Sprite.BEAK]: "#d93619",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#31353d",
[Sprite.HOOD]: "#e83a1b",
[Sprite.BELLY]: "#e83a1b",
[Sprite.UNDERBELLY]: "#dc3719",
[Sprite.WING]: "#d23215",
[Sprite.WING_EDGE]: "#b1321c",
}, ["tuft"]),
americanGoldfinch: new BirdType("American Goldfinch",
"Coloured a brilliant yellow, this bird feeds almost entirely on the seeds of plants such as thistle, sunflowers, and coneflowers.", {
[SPRITE.BEAK]: "#ffaf34",
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#fff255",
[SPRITE.NOSE]: "#383838",
[SPRITE.HOOD]: "#383838",
[SPRITE.BELLY]: "#fff255",
[SPRITE.UNDERBELLY]: "#f5ea63",
[SPRITE.WING]: "#e8e079",
[SPRITE.WING_EDGE]: "#191919",
[SPRITE.THEME_HIGHLIGHT]: "#ffcc00"
[Sprite.BEAK]: "#ffaf34",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#fff255",
[Sprite.NOSE]: "#383838",
[Sprite.HOOD]: "#383838",
[Sprite.BELLY]: "#fff255",
[Sprite.UNDERBELLY]: "#f5ea63",
[Sprite.WING]: "#e8e079",
[Sprite.WING_EDGE]: "#191919",
[Sprite.THEME_HIGHLIGHT]: "#ffcc00"
}),
barnSwallow: new BirdType("Barn Swallow",
"Agile birds that often roost in man-made structures, these birds are known to build nests near Ospreys for protection.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#db7c4d",
[SPRITE.BELLY]: "#f7e1c9",
[SPRITE.UNDERBELLY]: "#ebc9a3",
[SPRITE.WING]: "#2252a9",
[SPRITE.WING_EDGE]: "#1c448b",
[SPRITE.HOOD]: "#2252a9",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#db7c4d",
[Sprite.BELLY]: "#f7e1c9",
[Sprite.UNDERBELLY]: "#ebc9a3",
[Sprite.WING]: "#2252a9",
[Sprite.WING_EDGE]: "#1c448b",
[Sprite.HOOD]: "#2252a9",
}),
mistletoebird: new BirdType("Mistletoebird",
"Native to Australia, these birds eat mainly mistletoe and in turn spread the seeds far and wide.", {
[SPRITE.FOOT]: "#6c6a7c",
[SPRITE.FACE]: "#352e6d",
[SPRITE.BELLY]: "#fd6833",
[SPRITE.UNDERBELLY]: "#e6e1d8",
[SPRITE.WING]: "#342b7c",
[SPRITE.WING_EDGE]: "#282065",
[Sprite.FOOT]: "#6c6a7c",
[Sprite.FACE]: "#352e6d",
[Sprite.BELLY]: "#fd6833",
[Sprite.UNDERBELLY]: "#e6e1d8",
[Sprite.WING]: "#342b7c",
[Sprite.WING_EDGE]: "#282065",
}),
redAvadavat: new BirdType("Red Avadavat",
"Native to India and southeast Asia, these birds are also known as Strawberry Finches due to their speckled plumage.", {
[SPRITE.BEAK]: "#f71919",
[SPRITE.FOOT]: "#af7575",
[SPRITE.FACE]: "#cb092b",
[SPRITE.BELLY]: "#ae1724",
[SPRITE.UNDERBELLY]: "#831b24",
[SPRITE.WING]: "#7e3030",
[SPRITE.WING_EDGE]: "#490f0f",
[Sprite.BEAK]: "#f71919",
[Sprite.FOOT]: "#af7575",
[Sprite.FACE]: "#cb092b",
[Sprite.BELLY]: "#ae1724",
[Sprite.UNDERBELLY]: "#831b24",
[Sprite.WING]: "#7e3030",
[Sprite.WING_EDGE]: "#490f0f",
}),
scarletRobin: new BirdType("Scarlet Robin",
"Native to Australia, this striking robin can be found in Eucalyptus forests.", {
[SPRITE.FOOT]: "#494949",
[SPRITE.FACE]: "#3d3d3d",
[SPRITE.BELLY]: "#fc5633",
[SPRITE.UNDERBELLY]: "#dcdcdc",
[SPRITE.WING]: "#2b2b2b",
[SPRITE.WING_EDGE]: "#ebebeb",
[SPRITE.THEME_HIGHLIGHT]: "#fc5633",
[Sprite.FOOT]: "#494949",
[Sprite.FACE]: "#3d3d3d",
[Sprite.BELLY]: "#fc5633",
[Sprite.UNDERBELLY]: "#dcdcdc",
[Sprite.WING]: "#2b2b2b",
[Sprite.WING_EDGE]: "#ebebeb",
[Sprite.THEME_HIGHLIGHT]: "#fc5633",
}),
americanRobin: new BirdType("American Robin",
"While not a true robin, this social North American bird is so named due to its orange coloring. It seems unbothered by nearby humans.", {
[SPRITE.BEAK]: "#e89f30",
[SPRITE.FOOT]: "#9f8075",
[SPRITE.FACE]: "#2d2d2d",
[SPRITE.BELLY]: "#eb7a3a",
[SPRITE.UNDERBELLY]: "#eb7a3a",
[SPRITE.WING]: "#444444",
[SPRITE.WING_EDGE]: "#232323",
[SPRITE.THEME_HIGHLIGHT]: "#eb7a3a",
[Sprite.BEAK]: "#e89f30",
[Sprite.FOOT]: "#9f8075",
[Sprite.FACE]: "#2d2d2d",
[Sprite.BELLY]: "#eb7a3a",
[Sprite.UNDERBELLY]: "#eb7a3a",
[Sprite.WING]: "#444444",
[Sprite.WING_EDGE]: "#232323",
[Sprite.THEME_HIGHLIGHT]: "#eb7a3a",
}),
carolinaWren: new BirdType("Carolina Wren",
"Native to the eastern United States, these little birds are known for their curious and energetic nature.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#edc7a9",
[SPRITE.NOSE]: "#f7eee5",
[SPRITE.HOOD]: "#c58a5b",
[SPRITE.BELLY]: "#e1b796",
[SPRITE.UNDERBELLY]: "#c79e7c",
[SPRITE.WING]: "#c58a5b",
[SPRITE.WING_EDGE]: "#866348",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#edc7a9",
[Sprite.NOSE]: "#f7eee5",
[Sprite.HOOD]: "#c58a5b",
[Sprite.BELLY]: "#e1b796",
[Sprite.UNDERBELLY]: "#c79e7c",
[Sprite.WING]: "#c58a5b",
[Sprite.WING_EDGE]: "#866348",
}),
};
@@ -421,7 +421,7 @@
this.pixels = layers[0].pixels.map(row => row.slice());
// Pad from top with transparent pixels
while (this.pixels.length < maxHeight) {
this.pixels.unshift(new Array(this.pixels[0].length).fill(SPRITE.TRANSPARENT));
this.pixels.unshift(new Array(this.pixels[0].length).fill(Sprite.TRANSPARENT));
}
// Combine layers
for (let i = 1; i < layers.length; i++) {
@@ -430,7 +430,7 @@
let topMargin = maxHeight - layerPixels.length;
for (let y = 0; y < layerPixels.length; y++) {
for (let x = 0; x < layerPixels[y].length; x++) {
this.pixels[y + topMargin][x] = layerPixels[y][x] !== SPRITE.TRANSPARENT ? layerPixels[y][x] : this.pixels[y + topMargin][x];
this.pixels[y + topMargin][x] = layerPixels[y][x] !== Sprite.TRANSPARENT ? layerPixels[y][x] : this.pixels[y + topMargin][x];
}
}
}
@@ -1509,7 +1509,7 @@
const b = pixels[index + 2];
const a = pixels[index + 3];
if (a === 0) {
row.push(SPRITE.TRANSPARENT);
row.push(Sprite.TRANSPARENT);
continue;
}
const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
@@ -1519,7 +1519,7 @@
}
if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) {
error(`Unknown color: ${hex}`);
row.push(SPRITE.TRANSPARENT);
row.push(Sprite.TRANSPARENT);
}
row.push(SPRITE_SHEET_COLOR_MAP[hex]);
}
@@ -1563,7 +1563,7 @@
new MenuItem(`Pet ${birdBirb()}`, pet),
new MenuItem("Field Guide", insertFieldGuide),
new MenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote)),
new MenuItem(`Hide ${birdBirb()}`, hideBirb),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)),
new DebugMenuItem("Freeze/Unfreeze", () => {
frozen = !frozen;
}),
@@ -1622,7 +1622,7 @@
let petStack = [];
let currentSpecies = DEFAULT_BIRD;
let unlockedSpecies = [DEFAULT_BIRD];
let visible = true;
// let visible = true;
let lastPetTimestamp = 0;
/** @type {StickyNote[]} */
let stickyNotes = [];
@@ -1835,7 +1835,7 @@
// Hide bird if the browser is fullscreen
if (document.fullscreenElement) {
hideBirb();
birb.setVisible(false);
// Won't be restored on fullscreen exit
}
@@ -1862,7 +1862,7 @@
// 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) {
if (birb.isVisible() && Math.random() < FEATHER_CHANCE * petMod) {
lastPetTimestamp = 0;
activateFeather();
}
@@ -2077,26 +2077,13 @@
if (document.querySelector("#" + FIELD_GUIDE_ID)) {
return;
}
let html = `
<div class="birb-window-header">
<div class="birb-window-title">Field Guide</div>
<div class="birb-window-close">x</div>
</div>
<div class="birb-window-content">
<div class="birb-grid-content"></div>
<div class="birb-field-guide-description"></div>
</div>`;
const fieldGuide = makeElement("birb-window", undefined, FIELD_GUIDE_ID);
fieldGuide.innerHTML = html;
document.body.appendChild(fieldGuide);
makeDraggable(fieldGuide.querySelector(".birb-window-header"));
const closeButton = fieldGuide.querySelector(".birb-window-close");
if (closeButton) {
makeClosable(() => {
fieldGuide.remove();
}, closeButton);
}
const fieldGuide = createWindow(
FIELD_GUIDE_ID,
"Field Guide",
`<div class="birb-grid-content"></div>
<div class="birb-field-guide-description"></div>`
);
const content = fieldGuide.querySelector(".birb-grid-content");
if (!content) {
@@ -2166,7 +2153,7 @@
function switchSpecies(type) {
currentSpecies = type;
// Update CSS variable --birb-highlight to be wing color
document.documentElement.style.setProperty("--birb-highlight", SPECIES[type].colors[SPRITE.THEME_HIGHLIGHT]);
document.documentElement.style.setProperty("--birb-highlight", SPECIES[type].colors[Sprite.THEME_HIGHLIGHT]);
save();
}
@@ -2287,11 +2274,6 @@
}
}
function hideBirb() {
birb.setVisible(false);
visible = false;
}
/**
* @param {number} x
* @param {number} y
@@ -2323,14 +2305,7 @@
birb.setAnimation(Animations.BOB);
}
birb.setAbsolutePositioned(isAbsolute());
setY(birdY);
}
/**
* @param {number} y
*/
function setY(y) {
birb.setY(y);
birb.setY(birdY);
}
// Helper functions

295
dist/birb.user.js vendored
View File

@@ -1,7 +1,7 @@
// ==UserScript==
// @name Pocket Bird
// @namespace https://idreesinc.com
// @version 2025.10.26.402
// @version 2025.10.26.406
// @description birb
// @author Idrees
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js
@@ -202,7 +202,7 @@
}
/** Indicators for parts of the base bird sprite sheet */
const SPRITE = {
const Sprite = {
THEME_HIGHLIGHT: "theme-highlight",
TRANSPARENT: "transparent",
OUTLINE: "outline",
@@ -225,23 +225,23 @@
/** @type {Record<string, string>} */
const SPRITE_SHEET_COLOR_MAP = {
"transparent": SPRITE.TRANSPARENT,
"#ffffff": SPRITE.BORDER,
"#000000": SPRITE.OUTLINE,
"#010a19": SPRITE.BEAK,
"#190301": SPRITE.EYE,
"#af8e75": SPRITE.FOOT,
"#639bff": SPRITE.FACE,
"#99e550": SPRITE.HOOD,
"#d95763": SPRITE.NOSE,
"#f8b143": SPRITE.BELLY,
"#ec8637": SPRITE.UNDERBELLY,
"#578ae6": SPRITE.WING,
"#326ed9": SPRITE.WING_EDGE,
"#c82e2e": SPRITE.HEART,
"#501a1a": SPRITE.HEART_BORDER,
"#ff6b6b": SPRITE.HEART_SHINE,
"#373737": SPRITE.FEATHER_SPINE,
"transparent": Sprite.TRANSPARENT,
"#ffffff": Sprite.BORDER,
"#000000": Sprite.OUTLINE,
"#010a19": Sprite.BEAK,
"#190301": Sprite.EYE,
"#af8e75": Sprite.FOOT,
"#639bff": Sprite.FACE,
"#99e550": Sprite.HOOD,
"#d95763": Sprite.NOSE,
"#f8b143": Sprite.BELLY,
"#ec8637": Sprite.UNDERBELLY,
"#578ae6": Sprite.WING,
"#326ed9": Sprite.WING_EDGE,
"#c82e2e": Sprite.HEART,
"#501a1a": Sprite.HEART_BORDER,
"#ff6b6b": Sprite.HEART_SHINE,
"#373737": Sprite.FEATHER_SPINE,
};
class BirdType {
@@ -255,20 +255,20 @@
this.name = name;
this.description = description;
const defaultColors = {
[SPRITE.TRANSPARENT]: "transparent",
[SPRITE.OUTLINE]: "#000000",
[SPRITE.BORDER]: "#ffffff",
[SPRITE.BEAK]: "#000000",
[SPRITE.EYE]: "#000000",
[SPRITE.HEART]: "#c82e2e",
[SPRITE.HEART_BORDER]: "#501a1a",
[SPRITE.HEART_SHINE]: "#ff6b6b",
[SPRITE.FEATHER_SPINE]: "#373737",
[SPRITE.HOOD]: colors.face,
[SPRITE.NOSE]: colors.face,
[Sprite.TRANSPARENT]: "transparent",
[Sprite.OUTLINE]: "#000000",
[Sprite.BORDER]: "#ffffff",
[Sprite.BEAK]: "#000000",
[Sprite.EYE]: "#000000",
[Sprite.HEART]: "#c82e2e",
[Sprite.HEART_BORDER]: "#501a1a",
[Sprite.HEART_SHINE]: "#ff6b6b",
[Sprite.FEATHER_SPINE]: "#373737",
[Sprite.HOOD]: colors.face,
[Sprite.NOSE]: colors.face,
};
/** @type {Record<string, string>} */
this.colors = { ...defaultColors, ...colors, [SPRITE.THEME_HIGHLIGHT]: colors[SPRITE.THEME_HIGHLIGHT] ?? colors.hood ?? colors.face };
this.colors = { ...defaultColors, ...colors, [Sprite.THEME_HIGHLIGHT]: colors[Sprite.THEME_HIGHLIGHT] ?? colors.hood ?? colors.face };
this.tags = tags;
}
}
@@ -277,127 +277,127 @@
const SPECIES = {
bluebird: new BirdType("Eastern Bluebird",
"Native to North American and very social, though can be timid around people.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#639bff",
[SPRITE.BELLY]: "#f8b143",
[SPRITE.UNDERBELLY]: "#ec8637",
[SPRITE.WING]: "#578ae6",
[SPRITE.WING_EDGE]: "#326ed9",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#639bff",
[Sprite.BELLY]: "#f8b143",
[Sprite.UNDERBELLY]: "#ec8637",
[Sprite.WING]: "#578ae6",
[Sprite.WING_EDGE]: "#326ed9",
}),
shimaEnaga: new BirdType("Shima Enaga",
"Small, fluffy birds found in the snowy regions of Japan, these birds are highly sought after by ornithologists and nature photographers.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#ffffff",
[SPRITE.BELLY]: "#ebe9e8",
[SPRITE.UNDERBELLY]: "#ebd9d0",
[SPRITE.WING]: "#f3d3c1",
[SPRITE.WING_EDGE]: "#2d2d2dff",
[SPRITE.THEME_HIGHLIGHT]: "#d7ac93",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#ffffff",
[Sprite.BELLY]: "#ebe9e8",
[Sprite.UNDERBELLY]: "#ebd9d0",
[Sprite.WING]: "#f3d3c1",
[Sprite.WING_EDGE]: "#2d2d2dff",
[Sprite.THEME_HIGHLIGHT]: "#d7ac93",
}),
tuftedTitmouse: new BirdType("Tufted Titmouse",
"Native to the eastern United States, full of personality, and notably my wife's favorite bird.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#c7cad7",
[SPRITE.BELLY]: "#e4e5eb",
[SPRITE.UNDERBELLY]: "#d7cfcb",
[SPRITE.WING]: "#b1b5c5",
[SPRITE.WING_EDGE]: "#9d9fa9",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#c7cad7",
[Sprite.BELLY]: "#e4e5eb",
[Sprite.UNDERBELLY]: "#d7cfcb",
[Sprite.WING]: "#b1b5c5",
[Sprite.WING_EDGE]: "#9d9fa9",
}, ["tuft"]),
europeanRobin: new BirdType("European Robin",
"Native to western Europe, this is the quintessential robin. Quite friendly, you'll often find them searching for worms.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#ffaf34",
[SPRITE.HOOD]: "#aaa094",
[SPRITE.BELLY]: "#ffaf34",
[SPRITE.UNDERBELLY]: "#babec2",
[SPRITE.WING]: "#aaa094",
[SPRITE.WING_EDGE]: "#888580",
[SPRITE.THEME_HIGHLIGHT]: "#ffaf34",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#ffaf34",
[Sprite.HOOD]: "#aaa094",
[Sprite.BELLY]: "#ffaf34",
[Sprite.UNDERBELLY]: "#babec2",
[Sprite.WING]: "#aaa094",
[Sprite.WING_EDGE]: "#888580",
[Sprite.THEME_HIGHLIGHT]: "#ffaf34",
}),
redCardinal: new BirdType("Red Cardinal",
"Native to the eastern United States, this strikingly red bird is hard to miss.", {
[SPRITE.BEAK]: "#d93619",
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#31353d",
[SPRITE.HOOD]: "#e83a1b",
[SPRITE.BELLY]: "#e83a1b",
[SPRITE.UNDERBELLY]: "#dc3719",
[SPRITE.WING]: "#d23215",
[SPRITE.WING_EDGE]: "#b1321c",
[Sprite.BEAK]: "#d93619",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#31353d",
[Sprite.HOOD]: "#e83a1b",
[Sprite.BELLY]: "#e83a1b",
[Sprite.UNDERBELLY]: "#dc3719",
[Sprite.WING]: "#d23215",
[Sprite.WING_EDGE]: "#b1321c",
}, ["tuft"]),
americanGoldfinch: new BirdType("American Goldfinch",
"Coloured a brilliant yellow, this bird feeds almost entirely on the seeds of plants such as thistle, sunflowers, and coneflowers.", {
[SPRITE.BEAK]: "#ffaf34",
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#fff255",
[SPRITE.NOSE]: "#383838",
[SPRITE.HOOD]: "#383838",
[SPRITE.BELLY]: "#fff255",
[SPRITE.UNDERBELLY]: "#f5ea63",
[SPRITE.WING]: "#e8e079",
[SPRITE.WING_EDGE]: "#191919",
[SPRITE.THEME_HIGHLIGHT]: "#ffcc00"
[Sprite.BEAK]: "#ffaf34",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#fff255",
[Sprite.NOSE]: "#383838",
[Sprite.HOOD]: "#383838",
[Sprite.BELLY]: "#fff255",
[Sprite.UNDERBELLY]: "#f5ea63",
[Sprite.WING]: "#e8e079",
[Sprite.WING_EDGE]: "#191919",
[Sprite.THEME_HIGHLIGHT]: "#ffcc00"
}),
barnSwallow: new BirdType("Barn Swallow",
"Agile birds that often roost in man-made structures, these birds are known to build nests near Ospreys for protection.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#db7c4d",
[SPRITE.BELLY]: "#f7e1c9",
[SPRITE.UNDERBELLY]: "#ebc9a3",
[SPRITE.WING]: "#2252a9",
[SPRITE.WING_EDGE]: "#1c448b",
[SPRITE.HOOD]: "#2252a9",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#db7c4d",
[Sprite.BELLY]: "#f7e1c9",
[Sprite.UNDERBELLY]: "#ebc9a3",
[Sprite.WING]: "#2252a9",
[Sprite.WING_EDGE]: "#1c448b",
[Sprite.HOOD]: "#2252a9",
}),
mistletoebird: new BirdType("Mistletoebird",
"Native to Australia, these birds eat mainly mistletoe and in turn spread the seeds far and wide.", {
[SPRITE.FOOT]: "#6c6a7c",
[SPRITE.FACE]: "#352e6d",
[SPRITE.BELLY]: "#fd6833",
[SPRITE.UNDERBELLY]: "#e6e1d8",
[SPRITE.WING]: "#342b7c",
[SPRITE.WING_EDGE]: "#282065",
[Sprite.FOOT]: "#6c6a7c",
[Sprite.FACE]: "#352e6d",
[Sprite.BELLY]: "#fd6833",
[Sprite.UNDERBELLY]: "#e6e1d8",
[Sprite.WING]: "#342b7c",
[Sprite.WING_EDGE]: "#282065",
}),
redAvadavat: new BirdType("Red Avadavat",
"Native to India and southeast Asia, these birds are also known as Strawberry Finches due to their speckled plumage.", {
[SPRITE.BEAK]: "#f71919",
[SPRITE.FOOT]: "#af7575",
[SPRITE.FACE]: "#cb092b",
[SPRITE.BELLY]: "#ae1724",
[SPRITE.UNDERBELLY]: "#831b24",
[SPRITE.WING]: "#7e3030",
[SPRITE.WING_EDGE]: "#490f0f",
[Sprite.BEAK]: "#f71919",
[Sprite.FOOT]: "#af7575",
[Sprite.FACE]: "#cb092b",
[Sprite.BELLY]: "#ae1724",
[Sprite.UNDERBELLY]: "#831b24",
[Sprite.WING]: "#7e3030",
[Sprite.WING_EDGE]: "#490f0f",
}),
scarletRobin: new BirdType("Scarlet Robin",
"Native to Australia, this striking robin can be found in Eucalyptus forests.", {
[SPRITE.FOOT]: "#494949",
[SPRITE.FACE]: "#3d3d3d",
[SPRITE.BELLY]: "#fc5633",
[SPRITE.UNDERBELLY]: "#dcdcdc",
[SPRITE.WING]: "#2b2b2b",
[SPRITE.WING_EDGE]: "#ebebeb",
[SPRITE.THEME_HIGHLIGHT]: "#fc5633",
[Sprite.FOOT]: "#494949",
[Sprite.FACE]: "#3d3d3d",
[Sprite.BELLY]: "#fc5633",
[Sprite.UNDERBELLY]: "#dcdcdc",
[Sprite.WING]: "#2b2b2b",
[Sprite.WING_EDGE]: "#ebebeb",
[Sprite.THEME_HIGHLIGHT]: "#fc5633",
}),
americanRobin: new BirdType("American Robin",
"While not a true robin, this social North American bird is so named due to its orange coloring. It seems unbothered by nearby humans.", {
[SPRITE.BEAK]: "#e89f30",
[SPRITE.FOOT]: "#9f8075",
[SPRITE.FACE]: "#2d2d2d",
[SPRITE.BELLY]: "#eb7a3a",
[SPRITE.UNDERBELLY]: "#eb7a3a",
[SPRITE.WING]: "#444444",
[SPRITE.WING_EDGE]: "#232323",
[SPRITE.THEME_HIGHLIGHT]: "#eb7a3a",
[Sprite.BEAK]: "#e89f30",
[Sprite.FOOT]: "#9f8075",
[Sprite.FACE]: "#2d2d2d",
[Sprite.BELLY]: "#eb7a3a",
[Sprite.UNDERBELLY]: "#eb7a3a",
[Sprite.WING]: "#444444",
[Sprite.WING_EDGE]: "#232323",
[Sprite.THEME_HIGHLIGHT]: "#eb7a3a",
}),
carolinaWren: new BirdType("Carolina Wren",
"Native to the eastern United States, these little birds are known for their curious and energetic nature.", {
[SPRITE.FOOT]: "#af8e75",
[SPRITE.FACE]: "#edc7a9",
[SPRITE.NOSE]: "#f7eee5",
[SPRITE.HOOD]: "#c58a5b",
[SPRITE.BELLY]: "#e1b796",
[SPRITE.UNDERBELLY]: "#c79e7c",
[SPRITE.WING]: "#c58a5b",
[SPRITE.WING_EDGE]: "#866348",
[Sprite.FOOT]: "#af8e75",
[Sprite.FACE]: "#edc7a9",
[Sprite.NOSE]: "#f7eee5",
[Sprite.HOOD]: "#c58a5b",
[Sprite.BELLY]: "#e1b796",
[Sprite.UNDERBELLY]: "#c79e7c",
[Sprite.WING]: "#c58a5b",
[Sprite.WING_EDGE]: "#866348",
}),
};
@@ -435,7 +435,7 @@
this.pixels = layers[0].pixels.map(row => row.slice());
// Pad from top with transparent pixels
while (this.pixels.length < maxHeight) {
this.pixels.unshift(new Array(this.pixels[0].length).fill(SPRITE.TRANSPARENT));
this.pixels.unshift(new Array(this.pixels[0].length).fill(Sprite.TRANSPARENT));
}
// Combine layers
for (let i = 1; i < layers.length; i++) {
@@ -444,7 +444,7 @@
let topMargin = maxHeight - layerPixels.length;
for (let y = 0; y < layerPixels.length; y++) {
for (let x = 0; x < layerPixels[y].length; x++) {
this.pixels[y + topMargin][x] = layerPixels[y][x] !== SPRITE.TRANSPARENT ? layerPixels[y][x] : this.pixels[y + topMargin][x];
this.pixels[y + topMargin][x] = layerPixels[y][x] !== Sprite.TRANSPARENT ? layerPixels[y][x] : this.pixels[y + topMargin][x];
}
}
}
@@ -1523,7 +1523,7 @@
const b = pixels[index + 2];
const a = pixels[index + 3];
if (a === 0) {
row.push(SPRITE.TRANSPARENT);
row.push(Sprite.TRANSPARENT);
continue;
}
const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
@@ -1533,7 +1533,7 @@
}
if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) {
error(`Unknown color: ${hex}`);
row.push(SPRITE.TRANSPARENT);
row.push(Sprite.TRANSPARENT);
}
row.push(SPRITE_SHEET_COLOR_MAP[hex]);
}
@@ -1577,7 +1577,7 @@
new MenuItem(`Pet ${birdBirb()}`, pet),
new MenuItem("Field Guide", insertFieldGuide),
new MenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote)),
new MenuItem(`Hide ${birdBirb()}`, hideBirb),
new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)),
new DebugMenuItem("Freeze/Unfreeze", () => {
frozen = !frozen;
}),
@@ -1636,7 +1636,7 @@
let petStack = [];
let currentSpecies = DEFAULT_BIRD;
let unlockedSpecies = [DEFAULT_BIRD];
let visible = true;
// let visible = true;
let lastPetTimestamp = 0;
/** @type {StickyNote[]} */
let stickyNotes = [];
@@ -1849,7 +1849,7 @@
// Hide bird if the browser is fullscreen
if (document.fullscreenElement) {
hideBirb();
birb.setVisible(false);
// Won't be restored on fullscreen exit
}
@@ -1876,7 +1876,7 @@
// 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) {
if (birb.isVisible() && Math.random() < FEATHER_CHANCE * petMod) {
lastPetTimestamp = 0;
activateFeather();
}
@@ -2091,26 +2091,13 @@
if (document.querySelector("#" + FIELD_GUIDE_ID)) {
return;
}
let html = `
<div class="birb-window-header">
<div class="birb-window-title">Field Guide</div>
<div class="birb-window-close">x</div>
</div>
<div class="birb-window-content">
<div class="birb-grid-content"></div>
<div class="birb-field-guide-description"></div>
</div>`;
const fieldGuide = makeElement("birb-window", undefined, FIELD_GUIDE_ID);
fieldGuide.innerHTML = html;
document.body.appendChild(fieldGuide);
makeDraggable(fieldGuide.querySelector(".birb-window-header"));
const closeButton = fieldGuide.querySelector(".birb-window-close");
if (closeButton) {
makeClosable(() => {
fieldGuide.remove();
}, closeButton);
}
const fieldGuide = createWindow(
FIELD_GUIDE_ID,
"Field Guide",
`<div class="birb-grid-content"></div>
<div class="birb-field-guide-description"></div>`
);
const content = fieldGuide.querySelector(".birb-grid-content");
if (!content) {
@@ -2180,7 +2167,7 @@
function switchSpecies(type) {
currentSpecies = type;
// Update CSS variable --birb-highlight to be wing color
document.documentElement.style.setProperty("--birb-highlight", SPECIES[type].colors[SPRITE.THEME_HIGHLIGHT]);
document.documentElement.style.setProperty("--birb-highlight", SPECIES[type].colors[Sprite.THEME_HIGHLIGHT]);
save();
}
@@ -2301,11 +2288,6 @@
}
}
function hideBirb() {
birb.setVisible(false);
visible = false;
}
/**
* @param {number} x
* @param {number} y
@@ -2337,14 +2319,7 @@
birb.setAnimation(Animations.BOB);
}
birb.setAbsolutePositioned(isAbsolute());
setY(birdY);
}
/**
* @param {number} y
*/
function setY(y) {
birb.setY(y);
birb.setY(birdY);
}
// Helper functions

View File

@@ -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.26.402",
"version": "2025.10.26.406",
"homepage_url": "https://idreesinc.com",
"content_scripts": [
{

View File

@@ -710,26 +710,13 @@ Promise.all([
if (document.querySelector("#" + FIELD_GUIDE_ID)) {
return;
}
let html = `
<div class="birb-window-header">
<div class="birb-window-title">Field Guide</div>
<div class="birb-window-close">x</div>
</div>
<div class="birb-window-content">
<div class="birb-grid-content"></div>
<div class="birb-field-guide-description"></div>
</div>`
const fieldGuide = makeElement("birb-window", undefined, FIELD_GUIDE_ID);
fieldGuide.innerHTML = html;
document.body.appendChild(fieldGuide);
makeDraggable(fieldGuide.querySelector(".birb-window-header"));
const closeButton = fieldGuide.querySelector(".birb-window-close");
if (closeButton) {
makeClosable(() => {
fieldGuide.remove();
}, closeButton);
}
const fieldGuide = createWindow(
FIELD_GUIDE_ID,
"Field Guide",
`<div class="birb-grid-content"></div>
<div class="birb-field-guide-description"></div>`
)
const content = fieldGuide.querySelector(".birb-grid-content");
if (!content) {