mirror of
https://github.com/NohamR/Pocket-Bird.git
synced 2026-05-26 04:07:24 +00:00
Separate hat functions
This commit is contained in:
BIN
dist/extension.zip
vendored
BIN
dist/extension.zip
vendored
Binary file not shown.
275
dist/extension/birb.js
vendored
275
dist/extension/birb.js
vendored
@@ -605,137 +605,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const HAT = {
|
||||||
* @typedef {keyof typeof Animations} AnimationType
|
TOP_HAT: 'top-hat'
|
||||||
*/
|
};
|
||||||
|
|
||||||
const Animations = /** @type {const} */ ({
|
|
||||||
STILL: "STILL",
|
|
||||||
BOB: "BOB",
|
|
||||||
FLYING: "FLYING",
|
|
||||||
HEART: "HEART"
|
|
||||||
});
|
|
||||||
|
|
||||||
class Birb {
|
|
||||||
animStart = Date.now();
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
direction = Directions.RIGHT;
|
|
||||||
isAbsolutePositioned = false;
|
|
||||||
visible = true;
|
|
||||||
/** @type {AnimationType} */
|
|
||||||
currentAnimation = Animations.STILL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} birbCssScale
|
* @param {string[][]} spriteSheet
|
||||||
* @param {number} canvasPixelSize
|
* @returns {{ base: Layer[], down: Layer[] }}
|
||||||
* @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, hatSpriteSheet) {
|
function createHatLayers(spriteSheet) {
|
||||||
this.birbCssScale = birbCssScale;
|
const hatLayers = {
|
||||||
this.canvasPixelSize = canvasPixelSize;
|
base: [],
|
||||||
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
down: []
|
||||||
this.spriteWidth = spriteWidth;
|
|
||||||
this.spriteHeight = spriteHeight;
|
|
||||||
|
|
||||||
// Build layers from sprite sheet
|
|
||||||
this.layers = {
|
|
||||||
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
|
||||||
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
|
||||||
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
|
||||||
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
|
||||||
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
|
||||||
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
|
||||||
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
|
||||||
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
|
||||||
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
|
||||||
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
|
||||||
};
|
};
|
||||||
|
for (const hatName in HAT) {
|
||||||
// Build hat layers
|
const hatKey = HAT[hatName];
|
||||||
const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false);
|
const hatLayer = buildHatLayer(spriteSheet, hatKey, false);
|
||||||
const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1);
|
const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1);
|
||||||
|
hatLayers.base.push(hatLayer);
|
||||||
// Build frames from layers
|
hatLayers.down.push(downHatLayer);
|
||||||
this.frames = {
|
}
|
||||||
base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]),
|
return hatLayers;
|
||||||
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]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build animations from frames
|
|
||||||
this.animations = {
|
|
||||||
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
|
||||||
[Animations.BOB]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.headDown
|
|
||||||
], [
|
|
||||||
420,
|
|
||||||
420
|
|
||||||
]),
|
|
||||||
[Animations.FLYING]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.wingsUp,
|
|
||||||
this.frames.headDown,
|
|
||||||
this.frames.wingsDown,
|
|
||||||
], [
|
|
||||||
30,
|
|
||||||
80,
|
|
||||||
30,
|
|
||||||
60,
|
|
||||||
]),
|
|
||||||
[Animations.HEART]: new Anim([
|
|
||||||
this.frames.heartOne,
|
|
||||||
this.frames.heartTwo,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
], [
|
|
||||||
60,
|
|
||||||
80,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
], false),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create canvas element
|
|
||||||
this.canvas = document.createElement("canvas");
|
|
||||||
this.canvas.id = "birb";
|
|
||||||
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
|
||||||
this.canvas.height = spriteHeight * canvasPixelSize;
|
|
||||||
|
|
||||||
this.ctx = this.canvas.getContext("2d");
|
|
||||||
|
|
||||||
// Append to document
|
|
||||||
document.body.appendChild(this.canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the current animation frame
|
* @param {string[][]} spriteSheet
|
||||||
* @param {BirdType} species The species color data
|
* @param {string} hatName
|
||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @param {boolean} [outlineBottom=false]
|
||||||
|
* @param {number} [yOffset=0]
|
||||||
|
* @returns {Layer}
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
function buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
||||||
const anim = this.animations[this.currentAnimation];
|
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
|
||||||
const LEFT_PADDING = 6;
|
const LEFT_PADDING = 6;
|
||||||
const RIGHT_PADDING = 14;
|
const RIGHT_PADDING = 14;
|
||||||
const TOP_PADDING = 4 + yOffset;
|
const TOP_PADDING = 4 + yOffset;
|
||||||
@@ -802,6 +702,135 @@
|
|||||||
return new Layer(paddedHatPixels);
|
return new Layer(paddedHatPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {keyof typeof Animations} AnimationType
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animations = /** @type {const} */ ({
|
||||||
|
STILL: "STILL",
|
||||||
|
BOB: "BOB",
|
||||||
|
FLYING: "FLYING",
|
||||||
|
HEART: "HEART"
|
||||||
|
});
|
||||||
|
|
||||||
|
class Birb {
|
||||||
|
animStart = Date.now();
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
direction = Directions.RIGHT;
|
||||||
|
isAbsolutePositioned = false;
|
||||||
|
visible = true;
|
||||||
|
/** @type {AnimationType} */
|
||||||
|
currentAnimation = Animations.STILL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} birbCssScale
|
||||||
|
* @param {number} canvasPixelSize
|
||||||
|
* @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, hatSpriteSheet) {
|
||||||
|
this.birbCssScale = birbCssScale;
|
||||||
|
this.canvasPixelSize = canvasPixelSize;
|
||||||
|
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
||||||
|
this.spriteWidth = spriteWidth;
|
||||||
|
this.spriteHeight = spriteHeight;
|
||||||
|
|
||||||
|
// Build layers from sprite sheet
|
||||||
|
this.layers = {
|
||||||
|
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
||||||
|
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
||||||
|
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
||||||
|
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
||||||
|
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
||||||
|
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
||||||
|
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
||||||
|
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
||||||
|
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
||||||
|
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build hat layers
|
||||||
|
const hatLayers = createHatLayers(hatSpriteSheet);
|
||||||
|
|
||||||
|
// Build frames from layers
|
||||||
|
this.frames = {
|
||||||
|
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
|
||||||
|
this.animations = {
|
||||||
|
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
||||||
|
[Animations.BOB]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.headDown
|
||||||
|
], [
|
||||||
|
420,
|
||||||
|
420
|
||||||
|
]),
|
||||||
|
[Animations.FLYING]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.wingsUp,
|
||||||
|
this.frames.headDown,
|
||||||
|
this.frames.wingsDown,
|
||||||
|
], [
|
||||||
|
30,
|
||||||
|
80,
|
||||||
|
30,
|
||||||
|
60,
|
||||||
|
]),
|
||||||
|
[Animations.HEART]: new Anim([
|
||||||
|
this.frames.heartOne,
|
||||||
|
this.frames.heartTwo,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
], [
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
], false),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas element
|
||||||
|
this.canvas = document.createElement("canvas");
|
||||||
|
this.canvas.id = "birb";
|
||||||
|
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
||||||
|
this.canvas.height = spriteHeight * canvasPixelSize;
|
||||||
|
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Append to document
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the current animation frame
|
||||||
|
* @param {BirdType} species The species color data
|
||||||
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
|
*/
|
||||||
|
draw(species) {
|
||||||
|
const anim = this.animations[this.currentAnimation];
|
||||||
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {AnimationType} The current animation key
|
* @returns {AnimationType} The current animation key
|
||||||
|
|||||||
275
dist/obsidian/main.js
vendored
275
dist/obsidian/main.js
vendored
@@ -610,137 +610,37 @@ module.exports = class PocketBird extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const HAT = {
|
||||||
* @typedef {keyof typeof Animations} AnimationType
|
TOP_HAT: 'top-hat'
|
||||||
*/
|
};
|
||||||
|
|
||||||
const Animations = /** @type {const} */ ({
|
|
||||||
STILL: "STILL",
|
|
||||||
BOB: "BOB",
|
|
||||||
FLYING: "FLYING",
|
|
||||||
HEART: "HEART"
|
|
||||||
});
|
|
||||||
|
|
||||||
class Birb {
|
|
||||||
animStart = Date.now();
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
direction = Directions.RIGHT;
|
|
||||||
isAbsolutePositioned = false;
|
|
||||||
visible = true;
|
|
||||||
/** @type {AnimationType} */
|
|
||||||
currentAnimation = Animations.STILL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} birbCssScale
|
* @param {string[][]} spriteSheet
|
||||||
* @param {number} canvasPixelSize
|
* @returns {{ base: Layer[], down: Layer[] }}
|
||||||
* @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, hatSpriteSheet) {
|
function createHatLayers(spriteSheet) {
|
||||||
this.birbCssScale = birbCssScale;
|
const hatLayers = {
|
||||||
this.canvasPixelSize = canvasPixelSize;
|
base: [],
|
||||||
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
down: []
|
||||||
this.spriteWidth = spriteWidth;
|
|
||||||
this.spriteHeight = spriteHeight;
|
|
||||||
|
|
||||||
// Build layers from sprite sheet
|
|
||||||
this.layers = {
|
|
||||||
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
|
||||||
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
|
||||||
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
|
||||||
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
|
||||||
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
|
||||||
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
|
||||||
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
|
||||||
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
|
||||||
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
|
||||||
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
|
||||||
};
|
};
|
||||||
|
for (const hatName in HAT) {
|
||||||
// Build hat layers
|
const hatKey = HAT[hatName];
|
||||||
const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false);
|
const hatLayer = buildHatLayer(spriteSheet, hatKey, false);
|
||||||
const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1);
|
const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1);
|
||||||
|
hatLayers.base.push(hatLayer);
|
||||||
// Build frames from layers
|
hatLayers.down.push(downHatLayer);
|
||||||
this.frames = {
|
}
|
||||||
base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]),
|
return hatLayers;
|
||||||
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]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build animations from frames
|
|
||||||
this.animations = {
|
|
||||||
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
|
||||||
[Animations.BOB]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.headDown
|
|
||||||
], [
|
|
||||||
420,
|
|
||||||
420
|
|
||||||
]),
|
|
||||||
[Animations.FLYING]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.wingsUp,
|
|
||||||
this.frames.headDown,
|
|
||||||
this.frames.wingsDown,
|
|
||||||
], [
|
|
||||||
30,
|
|
||||||
80,
|
|
||||||
30,
|
|
||||||
60,
|
|
||||||
]),
|
|
||||||
[Animations.HEART]: new Anim([
|
|
||||||
this.frames.heartOne,
|
|
||||||
this.frames.heartTwo,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
], [
|
|
||||||
60,
|
|
||||||
80,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
], false),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create canvas element
|
|
||||||
this.canvas = document.createElement("canvas");
|
|
||||||
this.canvas.id = "birb";
|
|
||||||
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
|
||||||
this.canvas.height = spriteHeight * canvasPixelSize;
|
|
||||||
|
|
||||||
this.ctx = this.canvas.getContext("2d");
|
|
||||||
|
|
||||||
// Append to document
|
|
||||||
document.body.appendChild(this.canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the current animation frame
|
* @param {string[][]} spriteSheet
|
||||||
* @param {BirdType} species The species color data
|
* @param {string} hatName
|
||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @param {boolean} [outlineBottom=false]
|
||||||
|
* @param {number} [yOffset=0]
|
||||||
|
* @returns {Layer}
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
function buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
||||||
const anim = this.animations[this.currentAnimation];
|
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
|
||||||
const LEFT_PADDING = 6;
|
const LEFT_PADDING = 6;
|
||||||
const RIGHT_PADDING = 14;
|
const RIGHT_PADDING = 14;
|
||||||
const TOP_PADDING = 4 + yOffset;
|
const TOP_PADDING = 4 + yOffset;
|
||||||
@@ -807,6 +707,135 @@ module.exports = class PocketBird extends Plugin {
|
|||||||
return new Layer(paddedHatPixels);
|
return new Layer(paddedHatPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {keyof typeof Animations} AnimationType
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animations = /** @type {const} */ ({
|
||||||
|
STILL: "STILL",
|
||||||
|
BOB: "BOB",
|
||||||
|
FLYING: "FLYING",
|
||||||
|
HEART: "HEART"
|
||||||
|
});
|
||||||
|
|
||||||
|
class Birb {
|
||||||
|
animStart = Date.now();
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
direction = Directions.RIGHT;
|
||||||
|
isAbsolutePositioned = false;
|
||||||
|
visible = true;
|
||||||
|
/** @type {AnimationType} */
|
||||||
|
currentAnimation = Animations.STILL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} birbCssScale
|
||||||
|
* @param {number} canvasPixelSize
|
||||||
|
* @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, hatSpriteSheet) {
|
||||||
|
this.birbCssScale = birbCssScale;
|
||||||
|
this.canvasPixelSize = canvasPixelSize;
|
||||||
|
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
||||||
|
this.spriteWidth = spriteWidth;
|
||||||
|
this.spriteHeight = spriteHeight;
|
||||||
|
|
||||||
|
// Build layers from sprite sheet
|
||||||
|
this.layers = {
|
||||||
|
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
||||||
|
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
||||||
|
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
||||||
|
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
||||||
|
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
||||||
|
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
||||||
|
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
||||||
|
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
||||||
|
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
||||||
|
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build hat layers
|
||||||
|
const hatLayers = createHatLayers(hatSpriteSheet);
|
||||||
|
|
||||||
|
// Build frames from layers
|
||||||
|
this.frames = {
|
||||||
|
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
|
||||||
|
this.animations = {
|
||||||
|
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
||||||
|
[Animations.BOB]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.headDown
|
||||||
|
], [
|
||||||
|
420,
|
||||||
|
420
|
||||||
|
]),
|
||||||
|
[Animations.FLYING]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.wingsUp,
|
||||||
|
this.frames.headDown,
|
||||||
|
this.frames.wingsDown,
|
||||||
|
], [
|
||||||
|
30,
|
||||||
|
80,
|
||||||
|
30,
|
||||||
|
60,
|
||||||
|
]),
|
||||||
|
[Animations.HEART]: new Anim([
|
||||||
|
this.frames.heartOne,
|
||||||
|
this.frames.heartTwo,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
], [
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
], false),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas element
|
||||||
|
this.canvas = document.createElement("canvas");
|
||||||
|
this.canvas.id = "birb";
|
||||||
|
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
||||||
|
this.canvas.height = spriteHeight * canvasPixelSize;
|
||||||
|
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Append to document
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the current animation frame
|
||||||
|
* @param {BirdType} species The species color data
|
||||||
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
|
*/
|
||||||
|
draw(species) {
|
||||||
|
const anim = this.animations[this.currentAnimation];
|
||||||
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {AnimationType} The current animation key
|
* @returns {AnimationType} The current animation key
|
||||||
|
|||||||
275
dist/userscript/birb.user.js
vendored
275
dist/userscript/birb.user.js
vendored
@@ -619,137 +619,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const HAT = {
|
||||||
* @typedef {keyof typeof Animations} AnimationType
|
TOP_HAT: 'top-hat'
|
||||||
*/
|
};
|
||||||
|
|
||||||
const Animations = /** @type {const} */ ({
|
|
||||||
STILL: "STILL",
|
|
||||||
BOB: "BOB",
|
|
||||||
FLYING: "FLYING",
|
|
||||||
HEART: "HEART"
|
|
||||||
});
|
|
||||||
|
|
||||||
class Birb {
|
|
||||||
animStart = Date.now();
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
direction = Directions.RIGHT;
|
|
||||||
isAbsolutePositioned = false;
|
|
||||||
visible = true;
|
|
||||||
/** @type {AnimationType} */
|
|
||||||
currentAnimation = Animations.STILL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} birbCssScale
|
* @param {string[][]} spriteSheet
|
||||||
* @param {number} canvasPixelSize
|
* @returns {{ base: Layer[], down: Layer[] }}
|
||||||
* @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, hatSpriteSheet) {
|
function createHatLayers(spriteSheet) {
|
||||||
this.birbCssScale = birbCssScale;
|
const hatLayers = {
|
||||||
this.canvasPixelSize = canvasPixelSize;
|
base: [],
|
||||||
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
down: []
|
||||||
this.spriteWidth = spriteWidth;
|
|
||||||
this.spriteHeight = spriteHeight;
|
|
||||||
|
|
||||||
// Build layers from sprite sheet
|
|
||||||
this.layers = {
|
|
||||||
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
|
||||||
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
|
||||||
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
|
||||||
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
|
||||||
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
|
||||||
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
|
||||||
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
|
||||||
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
|
||||||
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
|
||||||
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
|
||||||
};
|
};
|
||||||
|
for (const hatName in HAT) {
|
||||||
// Build hat layers
|
const hatKey = HAT[hatName];
|
||||||
const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false);
|
const hatLayer = buildHatLayer(spriteSheet, hatKey, false);
|
||||||
const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1);
|
const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1);
|
||||||
|
hatLayers.base.push(hatLayer);
|
||||||
// Build frames from layers
|
hatLayers.down.push(downHatLayer);
|
||||||
this.frames = {
|
}
|
||||||
base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]),
|
return hatLayers;
|
||||||
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]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build animations from frames
|
|
||||||
this.animations = {
|
|
||||||
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
|
||||||
[Animations.BOB]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.headDown
|
|
||||||
], [
|
|
||||||
420,
|
|
||||||
420
|
|
||||||
]),
|
|
||||||
[Animations.FLYING]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.wingsUp,
|
|
||||||
this.frames.headDown,
|
|
||||||
this.frames.wingsDown,
|
|
||||||
], [
|
|
||||||
30,
|
|
||||||
80,
|
|
||||||
30,
|
|
||||||
60,
|
|
||||||
]),
|
|
||||||
[Animations.HEART]: new Anim([
|
|
||||||
this.frames.heartOne,
|
|
||||||
this.frames.heartTwo,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
], [
|
|
||||||
60,
|
|
||||||
80,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
], false),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create canvas element
|
|
||||||
this.canvas = document.createElement("canvas");
|
|
||||||
this.canvas.id = "birb";
|
|
||||||
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
|
||||||
this.canvas.height = spriteHeight * canvasPixelSize;
|
|
||||||
|
|
||||||
this.ctx = this.canvas.getContext("2d");
|
|
||||||
|
|
||||||
// Append to document
|
|
||||||
document.body.appendChild(this.canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the current animation frame
|
* @param {string[][]} spriteSheet
|
||||||
* @param {BirdType} species The species color data
|
* @param {string} hatName
|
||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @param {boolean} [outlineBottom=false]
|
||||||
|
* @param {number} [yOffset=0]
|
||||||
|
* @returns {Layer}
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
function buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
||||||
const anim = this.animations[this.currentAnimation];
|
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
|
||||||
const LEFT_PADDING = 6;
|
const LEFT_PADDING = 6;
|
||||||
const RIGHT_PADDING = 14;
|
const RIGHT_PADDING = 14;
|
||||||
const TOP_PADDING = 4 + yOffset;
|
const TOP_PADDING = 4 + yOffset;
|
||||||
@@ -816,6 +716,135 @@
|
|||||||
return new Layer(paddedHatPixels);
|
return new Layer(paddedHatPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {keyof typeof Animations} AnimationType
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animations = /** @type {const} */ ({
|
||||||
|
STILL: "STILL",
|
||||||
|
BOB: "BOB",
|
||||||
|
FLYING: "FLYING",
|
||||||
|
HEART: "HEART"
|
||||||
|
});
|
||||||
|
|
||||||
|
class Birb {
|
||||||
|
animStart = Date.now();
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
direction = Directions.RIGHT;
|
||||||
|
isAbsolutePositioned = false;
|
||||||
|
visible = true;
|
||||||
|
/** @type {AnimationType} */
|
||||||
|
currentAnimation = Animations.STILL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} birbCssScale
|
||||||
|
* @param {number} canvasPixelSize
|
||||||
|
* @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, hatSpriteSheet) {
|
||||||
|
this.birbCssScale = birbCssScale;
|
||||||
|
this.canvasPixelSize = canvasPixelSize;
|
||||||
|
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
||||||
|
this.spriteWidth = spriteWidth;
|
||||||
|
this.spriteHeight = spriteHeight;
|
||||||
|
|
||||||
|
// Build layers from sprite sheet
|
||||||
|
this.layers = {
|
||||||
|
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
||||||
|
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
||||||
|
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
||||||
|
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
||||||
|
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
||||||
|
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
||||||
|
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
||||||
|
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
||||||
|
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
||||||
|
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build hat layers
|
||||||
|
const hatLayers = createHatLayers(hatSpriteSheet);
|
||||||
|
|
||||||
|
// Build frames from layers
|
||||||
|
this.frames = {
|
||||||
|
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
|
||||||
|
this.animations = {
|
||||||
|
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
||||||
|
[Animations.BOB]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.headDown
|
||||||
|
], [
|
||||||
|
420,
|
||||||
|
420
|
||||||
|
]),
|
||||||
|
[Animations.FLYING]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.wingsUp,
|
||||||
|
this.frames.headDown,
|
||||||
|
this.frames.wingsDown,
|
||||||
|
], [
|
||||||
|
30,
|
||||||
|
80,
|
||||||
|
30,
|
||||||
|
60,
|
||||||
|
]),
|
||||||
|
[Animations.HEART]: new Anim([
|
||||||
|
this.frames.heartOne,
|
||||||
|
this.frames.heartTwo,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
], [
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
], false),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas element
|
||||||
|
this.canvas = document.createElement("canvas");
|
||||||
|
this.canvas.id = "birb";
|
||||||
|
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
||||||
|
this.canvas.height = spriteHeight * canvasPixelSize;
|
||||||
|
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Append to document
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the current animation frame
|
||||||
|
* @param {BirdType} species The species color data
|
||||||
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
|
*/
|
||||||
|
draw(species) {
|
||||||
|
const anim = this.animations[this.currentAnimation];
|
||||||
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {AnimationType} The current animation key
|
* @returns {AnimationType} The current animation key
|
||||||
|
|||||||
275
dist/web/birb.embed.js
vendored
275
dist/web/birb.embed.js
vendored
@@ -605,137 +605,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const HAT = {
|
||||||
* @typedef {keyof typeof Animations} AnimationType
|
TOP_HAT: 'top-hat'
|
||||||
*/
|
};
|
||||||
|
|
||||||
const Animations = /** @type {const} */ ({
|
|
||||||
STILL: "STILL",
|
|
||||||
BOB: "BOB",
|
|
||||||
FLYING: "FLYING",
|
|
||||||
HEART: "HEART"
|
|
||||||
});
|
|
||||||
|
|
||||||
class Birb {
|
|
||||||
animStart = Date.now();
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
direction = Directions.RIGHT;
|
|
||||||
isAbsolutePositioned = false;
|
|
||||||
visible = true;
|
|
||||||
/** @type {AnimationType} */
|
|
||||||
currentAnimation = Animations.STILL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} birbCssScale
|
* @param {string[][]} spriteSheet
|
||||||
* @param {number} canvasPixelSize
|
* @returns {{ base: Layer[], down: Layer[] }}
|
||||||
* @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, hatSpriteSheet) {
|
function createHatLayers(spriteSheet) {
|
||||||
this.birbCssScale = birbCssScale;
|
const hatLayers = {
|
||||||
this.canvasPixelSize = canvasPixelSize;
|
base: [],
|
||||||
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
down: []
|
||||||
this.spriteWidth = spriteWidth;
|
|
||||||
this.spriteHeight = spriteHeight;
|
|
||||||
|
|
||||||
// Build layers from sprite sheet
|
|
||||||
this.layers = {
|
|
||||||
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
|
||||||
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
|
||||||
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
|
||||||
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
|
||||||
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
|
||||||
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
|
||||||
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
|
||||||
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
|
||||||
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
|
||||||
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
|
||||||
};
|
};
|
||||||
|
for (const hatName in HAT) {
|
||||||
// Build hat layers
|
const hatKey = HAT[hatName];
|
||||||
const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false);
|
const hatLayer = buildHatLayer(spriteSheet, hatKey, false);
|
||||||
const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1);
|
const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1);
|
||||||
|
hatLayers.base.push(hatLayer);
|
||||||
// Build frames from layers
|
hatLayers.down.push(downHatLayer);
|
||||||
this.frames = {
|
}
|
||||||
base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]),
|
return hatLayers;
|
||||||
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]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build animations from frames
|
|
||||||
this.animations = {
|
|
||||||
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
|
||||||
[Animations.BOB]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.headDown
|
|
||||||
], [
|
|
||||||
420,
|
|
||||||
420
|
|
||||||
]),
|
|
||||||
[Animations.FLYING]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.wingsUp,
|
|
||||||
this.frames.headDown,
|
|
||||||
this.frames.wingsDown,
|
|
||||||
], [
|
|
||||||
30,
|
|
||||||
80,
|
|
||||||
30,
|
|
||||||
60,
|
|
||||||
]),
|
|
||||||
[Animations.HEART]: new Anim([
|
|
||||||
this.frames.heartOne,
|
|
||||||
this.frames.heartTwo,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
], [
|
|
||||||
60,
|
|
||||||
80,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
], false),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create canvas element
|
|
||||||
this.canvas = document.createElement("canvas");
|
|
||||||
this.canvas.id = "birb";
|
|
||||||
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
|
||||||
this.canvas.height = spriteHeight * canvasPixelSize;
|
|
||||||
|
|
||||||
this.ctx = this.canvas.getContext("2d");
|
|
||||||
|
|
||||||
// Append to document
|
|
||||||
document.body.appendChild(this.canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the current animation frame
|
* @param {string[][]} spriteSheet
|
||||||
* @param {BirdType} species The species color data
|
* @param {string} hatName
|
||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @param {boolean} [outlineBottom=false]
|
||||||
|
* @param {number} [yOffset=0]
|
||||||
|
* @returns {Layer}
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
function buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
||||||
const anim = this.animations[this.currentAnimation];
|
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
|
||||||
const LEFT_PADDING = 6;
|
const LEFT_PADDING = 6;
|
||||||
const RIGHT_PADDING = 14;
|
const RIGHT_PADDING = 14;
|
||||||
const TOP_PADDING = 4 + yOffset;
|
const TOP_PADDING = 4 + yOffset;
|
||||||
@@ -802,6 +702,135 @@
|
|||||||
return new Layer(paddedHatPixels);
|
return new Layer(paddedHatPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {keyof typeof Animations} AnimationType
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animations = /** @type {const} */ ({
|
||||||
|
STILL: "STILL",
|
||||||
|
BOB: "BOB",
|
||||||
|
FLYING: "FLYING",
|
||||||
|
HEART: "HEART"
|
||||||
|
});
|
||||||
|
|
||||||
|
class Birb {
|
||||||
|
animStart = Date.now();
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
direction = Directions.RIGHT;
|
||||||
|
isAbsolutePositioned = false;
|
||||||
|
visible = true;
|
||||||
|
/** @type {AnimationType} */
|
||||||
|
currentAnimation = Animations.STILL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} birbCssScale
|
||||||
|
* @param {number} canvasPixelSize
|
||||||
|
* @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, hatSpriteSheet) {
|
||||||
|
this.birbCssScale = birbCssScale;
|
||||||
|
this.canvasPixelSize = canvasPixelSize;
|
||||||
|
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
||||||
|
this.spriteWidth = spriteWidth;
|
||||||
|
this.spriteHeight = spriteHeight;
|
||||||
|
|
||||||
|
// Build layers from sprite sheet
|
||||||
|
this.layers = {
|
||||||
|
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
||||||
|
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
||||||
|
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
||||||
|
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
||||||
|
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
||||||
|
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
||||||
|
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
||||||
|
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
||||||
|
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
||||||
|
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build hat layers
|
||||||
|
const hatLayers = createHatLayers(hatSpriteSheet);
|
||||||
|
|
||||||
|
// Build frames from layers
|
||||||
|
this.frames = {
|
||||||
|
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
|
||||||
|
this.animations = {
|
||||||
|
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
||||||
|
[Animations.BOB]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.headDown
|
||||||
|
], [
|
||||||
|
420,
|
||||||
|
420
|
||||||
|
]),
|
||||||
|
[Animations.FLYING]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.wingsUp,
|
||||||
|
this.frames.headDown,
|
||||||
|
this.frames.wingsDown,
|
||||||
|
], [
|
||||||
|
30,
|
||||||
|
80,
|
||||||
|
30,
|
||||||
|
60,
|
||||||
|
]),
|
||||||
|
[Animations.HEART]: new Anim([
|
||||||
|
this.frames.heartOne,
|
||||||
|
this.frames.heartTwo,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
], [
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
], false),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas element
|
||||||
|
this.canvas = document.createElement("canvas");
|
||||||
|
this.canvas.id = "birb";
|
||||||
|
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
||||||
|
this.canvas.height = spriteHeight * canvasPixelSize;
|
||||||
|
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Append to document
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the current animation frame
|
||||||
|
* @param {BirdType} species The species color data
|
||||||
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
|
*/
|
||||||
|
draw(species) {
|
||||||
|
const anim = this.animations[this.currentAnimation];
|
||||||
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {AnimationType} The current animation key
|
* @returns {AnimationType} The current animation key
|
||||||
|
|||||||
275
dist/web/birb.js
vendored
275
dist/web/birb.js
vendored
@@ -605,137 +605,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const HAT = {
|
||||||
* @typedef {keyof typeof Animations} AnimationType
|
TOP_HAT: 'top-hat'
|
||||||
*/
|
};
|
||||||
|
|
||||||
const Animations = /** @type {const} */ ({
|
|
||||||
STILL: "STILL",
|
|
||||||
BOB: "BOB",
|
|
||||||
FLYING: "FLYING",
|
|
||||||
HEART: "HEART"
|
|
||||||
});
|
|
||||||
|
|
||||||
class Birb {
|
|
||||||
animStart = Date.now();
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
direction = Directions.RIGHT;
|
|
||||||
isAbsolutePositioned = false;
|
|
||||||
visible = true;
|
|
||||||
/** @type {AnimationType} */
|
|
||||||
currentAnimation = Animations.STILL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} birbCssScale
|
* @param {string[][]} spriteSheet
|
||||||
* @param {number} canvasPixelSize
|
* @returns {{ base: Layer[], down: Layer[] }}
|
||||||
* @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, hatSpriteSheet) {
|
function createHatLayers(spriteSheet) {
|
||||||
this.birbCssScale = birbCssScale;
|
const hatLayers = {
|
||||||
this.canvasPixelSize = canvasPixelSize;
|
base: [],
|
||||||
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
down: []
|
||||||
this.spriteWidth = spriteWidth;
|
|
||||||
this.spriteHeight = spriteHeight;
|
|
||||||
|
|
||||||
// Build layers from sprite sheet
|
|
||||||
this.layers = {
|
|
||||||
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
|
||||||
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
|
||||||
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
|
||||||
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
|
||||||
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
|
||||||
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
|
||||||
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
|
||||||
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
|
||||||
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
|
||||||
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
|
||||||
};
|
};
|
||||||
|
for (const hatName in HAT) {
|
||||||
// Build hat layers
|
const hatKey = HAT[hatName];
|
||||||
const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false);
|
const hatLayer = buildHatLayer(spriteSheet, hatKey, false);
|
||||||
const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1);
|
const downHatLayer = buildHatLayer(spriteSheet, hatKey, false, 1);
|
||||||
|
hatLayers.base.push(hatLayer);
|
||||||
// Build frames from layers
|
hatLayers.down.push(downHatLayer);
|
||||||
this.frames = {
|
}
|
||||||
base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]),
|
return hatLayers;
|
||||||
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]),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build animations from frames
|
|
||||||
this.animations = {
|
|
||||||
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
|
||||||
[Animations.BOB]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.headDown
|
|
||||||
], [
|
|
||||||
420,
|
|
||||||
420
|
|
||||||
]),
|
|
||||||
[Animations.FLYING]: new Anim([
|
|
||||||
this.frames.base,
|
|
||||||
this.frames.wingsUp,
|
|
||||||
this.frames.headDown,
|
|
||||||
this.frames.wingsDown,
|
|
||||||
], [
|
|
||||||
30,
|
|
||||||
80,
|
|
||||||
30,
|
|
||||||
60,
|
|
||||||
]),
|
|
||||||
[Animations.HEART]: new Anim([
|
|
||||||
this.frames.heartOne,
|
|
||||||
this.frames.heartTwo,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
this.frames.heartThree,
|
|
||||||
this.frames.heartFour,
|
|
||||||
], [
|
|
||||||
60,
|
|
||||||
80,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
250,
|
|
||||||
], false),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create canvas element
|
|
||||||
this.canvas = document.createElement("canvas");
|
|
||||||
this.canvas.id = "birb";
|
|
||||||
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
|
||||||
this.canvas.height = spriteHeight * canvasPixelSize;
|
|
||||||
|
|
||||||
this.ctx = this.canvas.getContext("2d");
|
|
||||||
|
|
||||||
// Append to document
|
|
||||||
document.body.appendChild(this.canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw the current animation frame
|
* @param {string[][]} spriteSheet
|
||||||
* @param {BirdType} species The species color data
|
* @param {string} hatName
|
||||||
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
* @param {boolean} [outlineBottom=false]
|
||||||
|
* @param {number} [yOffset=0]
|
||||||
|
* @returns {Layer}
|
||||||
*/
|
*/
|
||||||
draw(species) {
|
function buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
||||||
const anim = this.animations[this.currentAnimation];
|
|
||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildHatLayer(spriteSheet, hatName, outlineBottom = false, yOffset = 0) {
|
|
||||||
const LEFT_PADDING = 6;
|
const LEFT_PADDING = 6;
|
||||||
const RIGHT_PADDING = 14;
|
const RIGHT_PADDING = 14;
|
||||||
const TOP_PADDING = 4 + yOffset;
|
const TOP_PADDING = 4 + yOffset;
|
||||||
@@ -802,6 +702,135 @@
|
|||||||
return new Layer(paddedHatPixels);
|
return new Layer(paddedHatPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {keyof typeof Animations} AnimationType
|
||||||
|
*/
|
||||||
|
|
||||||
|
const Animations = /** @type {const} */ ({
|
||||||
|
STILL: "STILL",
|
||||||
|
BOB: "BOB",
|
||||||
|
FLYING: "FLYING",
|
||||||
|
HEART: "HEART"
|
||||||
|
});
|
||||||
|
|
||||||
|
class Birb {
|
||||||
|
animStart = Date.now();
|
||||||
|
x = 0;
|
||||||
|
y = 0;
|
||||||
|
direction = Directions.RIGHT;
|
||||||
|
isAbsolutePositioned = false;
|
||||||
|
visible = true;
|
||||||
|
/** @type {AnimationType} */
|
||||||
|
currentAnimation = Animations.STILL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} birbCssScale
|
||||||
|
* @param {number} canvasPixelSize
|
||||||
|
* @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, hatSpriteSheet) {
|
||||||
|
this.birbCssScale = birbCssScale;
|
||||||
|
this.canvasPixelSize = canvasPixelSize;
|
||||||
|
this.windowPixelSize = canvasPixelSize * birbCssScale;
|
||||||
|
this.spriteWidth = spriteWidth;
|
||||||
|
this.spriteHeight = spriteHeight;
|
||||||
|
|
||||||
|
// Build layers from sprite sheet
|
||||||
|
this.layers = {
|
||||||
|
base: new Layer(getLayerPixels(spriteSheet, 0, this.spriteWidth)),
|
||||||
|
down: new Layer(getLayerPixels(spriteSheet, 1, this.spriteWidth)),
|
||||||
|
heartOne: new Layer(getLayerPixels(spriteSheet, 2, this.spriteWidth)),
|
||||||
|
heartTwo: new Layer(getLayerPixels(spriteSheet, 3, this.spriteWidth)),
|
||||||
|
heartThree: new Layer(getLayerPixels(spriteSheet, 4, this.spriteWidth)),
|
||||||
|
tuftBase: new Layer(getLayerPixels(spriteSheet, 5, this.spriteWidth), "tuft"),
|
||||||
|
tuftDown: new Layer(getLayerPixels(spriteSheet, 6, this.spriteWidth), "tuft"),
|
||||||
|
wingsUp: new Layer(getLayerPixels(spriteSheet, 7, this.spriteWidth)),
|
||||||
|
wingsDown: new Layer(getLayerPixels(spriteSheet, 8, this.spriteWidth)),
|
||||||
|
happyEye: new Layer(getLayerPixels(spriteSheet, 9, this.spriteWidth)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build hat layers
|
||||||
|
const hatLayers = createHatLayers(hatSpriteSheet);
|
||||||
|
|
||||||
|
// Build frames from layers
|
||||||
|
this.frames = {
|
||||||
|
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
|
||||||
|
this.animations = {
|
||||||
|
[Animations.STILL]: new Anim([this.frames.base], [1000]),
|
||||||
|
[Animations.BOB]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.headDown
|
||||||
|
], [
|
||||||
|
420,
|
||||||
|
420
|
||||||
|
]),
|
||||||
|
[Animations.FLYING]: new Anim([
|
||||||
|
this.frames.base,
|
||||||
|
this.frames.wingsUp,
|
||||||
|
this.frames.headDown,
|
||||||
|
this.frames.wingsDown,
|
||||||
|
], [
|
||||||
|
30,
|
||||||
|
80,
|
||||||
|
30,
|
||||||
|
60,
|
||||||
|
]),
|
||||||
|
[Animations.HEART]: new Anim([
|
||||||
|
this.frames.heartOne,
|
||||||
|
this.frames.heartTwo,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
this.frames.heartThree,
|
||||||
|
this.frames.heartFour,
|
||||||
|
], [
|
||||||
|
60,
|
||||||
|
80,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
250,
|
||||||
|
], false),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas element
|
||||||
|
this.canvas = document.createElement("canvas");
|
||||||
|
this.canvas.id = "birb";
|
||||||
|
this.canvas.width = this.frames.base.getPixels()[0].length * canvasPixelSize;
|
||||||
|
this.canvas.height = spriteHeight * canvasPixelSize;
|
||||||
|
|
||||||
|
this.ctx = this.canvas.getContext("2d");
|
||||||
|
|
||||||
|
// Append to document
|
||||||
|
document.body.appendChild(this.canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the current animation frame
|
||||||
|
* @param {BirdType} species The species color data
|
||||||
|
* @returns {boolean} Whether the animation has completed (for non-looping animations)
|
||||||
|
*/
|
||||||
|
draw(species) {
|
||||||
|
const anim = this.animations[this.currentAnimation];
|
||||||
|
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {AnimationType} The current animation key
|
* @returns {AnimationType} The current animation key
|
||||||
|
|||||||
87
src/birb.js
87
src/birb.js
@@ -3,6 +3,7 @@ import Layer from './animation/layer.js';
|
|||||||
import Frame from './animation/frame.js';
|
import Frame from './animation/frame.js';
|
||||||
import Anim from './animation/anim.js';
|
import Anim from './animation/anim.js';
|
||||||
import { BirdType, PALETTE } from './animation/sprites.js';
|
import { BirdType, PALETTE } from './animation/sprites.js';
|
||||||
|
import { createHatLayers } from './hats.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {keyof typeof Animations} AnimationType
|
* @typedef {keyof typeof Animations} AnimationType
|
||||||
@@ -55,19 +56,18 @@ export class Birb {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Build hat layers
|
// Build hat layers
|
||||||
const hatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false);
|
const hatLayers = createHatLayers(hatSpriteSheet);
|
||||||
const downHatLayer = this.buildHatLayer(hatSpriteSheet, "top-hat", false, 1);
|
|
||||||
|
|
||||||
// Build frames from layers
|
// Build frames from layers
|
||||||
this.frames = {
|
this.frames = {
|
||||||
base: new Frame([this.layers.base, this.layers.tuftBase, hatLayer]),
|
base: new Frame([this.layers.base, this.layers.tuftBase, ...hatLayers.base]),
|
||||||
headDown: new Frame([this.layers.down, this.layers.tuftDown, downHatLayer]),
|
headDown: new Frame([this.layers.down, this.layers.tuftDown, ...hatLayers.down]),
|
||||||
wingsDown: new Frame([this.layers.base, this.layers.tuftBase, this.layers.wingsDown, hatLayer]),
|
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, downHatLayer]),
|
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, this.layers.heartOne]),
|
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, this.layers.heartTwo]),
|
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, this.layers.heartThree]),
|
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, this.layers.heartTwo]),
|
heartFour: new Frame([this.layers.base, this.layers.tuftBase, this.layers.happyEye, ...hatLayers.base, this.layers.heartTwo]),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build animations from frames
|
// Build animations from frames
|
||||||
@@ -134,73 +134,6 @@ export class Birb {
|
|||||||
return anim.draw(this.ctx, this.direction, this.animStart, this.canvasPixelSize, species);
|
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
|
* @returns {AnimationType} The current animation key
|
||||||
|
|||||||
100
src/hats.js
Normal file
100
src/hats.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user