add sprite code

main
anna 1 year ago
parent 3930220c3e
commit f93b50e870
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -0,0 +1,153 @@
import { Schema as JSONSchema, validate as validateJSON } from "./json";
/**
* JSON schema for a single sprite.
*/
interface ISpriteData {
/** URL to this tile image, appended to `IPaletteData.baseurl`. */
url: string,
/**
* All edge sections and their material IDs.
* Edges are divided into `IPaletteData.sections` sections,
* and their individual materials are stored here in clockwise order.
*
* The illustration below shows the indices of material IDs for
* 3-section edges.
*
* ```
* 0 1 2
* + - - - +
* 11 | | 3
* 10 | tile | 4
* 9 | | 5
* + - - - +
* 8 7 6
* ```
*/
edges: number[],
}
/**
* JSON schema for `/dist/sprites.json`
*/
export interface IPaletteData {
/**
* Absolute base URL prepended to all tile URLs.
* Must not end with a trailing slash.
*/
baseurl: string,
/**
* Number of sections per edge.
* The `ISprite.edges` array must be exactly 4 times as long as this value
* (as in the four edges of a quadratic tile).
*/
sections: number,
/** Array of all tile prototypes. */
sprites: ISpriteData[],
}
/**
* Stores and renders a single type of tile.
* Actual `Tile`s refer to instances of this class for their data.
*/
export class Sprite {
private readonly sections: number;
private readonly edges: number[];
private bitmap?: ImageBitmap;
public constructor(sections: number, baseurl: string, proto: ISpriteData) {
this.sections = sections;
if (proto.edges.length !== sections * 4) {
throw new TypeError("Invalid edge count");
}
this.edges = proto.edges;
this.fetchBitmapAsync(baseurl + proto.url)
.then(i => this.bitmap = i)
.catch(e => console.error(e));
}
public matches(ownDir: Direction, other: Sprite): boolean {
if (other.sections !== this.sections) {
return false;
}
const ownStart = ownDir * this.sections;
const otherDir: Direction = (ownDir + 2) % 4;
const otherStart = otherDir * other.sections;
let ownEdge = this.edges.slice(ownStart, ownStart + this.sections);
let otherEdge = other.edges.slice(otherStart, otherStart + other.sections);
return !ownEdge.some((v, i) => otherEdge[other.sections - i - 1] !== v);
}
/**
* Draw this sprite to the screen at the specified position and rotation.
*
* @param context The context to draw the sprite in
* @param dx horizontal offset in pixels
* @param dy vertical offset in pixels
* @param orientation If specified, rotate the sprite such that the north
* facing edge points to the specified direction.
*/
public draw(context: CanvasRenderingContext2D, dx: number, dy: number, orientation?: Direction) {
if (this.bitmap !== undefined) {
if (orientation === undefined || orientation === Direction.NORTH) {
context.drawImage(this.bitmap, dx, dy);
} else {
context.save();
const halfWidth = this.bitmap.width / 2;
const halfHeight = this.bitmap.height / 2;
context.translate(dx + halfWidth, dy + halfHeight);
context.rotate(Math.PI * (orientation / 2));
context.drawImage(this.bitmap, -halfWidth, -halfHeight);
context.restore();
}
}
}
private async fetchBitmapAsync(url: string): Promise<ImageBitmap> {
console.debug(`fetching bitmap ${url}`);
const response = await fetch(url);
const blob = await response.blob();
const image = await createImageBitmap(blob);
console.debug(`fetched and parsed bitmap ${url}`);
return image;
}
}
export const enum Direction {
NORTH = 0,
EAST = 1,
SOUTH = 2,
WEST = 3,
}
export class Palette {
private readonly sections: number;
public readonly sprites: Sprite[];
public constructor(proto: IPaletteData) {
this.sections = proto.sections;
const tiles = [];
for (let tile of proto.sprites) {
tiles.push(new Sprite(proto.sections, proto.baseurl, tile));
}
this.sprites = tiles;
}
}
const PALETTE_SCHEMA: JSONSchema = {
baseurl: "string",
sections: "number",
sprites: [{
url: "string",
edges: ["number"],
}],
};
export async function fetchPalette(rootURL: string): Promise<Palette> {
const response = await fetch(rootURL + "/palette.json");
const json = await response.json();
return new Palette(validateJSON(json, PALETTE_SCHEMA));
}
Loading…
Cancel
Save