diff --git a/aseprite/hats.aseprite b/aseprite/hats.aseprite index 7f768e4..59d937e 100644 Binary files a/aseprite/hats.aseprite and b/aseprite/hats.aseprite differ diff --git a/dist/extension.zip b/dist/extension.zip index 467d9b9..ef7542e 100644 Binary files a/dist/extension.zip and b/dist/extension.zip differ diff --git a/dist/extension/birb.js b/dist/extension/birb.js index fa74efa..9ae9445 100644 --- a/dist/extension/birb.js +++ b/dist/extension/birb.js @@ -618,6 +618,33 @@ FEZ: "fez", }; + const HAT_METADATA = { + [HAT.NONE]: { + name: "Invisible Hat", + description: "It's like you're wearing nothing at all!" + }, + [HAT.TOP_HAT]: { + name: "Top Hat", + description: "For that classy, sophisticated look." + }, + [HAT.VIKING_HELMET]: { + name: "Viking Helmet", + description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?" + }, + [HAT.COWBOY_HAT]: { + name: "Cowboy Hat", + description: "You can't jam with the console cowboys without the appropriate attire." + }, + [HAT.BOWLER_HAT]: { + name: "Bowler Hat", + description: "For that authentic, Victorian look!" + }, + [HAT.FEZ]: { + name: "Fez", + description: "It's a fez. Fezzes are cool." + }, + }; + /** * @param {string[][]} spriteSheet * @returns {{ base: Layer[], down: Layer[] }} @@ -1752,9 +1779,20 @@ width: 322px !important; } +#birb-wardrobe { + width: calc(322px - 64px - 14px) !important; +} + +#birb-field-guide .birb-grid-content { + grid-template-rows: repeat(3, auto); +} + +#birb-wardrobe .birb-grid-content { + grid-template-rows: repeat(2, auto); +} + .birb-grid-content { display: grid; - grid-template-rows: repeat(3, auto); grid-auto-flow: column; gap: 10px; padding-top: 8px; @@ -1881,11 +1919,12 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD9JREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBbmA0Qs0uSuSiloYSBgIRhCPkCQFIKCYNBKK6szjZWpbEyTziLY5k6RAwsjpDGvRXbWubmd3btzd2c293vgyGRvb9/Z25ln39l/BxBCCCGEkOlC+a4ACR8REdd7Sim2IVJb2HhrgE8B6djtZhMA0Ol2B8pV1IEQMqVITCuKpBVFQ+UsORYVvxVF8nl+XmRtbahcdnxCymTGdwVIPu1mExuNBjrt9lC5SvY/fcJGo5GUCak7FGCN8CWgTreLJ/3+wLQn/X4yBCaEkFIwh8Cf5+eTV1VD4LQ6VBmbEBIAkkLVsX0KyKwD5UfIlCEiZwf/jb9Vx/ctIB/yJ6RMLviuQN3Yv3HDS1yllBIR8XnpCS93IWRK0ZmPzv6YBRFSf7hHHwNTesyGqsfe6XAbkP+FDYjUAi0/7TwRqVyAFPCUknYGlENA4gHZ6bYEgLcTQHHsoNs/++no5F4Ibe55zRdy7lEtEgqYAMBOt6WLXk4AKaWSOoSW/dn9wkc/rSOZZ4HNL9NofNDTRMScp5QGYQ99jOkQEQmtIZLyeNB873Vb+xTwKJhdYWW7l0yj/9w4BWiK53DlPvAI2L79Onl/p9seOB5ThoxCEDAhGt8CzkCUUon0zjtXZpV8+yOFbAvnQkREZi5GA9PuPevhw+oMll6eAgCOf34DALxbjwb2MkXIaEjAGBTwraU2HjTf63kLi0tIzRCX+L4e/cLB8+teThiVxZVZJUsvT/FhdQZFSDBTgIgFdP9VegqtBYhYgjBklBpsjI3gW8AkbFa2e/JuPZr27Zwrv1CH66HgHALrOw9c75vyg3XMIY1Jhsmnv3tDAtbys2Pbw3HXOo0am4TDye6izC3vKV0GgLllv/LzLeCV7Z7XA3uu+HEiVJt+llnRWFg42V3E2o+PAIAXl28DAO4evh0pwNejXwAwUSqu46dloLaANToTTVkWQAnWln/i26t8+6ULuPp6mLgEZPa3kkXkzD7rJMGRBWgzt7yHmw8Pce3qpdTPWhtiIgH5FjAhmlDEpznZXRSzD9j9rQIBiav/T4UAYUgwDVt8mCD78i1gQkKmv7Ugaxc6wODIp6r27RQgaiTBXAEiPq5nS+j4yzEAoLG57/rsvyATSse3gAkJnf7WQtLA73x/A5y1fe8SNE9MhtzvciuWJiEtvzQam/uFrbhvARNCchGdhNgi1BIMuf+N9DzAeCXQ31rInK9I+SHjTLQpYLtORdeBEJKJOnh+/azDOUQYMrkC1BLKk2CZ4tGxkSK8qupACHGicDb0HhDhucJ8Gkbn6ePkqRi6XOYDCqwbvVPjl10HQkg+9hNzQu+PY/0splIKnaePk//NMkrMuvRys+Iz8yMkDOKbEYAa9MexfhPEHIra5SrIix/6l03IeadufXDs6/KcC6pgxX3HJ4ScL/4CWsLSrzMo7i0AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; - const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAATxJREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIRX8xyKGYjYTuQYfPnsJ2YL/yGw0ORSwtzMCbkZ/mDVRlikIqv5Hjgh84LONDcNnPT2G/6f54fYgy7Pg0giNXWLA/5a6dgYGBgaGmqZKohwF8/Sls48JqoV59MWXJwwcrJz/f/z+jj81fPrE8O/HD4bpvpwM/08zMDCafkSRxvDwGml2CINHloGBgYHB5DF2R9148JRh9trtDC8vXmBgYGBgePfpDUNRQTGDpq0Lw40HT9GVwwPi9SGEeZKY8hieISHgMQC6Z1EswJJ3caqFObKmrYeB48dvhnef3sAFucQkGVqqStDV/0dKyigAFsuFq47idM+LL08w9OGIaYJ5mJLCglDyxQggEtXTBAAAgRd0Slay6H4AAAAASUVORK5CYII="; + const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAAT5JREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIQf8R+OjmM1ErqGHz15CNvw/MhtNDgXs7YyAm9EfZk2UZQqCqv+RIwKfuxgYGBg+6+kx/D/NjyIGAyy4dEJjlxjwv6WunYGBgYGhpqmSGEfBPX3p7GOCamEeffHlCQMHK+f/H7+/400N/+3tGT49f84w3ZeT4f9pBgZG048o8hgeXiPNDmHwyDIwMDAwmDzG7qgbD54yzF67neHlxQsMDAwMDO8+vWEoKihm0LR1Ybjx4CmGO2CM14cQ5kliymN4hoSAxwDonkWxAEvexakW5siath4Gjh+/Gd59egMX5BKTZGipKkFX/x8pKaMAWCwXrjqK0z0vvjzB0IcjprGlMEacHBIBoeSLEUAkqqcJAAAmBnNJoDeWTQAAAABJRU5ErkJggg=="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; + const WARDROBE_ID = "birb-wardrobe"; const DEFAULT_BIRD = "bluebird"; @@ -1961,6 +2000,7 @@ const menuItems = [ new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem("Field Guide", insertFieldGuide), + new MenuItem("Wardrobe", insertWardrobe), new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), new DebugMenuItem("Freeze/Unfreeze", () => { @@ -2448,6 +2488,8 @@ if (document.querySelector("#" + FIELD_GUIDE_ID)) { return; } + // Remove wardrobe if open + removeWardrobe(); const contentContainer = document.createElement("div"); const content = makeElement("birb-grid-content"); @@ -2528,6 +2570,96 @@ } } + function insertWardrobe() { + console.log("Inserting wardrobe"); + if (document.querySelector("#" + WARDROBE_ID)) { + return; + } + // Remove field guide if open + removeFieldGuide(); + + const contentContainer = document.createElement("div"); + const content = makeElement("birb-grid-content"); + const description = makeElement("birb-field-guide-description"); + contentContainer.appendChild(content); + contentContainer.appendChild(description); + + const wardrobe = createWindow( + WARDROBE_ID, + "Wardrobe", + contentContainer + ); + + const generateDescription = (/** @type {string} */ hat) => { + const metadata = HAT_METADATA[hat] ?? { name: "Unknown Hat", description: "todo" }; + + const boldName = document.createElement("b"); + boldName.textContent = metadata.name; + + const spacer = document.createElement("div"); + spacer.style.height = "0.3em"; + + const descText = document.createTextNode(metadata.description); + + const fragment = document.createDocumentFragment(); + fragment.appendChild(boldName); + fragment.appendChild(spacer); + fragment.appendChild(descText); + + return fragment; + }; + + description.appendChild(generateDescription(currentHat)); + for (const hat of Object.values(HAT)) { + const hatElement = makeElement("birb-grid-item"); + if (hat === currentHat) { + hatElement.classList.add("birb-grid-item-selected"); + } + const hatCanvas = document.createElement("canvas"); + hatCanvas.width = SPRITE_WIDTH * CANVAS_PIXEL_SIZE; + hatCanvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + const hatCtx = hatCanvas.getContext("2d"); + if (!hatCtx) { + return; + } + console.log(hat); + birb.getFrames().base.draw( + hatCtx, + Directions.RIGHT, + CANVAS_PIXEL_SIZE, + SPECIES[currentSpecies].colors, + [...SPECIES[currentSpecies].tags, hat] + ); + hatElement.appendChild(hatCanvas); + content.appendChild(hatElement); + { + onClick(hatElement, () => { + switchHat(hat); + document.querySelectorAll(".birb-grid-item").forEach((element) => { + element.classList.remove("birb-grid-item-selected"); + }); + hatElement.classList.add("birb-grid-item-selected"); + }); + } + hatElement.addEventListener("mouseover", () => { + description.textContent = ""; + description.appendChild(generateDescription(hat)); + }); + hatElement.addEventListener("mouseout", () => { + description.textContent = ""; + description.appendChild(generateDescription(currentHat)); + }); + } + centerElement(wardrobe); + } + + function removeWardrobe() { + const wardrobe = document.querySelector("#" + WARDROBE_ID); + if (wardrobe) { + wardrobe.remove(); + } + } + /** * @param {string} type */ @@ -2538,6 +2670,14 @@ save(); } + /** + * @param {string} hat + */ + function switchHat(hat) { + currentHat = hat; + save(); + } + /** * Update the birds location from the start to the target location on a parabolic path * @param {number} speed The speed of the bird along the path diff --git a/dist/obsidian/main.js b/dist/obsidian/main.js index fc27476..ed55867 100644 --- a/dist/obsidian/main.js +++ b/dist/obsidian/main.js @@ -623,6 +623,33 @@ module.exports = class PocketBird extends Plugin { FEZ: "fez", }; + const HAT_METADATA = { + [HAT.NONE]: { + name: "Invisible Hat", + description: "It's like you're wearing nothing at all!" + }, + [HAT.TOP_HAT]: { + name: "Top Hat", + description: "For that classy, sophisticated look." + }, + [HAT.VIKING_HELMET]: { + name: "Viking Helmet", + description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?" + }, + [HAT.COWBOY_HAT]: { + name: "Cowboy Hat", + description: "You can't jam with the console cowboys without the appropriate attire." + }, + [HAT.BOWLER_HAT]: { + name: "Bowler Hat", + description: "For that authentic, Victorian look!" + }, + [HAT.FEZ]: { + name: "Fez", + description: "It's a fez. Fezzes are cool." + }, + }; + /** * @param {string[][]} spriteSheet * @returns {{ base: Layer[], down: Layer[] }} @@ -1795,9 +1822,20 @@ module.exports = class PocketBird extends Plugin { width: 322px !important; } +#birb-wardrobe { + width: calc(322px - 64px - 14px) !important; +} + +#birb-field-guide .birb-grid-content { + grid-template-rows: repeat(3, auto); +} + +#birb-wardrobe .birb-grid-content { + grid-template-rows: repeat(2, auto); +} + .birb-grid-content { display: grid; - grid-template-rows: repeat(3, auto); grid-auto-flow: column; gap: 10px; padding-top: 8px; @@ -1924,11 +1962,12 @@ module.exports = class PocketBird extends Plugin { }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD9JREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBbmA0Qs0uSuSiloYSBgIRhCPkCQFIKCYNBKK6szjZWpbEyTziLY5k6RAwsjpDGvRXbWubmd3btzd2c293vgyGRvb9/Z25ln39l/BxBCCCGEkOlC+a4ACR8REdd7Sim2IVJb2HhrgE8B6djtZhMA0Ol2B8pV1IEQMqVITCuKpBVFQ+UsORYVvxVF8nl+XmRtbahcdnxCymTGdwVIPu1mExuNBjrt9lC5SvY/fcJGo5GUCak7FGCN8CWgTreLJ/3+wLQn/X4yBCaEkFIwh8Cf5+eTV1VD4LQ6VBmbEBIAkkLVsX0KyKwD5UfIlCEiZwf/jb9Vx/ctIB/yJ6RMLviuQN3Yv3HDS1yllBIR8XnpCS93IWRK0ZmPzv6YBRFSf7hHHwNTesyGqsfe6XAbkP+FDYjUAi0/7TwRqVyAFPCUknYGlENA4gHZ6bYEgLcTQHHsoNs/++no5F4Ibe55zRdy7lEtEgqYAMBOt6WLXk4AKaWSOoSW/dn9wkc/rSOZZ4HNL9NofNDTRMScp5QGYQ99jOkQEQmtIZLyeNB873Vb+xTwKJhdYWW7l0yj/9w4BWiK53DlPvAI2L79Onl/p9seOB5ThoxCEDAhGt8CzkCUUon0zjtXZpV8+yOFbAvnQkREZi5GA9PuPevhw+oMll6eAgCOf34DALxbjwb2MkXIaEjAGBTwraU2HjTf63kLi0tIzRCX+L4e/cLB8+teThiVxZVZJUsvT/FhdQZFSDBTgIgFdP9VegqtBYhYgjBklBpsjI3gW8AkbFa2e/JuPZr27Zwrv1CH66HgHALrOw9c75vyg3XMIY1Jhsmnv3tDAtbys2Pbw3HXOo0am4TDye6izC3vKV0GgLllv/LzLeCV7Z7XA3uu+HEiVJt+llnRWFg42V3E2o+PAIAXl28DAO4evh0pwNejXwAwUSqu46dloLaANToTTVkWQAnWln/i26t8+6ULuPp6mLgEZPa3kkXkzD7rJMGRBWgzt7yHmw8Pce3qpdTPWhtiIgH5FjAhmlDEpznZXRSzD9j9rQIBiav/T4UAYUgwDVt8mCD78i1gQkKmv7Ugaxc6wODIp6r27RQgaiTBXAEiPq5nS+j4yzEAoLG57/rsvyATSse3gAkJnf7WQtLA73x/A5y1fe8SNE9MhtzvciuWJiEtvzQam/uFrbhvARNCchGdhNgi1BIMuf+N9DzAeCXQ31rInK9I+SHjTLQpYLtORdeBEJKJOnh+/azDOUQYMrkC1BLKk2CZ4tGxkSK8qupACHGicDb0HhDhucJ8Gkbn6ePkqRi6XOYDCqwbvVPjl10HQkg+9hNzQu+PY/0splIKnaePk//NMkrMuvRys+Iz8yMkDOKbEYAa9MexfhPEHIra5SrIix/6l03IeadufXDs6/KcC6pgxX3HJ4ScL/4CWsLSrzMo7i0AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; - const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAATxJREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIRX8xyKGYjYTuQYfPnsJ2YL/yGw0ORSwtzMCbkZ/mDVRlikIqv5Hjgh84LONDcNnPT2G/6f54fYgy7Pg0giNXWLA/5a6dgYGBgaGmqZKohwF8/Sls48JqoV59MWXJwwcrJz/f/z+jj81fPrE8O/HD4bpvpwM/08zMDCafkSRxvDwGml2CINHloGBgYHB5DF2R9148JRh9trtDC8vXmBgYGBgePfpDUNRQTGDpq0Lw40HT9GVwwPi9SGEeZKY8hieISHgMQC6Z1EswJJ3caqFObKmrYeB48dvhnef3sAFucQkGVqqStDV/0dKyigAFsuFq47idM+LL08w9OGIaYJ5mJLCglDyxQggEtXTBAAAgRd0Slay6H4AAAAASUVORK5CYII="; + const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAAT5JREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIQf8R+OjmM1ErqGHz15CNvw/MhtNDgXs7YyAm9EfZk2UZQqCqv+RIwKfuxgYGBg+6+kx/D/NjyIGAyy4dEJjlxjwv6WunYGBgYGhpqmSGEfBPX3p7GOCamEeffHlCQMHK+f/H7+/400N/+3tGT49f84w3ZeT4f9pBgZG048o8hgeXiPNDmHwyDIwMDAwmDzG7qgbD54yzF67neHlxQsMDAwMDO8+vWEoKihm0LR1Ybjx4CmGO2CM14cQ5kliymN4hoSAxwDonkWxAEvexakW5siath4Gjh+/Gd59egMX5BKTZGipKkFX/x8pKaMAWCwXrjqK0z0vvjzB0IcjprGlMEacHBIBoeSLEUAkqqcJAAAmBnNJoDeWTQAAAABJRU5ErkJggg=="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; + const WARDROBE_ID = "birb-wardrobe"; const DEFAULT_BIRD = "bluebird"; @@ -2004,6 +2043,7 @@ module.exports = class PocketBird extends Plugin { const menuItems = [ new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem("Field Guide", insertFieldGuide), + new MenuItem("Wardrobe", insertWardrobe), new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), new DebugMenuItem("Freeze/Unfreeze", () => { @@ -2491,6 +2531,8 @@ module.exports = class PocketBird extends Plugin { if (document.querySelector("#" + FIELD_GUIDE_ID)) { return; } + // Remove wardrobe if open + removeWardrobe(); const contentContainer = document.createElement("div"); const content = makeElement("birb-grid-content"); @@ -2571,6 +2613,96 @@ module.exports = class PocketBird extends Plugin { } } + function insertWardrobe() { + console.log("Inserting wardrobe"); + if (document.querySelector("#" + WARDROBE_ID)) { + return; + } + // Remove field guide if open + removeFieldGuide(); + + const contentContainer = document.createElement("div"); + const content = makeElement("birb-grid-content"); + const description = makeElement("birb-field-guide-description"); + contentContainer.appendChild(content); + contentContainer.appendChild(description); + + const wardrobe = createWindow( + WARDROBE_ID, + "Wardrobe", + contentContainer + ); + + const generateDescription = (/** @type {string} */ hat) => { + const metadata = HAT_METADATA[hat] ?? { name: "Unknown Hat", description: "todo" }; + + const boldName = document.createElement("b"); + boldName.textContent = metadata.name; + + const spacer = document.createElement("div"); + spacer.style.height = "0.3em"; + + const descText = document.createTextNode(metadata.description); + + const fragment = document.createDocumentFragment(); + fragment.appendChild(boldName); + fragment.appendChild(spacer); + fragment.appendChild(descText); + + return fragment; + }; + + description.appendChild(generateDescription(currentHat)); + for (const hat of Object.values(HAT)) { + const hatElement = makeElement("birb-grid-item"); + if (hat === currentHat) { + hatElement.classList.add("birb-grid-item-selected"); + } + const hatCanvas = document.createElement("canvas"); + hatCanvas.width = SPRITE_WIDTH * CANVAS_PIXEL_SIZE; + hatCanvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + const hatCtx = hatCanvas.getContext("2d"); + if (!hatCtx) { + return; + } + console.log(hat); + birb.getFrames().base.draw( + hatCtx, + Directions.RIGHT, + CANVAS_PIXEL_SIZE, + SPECIES[currentSpecies].colors, + [...SPECIES[currentSpecies].tags, hat] + ); + hatElement.appendChild(hatCanvas); + content.appendChild(hatElement); + { + onClick(hatElement, () => { + switchHat(hat); + document.querySelectorAll(".birb-grid-item").forEach((element) => { + element.classList.remove("birb-grid-item-selected"); + }); + hatElement.classList.add("birb-grid-item-selected"); + }); + } + hatElement.addEventListener("mouseover", () => { + description.textContent = ""; + description.appendChild(generateDescription(hat)); + }); + hatElement.addEventListener("mouseout", () => { + description.textContent = ""; + description.appendChild(generateDescription(currentHat)); + }); + } + centerElement(wardrobe); + } + + function removeWardrobe() { + const wardrobe = document.querySelector("#" + WARDROBE_ID); + if (wardrobe) { + wardrobe.remove(); + } + } + /** * @param {string} type */ @@ -2581,6 +2713,14 @@ module.exports = class PocketBird extends Plugin { save(); } + /** + * @param {string} hat + */ + function switchHat(hat) { + currentHat = hat; + save(); + } + /** * Update the birds location from the start to the target location on a parabolic path * @param {number} speed The speed of the bird along the path diff --git a/dist/userscript/birb.user.js b/dist/userscript/birb.user.js index dfc280a..443e645 100644 --- a/dist/userscript/birb.user.js +++ b/dist/userscript/birb.user.js @@ -632,6 +632,33 @@ FEZ: "fez", }; + const HAT_METADATA = { + [HAT.NONE]: { + name: "Invisible Hat", + description: "It's like you're wearing nothing at all!" + }, + [HAT.TOP_HAT]: { + name: "Top Hat", + description: "For that classy, sophisticated look." + }, + [HAT.VIKING_HELMET]: { + name: "Viking Helmet", + description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?" + }, + [HAT.COWBOY_HAT]: { + name: "Cowboy Hat", + description: "You can't jam with the console cowboys without the appropriate attire." + }, + [HAT.BOWLER_HAT]: { + name: "Bowler Hat", + description: "For that authentic, Victorian look!" + }, + [HAT.FEZ]: { + name: "Fez", + description: "It's a fez. Fezzes are cool." + }, + }; + /** * @param {string[][]} spriteSheet * @returns {{ base: Layer[], down: Layer[] }} @@ -1757,9 +1784,20 @@ width: 322px !important; } +#birb-wardrobe { + width: calc(322px - 64px - 14px) !important; +} + +#birb-field-guide .birb-grid-content { + grid-template-rows: repeat(3, auto); +} + +#birb-wardrobe .birb-grid-content { + grid-template-rows: repeat(2, auto); +} + .birb-grid-content { display: grid; - grid-template-rows: repeat(3, auto); grid-auto-flow: column; gap: 10px; padding-top: 8px; @@ -1886,11 +1924,12 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD9JREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBbmA0Qs0uSuSiloYSBgIRhCPkCQFIKCYNBKK6szjZWpbEyTziLY5k6RAwsjpDGvRXbWubmd3btzd2c293vgyGRvb9/Z25ln39l/BxBCCCGEkOlC+a4ACR8REdd7Sim2IVJb2HhrgE8B6djtZhMA0Ol2B8pV1IEQMqVITCuKpBVFQ+UsORYVvxVF8nl+XmRtbahcdnxCymTGdwVIPu1mExuNBjrt9lC5SvY/fcJGo5GUCak7FGCN8CWgTreLJ/3+wLQn/X4yBCaEkFIwh8Cf5+eTV1VD4LQ6VBmbEBIAkkLVsX0KyKwD5UfIlCEiZwf/jb9Vx/ctIB/yJ6RMLviuQN3Yv3HDS1yllBIR8XnpCS93IWRK0ZmPzv6YBRFSf7hHHwNTesyGqsfe6XAbkP+FDYjUAi0/7TwRqVyAFPCUknYGlENA4gHZ6bYEgLcTQHHsoNs/++no5F4Ibe55zRdy7lEtEgqYAMBOt6WLXk4AKaWSOoSW/dn9wkc/rSOZZ4HNL9NofNDTRMScp5QGYQ99jOkQEQmtIZLyeNB873Vb+xTwKJhdYWW7l0yj/9w4BWiK53DlPvAI2L79Onl/p9seOB5ThoxCEDAhGt8CzkCUUon0zjtXZpV8+yOFbAvnQkREZi5GA9PuPevhw+oMll6eAgCOf34DALxbjwb2MkXIaEjAGBTwraU2HjTf63kLi0tIzRCX+L4e/cLB8+teThiVxZVZJUsvT/FhdQZFSDBTgIgFdP9VegqtBYhYgjBklBpsjI3gW8AkbFa2e/JuPZr27Zwrv1CH66HgHALrOw9c75vyg3XMIY1Jhsmnv3tDAtbys2Pbw3HXOo0am4TDye6izC3vKV0GgLllv/LzLeCV7Z7XA3uu+HEiVJt+llnRWFg42V3E2o+PAIAXl28DAO4evh0pwNejXwAwUSqu46dloLaANToTTVkWQAnWln/i26t8+6ULuPp6mLgEZPa3kkXkzD7rJMGRBWgzt7yHmw8Pce3qpdTPWhtiIgH5FjAhmlDEpznZXRSzD9j9rQIBiav/T4UAYUgwDVt8mCD78i1gQkKmv7Ugaxc6wODIp6r27RQgaiTBXAEiPq5nS+j4yzEAoLG57/rsvyATSse3gAkJnf7WQtLA73x/A5y1fe8SNE9MhtzvciuWJiEtvzQam/uFrbhvARNCchGdhNgi1BIMuf+N9DzAeCXQ31rInK9I+SHjTLQpYLtORdeBEJKJOnh+/azDOUQYMrkC1BLKk2CZ4tGxkSK8qupACHGicDb0HhDhucJ8Gkbn6ePkqRi6XOYDCqwbvVPjl10HQkg+9hNzQu+PY/0splIKnaePk//NMkrMuvRys+Iz8yMkDOKbEYAa9MexfhPEHIra5SrIix/6l03IeadufXDs6/KcC6pgxX3HJ4ScL/4CWsLSrzMo7i0AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; - const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAATxJREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIRX8xyKGYjYTuQYfPnsJ2YL/yGw0ORSwtzMCbkZ/mDVRlikIqv5Hjgh84LONDcNnPT2G/6f54fYgy7Pg0giNXWLA/5a6dgYGBgaGmqZKohwF8/Sls48JqoV59MWXJwwcrJz/f/z+jj81fPrE8O/HD4bpvpwM/08zMDCafkSRxvDwGml2CINHloGBgYHB5DF2R9148JRh9trtDC8vXmBgYGBgePfpDUNRQTGDpq0Lw40HT9GVwwPi9SGEeZKY8hieISHgMQC6Z1EswJJ3caqFObKmrYeB48dvhnef3sAFucQkGVqqStDV/0dKyigAFsuFq47idM+LL08w9OGIaYJ5mJLCglDyxQggEtXTBAAAgRd0Slay6H4AAAAASUVORK5CYII="; + const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAAT5JREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIQf8R+OjmM1ErqGHz15CNvw/MhtNDgXs7YyAm9EfZk2UZQqCqv+RIwKfuxgYGBg+6+kx/D/NjyIGAyy4dEJjlxjwv6WunYGBgYGhpqmSGEfBPX3p7GOCamEeffHlCQMHK+f/H7+/400N/+3tGT49f84w3ZeT4f9pBgZG048o8hgeXiPNDmHwyDIwMDAwmDzG7qgbD54yzF67neHlxQsMDAwMDO8+vWEoKihm0LR1Ybjx4CmGO2CM14cQ5kliymN4hoSAxwDonkWxAEvexakW5siath4Gjh+/Gd59egMX5BKTZGipKkFX/x8pKaMAWCwXrjqK0z0vvjzB0IcjprGlMEacHBIBoeSLEUAkqqcJAAAmBnNJoDeWTQAAAABJRU5ErkJggg=="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; + const WARDROBE_ID = "birb-wardrobe"; const DEFAULT_BIRD = "bluebird"; @@ -1966,6 +2005,7 @@ const menuItems = [ new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem("Field Guide", insertFieldGuide), + new MenuItem("Wardrobe", insertWardrobe), new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), new DebugMenuItem("Freeze/Unfreeze", () => { @@ -2453,6 +2493,8 @@ if (document.querySelector("#" + FIELD_GUIDE_ID)) { return; } + // Remove wardrobe if open + removeWardrobe(); const contentContainer = document.createElement("div"); const content = makeElement("birb-grid-content"); @@ -2533,6 +2575,96 @@ } } + function insertWardrobe() { + console.log("Inserting wardrobe"); + if (document.querySelector("#" + WARDROBE_ID)) { + return; + } + // Remove field guide if open + removeFieldGuide(); + + const contentContainer = document.createElement("div"); + const content = makeElement("birb-grid-content"); + const description = makeElement("birb-field-guide-description"); + contentContainer.appendChild(content); + contentContainer.appendChild(description); + + const wardrobe = createWindow( + WARDROBE_ID, + "Wardrobe", + contentContainer + ); + + const generateDescription = (/** @type {string} */ hat) => { + const metadata = HAT_METADATA[hat] ?? { name: "Unknown Hat", description: "todo" }; + + const boldName = document.createElement("b"); + boldName.textContent = metadata.name; + + const spacer = document.createElement("div"); + spacer.style.height = "0.3em"; + + const descText = document.createTextNode(metadata.description); + + const fragment = document.createDocumentFragment(); + fragment.appendChild(boldName); + fragment.appendChild(spacer); + fragment.appendChild(descText); + + return fragment; + }; + + description.appendChild(generateDescription(currentHat)); + for (const hat of Object.values(HAT)) { + const hatElement = makeElement("birb-grid-item"); + if (hat === currentHat) { + hatElement.classList.add("birb-grid-item-selected"); + } + const hatCanvas = document.createElement("canvas"); + hatCanvas.width = SPRITE_WIDTH * CANVAS_PIXEL_SIZE; + hatCanvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + const hatCtx = hatCanvas.getContext("2d"); + if (!hatCtx) { + return; + } + console.log(hat); + birb.getFrames().base.draw( + hatCtx, + Directions.RIGHT, + CANVAS_PIXEL_SIZE, + SPECIES[currentSpecies].colors, + [...SPECIES[currentSpecies].tags, hat] + ); + hatElement.appendChild(hatCanvas); + content.appendChild(hatElement); + { + onClick(hatElement, () => { + switchHat(hat); + document.querySelectorAll(".birb-grid-item").forEach((element) => { + element.classList.remove("birb-grid-item-selected"); + }); + hatElement.classList.add("birb-grid-item-selected"); + }); + } + hatElement.addEventListener("mouseover", () => { + description.textContent = ""; + description.appendChild(generateDescription(hat)); + }); + hatElement.addEventListener("mouseout", () => { + description.textContent = ""; + description.appendChild(generateDescription(currentHat)); + }); + } + centerElement(wardrobe); + } + + function removeWardrobe() { + const wardrobe = document.querySelector("#" + WARDROBE_ID); + if (wardrobe) { + wardrobe.remove(); + } + } + /** * @param {string} type */ @@ -2543,6 +2675,14 @@ save(); } + /** + * @param {string} hat + */ + function switchHat(hat) { + currentHat = hat; + save(); + } + /** * Update the birds location from the start to the target location on a parabolic path * @param {number} speed The speed of the bird along the path diff --git a/dist/web/birb.embed.js b/dist/web/birb.embed.js index 294f737..72b9f6d 100644 --- a/dist/web/birb.embed.js +++ b/dist/web/birb.embed.js @@ -618,6 +618,33 @@ FEZ: "fez", }; + const HAT_METADATA = { + [HAT.NONE]: { + name: "Invisible Hat", + description: "It's like you're wearing nothing at all!" + }, + [HAT.TOP_HAT]: { + name: "Top Hat", + description: "For that classy, sophisticated look." + }, + [HAT.VIKING_HELMET]: { + name: "Viking Helmet", + description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?" + }, + [HAT.COWBOY_HAT]: { + name: "Cowboy Hat", + description: "You can't jam with the console cowboys without the appropriate attire." + }, + [HAT.BOWLER_HAT]: { + name: "Bowler Hat", + description: "For that authentic, Victorian look!" + }, + [HAT.FEZ]: { + name: "Fez", + description: "It's a fez. Fezzes are cool." + }, + }; + /** * @param {string[][]} spriteSheet * @returns {{ base: Layer[], down: Layer[] }} @@ -1737,9 +1764,20 @@ width: 322px !important; } +#birb-wardrobe { + width: calc(322px - 64px - 14px) !important; +} + +#birb-field-guide .birb-grid-content { + grid-template-rows: repeat(3, auto); +} + +#birb-wardrobe .birb-grid-content { + grid-template-rows: repeat(2, auto); +} + .birb-grid-content { display: grid; - grid-template-rows: repeat(3, auto); grid-auto-flow: column; gap: 10px; padding-top: 8px; @@ -1866,11 +1904,12 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD9JREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBbmA0Qs0uSuSiloYSBgIRhCPkCQFIKCYNBKK6szjZWpbEyTziLY5k6RAwsjpDGvRXbWubmd3btzd2c293vgyGRvb9/Z25ln39l/BxBCCCGEkOlC+a4ACR8REdd7Sim2IVJb2HhrgE8B6djtZhMA0Ol2B8pV1IEQMqVITCuKpBVFQ+UsORYVvxVF8nl+XmRtbahcdnxCymTGdwVIPu1mExuNBjrt9lC5SvY/fcJGo5GUCak7FGCN8CWgTreLJ/3+wLQn/X4yBCaEkFIwh8Cf5+eTV1VD4LQ6VBmbEBIAkkLVsX0KyKwD5UfIlCEiZwf/jb9Vx/ctIB/yJ6RMLviuQN3Yv3HDS1yllBIR8XnpCS93IWRK0ZmPzv6YBRFSf7hHHwNTesyGqsfe6XAbkP+FDYjUAi0/7TwRqVyAFPCUknYGlENA4gHZ6bYEgLcTQHHsoNs/++no5F4Ibe55zRdy7lEtEgqYAMBOt6WLXk4AKaWSOoSW/dn9wkc/rSOZZ4HNL9NofNDTRMScp5QGYQ99jOkQEQmtIZLyeNB873Vb+xTwKJhdYWW7l0yj/9w4BWiK53DlPvAI2L79Onl/p9seOB5ThoxCEDAhGt8CzkCUUon0zjtXZpV8+yOFbAvnQkREZi5GA9PuPevhw+oMll6eAgCOf34DALxbjwb2MkXIaEjAGBTwraU2HjTf63kLi0tIzRCX+L4e/cLB8+teThiVxZVZJUsvT/FhdQZFSDBTgIgFdP9VegqtBYhYgjBklBpsjI3gW8AkbFa2e/JuPZr27Zwrv1CH66HgHALrOw9c75vyg3XMIY1Jhsmnv3tDAtbys2Pbw3HXOo0am4TDye6izC3vKV0GgLllv/LzLeCV7Z7XA3uu+HEiVJt+llnRWFg42V3E2o+PAIAXl28DAO4evh0pwNejXwAwUSqu46dloLaANToTTVkWQAnWln/i26t8+6ULuPp6mLgEZPa3kkXkzD7rJMGRBWgzt7yHmw8Pce3qpdTPWhtiIgH5FjAhmlDEpznZXRSzD9j9rQIBiav/T4UAYUgwDVt8mCD78i1gQkKmv7Ugaxc6wODIp6r27RQgaiTBXAEiPq5nS+j4yzEAoLG57/rsvyATSse3gAkJnf7WQtLA73x/A5y1fe8SNE9MhtzvciuWJiEtvzQam/uFrbhvARNCchGdhNgi1BIMuf+N9DzAeCXQ31rInK9I+SHjTLQpYLtORdeBEJKJOnh+/azDOUQYMrkC1BLKk2CZ4tGxkSK8qupACHGicDb0HhDhucJ8Gkbn6ePkqRi6XOYDCqwbvVPjl10HQkg+9hNzQu+PY/0splIKnaePk//NMkrMuvRys+Iz8yMkDOKbEYAa9MexfhPEHIra5SrIix/6l03IeadufXDs6/KcC6pgxX3HJ4ScL/4CWsLSrzMo7i0AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; - const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAATxJREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIRX8xyKGYjYTuQYfPnsJ2YL/yGw0ORSwtzMCbkZ/mDVRlikIqv5Hjgh84LONDcNnPT2G/6f54fYgy7Pg0giNXWLA/5a6dgYGBgaGmqZKohwF8/Sls48JqoV59MWXJwwcrJz/f/z+jj81fPrE8O/HD4bpvpwM/08zMDCafkSRxvDwGml2CINHloGBgYHB5DF2R9148JRh9trtDC8vXmBgYGBgePfpDUNRQTGDpq0Lw40HT9GVwwPi9SGEeZKY8hieISHgMQC6Z1EswJJ3caqFObKmrYeB48dvhnef3sAFucQkGVqqStDV/0dKyigAFsuFq47idM+LL08w9OGIaYJ5mJLCglDyxQggEtXTBAAAgRd0Slay6H4AAAAASUVORK5CYII="; + const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAAT5JREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIQf8R+OjmM1ErqGHz15CNvw/MhtNDgXs7YyAm9EfZk2UZQqCqv+RIwKfuxgYGBg+6+kx/D/NjyIGAyy4dEJjlxjwv6WunYGBgYGhpqmSGEfBPX3p7GOCamEeffHlCQMHK+f/H7+/400N/+3tGT49f84w3ZeT4f9pBgZG048o8hgeXiPNDmHwyDIwMDAwmDzG7qgbD54yzF67neHlxQsMDAwMDO8+vWEoKihm0LR1Ybjx4CmGO2CM14cQ5kliymN4hoSAxwDonkWxAEvexakW5siath4Gjh+/Gd59egMX5BKTZGipKkFX/x8pKaMAWCwXrjqK0z0vvjzB0IcjprGlMEacHBIBoeSLEUAkqqcJAAAmBnNJoDeWTQAAAABJRU5ErkJggg=="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; + const WARDROBE_ID = "birb-wardrobe"; const DEFAULT_BIRD = "bluebird"; @@ -1946,6 +1985,7 @@ const menuItems = [ new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem("Field Guide", insertFieldGuide), + new MenuItem("Wardrobe", insertWardrobe), new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), new DebugMenuItem("Freeze/Unfreeze", () => { @@ -2433,6 +2473,8 @@ if (document.querySelector("#" + FIELD_GUIDE_ID)) { return; } + // Remove wardrobe if open + removeWardrobe(); const contentContainer = document.createElement("div"); const content = makeElement("birb-grid-content"); @@ -2513,6 +2555,96 @@ } } + function insertWardrobe() { + console.log("Inserting wardrobe"); + if (document.querySelector("#" + WARDROBE_ID)) { + return; + } + // Remove field guide if open + removeFieldGuide(); + + const contentContainer = document.createElement("div"); + const content = makeElement("birb-grid-content"); + const description = makeElement("birb-field-guide-description"); + contentContainer.appendChild(content); + contentContainer.appendChild(description); + + const wardrobe = createWindow( + WARDROBE_ID, + "Wardrobe", + contentContainer + ); + + const generateDescription = (/** @type {string} */ hat) => { + const metadata = HAT_METADATA[hat] ?? { name: "Unknown Hat", description: "todo" }; + + const boldName = document.createElement("b"); + boldName.textContent = metadata.name; + + const spacer = document.createElement("div"); + spacer.style.height = "0.3em"; + + const descText = document.createTextNode(metadata.description); + + const fragment = document.createDocumentFragment(); + fragment.appendChild(boldName); + fragment.appendChild(spacer); + fragment.appendChild(descText); + + return fragment; + }; + + description.appendChild(generateDescription(currentHat)); + for (const hat of Object.values(HAT)) { + const hatElement = makeElement("birb-grid-item"); + if (hat === currentHat) { + hatElement.classList.add("birb-grid-item-selected"); + } + const hatCanvas = document.createElement("canvas"); + hatCanvas.width = SPRITE_WIDTH * CANVAS_PIXEL_SIZE; + hatCanvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + const hatCtx = hatCanvas.getContext("2d"); + if (!hatCtx) { + return; + } + console.log(hat); + birb.getFrames().base.draw( + hatCtx, + Directions.RIGHT, + CANVAS_PIXEL_SIZE, + SPECIES[currentSpecies].colors, + [...SPECIES[currentSpecies].tags, hat] + ); + hatElement.appendChild(hatCanvas); + content.appendChild(hatElement); + { + onClick(hatElement, () => { + switchHat(hat); + document.querySelectorAll(".birb-grid-item").forEach((element) => { + element.classList.remove("birb-grid-item-selected"); + }); + hatElement.classList.add("birb-grid-item-selected"); + }); + } + hatElement.addEventListener("mouseover", () => { + description.textContent = ""; + description.appendChild(generateDescription(hat)); + }); + hatElement.addEventListener("mouseout", () => { + description.textContent = ""; + description.appendChild(generateDescription(currentHat)); + }); + } + centerElement(wardrobe); + } + + function removeWardrobe() { + const wardrobe = document.querySelector("#" + WARDROBE_ID); + if (wardrobe) { + wardrobe.remove(); + } + } + /** * @param {string} type */ @@ -2523,6 +2655,14 @@ save(); } + /** + * @param {string} hat + */ + function switchHat(hat) { + currentHat = hat; + save(); + } + /** * Update the birds location from the start to the target location on a parabolic path * @param {number} speed The speed of the bird along the path diff --git a/dist/web/birb.js b/dist/web/birb.js index 294f737..72b9f6d 100644 --- a/dist/web/birb.js +++ b/dist/web/birb.js @@ -618,6 +618,33 @@ FEZ: "fez", }; + const HAT_METADATA = { + [HAT.NONE]: { + name: "Invisible Hat", + description: "It's like you're wearing nothing at all!" + }, + [HAT.TOP_HAT]: { + name: "Top Hat", + description: "For that classy, sophisticated look." + }, + [HAT.VIKING_HELMET]: { + name: "Viking Helmet", + description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?" + }, + [HAT.COWBOY_HAT]: { + name: "Cowboy Hat", + description: "You can't jam with the console cowboys without the appropriate attire." + }, + [HAT.BOWLER_HAT]: { + name: "Bowler Hat", + description: "For that authentic, Victorian look!" + }, + [HAT.FEZ]: { + name: "Fez", + description: "It's a fez. Fezzes are cool." + }, + }; + /** * @param {string[][]} spriteSheet * @returns {{ base: Layer[], down: Layer[] }} @@ -1737,9 +1764,20 @@ width: 322px !important; } +#birb-wardrobe { + width: calc(322px - 64px - 14px) !important; +} + +#birb-field-guide .birb-grid-content { + grid-template-rows: repeat(3, auto); +} + +#birb-wardrobe .birb-grid-content { + grid-template-rows: repeat(2, auto); +} + .birb-grid-content { display: grid; - grid-template-rows: repeat(3, auto); grid-auto-flow: column; gap: 10px; padding-top: 8px; @@ -1866,11 +1904,12 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD9JREFUeJztnT9rFEEYh3+TWATE7hDcsxW7CBbmA0Qs0uSuSiloYSBgIRhCPkCQFIKCYNBKK6szjZWpbEyTziLY5k6RAwsjpDGvRXbWubmd3btzd2c293vgyGRvb9/Z25ln39l/BxBCCCGEkOlC+a4ACR8REdd7Sim2IVJb2HhrgE8B6djtZhMA0Ol2B8pV1IEQMqVITCuKpBVFQ+UsORYVvxVF8nl+XmRtbahcdnxCymTGdwVIPu1mExuNBjrt9lC5SvY/fcJGo5GUCak7FGCN8CWgTreLJ/3+wLQn/X4yBCaEkFIwh8Cf5+eTV1VD4LQ6VBmbEBIAkkLVsX0KyKwD5UfIlCEiZwf/jb9Vx/ctIB/yJ6RMLviuQN3Yv3HDS1yllBIR8XnpCS93IWRK0ZmPzv6YBRFSf7hHHwNTesyGqsfe6XAbkP+FDYjUAi0/7TwRqVyAFPCUknYGlENA4gHZ6bYEgLcTQHHsoNs/++no5F4Ibe55zRdy7lEtEgqYAMBOt6WLXk4AKaWSOoSW/dn9wkc/rSOZZ4HNL9NofNDTRMScp5QGYQ99jOkQEQmtIZLyeNB873Vb+xTwKJhdYWW7l0yj/9w4BWiK53DlPvAI2L79Onl/p9seOB5ThoxCEDAhGt8CzkCUUon0zjtXZpV8+yOFbAvnQkREZi5GA9PuPevhw+oMll6eAgCOf34DALxbjwb2MkXIaEjAGBTwraU2HjTf63kLi0tIzRCX+L4e/cLB8+teThiVxZVZJUsvT/FhdQZFSDBTgIgFdP9VegqtBYhYgjBklBpsjI3gW8AkbFa2e/JuPZr27Zwrv1CH66HgHALrOw9c75vyg3XMIY1Jhsmnv3tDAtbys2Pbw3HXOo0am4TDye6izC3vKV0GgLllv/LzLeCV7Z7XA3uu+HEiVJt+llnRWFg42V3E2o+PAIAXl28DAO4evh0pwNejXwAwUSqu46dloLaANToTTVkWQAnWln/i26t8+6ULuPp6mLgEZPa3kkXkzD7rJMGRBWgzt7yHmw8Pce3qpdTPWhtiIgH5FjAhmlDEpznZXRSzD9j9rQIBiav/T4UAYUgwDVt8mCD78i1gQkKmv7Ugaxc6wODIp6r27RQgaiTBXAEiPq5nS+j4yzEAoLG57/rsvyATSse3gAkJnf7WQtLA73x/A5y1fe8SNE9MhtzvciuWJiEtvzQam/uFrbhvARNCchGdhNgi1BIMuf+N9DzAeCXQ31rInK9I+SHjTLQpYLtORdeBEJKJOnh+/azDOUQYMrkC1BLKk2CZ4tGxkSK8qupACHGicDb0HhDhucJ8Gkbn6ePkqRi6XOYDCqwbvVPjl10HQkg+9hNzQu+PY/0splIKnaePk//NMkrMuvRys+Iz8yMkDOKbEYAa9MexfhPEHIra5SrIix/6l03IeadufXDs6/KcC6pgxX3HJ4ScL/4CWsLSrzMo7i0AAAAASUVORK5CYII="; const FEATHER_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAARhJREFUWIXtlbENwjAQRf8hSiZIRQ+9WQNRUFIAKzACBSsAA1Ag1mAABqCCBomG3hQQ9OMEx4ZDNH5SikSJ3/fZ5wCJRCKRSPwZ0RzMWmtLAhGvQyUAi9mXP/aFaGjJRQQiguHihMvcFMJUVUYlAMuHixPGy4en1WmVQqgHYHkuZjiEj6a2/LjtYzTY0eiZbgC37Mxh1UN3sn/dr6cCz/LHB/DJj9s+2oMdbtdz6TtfFwQHcMvOInfmQNjsgchNWLXmdfK6gyioAu/6uKrsm1kWLAciKuCuey5nYuXAh234bdmZ6INIUw4E/Ix49xtjCmXfzLL8nY/ktdgnAKwxxgIoXIyqmAOwvIqfiN0ALNd21HYBO9XXGMAdnZTYyHWzWjQAAAAASUVORK5CYII="; - const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAATxJREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIRX8xyKGYjYTuQYfPnsJ2YL/yGw0ORSwtzMCbkZ/mDVRlikIqv5Hjgh84LONDcNnPT2G/6f54fYgy7Pg0giNXWLA/5a6dgYGBgaGmqZKohwF8/Sls48JqoV59MWXJwwcrJz/f/z+jj81fPrE8O/HD4bpvpwM/08zMDCafkSRxvDwGml2CINHloGBgYHB5DF2R9148JRh9trtDC8vXmBgYGBgePfpDUNRQTGDpq0Lw40HT9GVwwPi9SGEeZKY8hieISHgMQC6Z1EswJJ3caqFObKmrYeB48dvhnef3sAFucQkGVqqStDV/0dKyigAFsuFq47idM+LL08w9OGIaYJ5mJLCglDyxQggEtXTBAAAgRd0Slay6H4AAAAASUVORK5CYII="; + const HATS_SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAAMCAYAAAA+ht7fAAAAAXNSR0IArs4c6QAAAT5JREFUSIljYBgFo2AUjIJRMHgBI7qAgqDqfxj7wfvbWNVAwX8y5P4jsRmR+LjMIQf8R+OjmM1ErqGHz15CNvw/MhtNDgXs7YyAm9EfZk2UZQqCqv+RIwKfuxgYGBg+6+kx/D/NjyIGAyy4dEJjlxjwv6WunYGBgYGhpqmSGEfBPX3p7GOCamEeffHlCQMHK+f/H7+/400N/+3tGT49f84w3ZeT4f9pBgZG048o8hgeXiPNDmHwyDIwMDAwmDzG7qgbD54yzF67neHlxQsMDAwMDO8+vWEoKihm0LR1Ybjx4CmGO2CM14cQ5kliymN4hoSAxwDonkWxAEvexakW5siath4Gjh+/Gd59egMX5BKTZGipKkFX/x8pKaMAWCwXrjqK0z0vvjzB0IcjprGlMEacHBIBoeSLEUAkqqcJAAAmBnNJoDeWTQAAAABJRU5ErkJggg=="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; + const WARDROBE_ID = "birb-wardrobe"; const DEFAULT_BIRD = "bluebird"; @@ -1946,6 +1985,7 @@ const menuItems = [ new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem("Field Guide", insertFieldGuide), + new MenuItem("Wardrobe", insertWardrobe), new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), new DebugMenuItem("Freeze/Unfreeze", () => { @@ -2433,6 +2473,8 @@ if (document.querySelector("#" + FIELD_GUIDE_ID)) { return; } + // Remove wardrobe if open + removeWardrobe(); const contentContainer = document.createElement("div"); const content = makeElement("birb-grid-content"); @@ -2513,6 +2555,96 @@ } } + function insertWardrobe() { + console.log("Inserting wardrobe"); + if (document.querySelector("#" + WARDROBE_ID)) { + return; + } + // Remove field guide if open + removeFieldGuide(); + + const contentContainer = document.createElement("div"); + const content = makeElement("birb-grid-content"); + const description = makeElement("birb-field-guide-description"); + contentContainer.appendChild(content); + contentContainer.appendChild(description); + + const wardrobe = createWindow( + WARDROBE_ID, + "Wardrobe", + contentContainer + ); + + const generateDescription = (/** @type {string} */ hat) => { + const metadata = HAT_METADATA[hat] ?? { name: "Unknown Hat", description: "todo" }; + + const boldName = document.createElement("b"); + boldName.textContent = metadata.name; + + const spacer = document.createElement("div"); + spacer.style.height = "0.3em"; + + const descText = document.createTextNode(metadata.description); + + const fragment = document.createDocumentFragment(); + fragment.appendChild(boldName); + fragment.appendChild(spacer); + fragment.appendChild(descText); + + return fragment; + }; + + description.appendChild(generateDescription(currentHat)); + for (const hat of Object.values(HAT)) { + const hatElement = makeElement("birb-grid-item"); + if (hat === currentHat) { + hatElement.classList.add("birb-grid-item-selected"); + } + const hatCanvas = document.createElement("canvas"); + hatCanvas.width = SPRITE_WIDTH * CANVAS_PIXEL_SIZE; + hatCanvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + const hatCtx = hatCanvas.getContext("2d"); + if (!hatCtx) { + return; + } + console.log(hat); + birb.getFrames().base.draw( + hatCtx, + Directions.RIGHT, + CANVAS_PIXEL_SIZE, + SPECIES[currentSpecies].colors, + [...SPECIES[currentSpecies].tags, hat] + ); + hatElement.appendChild(hatCanvas); + content.appendChild(hatElement); + { + onClick(hatElement, () => { + switchHat(hat); + document.querySelectorAll(".birb-grid-item").forEach((element) => { + element.classList.remove("birb-grid-item-selected"); + }); + hatElement.classList.add("birb-grid-item-selected"); + }); + } + hatElement.addEventListener("mouseover", () => { + description.textContent = ""; + description.appendChild(generateDescription(hat)); + }); + hatElement.addEventListener("mouseout", () => { + description.textContent = ""; + description.appendChild(generateDescription(currentHat)); + }); + } + centerElement(wardrobe); + } + + function removeWardrobe() { + const wardrobe = document.querySelector("#" + WARDROBE_ID); + if (wardrobe) { + wardrobe.remove(); + } + } + /** * @param {string} type */ @@ -2523,6 +2655,14 @@ save(); } + /** + * @param {string} hat + */ + function switchHat(hat) { + currentHat = hat; + save(); + } + /** * Update the birds location from the start to the target location on a parabolic path * @param {number} speed The speed of the bird along the path diff --git a/sprites/hats.png b/sprites/hats.png index e815000..0c4f52b 100644 Binary files a/sprites/hats.png and b/sprites/hats.png differ diff --git a/src/application.js b/src/application.js index 5b4ad56..0081034 100644 --- a/src/application.js +++ b/src/application.js @@ -43,7 +43,7 @@ import { switchMenuItems, MENU_EXIT_ID } from './menu.js'; -import { HAT } from './hats.js'; +import { HAT, HAT_METADATA } from './hats.js'; /** @@ -84,6 +84,7 @@ const HATS_SPRITE_SHEET = "__HATS_SPRITE_SHEET__"; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; const FEATHER_ID = "birb-feather"; +const WARDROBE_ID = "birb-wardrobe"; const DEFAULT_BIRD = "bluebird"; @@ -159,6 +160,7 @@ function startApplication(birbPixels, featherPixels, hatsPixels) { const menuItems = [ new MenuItem(`Pet ${birdBirb()}`, pet), new MenuItem("Field Guide", insertFieldGuide), + new MenuItem("Wardrobe", insertWardrobe), new ConditionalMenuItem("Sticky Note", () => createNewStickyNote(stickyNotes, save, deleteStickyNote), () => getContext().areStickyNotesEnabled()), new MenuItem(`Hide ${birdBirb()}`, () => birb.setVisible(false)), new DebugMenuItem("Freeze/Unfreeze", () => { @@ -650,6 +652,8 @@ function startApplication(birbPixels, featherPixels, hatsPixels) { if (document.querySelector("#" + FIELD_GUIDE_ID)) { return; } + // Remove wardrobe if open + removeWardrobe(); const contentContainer = document.createElement("div"); const content = makeElement("birb-grid-content"); @@ -730,6 +734,100 @@ function startApplication(birbPixels, featherPixels, hatsPixels) { } } + function insertWardrobe() { + console.log("Inserting wardrobe"); + if (document.querySelector("#" + WARDROBE_ID)) { + return; + } + // Remove field guide if open + removeFieldGuide(); + + const contentContainer = document.createElement("div"); + const content = makeElement("birb-grid-content"); + const description = makeElement("birb-field-guide-description"); + contentContainer.appendChild(content); + contentContainer.appendChild(description); + + const wardrobe = createWindow( + WARDROBE_ID, + "Wardrobe", + contentContainer + ); + + const generateDescription = (/** @type {string} */ hat) => { + const metadata = HAT_METADATA[hat] ?? { name: "Unknown Hat", description: "todo" }; + const unlocked = true; + + const boldName = document.createElement("b"); + boldName.textContent = metadata.name; + + const spacer = document.createElement("div"); + spacer.style.height = "0.3em"; + + const descText = document.createTextNode(!unlocked ? "Not yet unlocked" : metadata.description); + + const fragment = document.createDocumentFragment(); + fragment.appendChild(boldName); + fragment.appendChild(spacer); + fragment.appendChild(descText); + + return fragment; + }; + + description.appendChild(generateDescription(currentHat)); + for (const hat of Object.values(HAT)) { + const unlocked = true; + const hatElement = makeElement("birb-grid-item"); + if (hat === currentHat) { + hatElement.classList.add("birb-grid-item-selected"); + } + const hatCanvas = document.createElement("canvas"); + hatCanvas.width = SPRITE_WIDTH * CANVAS_PIXEL_SIZE; + hatCanvas.height = SPRITE_HEIGHT * CANVAS_PIXEL_SIZE; + const hatCtx = hatCanvas.getContext("2d"); + if (!hatCtx) { + return; + } + console.log(hat); + birb.getFrames().base.draw( + hatCtx, + Directions.RIGHT, + CANVAS_PIXEL_SIZE, + SPECIES[currentSpecies].colors, + [...SPECIES[currentSpecies].tags, hat] + ); + hatElement.appendChild(hatCanvas); + content.appendChild(hatElement); + if (unlocked) { + onClick(hatElement, () => { + switchHat(hat); + document.querySelectorAll(".birb-grid-item").forEach((element) => { + element.classList.remove("birb-grid-item-selected"); + }); + hatElement.classList.add("birb-grid-item-selected"); + }); + } else { + hatElement.classList.add("birb-grid-item-locked"); + } + hatElement.addEventListener("mouseover", () => { + description.textContent = ""; + description.appendChild(generateDescription(hat)); + }); + hatElement.addEventListener("mouseout", () => { + description.textContent = ""; + description.appendChild(generateDescription(currentHat)); + }); + } + centerElement(wardrobe); + } + + function removeWardrobe() { + const wardrobe = document.querySelector("#" + WARDROBE_ID); + if (wardrobe) { + wardrobe.remove(); + } + } + /** * @param {string} type */ @@ -740,6 +838,14 @@ function startApplication(birbPixels, featherPixels, hatsPixels) { save(); } + /** + * @param {string} hat + */ + function switchHat(hat) { + currentHat = hat; + save(); + } + /** * Update the birds location from the start to the target location on a parabolic path * @param {number} speed The speed of the bird along the path diff --git a/src/hats.js b/src/hats.js index a088b69..e15bbdc 100644 --- a/src/hats.js +++ b/src/hats.js @@ -13,6 +13,33 @@ export const HAT = { FEZ: "fez", }; +export const HAT_METADATA = { + [HAT.NONE]: { + name: "Invisible Hat", + description: "It's like you're wearing nothing at all!" + }, + [HAT.TOP_HAT]: { + name: "Top Hat", + description: "For that classy, sophisticated look." + }, + [HAT.VIKING_HELMET]: { + name: "Viking Helmet", + description: "Sure, vikings never actually wore this style of helmet, but why let facts get in the way of good fashion?" + }, + [HAT.COWBOY_HAT]: { + name: "Cowboy Hat", + description: "You can't jam with the console cowboys without the appropriate attire." + }, + [HAT.BOWLER_HAT]: { + name: "Bowler Hat", + description: "For that authentic, Victorian look!" + }, + [HAT.FEZ]: { + name: "Fez", + description: "It's a fez. Fezzes are cool." + }, +}; + /** * @param {string[][]} spriteSheet * @returns {{ base: Layer[], down: Layer[] }} diff --git a/src/stylesheet.css b/src/stylesheet.css index 917c0b8..39e2690 100644 --- a/src/stylesheet.css +++ b/src/stylesheet.css @@ -242,9 +242,20 @@ width: 322px !important; } +#birb-wardrobe { + width: calc(322px - 64px - 14px) !important; +} + +#birb-field-guide .birb-grid-content { + grid-template-rows: repeat(3, auto); +} + +#birb-wardrobe .birb-grid-content { + grid-template-rows: repeat(2, auto); +} + .birb-grid-content { display: grid; - grid-template-rows: repeat(3, auto); grid-auto-flow: column; gap: 10px; padding-top: 8px;