diff --git a/dist/extension.zip b/dist/extension.zip index 9a98b92..21abee3 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 423a1ee..ee81ae3 100644 --- a/dist/extension/birb.js +++ b/dist/extension/birb.js @@ -605,6 +605,103 @@ } } + const HAT = { + TOP_HAT: 'top-hat' + }; + + /** + * @param {string[][]} spriteSheet + * @returns {{ base: Layer[], down: Layer[] }} + */ + function createHatLayers(spriteSheet) { + const hatLayers = { + base: [], + down: [] + }; + for (const hatName in HAT) { + const hatKey = HAT[hatName]; + const hatLayer = buildHatLayer(spriteSheet, hatKey, false); + const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1); + hatLayers.base.push(hatLayer); + hatLayers.down.push(downHatLayer); + } + return hatLayers; + } + + /** + * @param {string[][]} spriteSheet + * @param {string} hatName + * @param {boolean} [outlineBottom=false] + * @param {number} [yOffset=0] + * @returns {Layer} + */ + function 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); + } + /** * @typedef {keyof typeof Animations} AnimationType */ @@ -656,19 +753,18 @@ }; // Build hat layers - const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); - const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + const hatLayers = createHatLayers(hatSpriteSheet); // Build frames from layers this.frames = { - 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]), - heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), + base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, ...hatLayers.base]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, ...hatLayers.down]), + heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartOne]), + heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base,this.layers.heartTwo]), + heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartThree]), + heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]), }; // Build animations from frames @@ -735,73 +831,6 @@ 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 diff --git a/dist/obsidian/main.js b/dist/obsidian/main.js index f02c753..ac84a97 100644 --- a/dist/obsidian/main.js +++ b/dist/obsidian/main.js @@ -610,6 +610,103 @@ module.exports = class PocketBird extends Plugin { } } + const HAT = { + TOP_HAT: 'top-hat' + }; + + /** + * @param {string[][]} spriteSheet + * @returns {{ base: Layer[], down: Layer[] }} + */ + function createHatLayers(spriteSheet) { + const hatLayers = { + base: [], + down: [] + }; + for (const hatName in HAT) { + const hatKey = HAT[hatName]; + const hatLayer = buildHatLayer(spriteSheet, hatKey, false); + const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1); + hatLayers.base.push(hatLayer); + hatLayers.down.push(downHatLayer); + } + return hatLayers; + } + + /** + * @param {string[][]} spriteSheet + * @param {string} hatName + * @param {boolean} [outlineBottom=false] + * @param {number} [yOffset=0] + * @returns {Layer} + */ + function 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); + } + /** * @typedef {keyof typeof Animations} AnimationType */ @@ -661,19 +758,18 @@ module.exports = class PocketBird extends Plugin { }; // Build hat layers - const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); - const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + const hatLayers = createHatLayers(hatSpriteSheet); // Build frames from layers this.frames = { - 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]), - heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), + base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, ...hatLayers.base]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, ...hatLayers.down]), + heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartOne]), + heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base,this.layers.heartTwo]), + heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartThree]), + heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]), }; // Build animations from frames @@ -740,73 +836,6 @@ 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 diff --git a/dist/userscript/birb.user.js b/dist/userscript/birb.user.js index f2c01f1..33cce0c 100644 --- a/dist/userscript/birb.user.js +++ b/dist/userscript/birb.user.js @@ -619,6 +619,103 @@ } } + const HAT = { + TOP_HAT: 'top-hat' + }; + + /** + * @param {string[][]} spriteSheet + * @returns {{ base: Layer[], down: Layer[] }} + */ + function createHatLayers(spriteSheet) { + const hatLayers = { + base: [], + down: [] + }; + for (const hatName in HAT) { + const hatKey = HAT[hatName]; + const hatLayer = buildHatLayer(spriteSheet, hatKey, false); + const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1); + hatLayers.base.push(hatLayer); + hatLayers.down.push(downHatLayer); + } + return hatLayers; + } + + /** + * @param {string[][]} spriteSheet + * @param {string} hatName + * @param {boolean} [outlineBottom=false] + * @param {number} [yOffset=0] + * @returns {Layer} + */ + function 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); + } + /** * @typedef {keyof typeof Animations} AnimationType */ @@ -670,19 +767,18 @@ }; // Build hat layers - const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); - const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + const hatLayers = createHatLayers(hatSpriteSheet); // Build frames from layers this.frames = { - 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]), - heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), + base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, ...hatLayers.base]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, ...hatLayers.down]), + heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartOne]), + heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base,this.layers.heartTwo]), + heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartThree]), + heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]), }; // Build animations from frames @@ -749,73 +845,6 @@ 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 diff --git a/dist/web/birb.embed.js b/dist/web/birb.embed.js index 2f4af0c..2f15ba2 100644 --- a/dist/web/birb.embed.js +++ b/dist/web/birb.embed.js @@ -605,6 +605,103 @@ } } + const HAT = { + TOP_HAT: 'top-hat' + }; + + /** + * @param {string[][]} spriteSheet + * @returns {{ base: Layer[], down: Layer[] }} + */ + function createHatLayers(spriteSheet) { + const hatLayers = { + base: [], + down: [] + }; + for (const hatName in HAT) { + const hatKey = HAT[hatName]; + const hatLayer = buildHatLayer(spriteSheet, hatKey, false); + const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1); + hatLayers.base.push(hatLayer); + hatLayers.down.push(downHatLayer); + } + return hatLayers; + } + + /** + * @param {string[][]} spriteSheet + * @param {string} hatName + * @param {boolean} [outlineBottom=false] + * @param {number} [yOffset=0] + * @returns {Layer} + */ + function 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); + } + /** * @typedef {keyof typeof Animations} AnimationType */ @@ -656,19 +753,18 @@ }; // Build hat layers - const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); - const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + const hatLayers = createHatLayers(hatSpriteSheet); // Build frames from layers this.frames = { - 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]), - heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), + base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, ...hatLayers.base]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, ...hatLayers.down]), + heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartOne]), + heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base,this.layers.heartTwo]), + heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartThree]), + heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]), }; // Build animations from frames @@ -735,73 +831,6 @@ 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 diff --git a/dist/web/birb.js b/dist/web/birb.js index 2f4af0c..2f15ba2 100644 --- a/dist/web/birb.js +++ b/dist/web/birb.js @@ -605,6 +605,103 @@ } } + const HAT = { + TOP_HAT: 'top-hat' + }; + + /** + * @param {string[][]} spriteSheet + * @returns {{ base: Layer[], down: Layer[] }} + */ + function createHatLayers(spriteSheet) { + const hatLayers = { + base: [], + down: [] + }; + for (const hatName in HAT) { + const hatKey = HAT[hatName]; + const hatLayer = buildHatLayer(spriteSheet, hatKey, false); + const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1); + hatLayers.base.push(hatLayer); + hatLayers.down.push(downHatLayer); + } + return hatLayers; + } + + /** + * @param {string[][]} spriteSheet + * @param {string} hatName + * @param {boolean} [outlineBottom=false] + * @param {number} [yOffset=0] + * @returns {Layer} + */ + function 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); + } + /** * @typedef {keyof typeof Animations} AnimationType */ @@ -656,19 +753,18 @@ }; // Build hat layers - const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); - const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + const hatLayers = createHatLayers(hatSpriteSheet); // Build frames from layers this.frames = { - 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]), - heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), + base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, ...hatLayers.base]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, ...hatLayers.down]), + heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartOne]), + heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base,this.layers.heartTwo]), + heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartThree]), + heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]), }; // Build animations from frames @@ -735,73 +831,6 @@ 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 diff --git a/src/birb.js b/src/birb.js index 55f56de..83f1490 100644 --- a/src/birb.js +++ b/src/birb.js @@ -3,6 +3,7 @@ import Layer from './animation/layer.js'; import Frame from './animation/frame.js'; import Anim from './animation/anim.js'; import { BirdType, PALETTE } from './animation/sprites.js'; +import { createHatLayers } from './hats.js'; /** * @typedef {keyof typeof Animations} AnimationType @@ -55,19 +56,18 @@ export class Birb { }; // Build hat layers - const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false); - const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1); + const hatLayers = createHatLayers(hatSpriteSheet); // Build frames from layers this.frames = { - 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]), - heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, this.layers.heartTwo]), + base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]), + headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]), + wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, ...hatLayers.base]), + wingsUp: new Frame([this.layers.down, this.layers.tuftDown, this.layers.wingsUp, ...hatLayers.down]), + heartOne: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartOne]), + heartTwo: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base,this.layers.heartTwo]), + heartThree: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartThree]), + heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]), }; // Build animations from frames @@ -134,73 +134,6 @@ 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 diff --git a/src/hats.js b/src/hats.js new file mode 100644 index 0000000..8d01392 --- /dev/null +++ b/src/hats.js @@ -0,0 +1,100 @@ +import Layer from "./animation/layer.js"; +import { PALETTE } from "./animation/sprites.js"; +import { getLayerPixels } from "./shared.js"; + +const HAT = { + TOP_HAT: 'top-hat' +}; + +/** + * @param {string[][]} spriteSheet + * @returns {{ base: Layer[], down: Layer[] }} + */ +export function createHatLayers(spriteSheet) { + const hatLayers = { + base: [], + down: [] + }; + for (const hatName in HAT) { + const hatKey = HAT[hatName]; + const hatLayer = buildHatLayer(spriteSheet, hatKey, false); + const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1); + hatLayers.base.push(hatLayer); + hatLayers.down.push(downHatLayer); + } + return hatLayers; +} + +/** + * @param {string[][]} spriteSheet + * @param {string} hatName + * @param {boolean} [outlineBottom=false] + * @param {number} [yOffset=0] + * @returns {Layer} + */ +function 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); +} \ No newline at end of file