From 8263fadfba5749acf19a65073bee91e31327dd68 Mon Sep 17 00:00:00 2001 From: Idrees Hassan Date: Sun, 18 Jan 2026 19:04:40 -0500 Subject: [PATCH] Add hat placeholder --- aseprite/hats.aseprite | Bin 0 -> 370 bytes build.js | 4 ++ dist/extension.zip | Bin 149653 -> 150422 bytes dist/extension/birb.js | 98 +++++++++++++++++++++++++++++++---- dist/obsidian/main.js | 98 +++++++++++++++++++++++++++++++---- dist/userscript/birb.user.js | 98 +++++++++++++++++++++++++++++++---- dist/web/birb.embed.js | 98 +++++++++++++++++++++++++++++++---- dist/web/birb.js | 98 +++++++++++++++++++++++++++++++---- sprites/hats.png | Bin 0 -> 116 bytes src/application.js | 15 ++++-- src/birb.js | 85 +++++++++++++++++++++++++++--- 11 files changed, 533 insertions(+), 61 deletions(-) create mode 100644 aseprite/hats.aseprite create mode 100644 sprites/hats.png diff --git a/aseprite/hats.aseprite b/aseprite/hats.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..0ddd6b090a528ba0e5251a931909eded59489377 GIT binary patch literal 370 zcmXS7WMFu(l#zi42o)HB9EJ%X0uIy|7{Po21_8JjH5s3PhW+};0=AU}$QA=)b_EnC z07Ytmm<8lIH6TMt!NgU=Ce6~YKg{}mq1w#_Y3~lt`u)jl=F@xg literal 0 HcmV?d00001 diff --git a/build.js b/build.js index 49ed684..f5faec5 100644 --- a/build.js +++ b/build.js @@ -46,6 +46,10 @@ const spriteSheets = [ { key: "__FEATHER_SPRITE_SHEET__", path: SPRITES_DIR + "/feather.png" + }, + { + key: "__HATS_SPRITE_SHEET__", + path: SPRITES_DIR + "/hats.png" } ]; diff --git a/dist/extension.zip b/dist/extension.zip index e463bc2dba15f6c4aa427b33ddfaf05814a3c39c..9a98b9237be991e35e13a1c1000ba846e8ccae1b 100644 GIT binary patch delta 28005 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�}K`eHt0`L#Len@X%A|AB{>0x>uY*<>b4}(vL<2x0vXh`^a41v@-~RK^$AA zk}u|(qP_ByHQ#AY1%+~PgHeHLO`nK7-6&?z7R{fq5<1{(y2-dgGL+BPW`m3FFoR;~ zx3f)ATXI}D(~qY1jlaZL3Iv+GKNSAn`?4c~hk4b*h~p$e$EYM?LNY01~ zf&Gf11~1~)$TcqQVwO33Kd3qVmR}q()X4{nl^lx}y|iG#MOaRk6~ETA?do^i$;jih z~V|g*NXcIBWXgbw#H7)=cv>-+W0&xu#%Us;E3wcj`(HLDN{iX!S zLJWTkc`%|SGZ;45x3k7xuxHBXMfvH>+@Zs+@h}-Hrm`3(&i&L2P$3oN)*$`O$Gf`c zUUdm`X2EG3r3$$a&d$Ow{mXZY(dkn2o=UFg(oVs-YUs3cpaJgE$ynWfj~Ey9;!k$1 z^;uUdv?r27ugv6zoG_&oa@9a2s)kP4MB-qilNm|19PmU*nWL_bqnE%y}4DD>4w6}akCyHTBM(!^*k=YB17pKTF& zE1cr!pFSu4y7C>=y>@dxB=X-1O}@RCvyls&Pn_?? z(8X4&y2x5ZMr~t7S2$jzUiD-DnT1za;`M+dKz1p4Ao;lqercqR<&>t@BK17uokl5A zc^jCpTYDZlEHOxAz_7euw@U2WV+psIhyAkrg_JEeOUu}?!oDAFTP@(_4nEg>F>j$} zYq*UqtU-U5Sn<*hA66|_;9Ci5wWdW?X?mK6;VtkV=TR#qpf)gr<)4WWsJKn z?H!1IntdO8f7yenD;jtBEAkS^0aOL@SGXPq$j2MNtnjU{$=hEj)?s8AT5J8y7h|KTu> zhB@r@9?}n%Sgl1K<-X${Jg5i5+9s=MkXEzuD+Lbf z(4zIU#kb*!6}}nPc$tl?{~S05IwJs^FyV9##a1s-){7#Gl2}q$#Ee&q!$Kx#ce!WE z){H547L^v;qd(J^!@m0GB#PaEL^`-6`Q*G%sxVsJ+QVo=!6|AbdmUN(jA33X2R`4p zlZqh3r;%6aYRKxeNV?!Ee1chnHzjhg_%_=f{;QdjaN($W)QDT2Uo83@P&QW_Hq29T zoaT^%m}~TnwYY|&hq=ieNU>QeS_3A*)k_!?OCHlBj;z5W{vy6%f@Ezg>Jp6VTk_Qw z-vdOPKKd=DXpTJWBa!@7@@vc_i3C`fbwExeVbsiJP_N002bPm>Cdj9ce42c999=qu zktRJK_zLmQ@DS6Pyw{FW!dVK=GptOKi^e0igr;$Z(T+xIx`^J`>DUrRqwS7zJ(|6} zdxzL9S73WHol40wkfz|gUTFjd$EU>1Jbo|Lu|!;e42+e!um^R~u3S~2#N4seK*`nvRm zWz?>>wDo2%t3w!nQX0xAw2T&9Hx<%e;w})zXIHB*LFGI17a=?h;1O_~Qy~Aq{i~GB z65CpT-2tAg$uu;(lHHd@10Puty+NUy_14w#(}Cv8>=`jVR-`o=SGQf)Thvd(FD>(1 zUx&gfB8z#jq=lmuJFzko?AzlhgRrTn%}0i&b>24yC+AT`tJb8rXjok)M?uts%T<9* zDZlnV7cy^*0trvwf=OOuv30+~#n;oK${{Iu!_m+krEpRRat&5G3?B$ym~Re~ow2@m zY+cg}e(OE_VVP|6vu8GDD0ukY8kV1cJF1L z-qJgKbLt_?YE<&09;5bJwQl-y6^j)76IpMAknj1Ai3(tU4hU>S@czz(Gl+A*wf{|a zPdNCS#M?WC$8Q;F%Bh4MY5O%7V)Zw|1KtX$K{CN5f#si21)+Asje1GDMvQc9Ta>tx zvw2a{wBIsb-h$3tA@RMAIKUg}t3msTA;%MMkyUJXi|=rx?oMF8^-pF0=(yh2z>3SkO5O2p6P5X)(FZZ&X0YOd38CqU}d`OZ_Yp$jZ zSU^Z_I!pV>Wa*Q?nFhItquZSVd2k(dA4djh5kfFrQdTvdPU_9t>wH|%DqGYPB_cZX z3rXA|&{o0X(i}rX_S@Qt(=O~c%E%6O35@u_pF!AS;qn9<9q6tP$8Eo6u2FydtZaj4 zvUNp=5Ke3_!h`K6wpq|0in;e*1uszw@F3SxZ#W`x2?#41)AznU81o$xLNImx97wI9 z(i3m=)uG#$F^e=u-&b%TXC?npt~jiP>F5Hu(j{Y+@~TgY;`2R~GdBzssps55vl*o% zIEE2Q9Vm(-0(zozSFWdR&wL3`~)+DgcgCG}csEaksEBW68NMFq{wy2_PJ#2O0Ph+S>vn|KbnjS+M-)8zdeX z$Ovhd0HQ$Y`d~7Ee|-Ie!9yPvA!~K(yaWf(iAXfRX>#S}{VN1&H+t z!0~@gh#i^&54`!K3Voi7!@nY9g0f%$|07UHv>Xrx!iVv^%qiGFrhoAmAp+@Bfy6*dJIJgkPz?B33Lz8&asjEzASzAgjH>@(SuFo%Q9{h5fLMR1+0{!VkY`%r z|4`qr{qAe}*MFvI{}1)$ z9V9>o$ocO@?0EdE`@B#7rWjfFIixqR5GT3kXn*e>h@$NO&$GdA(L1ESqBwXU3da8_ zFi6#JMljmH81RsGWFXxC)FHgQ4eH%bHvC|ENjg z{Z`{d{9Sx1>0>64vFE%1p+tX;J@41Q>vCK#%2-Ga04zTjKg)mBB_jD>D!V+8;?HU$ gGUluf!2kg7pM9bI7ZpJa;wTR!hewlmu5Q5p1BpoO<^TWy delta 27147 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 X+!gx&Neu*1h`S1q0s&1N_{{$YXAjKF diff --git a/dist/extension/birb.js b/dist/extension/birb.js index 1b60e97..423a1ee 100644 --- a/dist/extension/birb.js +++ b/dist/extension/birb.js @@ -632,8 +632,9 @@ * @param {string[][]} spriteSheet The loaded sprite sheet pixel data * @param {number} spriteWidth * @param {number} spriteHeight + * @param {string[][]} hatSpriteSheet The loaded hat sprite sheet pixel data */ - constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight) { + constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight, hatSpriteSheet) { this.birbCssScale = birbCssScale; this.canvasPixelSize = canvasPixelSize; this.windowPixelSize = canvasPixelSize * birbCssScale; @@ -654,12 +655,16 @@ happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)), }; + // Build hat layers + const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); + const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + // Build frames from layers this.frames = { - base: new Frame([this.layers.base, this.layers.tuftBase]), - headDown: new Frame([this.layers.down, this.layers.tuftDown]), - wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown]), - wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp]), + base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, downHatLayer]), heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartOne]), heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartThree]), @@ -730,6 +735,74 @@ return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species); } + buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) { + const LEFT_PADDING = 6; + const RIGHT_PADDING = 14; + const TOP_PADDING = 4 + yOffset; + const BOTTOM_PADDING = Math.max(0, 16 - yOffset); + + const hatPixels = getLayerPixels(spriteSheet, 0, 12); + const paddedHatPixels = []; + + // Top padding + for (let y = 0; y < TOP_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + // Left and right padding + for (let y = 0; y < hatPixels.length; y++) { + const row = []; + for (let x = 0; x < LEFT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + for (let x = 0; x < hatPixels[y].length; x++) { + row.push(hatPixels[y][x]); + } + + for (let x = 0; x < RIGHT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + paddedHatPixels.push(row); + } + // Bottom padding + for (let y = 0; y < BOTTOM_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + + // Add outline + let neighborOffsets = [ + [-1, 0], + [1, 0], + [0, -1], + [-1, -1], + [1, -1], + ]; + if (outlineBottom) { + neighborOffsets.push([0, 1], [-1, 1], [1, 1]); + } + for (let y = 0; y < paddedHatPixels.length; y++) { + for (let x = 0; x < paddedHatPixels[y].length; x++) { + const pixel = paddedHatPixels[y][x]; + if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) { + for (let [dx, dy] of neighborOffsets) { + const newX = x + dx; + const newY = y + dy; + if (newY >= 0 && newY < paddedHatPixels.length && newX >= 0 && newX < paddedHatPixels[newY].length && paddedHatPixels[newY][newX] === PALETTE.TRANSPARENT) { + paddedHatPixels[newY][newX] = PALETTE.BORDER; + } + } + } + } + } + return new Layer(paddedHatPixels); + } + + /** * @returns {AnimationType} The current animation key */ @@ -1763,6 +1836,7 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD5JREFUeJztnTFrFEEYht9JLAJidwju2YpdBAvzAyIWaXJXpRS0MBCwEBTJDwghhaAgGLTSyupMY2UqG9PYWQRb7yJyYJEIacxnkZ11bm5n9+7Y3Zm9ex8Imezd7Te7O9+zM7N7G4AQQgghhBBCCJkJlO8KkPAREXG9ppRiGyK1hY23BvgUkI7dbjYBAJ1ud6BcRR0IITOKxLSiSFpRNFTOkmNR8VtRJF8WF0U2NobKZccnpEzmfFeA5NNuNvG00UCn3R4qV8nB58942mgkZULqDgVYI3wJqNPtYrvfH1i23e8nQ2BCCCkFcwj8ZXEx+alqCJxWhypjE0ICQFKoOrZPAZl1oPwImTFE5Hzy3/hddXzfAvIhf0LK5ILvCtSNgxs3vMRVSikREZ+3nvB2F0JmFN3z0b0/9oKqx9cUBJleeEYfAzPp2BuqFr3v9W4XkcqPgS1dtoEZIe0CAM/AxAOy220JAG/zn3HsoNs/83R0cu8DNM+85g9yvqJVJBQwAYDdbksXvcx/KqWSOoTW+7Pzwkee1pHMiyDmzjQaH/QyETHfU0qDsIc+xnKIiITWEEl5PGh+8HqsfQp4FMxUWNvpJcvoPzdOAZriOVy7DzwCdm6/SV7f7bYH5mPKkFEIAiZE41vAGYhSKpHetHNlXsnRXynkWDhXIiIydzEaWHbveQ8f1+ew8uoMAHDy+wgA8P5JNHCWKUJGQwLGoIBvrbTxoPlBv7ewuITUDHGJ7/uPY3x9cd3LBaOyuDKvZOXVGT6uz6EICWYKELGA7r9O70JrASKWIAwZpQYb4yD4FjAJm7Wdnrx/Es36cc6VX6jD9VBwDoH1jbeu1035wZpzSGOSYfLZn96QgLX87Nj2cNy1TaPGJuFwurcsC6v7SpcBYGHVr/x8C3htp+d1Ys8VP+4I1SbPMisaCwune8vY+PUJAPDy8m0AwN3DdyMF+P7jGAAm6orr+Gk9UFvAGt0TTVkXQAnWlv/i26/8+KULuPp6mLgEZOZbySJy9j7rJMGRBWizsLqPmw8Pce3qpdTPWgdiIgH5FjAhmlDEpzndWxYzB+x8q0BA4sr/mRAgDAmmYYsPE/S+fAuYkJDpby3JxoUOMDjyqap9OwWIGkkwV4CI5/VsCZ18OwEANDYPXJ/9H2RC6fgWMCGh099aShr4nZ9vgfO2712C5oXJkPMut2JpEtLyS6OxeVDYhvsWMCEkF9GdEFuEWoIh599Ij8OKNwL9raXM9xUpP2RciTYFbNep6DoQQjJRX19cP084hwhDJleAWkJ5EixTPDo2UoRXVR0IIU4UzofeAyKcKsynYXSePU6eiqHLZT6gwPqid2r8sutACMnHfmJO6Pk41n+FU0qh8+xx8rdZRom9Lr3erPjs+RESBvGXEYAa5ONYj8Q3h6J2uQry4oe+swmZduqWg2Pfl+dcUQUb7js+IWS6+Ac8zd6eLzTjoQAAAABJRU5ErkJggg=="; 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,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAC5JREFUKJFjYBgFgwEwYhH7j08NE6k2sKALnJCVReFbPH6M0zp0p6ADRgYGBgYAqu4FCZWdtIcAAAAASUVORK5CYII="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; @@ -1808,17 +1882,20 @@ log("Loading sprite sheets..."); const birbPixels = await loadSpriteSheetPixels(SPRITE_SHEET); const featherPixels = await loadSpriteSheetPixels(FEATHER_SPRITE_SHEET); - startApplication(birbPixels, featherPixels); + const hatsPixels = await loadSpriteSheetPixels(HATS_SPRITE_SHEET); + startApplication(birbPixels, featherPixels, hatsPixels); } /** * @param {string[][]} birbPixels * @param {string[][]} featherPixels + * @param {string[][]} hatsPixels */ - function startApplication(birbPixels, featherPixels) { + function startApplication(birbPixels, featherPixels, hatsPixels) { const SPRITE_SHEET = birbPixels; const FEATHER_SPRITE_SHEET = featherPixels; + const HATS_SPRITE_SHEET = hatsPixels; const featherLayers = { feather: new Layer(getLayerPixels(FEATHER_SPRITE_SHEET, 0, FEATHER_SPRITE_WIDTH)), @@ -2006,7 +2083,7 @@ styleElement.textContent = STYLESHEET; document.head.appendChild(styleElement); - birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT); + birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT, HATS_SPRITE_SHEET); birb.setAnimation(Animations.BOB); window.addEventListener("scroll", () => { @@ -2688,8 +2765,9 @@ continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - error(`Unknown color: ${hex}`); - row.push(PALETTE.TRANSPARENT); + // Return the color as-is if not found in the map + row.push(hex); + continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } diff --git a/dist/obsidian/main.js b/dist/obsidian/main.js index e847f07..f02c753 100644 --- a/dist/obsidian/main.js +++ b/dist/obsidian/main.js @@ -637,8 +637,9 @@ module.exports = class PocketBird extends Plugin { * @param {string[][]} spriteSheet The loaded sprite sheet pixel data * @param {number} spriteWidth * @param {number} spriteHeight + * @param {string[][]} hatSpriteSheet The loaded hat sprite sheet pixel data */ - constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight) { + constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight, hatSpriteSheet) { this.birbCssScale = birbCssScale; this.canvasPixelSize = canvasPixelSize; this.windowPixelSize = canvasPixelSize * birbCssScale; @@ -659,12 +660,16 @@ module.exports = class PocketBird extends Plugin { happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)), }; + // Build hat layers + const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); + const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + // Build frames from layers this.frames = { - base: new Frame([this.layers.base, this.layers.tuftBase]), - headDown: new Frame([this.layers.down, this.layers.tuftDown]), - wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown]), - wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp]), + base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, downHatLayer]), heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartOne]), heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartThree]), @@ -735,6 +740,74 @@ module.exports = class PocketBird extends Plugin { return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species); } + buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) { + const LEFT_PADDING = 6; + const RIGHT_PADDING = 14; + const TOP_PADDING = 4 + yOffset; + const BOTTOM_PADDING = Math.max(0, 16 - yOffset); + + const hatPixels = getLayerPixels(spriteSheet, 0, 12); + const paddedHatPixels = []; + + // Top padding + for (let y = 0; y < TOP_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + // Left and right padding + for (let y = 0; y < hatPixels.length; y++) { + const row = []; + for (let x = 0; x < LEFT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + for (let x = 0; x < hatPixels[y].length; x++) { + row.push(hatPixels[y][x]); + } + + for (let x = 0; x < RIGHT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + paddedHatPixels.push(row); + } + // Bottom padding + for (let y = 0; y < BOTTOM_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + + // Add outline + let neighborOffsets = [ + [-1, 0], + [1, 0], + [0, -1], + [-1, -1], + [1, -1], + ]; + if (outlineBottom) { + neighborOffsets.push([0, 1], [-1, 1], [1, 1]); + } + for (let y = 0; y < paddedHatPixels.length; y++) { + for (let x = 0; x < paddedHatPixels[y].length; x++) { + const pixel = paddedHatPixels[y][x]; + if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) { + for (let [dx, dy] of neighborOffsets) { + const newX = x + dx; + const newY = y + dy; + if (newY >= 0 && newY < paddedHatPixels.length && newX >= 0 && newX < paddedHatPixels[newY].length && paddedHatPixels[newY][newX] === PALETTE.TRANSPARENT) { + paddedHatPixels[newY][newX] = PALETTE.BORDER; + } + } + } + } + } + return new Layer(paddedHatPixels); + } + + /** * @returns {AnimationType} The current animation key */ @@ -1806,6 +1879,7 @@ module.exports = class PocketBird extends Plugin { }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD5JREFUeJztnTFrFEEYht9JLAJidwju2YpdBAvzAyIWaXJXpRS0MBCwEBTJDwghhaAgGLTSyupMY2UqG9PYWQRb7yJyYJEIacxnkZ11bm5n9+7Y3Zm9ex8Imezd7Te7O9+zM7N7G4AQQgghhBBCCJkJlO8KkPAREXG9ppRiGyK1hY23BvgUkI7dbjYBAJ1ud6BcRR0IITOKxLSiSFpRNFTOkmNR8VtRJF8WF0U2NobKZccnpEzmfFeA5NNuNvG00UCn3R4qV8nB58942mgkZULqDgVYI3wJqNPtYrvfH1i23e8nQ2BCCCkFcwj8ZXEx+alqCJxWhypjE0ICQFKoOrZPAZl1oPwImTFE5Hzy3/hddXzfAvIhf0LK5ILvCtSNgxs3vMRVSikREZ+3nvB2F0JmFN3z0b0/9oKqx9cUBJleeEYfAzPp2BuqFr3v9W4XkcqPgS1dtoEZIe0CAM/AxAOy220JAG/zn3HsoNs/83R0cu8DNM+85g9yvqJVJBQwAYDdbksXvcx/KqWSOoTW+7Pzwkee1pHMiyDmzjQaH/QyETHfU0qDsIc+xnKIiITWEEl5PGh+8HqsfQp4FMxUWNvpJcvoPzdOAZriOVy7DzwCdm6/SV7f7bYH5mPKkFEIAiZE41vAGYhSKpHetHNlXsnRXynkWDhXIiIydzEaWHbveQ8f1+ew8uoMAHDy+wgA8P5JNHCWKUJGQwLGoIBvrbTxoPlBv7ewuITUDHGJ7/uPY3x9cd3LBaOyuDKvZOXVGT6uz6EICWYKELGA7r9O70JrASKWIAwZpQYb4yD4FjAJm7Wdnrx/Es36cc6VX6jD9VBwDoH1jbeu1035wZpzSGOSYfLZn96QgLX87Nj2cNy1TaPGJuFwurcsC6v7SpcBYGHVr/x8C3htp+d1Ys8VP+4I1SbPMisaCwune8vY+PUJAPDy8m0AwN3DdyMF+P7jGAAm6orr+Gk9UFvAGt0TTVkXQAnWlv/i26/8+KULuPp6mLgEZOZbySJy9j7rJMGRBWizsLqPmw8Pce3qpdTPWgdiIgH5FjAhmlDEpzndWxYzB+x8q0BA4sr/mRAgDAmmYYsPE/S+fAuYkJDpby3JxoUOMDjyqap9OwWIGkkwV4CI5/VsCZ18OwEANDYPXJ/9H2RC6fgWMCGh099aShr4nZ9vgfO2712C5oXJkPMut2JpEtLyS6OxeVDYhvsWMCEkF9GdEFuEWoIh599Ij8OKNwL9raXM9xUpP2RciTYFbNep6DoQQjJRX19cP084hwhDJleAWkJ5EixTPDo2UoRXVR0IIU4UzofeAyKcKsynYXSePU6eiqHLZT6gwPqid2r8sutACMnHfmJO6Pk41n+FU0qh8+xx8rdZRom9Lr3erPjs+RESBvGXEYAa5ONYj8Q3h6J2uQry4oe+swmZduqWg2Pfl+dcUQUb7js+IWS6+Ac8zd6eLzTjoQAAAABJRU5ErkJggg=="; 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,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAC5JREFUKJFjYBgFgwEwYhH7j08NE6k2sKALnJCVReFbPH6M0zp0p6ADRgYGBgYAqu4FCZWdtIcAAAAASUVORK5CYII="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; @@ -1851,17 +1925,20 @@ module.exports = class PocketBird extends Plugin { log("Loading sprite sheets..."); const birbPixels = await loadSpriteSheetPixels(SPRITE_SHEET); const featherPixels = await loadSpriteSheetPixels(FEATHER_SPRITE_SHEET); - startApplication(birbPixels, featherPixels); + const hatsPixels = await loadSpriteSheetPixels(HATS_SPRITE_SHEET); + startApplication(birbPixels, featherPixels, hatsPixels); } /** * @param {string[][]} birbPixels * @param {string[][]} featherPixels + * @param {string[][]} hatsPixels */ - function startApplication(birbPixels, featherPixels) { + function startApplication(birbPixels, featherPixels, hatsPixels) { const SPRITE_SHEET = birbPixels; const FEATHER_SPRITE_SHEET = featherPixels; + const HATS_SPRITE_SHEET = hatsPixels; const featherLayers = { feather: new Layer(getLayerPixels(FEATHER_SPRITE_SHEET, 0, FEATHER_SPRITE_WIDTH)), @@ -2049,7 +2126,7 @@ module.exports = class PocketBird extends Plugin { styleElement.textContent = STYLESHEET; document.head.appendChild(styleElement); - birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT); + birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT, HATS_SPRITE_SHEET); birb.setAnimation(Animations.BOB); window.addEventListener("scroll", () => { @@ -2731,8 +2808,9 @@ module.exports = class PocketBird extends Plugin { continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - error(`Unknown color: ${hex}`); - row.push(PALETTE.TRANSPARENT); + // Return the color as-is if not found in the map + row.push(hex); + continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } diff --git a/dist/userscript/birb.user.js b/dist/userscript/birb.user.js index c794adb..f2c01f1 100644 --- a/dist/userscript/birb.user.js +++ b/dist/userscript/birb.user.js @@ -646,8 +646,9 @@ * @param {string[][]} spriteSheet The loaded sprite sheet pixel data * @param {number} spriteWidth * @param {number} spriteHeight + * @param {string[][]} hatSpriteSheet The loaded hat sprite sheet pixel data */ - constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight) { + constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight, hatSpriteSheet) { this.birbCssScale = birbCssScale; this.canvasPixelSize = canvasPixelSize; this.windowPixelSize = canvasPixelSize * birbCssScale; @@ -668,12 +669,16 @@ happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)), }; + // Build hat layers + const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); + const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + // Build frames from layers this.frames = { - base: new Frame([this.layers.base, this.layers.tuftBase]), - headDown: new Frame([this.layers.down, this.layers.tuftDown]), - wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown]), - wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp]), + base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, downHatLayer]), heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartOne]), heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartThree]), @@ -744,6 +749,74 @@ return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species); } + buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) { + const LEFT_PADDING = 6; + const RIGHT_PADDING = 14; + const TOP_PADDING = 4 + yOffset; + const BOTTOM_PADDING = Math.max(0, 16 - yOffset); + + const hatPixels = getLayerPixels(spriteSheet, 0, 12); + const paddedHatPixels = []; + + // Top padding + for (let y = 0; y < TOP_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + // Left and right padding + for (let y = 0; y < hatPixels.length; y++) { + const row = []; + for (let x = 0; x < LEFT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + for (let x = 0; x < hatPixels[y].length; x++) { + row.push(hatPixels[y][x]); + } + + for (let x = 0; x < RIGHT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + paddedHatPixels.push(row); + } + // Bottom padding + for (let y = 0; y < BOTTOM_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + + // Add outline + let neighborOffsets = [ + [-1, 0], + [1, 0], + [0, -1], + [-1, -1], + [1, -1], + ]; + if (outlineBottom) { + neighborOffsets.push([0, 1], [-1, 1], [1, 1]); + } + for (let y = 0; y < paddedHatPixels.length; y++) { + for (let x = 0; x < paddedHatPixels[y].length; x++) { + const pixel = paddedHatPixels[y][x]; + if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) { + for (let [dx, dy] of neighborOffsets) { + const newX = x + dx; + const newY = y + dy; + if (newY >= 0 && newY < paddedHatPixels.length && newX >= 0 && newX < paddedHatPixels[newY].length && paddedHatPixels[newY][newX] === PALETTE.TRANSPARENT) { + paddedHatPixels[newY][newX] = PALETTE.BORDER; + } + } + } + } + } + return new Layer(paddedHatPixels); + } + + /** * @returns {AnimationType} The current animation key */ @@ -1768,6 +1841,7 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD5JREFUeJztnTFrFEEYht9JLAJidwju2YpdBAvzAyIWaXJXpRS0MBCwEBTJDwghhaAgGLTSyupMY2UqG9PYWQRb7yJyYJEIacxnkZ11bm5n9+7Y3Zm9ex8Imezd7Te7O9+zM7N7G4AQQgghhBBCCJkJlO8KkPAREXG9ppRiGyK1hY23BvgUkI7dbjYBAJ1ud6BcRR0IITOKxLSiSFpRNFTOkmNR8VtRJF8WF0U2NobKZccnpEzmfFeA5NNuNvG00UCn3R4qV8nB58942mgkZULqDgVYI3wJqNPtYrvfH1i23e8nQ2BCCCkFcwj8ZXEx+alqCJxWhypjE0ICQFKoOrZPAZl1oPwImTFE5Hzy3/hddXzfAvIhf0LK5ILvCtSNgxs3vMRVSikREZ+3nvB2F0JmFN3z0b0/9oKqx9cUBJleeEYfAzPp2BuqFr3v9W4XkcqPgS1dtoEZIe0CAM/AxAOy220JAG/zn3HsoNs/83R0cu8DNM+85g9yvqJVJBQwAYDdbksXvcx/KqWSOoTW+7Pzwkee1pHMiyDmzjQaH/QyETHfU0qDsIc+xnKIiITWEEl5PGh+8HqsfQp4FMxUWNvpJcvoPzdOAZriOVy7DzwCdm6/SV7f7bYH5mPKkFEIAiZE41vAGYhSKpHetHNlXsnRXynkWDhXIiIydzEaWHbveQ8f1+ew8uoMAHDy+wgA8P5JNHCWKUJGQwLGoIBvrbTxoPlBv7ewuITUDHGJ7/uPY3x9cd3LBaOyuDKvZOXVGT6uz6EICWYKELGA7r9O70JrASKWIAwZpQYb4yD4FjAJm7Wdnrx/Es36cc6VX6jD9VBwDoH1jbeu1035wZpzSGOSYfLZn96QgLX87Nj2cNy1TaPGJuFwurcsC6v7SpcBYGHVr/x8C3htp+d1Ys8VP+4I1SbPMisaCwune8vY+PUJAPDy8m0AwN3DdyMF+P7jGAAm6orr+Gk9UFvAGt0TTVkXQAnWlv/i26/8+KULuPp6mLgEZOZbySJy9j7rJMGRBWizsLqPmw8Pce3qpdTPWgdiIgH5FjAhmlDEpzndWxYzB+x8q0BA4sr/mRAgDAmmYYsPE/S+fAuYkJDpby3JxoUOMDjyqap9OwWIGkkwV4CI5/VsCZ18OwEANDYPXJ/9H2RC6fgWMCGh099aShr4nZ9vgfO2712C5oXJkPMut2JpEtLyS6OxeVDYhvsWMCEkF9GdEFuEWoIh599Ij8OKNwL9raXM9xUpP2RciTYFbNep6DoQQjJRX19cP084hwhDJleAWkJ5EixTPDo2UoRXVR0IIU4UzofeAyKcKsynYXSePU6eiqHLZT6gwPqid2r8sutACMnHfmJO6Pk41n+FU0qh8+xx8rdZRom9Lr3erPjs+RESBvGXEYAa5ONYj8Q3h6J2uQry4oe+swmZduqWg2Pfl+dcUQUb7js+IWS6+Ac8zd6eLzTjoQAAAABJRU5ErkJggg=="; 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,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAC5JREFUKJFjYBgFgwEwYhH7j08NE6k2sKALnJCVReFbPH6M0zp0p6ADRgYGBgYAqu4FCZWdtIcAAAAASUVORK5CYII="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; @@ -1813,17 +1887,20 @@ log("Loading sprite sheets..."); const birbPixels = await loadSpriteSheetPixels(SPRITE_SHEET); const featherPixels = await loadSpriteSheetPixels(FEATHER_SPRITE_SHEET); - startApplication(birbPixels, featherPixels); + const hatsPixels = await loadSpriteSheetPixels(HATS_SPRITE_SHEET); + startApplication(birbPixels, featherPixels, hatsPixels); } /** * @param {string[][]} birbPixels * @param {string[][]} featherPixels + * @param {string[][]} hatsPixels */ - function startApplication(birbPixels, featherPixels) { + function startApplication(birbPixels, featherPixels, hatsPixels) { const SPRITE_SHEET = birbPixels; const FEATHER_SPRITE_SHEET = featherPixels; + const HATS_SPRITE_SHEET = hatsPixels; const featherLayers = { feather: new Layer(getLayerPixels(FEATHER_SPRITE_SHEET, 0, FEATHER_SPRITE_WIDTH)), @@ -2011,7 +2088,7 @@ styleElement.textContent = STYLESHEET; document.head.appendChild(styleElement); - birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT); + birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT, HATS_SPRITE_SHEET); birb.setAnimation(Animations.BOB); window.addEventListener("scroll", () => { @@ -2693,8 +2770,9 @@ continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - error(`Unknown color: ${hex}`); - row.push(PALETTE.TRANSPARENT); + // Return the color as-is if not found in the map + row.push(hex); + continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } diff --git a/dist/web/birb.embed.js b/dist/web/birb.embed.js index 0d3bcb5..2f4af0c 100644 --- a/dist/web/birb.embed.js +++ b/dist/web/birb.embed.js @@ -632,8 +632,9 @@ * @param {string[][]} spriteSheet The loaded sprite sheet pixel data * @param {number} spriteWidth * @param {number} spriteHeight + * @param {string[][]} hatSpriteSheet The loaded hat sprite sheet pixel data */ - constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight) { + constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight, hatSpriteSheet) { this.birbCssScale = birbCssScale; this.canvasPixelSize = canvasPixelSize; this.windowPixelSize = canvasPixelSize * birbCssScale; @@ -654,12 +655,16 @@ happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)), }; + // Build hat layers + const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); + const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + // Build frames from layers this.frames = { - base: new Frame([this.layers.base, this.layers.tuftBase]), - headDown: new Frame([this.layers.down, this.layers.tuftDown]), - wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown]), - wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp]), + base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, downHatLayer]), heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartOne]), heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartThree]), @@ -730,6 +735,74 @@ return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species); } + buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) { + const LEFT_PADDING = 6; + const RIGHT_PADDING = 14; + const TOP_PADDING = 4 + yOffset; + const BOTTOM_PADDING = Math.max(0, 16 - yOffset); + + const hatPixels = getLayerPixels(spriteSheet, 0, 12); + const paddedHatPixels = []; + + // Top padding + for (let y = 0; y < TOP_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + // Left and right padding + for (let y = 0; y < hatPixels.length; y++) { + const row = []; + for (let x = 0; x < LEFT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + for (let x = 0; x < hatPixels[y].length; x++) { + row.push(hatPixels[y][x]); + } + + for (let x = 0; x < RIGHT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + paddedHatPixels.push(row); + } + // Bottom padding + for (let y = 0; y < BOTTOM_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + + // Add outline + let neighborOffsets = [ + [-1, 0], + [1, 0], + [0, -1], + [-1, -1], + [1, -1], + ]; + if (outlineBottom) { + neighborOffsets.push([0, 1], [-1, 1], [1, 1]); + } + for (let y = 0; y < paddedHatPixels.length; y++) { + for (let x = 0; x < paddedHatPixels[y].length; x++) { + const pixel = paddedHatPixels[y][x]; + if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) { + for (let [dx, dy] of neighborOffsets) { + const newX = x + dx; + const newY = y + dy; + if (newY >= 0 && newY < paddedHatPixels.length && newX >= 0 && newX < paddedHatPixels[newY].length && paddedHatPixels[newY][newX] === PALETTE.TRANSPARENT) { + paddedHatPixels[newY][newX] = PALETTE.BORDER; + } + } + } + } + } + return new Layer(paddedHatPixels); + } + + /** * @returns {AnimationType} The current animation key */ @@ -1748,6 +1821,7 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD5JREFUeJztnTFrFEEYht9JLAJidwju2YpdBAvzAyIWaXJXpRS0MBCwEBTJDwghhaAgGLTSyupMY2UqG9PYWQRb7yJyYJEIacxnkZ11bm5n9+7Y3Zm9ex8Imezd7Te7O9+zM7N7G4AQQgghhBBCCJkJlO8KkPAREXG9ppRiGyK1hY23BvgUkI7dbjYBAJ1ud6BcRR0IITOKxLSiSFpRNFTOkmNR8VtRJF8WF0U2NobKZccnpEzmfFeA5NNuNvG00UCn3R4qV8nB58942mgkZULqDgVYI3wJqNPtYrvfH1i23e8nQ2BCCCkFcwj8ZXEx+alqCJxWhypjE0ICQFKoOrZPAZl1oPwImTFE5Hzy3/hddXzfAvIhf0LK5ILvCtSNgxs3vMRVSikREZ+3nvB2F0JmFN3z0b0/9oKqx9cUBJleeEYfAzPp2BuqFr3v9W4XkcqPgS1dtoEZIe0CAM/AxAOy220JAG/zn3HsoNs/83R0cu8DNM+85g9yvqJVJBQwAYDdbksXvcx/KqWSOoTW+7Pzwkee1pHMiyDmzjQaH/QyETHfU0qDsIc+xnKIiITWEEl5PGh+8HqsfQp4FMxUWNvpJcvoPzdOAZriOVy7DzwCdm6/SV7f7bYH5mPKkFEIAiZE41vAGYhSKpHetHNlXsnRXynkWDhXIiIydzEaWHbveQ8f1+ew8uoMAHDy+wgA8P5JNHCWKUJGQwLGoIBvrbTxoPlBv7ewuITUDHGJ7/uPY3x9cd3LBaOyuDKvZOXVGT6uz6EICWYKELGA7r9O70JrASKWIAwZpQYb4yD4FjAJm7Wdnrx/Es36cc6VX6jD9VBwDoH1jbeu1035wZpzSGOSYfLZn96QgLX87Nj2cNy1TaPGJuFwurcsC6v7SpcBYGHVr/x8C3htp+d1Ys8VP+4I1SbPMisaCwune8vY+PUJAPDy8m0AwN3DdyMF+P7jGAAm6orr+Gk9UFvAGt0TTVkXQAnWlv/i26/8+KULuPp6mLgEZOZbySJy9j7rJMGRBWizsLqPmw8Pce3qpdTPWgdiIgH5FjAhmlDEpzndWxYzB+x8q0BA4sr/mRAgDAmmYYsPE/S+fAuYkJDpby3JxoUOMDjyqap9OwWIGkkwV4CI5/VsCZ18OwEANDYPXJ/9H2RC6fgWMCGh099aShr4nZ9vgfO2712C5oXJkPMut2JpEtLyS6OxeVDYhvsWMCEkF9GdEFuEWoIh599Ij8OKNwL9raXM9xUpP2RciTYFbNep6DoQQjJRX19cP084hwhDJleAWkJ5EixTPDo2UoRXVR0IIU4UzofeAyKcKsynYXSePU6eiqHLZT6gwPqid2r8sutACMnHfmJO6Pk41n+FU0qh8+xx8rdZRom9Lr3erPjs+RESBvGXEYAa5ONYj8Q3h6J2uQry4oe+swmZduqWg2Pfl+dcUQUb7js+IWS6+Ac8zd6eLzTjoQAAAABJRU5ErkJggg=="; 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,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAC5JREFUKJFjYBgFgwEwYhH7j08NE6k2sKALnJCVReFbPH6M0zp0p6ADRgYGBgYAqu4FCZWdtIcAAAAASUVORK5CYII="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; @@ -1793,17 +1867,20 @@ log("Loading sprite sheets..."); const birbPixels = await loadSpriteSheetPixels(SPRITE_SHEET); const featherPixels = await loadSpriteSheetPixels(FEATHER_SPRITE_SHEET); - startApplication(birbPixels, featherPixels); + const hatsPixels = await loadSpriteSheetPixels(HATS_SPRITE_SHEET); + startApplication(birbPixels, featherPixels, hatsPixels); } /** * @param {string[][]} birbPixels * @param {string[][]} featherPixels + * @param {string[][]} hatsPixels */ - function startApplication(birbPixels, featherPixels) { + function startApplication(birbPixels, featherPixels, hatsPixels) { const SPRITE_SHEET = birbPixels; const FEATHER_SPRITE_SHEET = featherPixels; + const HATS_SPRITE_SHEET = hatsPixels; const featherLayers = { feather: new Layer(getLayerPixels(FEATHER_SPRITE_SHEET, 0, FEATHER_SPRITE_WIDTH)), @@ -1991,7 +2068,7 @@ styleElement.textContent = STYLESHEET; document.head.appendChild(styleElement); - birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT); + birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT, HATS_SPRITE_SHEET); birb.setAnimation(Animations.BOB); window.addEventListener("scroll", () => { @@ -2673,8 +2750,9 @@ continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - error(`Unknown color: ${hex}`); - row.push(PALETTE.TRANSPARENT); + // Return the color as-is if not found in the map + row.push(hex); + continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } diff --git a/dist/web/birb.js b/dist/web/birb.js index 0d3bcb5..2f4af0c 100644 --- a/dist/web/birb.js +++ b/dist/web/birb.js @@ -632,8 +632,9 @@ * @param {string[][]} spriteSheet The loaded sprite sheet pixel data * @param {number} spriteWidth * @param {number} spriteHeight + * @param {string[][]} hatSpriteSheet The loaded hat sprite sheet pixel data */ - constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight) { + constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight, hatSpriteSheet) { this.birbCssScale = birbCssScale; this.canvasPixelSize = canvasPixelSize; this.windowPixelSize = canvasPixelSize * birbCssScale; @@ -654,12 +655,16 @@ happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)), }; + // Build hat layers + const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); + const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + // Build frames from layers this.frames = { - base: new Frame([this.layers.base, this.layers.tuftBase]), - headDown: new Frame([this.layers.down, this.layers.tuftDown]), - wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown]), - wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp]), + base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, downHatLayer]), heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartOne]), heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartThree]), @@ -730,6 +735,74 @@ return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species); } + buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) { + const LEFT_PADDING = 6; + const RIGHT_PADDING = 14; + const TOP_PADDING = 4 + yOffset; + const BOTTOM_PADDING = Math.max(0, 16 - yOffset); + + const hatPixels = getLayerPixels(spriteSheet, 0, 12); + const paddedHatPixels = []; + + // Top padding + for (let y = 0; y < TOP_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + // Left and right padding + for (let y = 0; y < hatPixels.length; y++) { + const row = []; + for (let x = 0; x < LEFT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + for (let x = 0; x < hatPixels[y].length; x++) { + row.push(hatPixels[y][x]); + } + + for (let x = 0; x < RIGHT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + paddedHatPixels.push(row); + } + // Bottom padding + for (let y = 0; y < BOTTOM_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + + // Add outline + let neighborOffsets = [ + [-1, 0], + [1, 0], + [0, -1], + [-1, -1], + [1, -1], + ]; + if (outlineBottom) { + neighborOffsets.push([0, 1], [-1, 1], [1, 1]); + } + for (let y = 0; y < paddedHatPixels.length; y++) { + for (let x = 0; x < paddedHatPixels[y].length; x++) { + const pixel = paddedHatPixels[y][x]; + if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) { + for (let [dx, dy] of neighborOffsets) { + const newX = x + dx; + const newY = y + dy; + if (newY >= 0 && newY < paddedHatPixels.length && newX >= 0 && newX < paddedHatPixels[newY].length && paddedHatPixels[newY][newX] === PALETTE.TRANSPARENT) { + paddedHatPixels[newY][newX] = PALETTE.BORDER; + } + } + } + } + } + return new Layer(paddedHatPixels); + } + + /** * @returns {AnimationType} The current animation key */ @@ -1748,6 +1821,7 @@ }`; const SPRITE_SHEET = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAgCAYAAABjE6FEAAAAAXNSR0IArs4c6QAABD5JREFUeJztnTFrFEEYht9JLAJidwju2YpdBAvzAyIWaXJXpRS0MBCwEBTJDwghhaAgGLTSyupMY2UqG9PYWQRb7yJyYJEIacxnkZ11bm5n9+7Y3Zm9ex8Imezd7Te7O9+zM7N7G4AQQgghhBBCCJkJlO8KkPAREXG9ppRiGyK1hY23BvgUkI7dbjYBAJ1ud6BcRR0IITOKxLSiSFpRNFTOkmNR8VtRJF8WF0U2NobKZccnpEzmfFeA5NNuNvG00UCn3R4qV8nB58942mgkZULqDgVYI3wJqNPtYrvfH1i23e8nQ2BCCCkFcwj8ZXEx+alqCJxWhypjE0ICQFKoOrZPAZl1oPwImTFE5Hzy3/hddXzfAvIhf0LK5ILvCtSNgxs3vMRVSikREZ+3nvB2F0JmFN3z0b0/9oKqx9cUBJleeEYfAzPp2BuqFr3v9W4XkcqPgS1dtoEZIe0CAM/AxAOy220JAG/zn3HsoNs/83R0cu8DNM+85g9yvqJVJBQwAYDdbksXvcx/KqWSOoTW+7Pzwkee1pHMiyDmzjQaH/QyETHfU0qDsIc+xnKIiITWEEl5PGh+8HqsfQp4FMxUWNvpJcvoPzdOAZriOVy7DzwCdm6/SV7f7bYH5mPKkFEIAiZE41vAGYhSKpHetHNlXsnRXynkWDhXIiIydzEaWHbveQ8f1+ew8uoMAHDy+wgA8P5JNHCWKUJGQwLGoIBvrbTxoPlBv7ewuITUDHGJ7/uPY3x9cd3LBaOyuDKvZOXVGT6uz6EICWYKELGA7r9O70JrASKWIAwZpQYb4yD4FjAJm7Wdnrx/Es36cc6VX6jD9VBwDoH1jbeu1035wZpzSGOSYfLZn96QgLX87Nj2cNy1TaPGJuFwurcsC6v7SpcBYGHVr/x8C3htp+d1Ys8VP+4I1SbPMisaCwune8vY+PUJAPDy8m0AwN3DdyMF+P7jGAAm6orr+Gk9UFvAGt0TTVkXQAnWlv/i26/8+KULuPp6mLgEZOZbySJy9j7rJMGRBWizsLqPmw8Pce3qpdTPWgdiIgH5FjAhmlDEpzndWxYzB+x8q0BA4sr/mRAgDAmmYYsPE/S+fAuYkJDpby3JxoUOMDjyqap9OwWIGkkwV4CI5/VsCZ18OwEANDYPXJ/9H2RC6fgWMCGh099aShr4nZ9vgfO2712C5oXJkPMut2JpEtLyS6OxeVDYhvsWMCEkF9GdEFuEWoIh599Ij8OKNwL9raXM9xUpP2RciTYFbNep6DoQQjJRX19cP084hwhDJleAWkJ5EixTPDo2UoRXVR0IIU4UzofeAyKcKsynYXSePU6eiqHLZT6gwPqid2r8sutACMnHfmJO6Pk41n+FU0qh8+xx8rdZRom9Lr3erPjs+RESBvGXEYAa5ONYj8Q3h6J2uQry4oe+swmZduqWg2Pfl+dcUQUb7js+IWS6+Ac8zd6eLzTjoQAAAABJRU5ErkJggg=="; 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,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAAXNSR0IArs4c6QAAAC5JREFUKJFjYBgFgwEwYhH7j08NE6k2sKALnJCVReFbPH6M0zp0p6ADRgYGBgYAqu4FCZWdtIcAAAAASUVORK5CYII="; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; @@ -1793,17 +1867,20 @@ log("Loading sprite sheets..."); const birbPixels = await loadSpriteSheetPixels(SPRITE_SHEET); const featherPixels = await loadSpriteSheetPixels(FEATHER_SPRITE_SHEET); - startApplication(birbPixels, featherPixels); + const hatsPixels = await loadSpriteSheetPixels(HATS_SPRITE_SHEET); + startApplication(birbPixels, featherPixels, hatsPixels); } /** * @param {string[][]} birbPixels * @param {string[][]} featherPixels + * @param {string[][]} hatsPixels */ - function startApplication(birbPixels, featherPixels) { + function startApplication(birbPixels, featherPixels, hatsPixels) { const SPRITE_SHEET = birbPixels; const FEATHER_SPRITE_SHEET = featherPixels; + const HATS_SPRITE_SHEET = hatsPixels; const featherLayers = { feather: new Layer(getLayerPixels(FEATHER_SPRITE_SHEET, 0, FEATHER_SPRITE_WIDTH)), @@ -1991,7 +2068,7 @@ styleElement.textContent = STYLESHEET; document.head.appendChild(styleElement); - birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT); + birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT, HATS_SPRITE_SHEET); birb.setAnimation(Animations.BOB); window.addEventListener("scroll", () => { @@ -2673,8 +2750,9 @@ continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - error(`Unknown color: ${hex}`); - row.push(PALETTE.TRANSPARENT); + // Return the color as-is if not found in the map + row.push(hex); + continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } diff --git a/sprites/hats.png b/sprites/hats.png new file mode 100644 index 0000000000000000000000000000000000000000..1840ac978e4704bd9919e5740079d216da97d2b2 GIT binary patch literal 116 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&k#^NA%Cx&(BWL^R}dY&$hAsQ2t z6C_xh84Z#IfA{QVuz-8cgsH9%qiyPXE?bo>U%>3f1_TVN-m!8{ox7zSsFT6d L)z4*}Q$iB}AZ;Jp literal 0 HcmV?d00001 diff --git a/src/application.js b/src/application.js index 42806a0..713e79b 100644 --- a/src/application.js +++ b/src/application.js @@ -78,6 +78,7 @@ const WINDOW_PIXEL_SIZE = CANVAS_PIXEL_SIZE * BIRB_CSS_SCALE; const STYLESHEET = `___STYLESHEET___`; const SPRITE_SHEET = "__SPRITE_SHEET__"; const FEATHER_SPRITE_SHEET = "__FEATHER_SPRITE_SHEET__"; +const HATS_SPRITE_SHEET = "__HATS_SPRITE_SHEET__"; // Element IDs const FIELD_GUIDE_ID = "birb-field-guide"; @@ -123,17 +124,20 @@ export async function initializeApplication(context) { log("Loading sprite sheets..."); const birbPixels = await loadSpriteSheetPixels(SPRITE_SHEET); const featherPixels = await loadSpriteSheetPixels(FEATHER_SPRITE_SHEET); - startApplication(birbPixels, featherPixels); + const hatsPixels = await loadSpriteSheetPixels(HATS_SPRITE_SHEET); + startApplication(birbPixels, featherPixels, hatsPixels); } /** * @param {string[][]} birbPixels * @param {string[][]} featherPixels + * @param {string[][]} hatsPixels */ -function startApplication(birbPixels, featherPixels) { +function startApplication(birbPixels, featherPixels, hatsPixels) { const SPRITE_SHEET = birbPixels; const FEATHER_SPRITE_SHEET = featherPixels; + const HATS_SPRITE_SHEET = hatsPixels; const featherLayers = { feather: new Layer(getLayerPixels(FEATHER_SPRITE_SHEET, 0, FEATHER_SPRITE_WIDTH)), @@ -321,7 +325,7 @@ function startApplication(birbPixels, featherPixels) { styleElement.textContent = STYLESHEET; document.head.appendChild(styleElement); - birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT); + birb = new Birb(BIRB_CSS_SCALE, CANVAS_PIXEL_SIZE, SPRITE_SHEET, SPRITE_WIDTH, SPRITE_HEIGHT, HATS_SPRITE_SHEET); birb.setAnimation(Animations.BOB); window.addEventListener("scroll", () => { @@ -1021,8 +1025,9 @@ function loadSpriteSheetPixels(dataUri, templateColors = true) { continue; } if (SPRITE_SHEET_COLOR_MAP[hex] === undefined) { - error(`Unknown color: ${hex}`); - row.push(PALETTE.TRANSPARENT); + // Return the color as-is if not found in the map + row.push(hex); + continue; } row.push(SPRITE_SHEET_COLOR_MAP[hex]); } diff --git a/src/birb.js b/src/birb.js index bf12dff..55f56de 100644 --- a/src/birb.js +++ b/src/birb.js @@ -2,7 +2,7 @@ import { Directions, getLayerPixels, getWindowHeight, getFixedWindowHeight } fro import Layer from './animation/layer.js'; import Frame from './animation/frame.js'; import Anim from './animation/anim.js'; -import { BirdType } from './animation/sprites.js'; +import { BirdType, PALETTE } from './animation/sprites.js'; /** * @typedef {keyof typeof Animations} AnimationType @@ -31,8 +31,9 @@ export class Birb { * @param {string[][]} spriteSheet The loaded sprite sheet pixel data * @param {number} spriteWidth * @param {number} spriteHeight + * @param {string[][]} hatSpriteSheet The loaded hat sprite sheet pixel data */ - constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight) { + constructor(birbCssScale, canvasPixelSize, spriteSheet, spriteWidth, spriteHeight, hatSpriteSheet) { this.birbCssScale = birbCssScale; this.canvasPixelSize = canvasPixelSize; this.windowPixelSize = canvasPixelSize * birbCssScale; @@ -53,12 +54,16 @@ export class Birb { happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)), }; + // Build hat layers + const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); + const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + // Build frames from layers this.frames = { - base: new Frame([this.layers.base, this.layers.tuftBase]), - headDown: new Frame([this.layers.down, this.layers.tuftDown]), - wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown]), - wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp]), + base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, downHatLayer]), heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartOne]), heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartThree]), @@ -129,6 +134,74 @@ export class Birb { return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species); } + buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) { + const LEFT_PADDING = 6; + const RIGHT_PADDING = 14; + const TOP_PADDING = 4 + yOffset; + const BOTTOM_PADDING = Math.max(0, 16 - yOffset); + + const hatPixels = getLayerPixels(spriteSheet, 0, 12); + const paddedHatPixels = []; + + // Top padding + for (let y = 0; y < TOP_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + // Left and right padding + for (let y = 0; y < hatPixels.length; y++) { + const row = []; + for (let x = 0; x < LEFT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + for (let x = 0; x < hatPixels[y].length; x++) { + row.push(hatPixels[y][x]); + } + + for (let x = 0; x < RIGHT_PADDING; x++) { + row.push(PALETTE.TRANSPARENT); + } + + paddedHatPixels.push(row); + } + // Bottom padding + for (let y = 0; y < BOTTOM_PADDING; y++) { + paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING) + .fill(PALETTE.TRANSPARENT) + ); + } + + // Add outline + let neighborOffsets = [ + [-1, 0], + [1, 0], + [0, -1], + [-1, -1], + [1, -1], + ]; + if (outlineBottom) { + neighborOffsets.push([0, 1], [-1, 1], [1, 1]); + } + for (let y = 0; y < paddedHatPixels.length; y++) { + for (let x = 0; x < paddedHatPixels[y].length; x++) { + const pixel = paddedHatPixels[y][x]; + if (pixel !== PALETTE.TRANSPARENT && pixel !== PALETTE.BORDER) { + for (let [dx, dy] of neighborOffsets) { + const newX = x + dx; + const newY = y + dy; + if (newY >= 0 && newY < paddedHatPixels.length && newX >= 0 && newX < paddedHatPixels[newY].length && paddedHatPixels[newY][newX] === PALETTE.TRANSPARENT) { + paddedHatPixels[newY][newX] = PALETTE.BORDER; + } + } + } + } + } + return new Layer(paddedHatPixels); + } + + /** * @returns {AnimationType} The current animation key */