mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-25 19:59:38 +00:00
Add hat item
This commit is contained in:
132
src/hats.js
132
src/hats.js
@@ -1,4 +1,6 @@
|
||||
import Layer from "./animation/layer.js";
|
||||
import Anim from "./animation/anim.js";
|
||||
import Frame from "./animation/frame.js";
|
||||
import Layer, { TAG } from "./animation/layer.js";
|
||||
import { PALETTE } from "./animation/sprites.js";
|
||||
import { getLayerPixels } from "./shared.js";
|
||||
|
||||
@@ -16,6 +18,7 @@ export const HAT = {
|
||||
FLOWER_HAT: "flower-hat"
|
||||
};
|
||||
|
||||
/** @type {{ [hatId: string]: { name: string, description: string } }} */
|
||||
export const HAT_METADATA = {
|
||||
[HAT.NONE]: {
|
||||
name: "Invisible Hat",
|
||||
@@ -71,62 +74,108 @@ export function createHatLayers(spriteSheet) {
|
||||
}
|
||||
const index = i - 1;
|
||||
const hatKey = HAT[hatName];
|
||||
const hatLayer = buildHatLayer(spriteSheet, hatKey, index, false);
|
||||
const downHatLayer = buildHatLayer(spriteSheet, hatKey, index, false, 1);
|
||||
const hatLayer = buildHatLayer(spriteSheet, hatKey, index);
|
||||
const downHatLayer = buildHatLayer(spriteSheet, hatKey, index, 1);
|
||||
hatLayers.base.push(hatLayer);
|
||||
hatLayers.down.push(downHatLayer);
|
||||
}
|
||||
return hatLayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[][]} spriteSheet
|
||||
* @param {string} hatId
|
||||
* @returns {Anim}
|
||||
*/
|
||||
export function createHatItemAnimation(hatId, spriteSheet) {
|
||||
const hatLayer = buildHatItemLayer(spriteSheet, hatId);
|
||||
const frames = [
|
||||
new Frame([hatLayer])
|
||||
];
|
||||
return new Anim(frames, [1000], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[][]} spriteSheet
|
||||
* @param {string} hatName
|
||||
* @param {number} hatIndex
|
||||
* @param {boolean} [outlineBottom=false]
|
||||
* @param {number} [yOffset=0]
|
||||
* @returns {Layer}
|
||||
*/
|
||||
function buildHatLayer(spriteSheet, hatName, hatIndex, outlineBottom = false, yOffset = 0) {
|
||||
function buildHatLayer(spriteSheet, hatName, hatIndex, yOffset = 0) {
|
||||
const LEFT_PADDING = 6;
|
||||
const RIGHT_PADDING = 14;
|
||||
const TOP_PADDING = 5 + yOffset;
|
||||
const BOTTOM_PADDING = Math.max(0, 15 - yOffset);
|
||||
|
||||
const hatPixels = getLayerPixels(spriteSheet, hatIndex, HAT_WIDTH);
|
||||
const paddedHatPixels = [];
|
||||
let hatPixels = getLayerPixels(spriteSheet, hatIndex, HAT_WIDTH);
|
||||
hatPixels = pad(hatPixels, TOP_PADDING, BOTTOM_PADDING, LEFT_PADDING, RIGHT_PADDING);
|
||||
hatPixels = drawOutline(hatPixels, false);
|
||||
|
||||
return new Layer(hatPixels, hatName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[][]} spriteSheet
|
||||
* @param {string} hatId
|
||||
* @returns {Layer}
|
||||
*/
|
||||
function buildHatItemLayer(spriteSheet, hatId) {
|
||||
if (hatId === HAT.NONE) {
|
||||
return new Layer([], TAG.DEFAULT);
|
||||
}
|
||||
const hatIndex = Object.keys(HAT).indexOf(hatId) - 1;
|
||||
let hatPixels = getLayerPixels(spriteSheet, hatIndex, HAT_WIDTH);
|
||||
hatPixels = pad(hatPixels, 1, 1, 1, 1);
|
||||
hatPixels = drawOutline(hatPixels, true);
|
||||
hatPixels = pushToBottom(hatPixels);
|
||||
return new Layer(hatPixels, TAG.DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add transparent padding around the pixel array
|
||||
* @param {string[][]} pixels
|
||||
* @param {number} top
|
||||
* @param {number} bottom
|
||||
* @param {number} left
|
||||
* @param {number} right
|
||||
* @returns {string[][]}
|
||||
*/
|
||||
function pad(pixels, top, bottom, left, right) {
|
||||
const paddedPixels = [];
|
||||
const rowLength = pixels[0].length + left + right;
|
||||
// Top padding
|
||||
for (let y = 0; y < TOP_PADDING; y++) {
|
||||
paddedHatPixels.push(Array(hatPixels[0].length + LEFT_PADDING + RIGHT_PADDING)
|
||||
.fill(PALETTE.TRANSPARENT)
|
||||
);
|
||||
for (let y = 0; y < top; y++) {
|
||||
paddedPixels.push(Array(rowLength).fill(PALETTE.TRANSPARENT));
|
||||
}
|
||||
// Left and right padding
|
||||
for (let y = 0; y < hatPixels.length; y++) {
|
||||
for (let y = 0; y < pixels.length; y++) {
|
||||
const row = [];
|
||||
for (let x = 0; x < LEFT_PADDING; x++) {
|
||||
for (let x = 0; x < left; x++) {
|
||||
row.push(PALETTE.TRANSPARENT);
|
||||
}
|
||||
|
||||
for (let x = 0; x < hatPixels[y].length; x++) {
|
||||
row.push(hatPixels[y][x]);
|
||||
for (let x = 0; x < pixels[y].length; x++) {
|
||||
row.push(pixels[y][x]);
|
||||
}
|
||||
|
||||
for (let x = 0; x < RIGHT_PADDING; x++) {
|
||||
for (let x = 0; x < right; x++) {
|
||||
row.push(PALETTE.TRANSPARENT);
|
||||
}
|
||||
|
||||
paddedHatPixels.push(row);
|
||||
paddedPixels.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)
|
||||
);
|
||||
for (let y = 0; y < bottom; y++) {
|
||||
paddedPixels.push(Array(rowLength).fill(PALETTE.TRANSPARENT));
|
||||
}
|
||||
return paddedPixels;
|
||||
}
|
||||
|
||||
// Add outline
|
||||
/**
|
||||
* Draw an outline around non-transparent pixels
|
||||
* @param {string[][]} pixels
|
||||
* @param {boolean} [outlineBottom=false]
|
||||
* @return {string[][]}
|
||||
*/
|
||||
function drawOutline(pixels, outlineBottom = false) {
|
||||
let neighborOffsets = [
|
||||
[-1, 0],
|
||||
[1, 0],
|
||||
@@ -137,19 +186,40 @@ function buildHatLayer(spriteSheet, hatName, hatIndex, outlineBottom = false, yO
|
||||
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];
|
||||
for (let y = 0; y < pixels.length; y++) {
|
||||
for (let x = 0; x < pixels[y].length; x++) {
|
||||
const pixel = pixels[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;
|
||||
if (newY >= 0 && newY < pixels.length && newX >= 0 && newX < pixels[newY].length && pixels[newY][newX] === PALETTE.TRANSPARENT) {
|
||||
pixels[newY][newX] = PALETTE.BORDER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Layer(paddedHatPixels, hatName);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim transparent rows from the bottom and push them to the top
|
||||
* @param {string[][]} pixels
|
||||
* @returns {string[][]}
|
||||
*/
|
||||
function pushToBottom(pixels) {
|
||||
let trimmedPixels = pixels.slice();
|
||||
let trimCount = 0;
|
||||
while (trimmedPixels.length > 1) {
|
||||
const firstRow = trimmedPixels[trimmedPixels.length - 1];
|
||||
if (firstRow.every(pixel => pixel === PALETTE.TRANSPARENT)) {
|
||||
trimmedPixels.pop();
|
||||
trimCount++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
trimmedPixels = pad(trimmedPixels, trimCount, 0, 0, 0);
|
||||
return trimmedPixels;
|
||||
}
|
||||
Reference in New Issue
Block a user