pixelbox
Version:
A framework to fast-prototype pixel-based games
308 lines (263 loc) • 11.5 kB
JavaScript
var Texture = require('./index');
var createCanvas = require('../domUtils/createCanvas');
var TILE_WIDTH = 8;
var TILE_HEIGHT = 8;
var TILES_PER_LINE = 16; // (in a tilesheet)
var PI2 = Math.PI / 2;
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
// NOTA: this is used by Tool
Texture.setTileSize = function (width, height) {
TILE_WIDTH = ~~width;
TILE_HEIGHT = ~~height;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype._init = function () {
this.canvas = createCanvas(this.width, this.height);
this.ctx = this.canvas.getContext('2d');
this.ctx.fillStyle = this.palette[0].string;
this.ctx.strokeStyle = this.palette[1].string;
this.resize(this.width, this.height);
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Resize texture. The texture is cleared.
* @param {number} width - texture new width in pixels
* @param {number} height - texture new height in pixels
* @returns {Texture} the texture itself
*/
Texture.prototype.resize = function (width, height) {
this.canvas.width = this.width = width;
this.canvas.height = this.height = height;
this.clear();
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Draw a tile in Texture using the current tilesheet.
* @param {number} tile - tile index (number between 0 and 255)
* @param {number} [x] - x position in pixels
* @param {number} [y] - y position in pixels
* @param {boolean} [flipH] - if set, the tile is horizontally flipped
* @param {boolean} [flipV] - if set, the tile is vertically flipped
* @param {boolean} [flipR] - if set, the tile rotated by 90 degree
*
* @returns {Texture} the texture itself
*/
Texture.prototype.sprite = function (tile, x, y, flipH, flipV, flipR) {
var sx = TILE_WIDTH * (tile % TILES_PER_LINE);
var sy = TILE_HEIGHT * ~~(tile / TILES_PER_LINE);
var ctx = this.ctx;
// add camera and round to the pixel
x = x || 0;
y = y || 0;
x = ~~Math.round(x - this.camera.x);
y = ~~Math.round(y - this.camera.y);
if (!flipH && !flipV && !flipR) {
ctx.drawImage(this.tilesheet, this.ox + sx, this.oy + sy, TILE_WIDTH, TILE_HEIGHT, x, y, TILE_WIDTH, TILE_HEIGHT);
return this;
}
ctx.save();
if (flipH) {
ctx.scale(-1, 1);
x = -x - TILE_WIDTH;
}
if (flipV) {
ctx.scale(1, -1);
y = -y - TILE_HEIGHT;
}
if (flipR) {
ctx.translate(x + TILE_HEIGHT, y);
ctx.rotate(PI2);
} else {
ctx.translate(x, y);
}
ctx.drawImage(this.tilesheet, this.ox + sx, this.oy + sy, TILE_WIDTH, TILE_HEIGHT, 0, 0, TILE_WIDTH, TILE_HEIGHT);
ctx.restore();
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Draw an Image (or anything drawable) in the texture
*
* @param {Image | Canvas | Texture | Map} element - thing to draw in the texture
* @param {number} [x] - x coordinate of where to draw the image. The value is offseted by Texture's camera position
* @param {number} [y] - y coordinate of where to draw the image. The value is offseted by Texture's camera position
* @param {boolean} [flipH] - if set, the image is horizontally flipped
* @param {boolean} [flipV] - if set, the image is vertically flipped
* @param {boolean} [flipR] - if set, the image rotated by 90 degree
*
* @returns {Texture} the texture itself
*/
Texture.prototype.draw = function (img, x, y, flipH, flipV, flipR) {
if (!img) {
console.error('Invalid asset');
return this;
}
if (img._isNineSlice) {
img._draw(this, x, y, flipH, flipV);
return this;
}
var hasFlip = flipH || flipV || flipR;
// spritesheet element
if (img._isSprite) {
var sprite = img;
img = sprite.img;
var sx = sprite.x;
var sy = sprite.y;
var sw = sprite.width;
var sh = sprite.height;
// TODO: pivot point
var px = ~~Math.round((x || 0) - this.camera.x);
var py = ~~Math.round((y || 0) - this.camera.y);
if (!hasFlip) {
// fast version
this.ctx.drawImage(img, sx, sy, sw, sh, px, py, sw, sh);
return this;
}
var ctx = this.ctx;
ctx.save();
if (flipR) {
if (flipH) { ctx.scale(-1, 1); px *= -1; px -= sh; }
if (flipV) { ctx.scale(1, -1); py *= -1; py -= sw; }
ctx.translate(px + sh, py);
ctx.rotate(PI2);
} else {
if (flipH) { ctx.scale(-1, 1); px *= -1; px -= sw; }
if (flipV) { ctx.scale(1, -1); py *= -1; py -= sh; }
ctx.translate(px, py);
}
ctx.drawImage(img, sx, sy, sw, sh, 0, 0, sw, sh);
ctx.restore();
return this;
}
// Image, Texture or TileMap
if (img._isTileMap) {
if (!img.texture) img._prepareTexture();
img = img.texture.canvas;
}
if (img._isTexture) img = img.canvas;
var px = ~~Math.round((x || 0) - this.camera.x);
var py = ~~Math.round((y || 0) - this.camera.y);
if (!hasFlip) {
// fast version
this.ctx.drawImage(img, px, py);
return this;
}
var ctx = this.ctx;
ctx.save();
if (flipR) {
if (flipH) { ctx.scale(-1, 1); px *= -1; px -= img.height; }
if (flipV) { ctx.scale(1, -1); py *= -1; py -= img.width; }
ctx.translate(px + img.height, py);
ctx.rotate(PI2);
} else {
if (flipH) { ctx.scale(-1, 1); px *= -1; px -= img.width; }
if (flipV) { ctx.scale(1, -1); py *= -1; py -= img.height; }
ctx.translate(px, py);
}
ctx.drawImage(img, 0, 0);
ctx.restore();
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
Texture.prototype.stretchDraw = function (asset, x, y, w, h) {
// spritesheet element
if (asset._isSprite) {
var sprite = asset;
asset = sprite.img;
var sx = sprite.x;
var sy = sprite.y;
var sw = sprite.width;
var sh = sprite.height;
var px = ~~Math.round((x || 0) - this.camera.x);
var py = ~~Math.round((y || 0) - this.camera.y);
this.ctx.drawImage(asset, sx, sy, sw, sh, px, py, w, h);
return this;
}
// Image or Texture
if (asset._isTexture) asset = asset.canvas;
var px = ~~Math.round((x || 0) - this.camera.x);
var py = ~~Math.round((y || 0) - this.camera.y);
this.ctx.drawImage(asset, 0, 0, asset.width, asset.height, px, py, w, h);
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Clear the whole texture (make it transparent)
* @returns {Texture} the texture itself
*/
Texture.prototype.clear = function () {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Clear texture with current paper color (CLear Screen)
* @returns {Texture} the texture itself
*/
Texture.prototype.cls = function () {
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Set PEN color index. This color is used when printing text in the texture,
* and for outline when drawing shapes.
* @param {number} p - pen color index in the palette
*
* @returns {Texture} the texture itself
*/
Texture.prototype.pen = function (p) {
this._pen = p % this.palette.length;
this.ctx.strokeStyle = this.palette[this._pen].string;
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Set PAPER color index. This color is used for fill when drawing shapes
* or when clearing the texture (cls)
* @param {number} p - pen color index in the palette
* @returns {Texture} the texture itself
*/
Texture.prototype.paper = function (p) {
this._paper = p % this.palette.length;
this.ctx.fillStyle = this.palette[this._paper].string;
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Draw an outlined rectangle, using current PEN color.
* Drawing is offset by Texture's camera.
* @param {number} x - x coordinate of rectangle upper left corner
* @param {number} y - y coordinate of rectangle upper left corner
* @param {number} w - rectangle width
* @param {number} h - rectangle height
*
* @returns {Texture} the texture itself
*/
Texture.prototype.rect = function (x, y, w, h) {
if (w <= 1 || h <= 1) {
var fill = this.ctx.fillStyle;
this.ctx.fillStyle = this.ctx.strokeStyle;
this.ctx.fillRect(~~(x - this.camera.x), ~~(y - this.camera.y), ~~w, ~~h);
this.ctx.fillStyle = fill;
return this;
}
this.ctx.strokeRect(~~(x - this.camera.x) + 0.5, ~~(y - this.camera.y) + 0.5, ~~(w - 1), ~~(h - 1));
return this;
};
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
/** Draw a filled rectangle, using current PAPER color.
* Drawing is offset by Texture's camera.
*
* The minimum size of a filled rectangle is 2.
* If `w` or `h` is smaller than 2, nothing is drawn.
*
* @param {number} x - x coordinate of rectangle upper left corner
* @param {number} y - y coordinate of rectangle upper left corner
* @param {number} w - rectangle width
* @param {number} h - rectangle height
*
* @returns {Texture} the texture itself
*/
Texture.prototype.rectf = function (x, y, w, h) {
this.ctx.fillRect(~~(x - this.camera.x), ~~(y - this.camera.y), ~~w, ~~h);
return this;
};
// legacy
Texture.prototype.rectfill = Texture.prototype.rectf;
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
// TODO: do we need the default tilesheet to have full dimensions?
Texture.prototype.tilesheet = createCanvas(TILE_WIDTH * TILES_PER_LINE, TILE_HEIGHT * TILES_PER_LINE);